PDA

Просмотр полной версии : Universal Modbus Device или библиотека Modbus.lib?



Hemann
23.01.2017, 22:24
Коллеги, приветствую!

Подвернулась задачка с весами Тензо-М. Все бы хорошо, но есть один нюанс. В необходимом нам алгоритме работы этих весов (для тех кто в теме - режим вычитающего дозатора "AL3"), запуск дозирования производится неким битом, который управляется командой 5 протокола Modbus (Write Single Coil). В CoDeSys 2.3, в Modbus-мастере на вкладке "Конфигурация ПЛК" ни в одном из подэлементов ветви Universal Modbus Device нет этой пресловутой команды. Это получается, что нужно применить библиотеку Modbus.lib? В общем-то проблем с этим нет, но тогда прошу подсказать, как при помощи этой библиотеки передать в ведомое устройство число с плавающей точкой командой 16 (Write Multiple Registers)? Главным образом интересует как переформировать число из формата REAL в 4 байта буфера для MB_WR_REGS.
Заранее спасибо!

Евгений Дударев
24.01.2017, 14:43
в примере кусок по работе с 10 функцией и "как переформировать число из формата REAL в 4 байта буфера для MB_WR_REGS."

Атаман
28.01.2017, 22:56
Тоже пробую разобраться с modbus.lib.

На столе ПЛК-100 + МК110-8ДН-4Р

Пробую записать 48 регистр, а потом его прочитать. Не получается записать, ошибка сразу же (Err = 255). Конфигуратором пишет, читает. Не вижу затыка, хелп!



PROGRAM PLC_PRG
VAR
get1_modbus: MB_RD_HOLD_REGS; (*ôóíêöèÿ 03 - ÷òåíèå ïàðàìåòðà òèïà INT*)
get2_modbus: MB_RD_INP_REGS; (*ôóíêöèÿ 04 - ÷òåíèå òðåõ ïàðàìåòðîâ òèïà INT*)
get3_modbus: MB_RD_HOLD_REGS; (*ôóíêöèÿ 03 - ÷òåíèå ïàðàìåòðà òèïà Float*)
send2_modbus: MB_WR_REGS; (*ôóíêöèÿ 6 - çàïèñü ïàðàìåòðîâ*)

Buffer_out: ARRAY[0..255] OF BYTE; (* áàéòîâûé áóôåð äàííûõ *)
cmpl: BOOL;
port_opened: BYTE := 0;
Init: BOOL; (* ïðèçíàê èíèöèàëèçàöèè ïîëüçîâàòåëüñêîé ïðîãðàììû *)
Settings:COMSETTINGS; (* íàñòðîéêè ïîñëåäîâàòåëüíîãî ïîðòà *)
com_num: PORTS:=0; (*0 - RS-485, 1 - RS-232*)
enabl: BOOL:=TRUE; (*ñîñòîÿíèå ðàáîòû áëîêà*)
err: INT; (*íîìåð îøèáêè*)
TimeOut: TIME:=T#50ms; (*òàéìàóò*)
Exception: BYTE;
DataSize: WORD;
master1: BYTE:= 1;
TimeOut2 : TON;

t: DWORD; (*ïåðåìåííàÿ äëÿ îðãàíèçàöèè ñ÷åò÷èêà*)
A: WORD := 4; (*ñ÷åò÷èê*)
x:WORD; (*ñ÷èòàííîå çíà÷åíèå*)
x1: WORD; (*ïåðåìåííàÿ äëÿ çàïèñè ïî ñåòè*)
x2: WORD; (*ïåðåìåííàÿ äëÿ çàïèñè ïî ñåòè*)
x3: WORD; (*ïåðåìåííàÿ äëÿ çàïèñè ïî ñåòè*)
d: REAL; (*ñ÷èòàííîå çíà÷åíèå*)
f1:DINT:= 2;
ptr_f2:POINTER TO BYTE;
ptr_D:POINTER TO BYTE;

COM_SERVICE1: COM_SERVICE;
END_VAR


(*Óñòàíàâëèâàåì íàñòðîéêè COM-ïîðòà*)
IF port_opened=0 THEN
Settings.Port:=com_num; (*íîìåð COM-ïîðòà*)
Settings.dwBaudRate:=9600; (*ñêîðîñòü*)
Settings.byParity:=0;
Settings.dwTimeout:=0;
Settings.byStopBits:=0;
Settings.dwBufferSize:=0;
Settings.dwScan:=0;
END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );
(*Åñëè COM-ïîðò îòêðûò, òî ïåðåõîäèì ê ïðèåìó è ïåðåäà÷è äàííûõ *)
IF COM_SERVICE1.ready THEN
port_opened:=2;
END_IF

IF port_opened=2 THEN (*Óäà÷íî ïðîèíèöèàëèçèðîâàëè*)

CASE master1 OF

0: (* ôóíêöèÿ 03 èíò - ÔÁ ñ÷èòûâàåò çíà÷åíèå ïàðàìåòðà òèïà int èç ïðèáîðà ñ àäðåñîì162 â ðåãèñòð ñ íîìåðîì 48 ïî ïðîòîêîëó Modbus-RTU*)
get1_modbus(
Enable:=enabl , (* ðàçðåøåíèå ðàáîòû áëîêà *)
Mode:=MB_RTU, (*ðåæèì ïåðåäà÷è*)
DevAddr:=16 , (*àäðåñ*)
FirstAddr:=48 , (*íîìåð ðåãèñòðà*)
Quantity:=1, (*êîëè÷åñòâî ðåãèñòðîâ*)
ComHandle:=Settings.Port , (*íîìåð COM-ïîðòà*)
TimeOut:=TimeOut , (*Òàéìàóò T#50ms*)
Buffer:=Buffer_out, (* áóôåð äàííûõ *)
Complete=>cmpl , (* ñêîïèðîâàòü ïðèçíàê çàâåðøåíèÿ îïåðàöèè *)
Exception=>err , (* ñêîïèðîâàòü ðåãèñòð îøèáîê *)
ByteCnt=>DataSize ); (*êîë-âî ñ÷èòàííûõ áàéòîâ *)
(*åñëè óñòàíîâëåí ïðèçíàê çàâåðøåíèÿ îïåðàöèè, òî *)
IF cmpl THEN
IF err=0 THEN (*Åñëè íåò îøèáîê, òî ïîëó÷àåì äàííûå èç áóôåðà òèïà INT*)
x:=BYTE_TO_WORD(BUFFER_out[1]) OR SHL(BYTE_TO_WORD(BUFFER_out[0]),8);
enabl := FALSE;
END_IF

END_IF

1: (* ôóíêöèÿ 16 - çàïèñü ïàðàìåòðîâ òèïà Int (ðåãèñòð 48) ïðèáîð ñ àäðåñîì 16 *)
(*çàïèñü â áóôôåð ïàðàìåòðà òèïà INT*)
ptr_f2:=ADR(A);
Buffer_out[1]:=ptr_f2^;
ptr_f2:=ptr_f2+1;
Buffer_out[0]:=ptr_f2^;

send2_modbus(
Enable:= enabl, (* ðàçðåøåíèå ðàáîòû áëîêà *)
Mode:=MB_RTU , (*ðåæèì ïåðåäà÷è*)
DevAddr:=16 , (*àäðåñ*)
FirstAddr:= 48, (*íîìåð ðåãèñòðà*)
Quantity:= 1, (*êîëè÷åñòâî çàïèñûâàåìûõ ðåãèñòðîâ*)
ComHandle:=Settings.Port ,(*íîìåð ñîì-ïîðòà*)
TimeOut:=TimeOut , (*òàéìàóò T#50ms*)
Buffer:=Buffer_out , (* áóôåð äàííûõ *)
Complete=>cmpl , (* ñêîïèðîâàòü ïðèçíàê çàâåðøåíèÿ îïåðàöèè *)
Exception=>err , (* ñêîïèðîâàòü ðåãèñòð îøèáîê *)
RegCnt=> DataSize); (*êîë-âî ñ÷èòàííûõ áàéòîâ *)
(*åñëè óñòàíîâëåí ïðèçíàê çàâåðøåíèÿ îïåðàöèè, òî *)
IF cmpl THEN
IF err =0 THEN
master1:=2;(*ïåðåõîäèì ê âûïîëíåíèþ ñëåäóþùåãî áëîêà*)
END_IF
END_IF

2:
TimeOut2(IN :=TRUE,PT:=T#5ms);
TimeOut2;
IF TimeOut2.Q THEN
master1 := 0;
END_IF

END_CASE

END_IF

capzap
29.01.2017, 09:33
а то что не получается записать определяете по ошибке или по физическим выходам, при записи я вобще не анализировал что возвращает функция

Атаман
29.01.2017, 09:46
а то что не получается записать определяете по ошибке или по физическим выходам, при записи я вобще не анализировал что возвращает функция

Подключил лампочку, поменял регистр на управление реле и убрал проверку на ошибку после send2_modbus. Всё работает.

Тогда попутный вопрос. Приборы овен в slave ничего не возвращают после получения команды на запись регистров, как в описании протокола модбас сказано?

capzap
29.01.2017, 09:54
опросите прибор с ПК и сразу станет ясно куда глядеть

Атаман
29.01.2017, 10:08
Нет конвертера пк-rs485.

В протоколе написано:
"6.12 16 (0x10) Write Multiple registers
This function code is used to write a block of contiguous registers (1 to 123 registers) in a
remote device.
The requested written values are specified in the request data field. Data is packed as two
bytes per register.
The normal response returns the function code, starting address, and quantity of registers
written. "

Может кто-нибудь проверял вот эти response ОВЕНы отправляют? с какой задержкой и т.п.?

Атаман
29.01.2017, 10:39
Прочитал комментарии в modbus.lib:


FUNCTION_BLOCK MB_WR_REGS
VAR_OUTPUT
Complete: BOOL; (* если = TRUE, то обмен завершен *)
Exception: BYTE; (* исключения протокола MODBUS или 0хFF - таймаут *)
RegCnt: BYTE; (* количество записанных регистров *)

То есть, если есть исключения, то посылаются они, а если нет то ничего?!
Приходит один comlete = true, а exception = 255 и regcnt = 0 (
То есть после записи регистра, обязательно нужно его читать для проверки?

Sergey666
29.01.2017, 11:25
В выше приведенном коде enabl не переключается в true , вообще пример дурацкий , писал видимо студент . Т.к по идее запускается блок при переходе из 0 в 1 и мало запустить , надо и сбросить , т.е вызвать блок с enabl=false .
А exception = 255 и regcnt = 0 означает что связи нет раб не ответил хозяину - раб не слышит , или не понимает :
1. Блок МВ110 настроен правильно ?
2. Com порт тот? Ports=0-485(1),Ports=2-485(2)
3. Проводки правильно соединены?
4. Человек , берущий такие задачки должен иметь преобразователь usb-485.

P.S
Ха-ха-ха , дошло , как до жирафа :- Батенька (Атаман) , если у вас нет преобразователя usb-485 , то как вы модуль Овен настраивали??? Он из коробочки Модбас не понимает!
Во-блин дедукция...

Атаман
29.01.2017, 11:37
В выше приведенном коде enabl не переключается в true , вообще пример дурацкий , писал видимо студент . Т.к по идее запускается блок при переходе из 0 в 1 и мало запустить , надо и сбросить , т.е вызвать блок с enabl=false .
А exception = 255 и regcnt = 0 означает что связи нет раб не ответил хозяину - раб не слышит , или не понимает :
1. Блок МВ110 настроен правильно ?
2. Com порт тот? Ports=0-485(1),Ports=2-485(2)
3. Проводки правильно соединены?
4. Человек , берущий такие задачки должен иметь преобразователь usb-485.


Сергей, я дальше написал ответы на вопросы. Сейчас всё работает, только также в ответ на send_modbus приходит error=255 и regcnt = 0, но регистр при этом записывается! Запихнул блоки из modbus.lib в мой проект для отладки.

error 255 (EXC_TO) устанавливается в MB_UNI_IO здесь:
(*кадр принят, контроль CRC*)
DataSize := DataSize - 2;
CrcReg := MB_CRC(ADR(DataBuf), DataSize);
IF DataBuf[DataSize] <> WORD_TO_BYTE(CrcReg) OR
DataBuf[DataSize + 1] <> WORD_TO_BYTE(SHR(CrcReg, 8)) THEN
Exception := EXC_TO;
.....

то есть что то не то с CRC я так понял?


добавлено:

CRC расчетное и в принятом пакете не совпадают. Это почему может быть?

Sergey666
29.01.2017, 11:58
Мда сейчас открыл свой проект с таким блоком и...да при записи в МВ110-8д4р контроля ошибок и записанных байт нету.
Просто я сначала читаю маску входов из модуля , там контроль связи работает , а следующим шагом пишу маску выходов и вот там да с контроля нет ...:(,по ходу да сейчас вспомнил то-же самое было - выход срабатывает , а ответ не правильный :mad: , просто забил на это и забыл , теперь вспомнил . ПО от Овена оно такое ... списифическое , путают робяты теплое с мягким постоянно...

Атаман
29.01.2017, 12:02
Мда сейчас открыл свой проект с таким блоком и...да при записи в МВ110-8д4р контроля ошибок и записанных байт нету.
Просто я сначала читаю маску входов из модуля , там контроль связи работает , а следующим шагом пишу маску выходов и вот там да с контроля нет ...:(,по ходу да сейчас вспомнил то-же самое было - выход срабатывает , а ответ не правильный :mad: , просто забил на это и забыл , теперь вспомнил . ПО от Овена оно такое ... списифическое , путают робяты теплое с мягким постоянно...

так вот ошибка таймаута при записи возникает, потому что CRC неправильное...

Это что же, проверять включились релюхи или нет нужно доп. опросом? И так миллисекунды считаю (

Sergey666
29.01.2017, 12:35
При опросе модуля МДВВ (опять же из своих старых проектов) все нормально,полагаю что и с му110-8р то-же все ОК , может быть что-то не так с самим 8д4р...х.з (кокой-то он ... кастрированный:mad:)

Если критично контроль срабатывания то придется читать маску выходов доп.блоком , а если нет ...-забить и забыть.

З.Ы
По стандарту ошибка CRC - это не отсутствие ответа .
Да и другие странности есть в работе этой библиотеки , для надежного обмена товарищи пишут используя SysLibCom , я тоже с этим согласен.

Атаман
29.01.2017, 12:51
По стандарту ошибка CRC - это не отсутствие ответа .


Как же не таймаут...


If the server receives the request, but detects a communication error (parity, LRC,
CRC, ...), no response is returned. The client program will eventually process a
timeout condition for the request.

Sergey666
29.01.2017, 13:29
Смотря кто курица , а кто яйцо...:), ПЛК у нас - клиент , а модуль - сервер .Если модуль включил релюху , значит принял команду и,должен был отправить ответ .
Щас от нефиг делать открыл библиотеку и... блок MB_UNI_IO - T_FRTU: TIME := T#3ms; (* межкадровый интервал режима RTU *) по ходу из-за этого на низких скоростях 19200 не работает
.

Ой ... та як воно тута...шедеврально , эдакий мастер... так и напрашивается на ...покусание сторожевой собакой:
(* Чтение / запись данных по протоколу MODBUS. Complete -
* выставляется на один цикл до начала следующего чтения / записи. *)
Complete := FALSE; (* снять признак готовности результата *)
Exception := EXC_NO; (* очистить регистр ошибок *)
IF Active = FALSE THEN (* проверить доступность нового цикла обмена *)
Active := TRUE; (* установить признак активности цикла обмена *)
WHILE SysComRead(ComHandle, ADR(DataBuf), SIZEOF(DataBuf), 0) <> 0 DO;
END_WHILE
Read := FALSE; (* снять признак чтения кадра *)
tonTimer(IN := FALSE); (* остановить таймер тайм-аута *)
END_IF

Овен в своем амплуа и репертуаре...

Атаман
29.01.2017, 14:51
(не помню какая, но в стоп-листе написано "оставить rs.dl=2 - на .... ?") - но все работало.

Вот это непонятно. Оставить задержку ответа 2мс или заменить на ....?

Sergey666
29.01.2017, 15:54
8Д4Р давно юзал, и помню была какая-то особенность (не помню какая, но в стоп-листе написано "оставить rs.dl=2 - на .... ?") - но все работало. И все отвечало со всеми CRC и т.д.

Кто такой "Стоп-лист" ? Ну подождал раб 2мс , типо с мислями собрался , нормальное поведение , даже и не тормоз...

Атаман
29.01.2017, 16:44
У меня rs.dl= 2. Все настройки по умолчанию... неправильное crc

Sergey666
29.01.2017, 16:57
У меня rs.dl= 2. Все настройки по умолчанию... неправильное crc

Надо его модбас-тестером опросить... в принципе завтра смогу .
А бибка modbus.lib -чудная какая-то...:confused:

Гарчев Евгений
30.01.2017, 16:46
про тонкости работы с modbus.lib уже не раз говорилось... Не надо формировать буфер на запись в каждом цикле, т.к. ф.б. на запись не успеет отработать за один цикл, а буфер используется один и тот же и на отправку запроса и на прием ответа. Таким образом, при ответе от слейв-устройства в буфере будет "каша" из ответа от устройства и данных, которые вы отправляете на него.

Sergey666
30.01.2017, 21:39
Так мы не против , Евгений , только примерчик ваш :

1: (* функция 16 - запись параметров типа Int (регистр 4) и Real (регистр 6) в прибор с адресом 2 *)
(*запись в буффер параметра типа INT*)
Buffer[1] := DINT_TO_BYTE(f1);
Buffer[0] := DINT_TO_BYTE( SHR(f1,8));
Buffer[3] := DINT_TO_BYTE( SHR(f1,16));
Buffer[2] := DINT_TO_BYTE( SHR(f1,24));

(*запись в буффер параметра типа Float*)
ptr_f2:=ADR(f2);
buffer[5] := ptr_f2^;
ptr_f2:=ptr_f2+1;
buffer[4] := ptr_f2^;
ptr_f2:=ptr_f2+1;
buffer[7] := ptr_f2^;
ptr_f2:=ptr_f2+1;
buffer[6] := ptr_f2^;

send2_modbus(
Enable:= enabl, (* разрешение работы блока *)
Mode:=MB_ASCII , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:= 4, (*номер регистра*)
Quantity:= 4, (*количество записываемых регистров*)
ComHandle:=Settings.Port ,(*номер сом-порта*)
TimeOut:=TimeOut , (*таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
RegCnt=> DataSize); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)
IF cmpl THEN
master1:=0;(*переходим к выполнению следующего блока*)
END_IF

END_CASE

IF enabl = FALSE THEN
enabl := TRUE;
END_IF
IF err <> 0 THEN
enabl := FALSE;
END_IF

Кто примеры пишет и ни фига не проверяет , а если проверяет то плохо ???
10 лет уже ПЛКшки выпускаете , а вилы и грабли все те-же , только понтов не меряно.
И описания для библиотек вместе (в одном месте!!!)с библиотеками и примерами нормальными не только на форуме должны быть , а на Оф.сайте в соответствующем и !!! легко находимом разделе .
А то шаришся по форуму , который похож сейчас на птичий базар , только что какашки не летят...
Щас тема подзатеряется и ... опять тот - же вопрос , те-же вилы,грабли...надоело!:mad:

Атаман
31.01.2017, 16:19
Эврика! ))

Спасибо всем за помощь. Евгений прав, буфер не надо каждый цикл переписывать и всё работает ) Хотя да.... писал с вашего примера

Boris_K
31.01.2017, 17:02
modbus.lib да, своеобразная, и в описалове к ней есть пара ошибок (в типах данных), но при должном подходе она работает нормально. В моём случае работает уже 3 года на объекте, полёт нормальный.


И описания для библиотек вместе (в одном месте!!!)с библиотеками и примерами нормальными не только на форуме должны быть , а на Оф.сайте в соответствующем и !!! легко находимом разделе .
А то шаришся по форуму , который похож сейчас на птичий базар , только что какашки не летят...
Щас тема подзатеряется и ... опять тот - же вопрос , те-же вилы,грабли...надоело!

С этим абсолютно солидарен.

Гарчев Евгений
01.02.2017, 10:56
С этим и я согласен, надеюсь приведем все в надлежащий вид, когда-нибудь ... только вот мне непонятно где кто какие понты разглядел???

Атаман
08.02.2017, 17:41
Добрый вечер )

Мысль переписать по modbus.lib возникла после безуспешной борьбы с панелью СМИ1 через конфигуратор. При передаче по протоколу Modbus-RTU (СМИ1 - slave) на панели иногда гас экран и шла ошибка 81. Через несколько секунд работа панели возобновлялась. Всё происходит случайным образом без какой-либо закономерности. Изменение параметров не помогало. Сейчас переписал всё на modbus.lib. Происходит все тоже самое с небольшим нюансом - в панели при таймауте гаснет экран и включается при остановке программы в ПЛК-100. То есть после нормальной работы в течение 5-30 минут гаснет экран и в цикле идут следующие кейсы с ошибкой 255 до тех пор пока не остановлю программу.



FUNCTION_BLOCK Modbus
VAR_IN_OUT
VesViklNew1,VesViklNew2,Tara:BOOL;
BigError:BOOL;
END_VAR
VAR_INPUT
Ves_Post2,Ves_Post1:REAL;
END_VAR
VAR_OUTPUT
err:INT;
VesVikl_Post1, VesVikl_Post2,VesTenzo: REAL;
END_VAR
VAR
get1_modbus: MB_RD_HOLD_REGS; (*функция 03 - чтение параметра типа INT*)
send2_modbus: MB_WR_REGS; (*функция 16 - запись параметров*)
com_ready1:BOOL;
Settings1:COMSETTINGS; (* настройки последовательного порта *)
com_num1: PORTS; (*0 - RS-485, 1 - RS-232*)
Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)
Buffer_ok: ARRAY[0..255] OF BYTE;
Buffer_error: ARRAY[0..255] OF BYTE;
TimeOut: TIME:=T#50ms; (*таймаут*)
cmpl:BOOL;
rejim, rejimerror :BYTE;
DataSize: WORD;
TimeOut2 : TON;
Send: R_TRIG;
ptr:POINTER TO BYTE;
COM_SERVICE1: COM_SERVICE;
x: REAL;
errorcount,errorcount0,errorcount1,errorcount2,err orcount3,errorcount4,errorcount5,errorcount6: BYTE;
VesTaraNew, VesTaraOld :REAL;
END_VAR

......

1: (* Отправка в СМИ1 значения Ves_Post1*)
Send.CLK := TRUE;
Send;
IF Send.Q = TRUE THEN
ptr:=ADR(Ves_Post1);
buffer[1]:=ptr^;
ptr:=ptr+1;
buffer[0]:=ptr^;
ptr:=ptr+1;
buffer[3]:=ptr^;
ptr:=ptr+1;
buffer[2]:=ptr^;
ptr:=ptr+1;
END_IF

send2_modbus(
Enable:= TRUE, (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=32 , (*адрес*)
FirstAddr:= 0, (*номер регистра*)
Quantity:= 2, (*количество записываемых регистров*)
ComHandle:=Settings1.Port ,(*номер сом-порта*)
TimeOut:=TimeOut , (*таймаут T#50ms*)
Buffer:=buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
RegCnt=> DataSize); (*кол-во записанных байтов *)
(*если установлен признак завершения операции, то *)
IF cmpl THEN
IF err = 0 THEN
rejim:=100;(*переходим к выполнению следующего блока*)
ELSE
rejimerror := rejim;
rejim := 101;
END_IF
END_IF

.......

101:(*При ошибке:*)
TimeOut2(IN :=TRUE,PT:=T#50ms);
TimeOut2;
IF TimeOut2.Q THEN
rejim := rejimerror;
Send.CLK :=FALSE;
Send;
TimeOut2.IN :=FALSE;
TimeOut2;
END_IF


У кого-нибудь мысли есть, что со СМИ1 происходит в этот момент? Может кто то сталкивался с таким?

P.S. Все остальные модули ОВЕН работают без вопросов. Проблема только со СМИ1. Панели 2шт. - обе ведут себя одинаково.

Вольд
08.02.2017, 18:52
А то что ПЛК посылает в панель правильно отображается ?

Атаман
08.02.2017, 19:51
А то что ПЛК посылает в панель правильно отображается ?

Да, все правильно отображается до появления ошибки.

Атаман
10.02.2017, 15:59
Чувствую, что где то рядом, но нет....

Результаты ниже, может у кого то мысль возникнет )

На ASCII все прекрасно работает - ошибок 0.
Оставляю блок, который приводил выше, все остальные опросы убираю. То есть записывает в СМИ1 разные значения в один регистр непрерывно. Работал так пару часов - ошибок 0.
Как только подключаю весь остальной проект возникает ошибка в этом же блоке, при этом в СМИ1 гаснет экран и обе лампочки. И так остается до тех пор, пока не остановлю программу. В программе выполняется последовательно этот блок. Пробовал на горячую при зависании изменить таймаут между отправкой запроса, изменял до 10 сек, не помогает - экран включается но горят нули и всё равно таймаут. Но! Как только меняю на горячую CASE на другой, экран отвисает и всё работает. Вот этот момент я вообще не пойму. Такое ощущение, что где-то что-то не переключается или не отпускает.
Тот же проект, но с конфигуратором вместо modbus.lib также дает сотни ошибок таймаута.
Резистор ставил 100, 120, 620 ОМ, с бубном плясал....

Вообщем ХЕЛП!

И да... нужно ли буфер обнулять перед отправкой запроса? И как это делается? Пробовал тупо
for i:= 1TO255 do
buffer[i] := 0;
next_for
уходит в перезагрузку.

lazy
10.02.2017, 16:23
Атаман, Enable взводицо на один цикл. В твоем случае, например так:

send2_modbus(
Enable:= Send.Q,

Буфер обнулять не надо.

IF Send.Q = TRUE THEN - масло масляное. Достаточно: IF Send.Q THEN

50ms - маловато для таймаута, будет многовато ошибок. У меня в проектах 250ms.

Атаман
10.02.2017, 17:29
lazy, спасибо за наводку! )

Но только все же Enable:= Send.Q нельзя, так как Send.Q становится FALSE после Complete в другом кейсе.

lazy
10.02.2017, 17:45
Но только все же Enable:= Send.Q нельзя, так как Send.Q становится FALSE после Complete в другом кейсе.

Не пойму логики, он, что при каждом вызове кейса номер 1 равен ТRUE?. Тогда и
IF Send.Q THEN
ptr:=ADR(Ves_Post1);
buffer[1]:=ptr^;
ptr:=ptr+1;
buffer[0]:=ptr^;
ptr:=ptr+1;
buffer[3]:=ptr^;
ptr:=ptr+1;
buffer[2]:=ptr^;
ptr:=ptr+1;
END_IF
нельзя! Так как трогать буфер после отправки до завершения операции нельзя. )

Атаман
10.02.2017, 20:10
Не пойму логики, он, что при каждом вызове кейса номер 1 равен ТRUE?.

lazy, торможу в пятницу. Да, с буфером всё норм, надо сделать Enable:= Send.Q. Спасибо!

Атаман
13.02.2017, 12:07
Включение Enable на один цикл не помогло. Периодическая случайная ошибка таймаута при записи регистров в СМИ1 командой 16 по Modbus-RTU. Где еще копнуть можно?

Атаман
14.02.2017, 10:37
Начал копать вверх по modbus.lib.... Таймаут устанавливается в MB_UNI_IO в блоке:




IF tonTimer.Q = TRUE THEN (* стработал таймер любого тайм-аута *)
IF StartFrame = FALSE (* кадр не принимался или *)
OR DataSize < 5 (* размер кадра меньше допустимого *)
THEN
Exception := EXC_TO; (* установить ошибку тайм-аута *)

В момент таймаута у меня StartFrame = FALSE , но DataSize = 13. Как такое может быть?

Атаман
02.03.2017, 20:00
Посмотрите свежим взглядом, не могу найти ошибку...
Тестовое приложение, опрос двух модулей по модбас:



PROGRAM PLC_PRG
VAR
get1_modbus: MB_RD_HOLD_REGS; (*функция 03 - чтение параметра типа INT*)
send2_modbus: MB_WR_REGS; (*функция 6 - запись параметров*)

Buffer_out: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)
cmpl: BOOL;
port_opened: BYTE := 0;
Init: BOOL; (* признак инициализации пользовательской программы *)
Settings:COMSETTINGS; (* настройки последовательного порта *)
com_num: PORTS:=0; (*0 - RS-485, 1 - RS-232*)
enabl: BOOL:=TRUE; (*состояние работы блока*)
err,errcount: INT; (*номер ошибки*)
g: REAL;
TimeOut: TIME:=T#50ms; (*таймаут*)
Exception: BYTE;
DataSize: WORD;
master1: BYTE:= 0;
TimeOut2 : TON;
Send: R_TRIG;
f1:DINT:= 2;
COM_SERVICE1: COM_SERVICE;
x:WORD;
END_VAR

(*Устанавливаем настройки COM-порта*)
IF port_opened=0 THEN
Settings.Port:=com_num; (*номер COM-порта*)
Settings.dwBaudRate:= 115200; (*скорость*)
Settings.byParity:=0;
Settings.dwTimeout:=0;
Settings.byStopBits:=0;
Settings.dwBufferSize:=0;
Settings.dwScan:=0;
END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );
(*Если COM-порт открыт, то переходим к приему и передачи данных *)
IF COM_SERVICE1.ready THEN
port_opened:=2;
END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

0: (* функция 03 инт - ФБ считывает значение параметра типа int из прибора с адресом162 в регистр с номером 48 по протоколу Modbus-RTU*)
get1_modbus(
Enable:= enabl , (* разрешение работы блока *)
Mode:=MB_RTU, (*режим передачи*)
DevAddr:=8 , (*адрес*)
FirstAddr:=2 , (*номер регистра*)
Quantity:=1, (*количество регистров*)
ComHandle:=Settings.Port , (*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer_out, (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)
IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x:=BYTE_TO_WORD(BUFFER_out[1]) OR SHL(BYTE_TO_WORD(BUFFER_out[0]),8);
master1 := 3;
ELSE
errcount:=errcount +1;
END_IF

END_IF


2: get1_modbus(
Enable:=enabl , (* разрешение работы блока *)
Mode:=MB_RTU, (*режим передачи*)
DevAddr:=48 , (*адрес*)
FirstAddr:=50 , (*номер регистра*)
Quantity:=1, (*количество регистров*)
ComHandle:=Settings.Port , (*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer_out, (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)
IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x:=BYTE_TO_WORD(BUFFER_out[1]) OR SHL(BYTE_TO_WORD(BUFFER_out[0]),8);
master1 := 3;
ELSE
errcount:=errcount +1;
END_IF

END_IF


3:
TimeOut2(IN :=TRUE,PT:=T#50ms);
TimeOut2;
IF TimeOut2.Q THEN
g:=g+1;
master1 := 0;
(*IF g/2 = TRUNC(g/2) THEN
master1 := 2;
END_IF*)
TimeOut2.IN := FALSE;
TimeOut;
END_IF

END_CASE

END_IF


Код выше работает без ошибок. Убираю коммент в 3м кейсе - подключаю опрос второго блока каждый второй раз и в первом модуле каждый второй опрос появляется ошибка таймаута. Что за ерунда??

lazy
07.03.2017, 10:49
Тыж вроде сам уже нашел:

TimeOut2.IN := FALSE;
TimeOut;

Вместо этого нужна одна строка:
TimeOut2( IN := FALSE );

Schneider
26.05.2022, 13:49
про тонкости работы с modbus.lib уже не раз говорилось... Не надо формировать буфер на запись в каждом цикле, т.к. ф.б. на запись не успеет отработать за один цикл, а буфер используется один и тот же и на отправку запроса и на прием ответа. Таким образом, при ответе от слейв-устройства в буфере будет "каша" из ответа от устройства и данных, которые вы отправляете на него.

пять лет прошло, а грабли как новые. :)
попался мне плк 73 c платкой модбаса,
залил тестовый проект в него Modbus чтение INT и REAL (ST),
и к этому же компу через usb-rs485 модбас от контроллера пробросил, софт MasterOPCUniversal включил и смотрю.
Ага, переключил в коде на MB_RTU , т.к. в MasterOPCUniversal так по умолчанию.
забил цифры в регистры с 8 по 14 (как в примере) в slave устройстве на софте MasterOPCUniversal
INT нормально считывает, REAL байты только переставил и все норм тоже.
А считывание трех INT подряд - нули. что то не так....
настроил цикл задачи на 500ms и увидел что код ошибки 255 прилетает как раз в тот момент как прошло завершение чтения.
таким образом ничего в результат не попадает.
вот в поиске и нашел эту ветку.
уменьшив в запросе количество байт до 4х - ошибка пропадает, ставишь больше появляется.

собственно вопрос тот же (как не наступить на грабли ?) как работать с modbus.lib?
не совсем ясна фраза "Не надо формировать буфер на запись в каждом цикле" ? хотя чувствую что в ней зарыт смысл...
где посмотреть подробнее?

PS. не хочется лезть в библиотеку, благо можно открыть либу текстовым редактором и скопипастить это чудо и покопаться.
Не попадись мне контроллер для опытов just for fun так сказать, не купил бы. А ведь цена на него стоит сейчас в 30 тысяч!
(хоть и снят с производства) и при этом к нему бубен не прилагается!

A.Simonov
26.05.2022, 16:01
пять лет прошло, а грабли как новые. :)
попался мне плк 73 c платкой модбаса,
залил тестовый проект в него Modbus чтение INT и REAL (ST),
и к этому же компу через usb-rs485 модбас от контроллера пробросил, софт MasterOPCUniversal включил и смотрю.
Ага, переключил в коде на MB_RTU , т.к. в MasterOPCUniversal так по умолчанию.
забил цифры в регистры с 8 по 14 (как в примере) в slave устройстве на софте MasterOPCUniversal
INT нормально считывает, REAL байты только переставил и все норм тоже.
А считывание трех INT подряд - нули. что то не так....
настроил цикл задачи на 500ms и увидел что код ошибки 255 прилетает как раз в тот момент как прошло завершение чтения.
таким образом ничего в результат не попадает.
вот в поиске и нашел эту ветку.
уменьшив в запросе количество байт до 4х - ошибка пропадает, ставишь больше появляется.

собственно вопрос тот же (как не наступить на грабли ?) как работать с modbus.lib?
не совсем ясна фраза "Не надо формировать буфер на запись в каждом цикле" ? хотя чувствую что в ней зарыт смысл...
где посмотреть подробнее?

PS. не хочется лезть в библиотеку, благо можно открыть либу текстовым редактором и скопипастить это чудо и покопаться.
Не попадись мне контроллер для опытов just for fun так сказать, не купил бы. А ведь цена на него стоит сейчас в 30 тысяч!
(хоть и снят с производства) и при этом к нему бубен не прилагается!

Добрый день.
Без кода не очень понятно что и как у вас реализовано.
Но вообще, если успешность запроса зависит от числа запрашиваемых байт...
Так может slave не поддерживает групповой опрос? и каждый параметр нужно отдельно опрашивать?

Прикладываю примеры опроса, которые у меня завалялись.

Schneider
27.05.2022, 14:02
Добрый день.
Без кода не очень понятно что и как у вас реализовано.
Но вообще, если успешность запроса зависит от числа запрашиваемых байт...
Так может slave не поддерживает групповой опрос? и каждый параметр нужно отдельно опрашивать?

Прикладываю примеры опроса, которые у меня завалялись.

Спасибо, что откликнулись.
Я читал инструкцию к библиотеке, действительно, как писали до меня здесь,
довольно странно описывать в ней процедуру установки библиотеки в среду
и не говорить о важных деталях работы с ней, чего я видимо не понимаю как раз.
Но почитав сообщения здесь, понимаю что не я один спотыкался.

пользуюсь я кодом, который в примере на сайте выложен в свободном доступе
немного модифицированный но вряд ли это что то меняет.


PROGRAM PLC_PRG
VAR
get1_modbus: MB_RD_HOLD_REGS; (*функция 03 - чтение параметра типа INT*)
get2_modbus: MB_RD_INP_REGS; (*функция 04 - чтение трех параметров типа INT*)
get3_modbus: MB_RD_HOLD_REGS; (*функция 03 - чтение параметра типа Float*)

Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)
cmpl: BOOL;
port_opened: BYTE := 0;
Init: BOOL; (* признак инициализации пользовательской программы *)
Settings:COMSETTINGS; (* настройки последовательного порта *)
com_num: PORTS:=0; (*0 - RS-485, 1 - RS-232*)
enabl: BOOL; (*состояние работы блока*)
err: INT; (*номер ошибки*)
TimeOut: TIME:=T#250ms; (*таймаут*)
Exception: BYTE;
DataSize: WORD;
master1: BYTE:= 2;

t: DWORD; (*переменная для организации счетчика*)
A: WORD := 0; (*счетчик*)
x:WORD; (*считанное значение*)
x1: INT; (*переменная для записи по сети*)
x2: INT; (*переменная для записи по сети*)
x3: INT; (*переменная для записи по сети*)
d: REAL; (*считанное значение*)
ptr_D:POINTER TO BYTE;

COM_SERVICE1: COM_SERVICE;
x_1: INT;
delay: TON;
END_VAR
--------------------------------------------

(*Организуем счетчик, что бы передавать эти данные по сети*)
t:=t+1;
IF (t MOD 1000)=0 THEN
A := A + 1;
IF A > 9999 THEN
A := 0;
END_IF
END_IF
delay(PT:=T#1S);
(*Устанавливаем настройки COM-порта*)
IF port_opened=0 THEN
Settings.Port:=com_num; (*номер COM-порта*)
Settings.dwBaudRate:=19200; (*скорость*)
Settings.byParity:=0;
Settings.dwTimeout:=0;
Settings.byStopBits:=0;
Settings.dwBufferSize:=0;
Settings.dwScan:=0;
END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );
(*Если COM-порт открыт, то переходим к приему и передачи данных *)
IF COM_SERVICE1.ready THEN
port_opened:=2;
END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

0: (* функция 03 инт - ФБ считывает значение параметра типа int из прибора с адресом 2 в регистр с номером 8 по протоколу Modbus-ASCII*)
get1_modbus(
Enable:=enabl , (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=8 , (*номер регистра*)
Quantity:=1, (*количество регистров*)
ComHandle:=Settings.Port , (*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)
enabl:=FALSE;
delay(IN:=TRUE);

IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x:=BYTE_TO_WORD(BUFFER[1]) OR SHL(BYTE_TO_WORD(BUFFER[0]),8);
END_IF
enabl:=TRUE;
(*master1:=1; переходим к выполнению следующего ФБ*)
END_IF

1: (* функция 03 флоат - ФБ считывает значение параметра типа int из прибора с адресом 2 в регистр с номаром 10 по протоколу Modbus-ASCII *)
get3_modbus(
Enable:=enabl , (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=10 , (*номер регистра*)
Quantity:=2, (*количество регистров*)
ComHandle:=Settings.Port ,(*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)

enabl:=FALSE;
delay(IN:=TRUE);

(*если установлен признак завершения операции, то *)
IF cmpl THEN
(*master1:=2; переходим к выполнению следующего ФБ*)
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа FLOAT*)
ptr_D:=ADR(d);
ptr_D^:=buffer[1];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[0];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[3];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[2];
END_IF
enabl:=TRUE;
END_IF

2: (* функция 04 инт - ФБ считывает значения трех параметров типа Int из прибора с адресом 2 начиная с регистра с номeром 12*)
get2_modbus(
Enable:= enabl, (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=12 , (*номер регистра*)
Quantity:=8 , (*количество регистров*)
ComHandle:= Settings.Port,(*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=> DataSize); (*кол-во считанных байтов *)
enabl:=FALSE;
delay(IN:=TRUE);
(*если установлен признак завершения операции, то *)
IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x1:=BYTE_TO_WORD(BUFFER[4]) OR SHL(BYTE_TO_WORD(BUFFER[3]),8);
x2:=BYTE_TO_WORD(BUFFER[8]) OR SHL(BYTE_TO_WORD(BUFFER[7]),8);
x3:=BYTE_TO_WORD(BUFFER[12]) OR SHL(BYTE_TO_WORD(BUFFER[11]),8);

END_IF
enabl:=TRUE;
(*master1:=0; переходим к выполнению следующего ФБ*)
END_IF
END_CASE

IF enabl = FALSE AND delay.Q THEN
enabl := TRUE;
delay(IN:=FALSE);
END_IF

IF err <> 0 THEN
enabl := FALSE;
END_IF

END_IF




включил MB_RTU и тоже самое в настройках сервера
специально оставил включенной только мультирегистровый запрос:
master1: BYTE:= 2;
а также прибавил количество запрашиваемых регистров
Quantity:=8 , (*количество регистров*)

софт MasterOPCUniversal позволяет отслеживать, что проходит через порт.
последний приведенный пример дает следующее

27-05-2022 14:30:26.065 Node2::plc73_answer:(COM3) Tx: [0021] 02 04 10 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 2E CE
27-05-2022 14:30:26.065 Node2::plc73_answer:(COM3) Rx: [0008] 02 04 00 0C 00 08 31 FC

соответственно мы видим запрос Rx и ответ Tx размером 21 байт (включительно с контрольной суммой)
контрольную сумму я даже проверял в считалке на сайте, совпадает!!!

но вот что я обнаружил в Codesys 2 среде во время работы контроллера!
если выставить количество регистров 4, все отрабатывает нормально
но если 6 и более, как в исходном тексте примера, то в буфере мелькает занятых байт
в массиве Buffer только 15 байт ! а далее нули.

Если указать в запросе только 4 байта, то два байта контрольной суммы видны в Buffer, а более -нули в конце.
60936

код ошибки прилетает 255 (таймаут), ну получается как будто действительно таймаут, потому как последних байт в последовательности нету.

Подскажите, что может быть не так!?

Валенок
27.05.2022, 15:15
накой delay ?

Schneider
27.05.2022, 15:41
накой delay ?
вы правы , не суть. убрал его.
это я пытался делать чтобы enable=TRUE только один цикл,
обрабатываем буфер, выставляем в FALSE, для сброса типа, в форуме вычитал в коде у кого-то, хотел попробовать.
не помогло. а delay хотел прикрутить если прилетает какая либо ошибка. то через таймаут перезапуск был.

сделал как в примере исходном, только MB_RTU и 8 регистров считываю.

(*Организуем счетчик, что бы передавать эти данные по сети*)
t:=t+1;
IF (t MOD 1000)=0 THEN
A := A + 1;
IF A > 9999 THEN
A := 0;
END_IF
END_IF
(*Устанавливаем настройки COM-порта*)
IF port_opened=0 THEN
Settings.Port:=com_num; (*номер COM-порта*)
Settings.dwBaudRate:=19200; (*скорость*)
Settings.byParity:=0;
Settings.dwTimeout:=0;
Settings.byStopBits:=0;
Settings.dwBufferSize:=0;
Settings.dwScan:=0;
END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );
(*Если COM-порт открыт, то переходим к приему и передачи данных *)
IF COM_SERVICE1.ready THEN
port_opened:=2;
END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

0: (* функция 03 инт - ФБ считывает значение параметра типа int из прибора с адресом 2 в регистр с номером 8 по протоколу Modbus-ASCII*)
get1_modbus(
Enable:=enabl , (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=8 , (*номер регистра*)
Quantity:=1, (*количество регистров*)
ComHandle:=Settings.Port , (*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)

IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x:=BYTE_TO_WORD(BUFFER[1]) OR SHL(BYTE_TO_WORD(BUFFER[0]),8);
END_IF
(*master1:=1; переходим к выполнению следующего ФБ*)
END_IF

1: (* функция 03 флоат - ФБ считывает значение параметра типа int из прибора с адресом 2 в регистр с номаром 10 по протоколу Modbus-ASCII *)
get3_modbus(
Enable:=enabl , (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=10 , (*номер регистра*)
Quantity:=2, (*количество регистров*)
ComHandle:=Settings.Port ,(*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=>DataSize ); (*кол-во считанных байтов *)


(*если установлен признак завершения операции, то *)
IF cmpl THEN
(*master1:=2; переходим к выполнению следующего ФБ*)
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа FLOAT*)
ptr_D:=ADR(d);
ptr_D^:=buffer[1];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[0];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[3];
ptr_D:=ptr_D+1;
ptr_D^:=buffer[2];
END_IF
END_IF

2: (* функция 04 инт - ФБ считывает значения трех параметров типа Int из прибора с адресом 2 начиная с регистра с номeром 12*)
get2_modbus(
Enable:= enabl, (* разрешение работы блока *)
Mode:=MB_RTU , (*режим передачи*)
DevAddr:=2 , (*адрес*)
FirstAddr:=12 , (*номер регистра*)
Quantity:=8 , (*количество регистров*)
ComHandle:= Settings.Port,(*номер COM-порта*)
TimeOut:=TimeOut , (*Таймаут T#50ms*)
Buffer:=Buffer , (* буфер данных *)
Complete=>cmpl , (* скопировать признак завершения операции *)
Exception=>err , (* скопировать регистр ошибок *)
ByteCnt=> DataSize); (*кол-во считанных байтов *)
(*если установлен признак завершения операции, то *)
IF cmpl THEN
IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)
x1:=BYTE_TO_WORD(BUFFER[4]) OR SHL(BYTE_TO_WORD(BUFFER[3]),8);
x2:=BYTE_TO_WORD(BUFFER[6]) OR SHL(BYTE_TO_WORD(BUFFER[5]),8);
x3:=BYTE_TO_WORD(BUFFER[8]) OR SHL(BYTE_TO_WORD(BUFFER[7]),8);
x4:=BYTE_TO_WORD(BUFFER[10]) OR SHL(BYTE_TO_WORD(BUFFER[9]),8);
x5:=BYTE_TO_WORD(BUFFER[12]) OR SHL(BYTE_TO_WORD(BUFFER[11]),8);
x6:=BYTE_TO_WORD(BUFFER[14]) OR SHL(BYTE_TO_WORD(BUFFER[13]),8);
x7:=BYTE_TO_WORD(BUFFER[16]) OR SHL(BYTE_TO_WORD(BUFFER[15]),8);
x8:=BYTE_TO_WORD(BUFFER[18]) OR SHL(BYTE_TO_WORD(BUFFER[17]),8);

END_IF
(*master1:=0; переходим к выполнению следующего ФБ*)
END_IF
END_CASE

IF enabl = FALSE THEN
enabl := TRUE;
END_IF

IF err <> 0 THEN
enabl := FALSE;
END_IF

END_IF

вот так выглядят скрины работы.
видно что прилетает только 15 байт. и в регистрах (байтах) старше шестого нули идут.
таймаут не помогает.
60938
60939

Schneider
27.05.2022, 18:39
выкиньте case.1 коли не юзаете

в принципе :

это сильный ход ))

но вообще - выкиньте это

Из песни слов не выкинишь. Такой "гениальный" пример с офф сайта.
Попробую сейчас ваши советы.

Schneider
28.05.2022, 06:19
PROGRAM PLC_PRG
VAR
get_modbus: MB_RD_INP_REGS:= (Enable := TRUE); (*функция 04 - чтение трех параметров типа INT*)

Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)
cmpl: BOOL;
port_opened: BYTE := 0;
Init: BOOL; (* признак инициализации пользовательской программы *)
Settings:COMSETTINGS; (* настройки последовательного порта *)
com_num: PORTS:=0; (*0 - RS-485, 1 - RS-232*)
enabl: BOOL; (*состояние работы блока*)
err: INT; (*номер ошибки*)
TimeOut: TIME:=T#50ms; (*таймаут*)
Exception: BYTE;
DataSize: WORD;


t: DWORD; (*переменная для организации счетчика*)
A: WORD := 0; (*счетчик*)
x:WORD; (*считанное значение*)
x1: INT; (*переменная для записи по сети*)
x2: INT; (*переменная для записи по сети*)
x3: INT; (*переменная для записи по сети*)
d: REAL; (*считанное значение*)
ptr_D:POINTER TO BYTE;

COM_SERVICE1: COM_SERVICE;
x_1: INT;

x4: INT;
x5: INT;
x6: INT;
x7: INT;
x8: INT;
END_VAR
--------------------------------
IF port_opened=0 THEN
Settings.Port:=com_num;
Settings.dwBaudRate:=19200;
Settings.byParity:=0;
Settings.dwTimeout:=0;
Settings.byStopBits:=0;
Settings.dwBufferSize:=0;
Settings.dwScan:=0;
END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );
IF COM_SERVICE1.ready THEN
port_opened:=2;
END_IF

IF port_opened=2 THEN

get_modbus(

Mode:=MB_RTU ,
DevAddr:=2 ,
FirstAddr:=12 ,
Quantity:=8 ,
ComHandle:= Settings.Port,
TimeOut:=TimeOut ,
Buffer:=Buffer ,
Complete=>get_modbus.Enable ,
Exception=>err ,
ByteCnt=> DataSize);
IF get_modbus.Complete THEN
IF err=0 THEN
x1:=BYTE_TO_WORD(BUFFER[4]) OR SHL(BYTE_TO_WORD(BUFFER[3]),8);
x2:=BYTE_TO_WORD(BUFFER[6]) OR SHL(BYTE_TO_WORD(BUFFER[5]),8);
x3:=BYTE_TO_WORD(BUFFER[8]) OR SHL(BYTE_TO_WORD(BUFFER[7]),8);
x4:=BYTE_TO_WORD(BUFFER[10]) OR SHL(BYTE_TO_WORD(BUFFER[9]),8);
x5:=BYTE_TO_WORD(BUFFER[12]) OR SHL(BYTE_TO_WORD(BUFFER[11]),8);
x6:=BYTE_TO_WORD(BUFFER[14]) OR SHL(BYTE_TO_WORD(BUFFER[13]),8);
x7:=BYTE_TO_WORD(BUFFER[16]) OR SHL(BYTE_TO_WORD(BUFFER[15]),8);
x8:=BYTE_TO_WORD(BUFFER[18]) OR SHL(BYTE_TO_WORD(BUFFER[17]),8);

END_IF
END_IF

END_IF

не помогло. таже история ошибка 255 в конце. и да кстати enable один цикл крутится только.
но в буфере только 15 байт и переменная количество байт показывает именно это число...:(

библиотеку поправили, видимо с 2017 года, поскольку ф.б. enable сам переключается в disable.
поэтому "Валенок" рекомендовал добавить Complete=>get_modbus.Enable , в код. перезапуск после завершения.

PS. в общем я смотрю ни у кого нет на форуме вариантов где бы использовался запрос больше 2х регистров.
походу эта библиотека не умеет больше 4 регистров получать.
Дайте рабочий пример, если я ошибаюсь.

Schneider
28.05.2022, 19:13
Либа рабочая и её авторам - спасибо. Код выше - рабочий.
Я этой либой давно не пользуюсь, но сейчас её тупо попробывал и прочитал спокойно не 4, а 125 регистров. На 115200.


понял, ок значит надо смотреть в сторону устройства, а может модбас сервера, которым я имитирую слейв устройство.

Schneider
30.05.2022, 11:00
платка с 2-мя портами ?

да, ну я поменял. там стояла плата только под MP1 выход. а сейчас + два 485 порта

Schneider
30.05.2022, 16:23
Либа рабочая и её авторам - спасибо. Код выше - рабочий.
Я этой либой давно не пользуюсь, но сейчас её тупо попробывал и прочитал спокойно не 4, а 125 регистров. На 115200.

*
спасибо большое, что откликнулись. но все же прошу выслать ваш проект в котором у вас работает одним запросом 125 регистров считывать.
только через "сохранить/отправить архив" чтобы вместе с либами.(такой файл можно прикрепить на форуме)

дело в том что я только что взял МВ110 и спокойно настроив в MasterOPC сервер (в винде) через адаптер usb-rs485 считал с нее сразу 8 регистров (это 16 байт) за один запрос
но! подключив плк 73 к MB110 получаю такую же шляпу и обрезанными данными в буфере обмена и с ошибкой по таймауту. попробовал на второй порт rs485 подключить, не помогло.
надо же все таки разобраться с этим чудом!

Schneider
31.05.2022, 07:19
Выкладывайте свой проект

сделал архив с либами, модбас либ скачана недавно с сайта owen так что должна быть последняя.
задачу в цикле делал от 50 до 500 мс. как и таймауты разные. ничего не помогает.
вроде RS-1 RS-2 говорят не надо настраивать, они в режиме слейв с контроллера работают вроде.
посмотрите пожалуйста. что же я там не так сделал?

PS. я вот думаю, может это интерфейсная плата в 73м такая глючная, может у нее буфер больше 15 байт хранить не умеет?
а на другом плк, новее все работает.

Schneider
31.05.2022, 08:16
скачаю посмотрю - позже, или кто-то.
пока : Какие задачи ? тасков напихали?
каких? у меня всего одна задача :)
60990
можно конечно совсем удалить её и установить минимум и максимум
60991
и так пробовал , только минимум на 100 ставил. без разницы.

Schneider
31.05.2022, 13:21
Правильное решение. Накой задачи на 1-ядре ?

Вот накой ? Дефолт. Даже не, просто 0
а при чем здесь количество ядер? если мне не нужно чтобы задача выполнялась так часто, зачем же единственный процессор нагружать пустыми хлопотами?
это как раз такой случай, мне вполне достаточно чтобы раз в секунду вообще выполнялась эта задача.
а когда я по дефолту без менеджера задач оставляю 0 проц этой единственной задачей нагружается более 1000 раз в секунду.
разве я не прав?
PS. получилось понять, что у меня не так с опросами? жду с нетерпением вердикт...

Schneider
31.05.2022, 16:21
goto пост 37

Не понял, на что мне обратить внимание? Это же мой пост.

Schneider
01.06.2022, 10:27
сам разобрался.
поставил в конфигурации задач 10ms на задачу где обрабатывается запрос по modbus и заработало.
стоило прибавить цикличность выполнения задачи до 20 ms как через раз посыпалась опять ошибка 255.
заметил что функциональный блок срабатывает три или четыре раза прежде чем результат "готов" и, видимо при большем периоде срабатывания блока
времени не хватает, и происходит timeout, именно он а не ошибка контрольной суммы которая трактуется как timeout, как предполагал кто то здесь ранее.
скорость порта 9600.
Такой режим (уменьшение времени цикла) ранее я пробовал. Но ответчиком (slave устройством) была программа MasterOPC Universal Modbus Server,
которая сразу же после включения контроллера, слетала тихо. Поэтому ранее я не получил результата. а с блоком МВ110 работает.
опрос 8ми регистров - счетчиков срабатывания опрашивает!


PS.опытным путем: если скорость порта 115200, то цикл задачи должен быть еще меньше, 10ms -много.
Убрал вообще из диспетчера задания, только в "конфигурации ПЛК" можно видеть по умолчанию 1ms - минимальный цикл.
тогда работает без ошибок!
собственно теперь ясно удивление:

Какие задачи ? тасков напихали?
видимо как раз задача в свободно плавании была у коллеги, поэтому все работало норм. а я заморочился.

PPS. еще пару дней экспериментов ушло на то, чтобы найти еще одну настройку, чтобы работало все на отлично.
Никто не сказал обратить внимание, и я чего то не встречал про это информацию здесь.
речь идет о инициализации порта:

Settings.dwBufferSize:=100; (*размер буфера*)

пока я не не сменил ноль на 100, чтение пятидесяти регистров за раз проходило с ошибкой 50% , то есть через раз читало нормально.
Как только поставил 100(установил на вскидку), читает без ошибок в ста процентах случаев!

Прикрепил проект тестовый, где разнес задачи.
в codesys 2.3 нет возможности поставить задачу на выполнение по состоянию переменной (выполнение пока TRUE)
при этом задача опроса порта должна крутиться максимально быстро в момент опроса, при этом для многих приложений не нужно частое обновление данных,
то код опроса у меня обернут в IF(по глобальной переменной)....END_IF чтобы, пока не нужно опрашивать порт, задача минимально занимала процессор,
а когда нужно включалась по глобальной переменной из главной задачи.

чтобы не было ошибок при симуляции slave устройства на компьютере OPC сервером, как это делал я, нужно уменьшить в настройках драйвера usb-rs485 таймаут с 16 до 1 мс.
61060

Schneider
04.06.2022, 07:04
Добрый день. Помогите разобраться в подключении к плк160 мк110-8д.4р.

у вас проект запароленный. проект мой прикрепленный выше как раз с подключением к МВ110 правда, но с 73 контроллера.
в плк160 как то в дереве проекта часть модбаса настраивается, так что не могу посоветовать. только с 73м работал и с последней серией спк.