PDA

Просмотр полной версии : Обмен Modbus RTU библиотекой Modbus.lib



Silver21
06.09.2016, 10:51
Привет форумчанам.
Пытаюсь через библиотеку modbus.lib организовать опрос slave-устройств, однако, что на технологическом оборудовании, что на стенде происходят непонятные вещи.
Исходник программы стенда (опрос МВ110-16ДН):
IF port_opened=0 THEN
Settings.Port := 0; (*COM1*)
Settings.dwBaudRate := 9600;
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
get1_modbus(
Enable := enabl,
Mode := MB_RTU,
DevAddr := 16,
FirstAddr := 16#30,
Quantity := 1,
ComHandle := Settings.Port,
TimeOut := T#100ms,
Buffer := Buffer);

enabl := FALSE;
IF get1_modbus.Complete THEN
IF get1_modbus.Exception= 0 THEN
count := count + 1;
END_IF;
enabl := TRUE;
END_IF;
END_IF;

По алгоритму, опрос должен осуществляться непрерывно, и счетчик count должен постоянно инкрементироваться. Однако, по факту, счетчик идет рывками и редко, и очень часто возникают ошибки Exception 255 (таймаут).
Подключил прослушку RS-485 сети, и вижу следующие пакеты:
ПЛК73: 10 04 00 30 00 01 32 84
МВ110: 10 04 02 00 00 45 33
ПЛК73: 33 04 00 30 00 01 35 D7
МВ110: молчит, потому что обращение идет почему то к устройству с адресом 16#33
Повторяется...

Таким образом:
1) МВ110 отвечает правильно, но почему ПЛК73 не может принять и распознать пакет, и в итоге завершает блок с ошибкой 255 (таймаут)?
2) Почему при повторном запросе блок выставляет адрес 16#33? Причем замечено, что адрес этого ошибочного запроса всегда совпадает с последним байтом последнего ответа от МВ110. Испытывал, и исход такой же при опросе другого оборудования.

capzap
06.09.2016, 12:05
уже каждую неделю спрашивают про "мусор" в передаче данных. Предлагаю возможное решение проблемы. Потребуется зайти в бибку и немного подправить ФБ MB_UNI_IO. Обратите внимание что при проверке доступности нового цикла обмена используется тот же буфер, который из вне уже сформирован командой запроса и естественно если в порту остается какой то мусор он заполняет сей буфер и портит команду. Можно добавить в темп еще один буфер который и будет очищать порт

Спорягин Кирилл
06.09.2016, 12:22
Сталкивался с подобной ошибкой. Подробное описание причины тут (http://www.owen.ru/forum/showthread.php?t=21940&page=14&p=219033&viewfull=1#post219033) (пункт 2).
Исправляется, простым переформирование буфера.

Silver21
06.09.2016, 12:23
Спасибо за отличные советы! Про возможность корректировки библиотеки не знал, думал они закрытые.
По результатам испытания отпишусь...

Спорягин Кирилл
06.09.2016, 12:30
Спасибо за отличные советы! Про возможность корректировки библиотеки не знал, думал они закрытые.
По результатам испытания отпишусь...

Возможность есть, но мне кажется это совершенно излишнее.

capzap
06.09.2016, 12:35
Сталкивался с подобной ошибкой. Подробное описание причины тут (http://www.owen.ru/forum/showthread.php?t=21940&page=14&p=219033&viewfull=1#post219033) (пункт 2).
Исправляется, простым переформирование буфера.
это всё сводится к тому, о чем я написал выше. когда порт вычитан полностью, тогда можно и без дополнительных переформирований буфера отправлять очередной запрос. Бибку кстати можно и не править, просто в своем коде самостоятельно очищать порт, после прихода ответа на запрос

capzap
06.09.2016, 15:15
Про порт - а это в б-ке есть.

я про этот код
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

Silver21
06.09.2016, 16:35
Разобрался. Пришлось изменить функцию MB_UNI_IO библиотеки Modbus.lib, иначе никак.

Переписал 7ю строку функции как:
WHILE SysComRead(ComHandle, ADR(DataBuf)+DataSize, SIZEOF(DataBuf)-DataSize, 0) <> 0 DO;
Пусть после основного блока данных функция SysComRead записывает все что хочет, там ничего важного нет.

И по первой проблеме, пришлось строку 53 tonTimer(); перенести на 71 после условия IF tonTimer.Q = TRUE THEN
Теперь на прием данных времени есть еще целый цикл программы. Таким образом, данные,если они даже и есть, по-любому успеют считаться.

В итоге, все ошибки практически пропали. Спасибо, друзья!

capzap
06.09.2016, 16:48
вообще то так писать не желательно, вместо суммирования с указателем, лучше будет adr(databufer[datasize])

Филоненко Владислав
07.09.2016, 10:28
Разобрался. Пришлось изменить функцию MB_UNI_IO библиотеки Modbus.lib, иначе никак.

Переписал 7ю строку функции как:
WHILE SysComRead(ComHandle, ADR(DataBuf)+DataSize, SIZEOF(DataBuf)-DataSize, 0) <> 0 DO;
Пусть после основного блока данных функция SysComRead записывает все что хочет, там ничего важного нет.

И по первой проблеме, пришлось строку 53 tonTimer(); перенести на 71 после условия IF tonTimer.Q = TRUE THEN
Теперь на прием данных времени есть еще целый цикл программы. Таким образом, данные,если они даже и есть, по-любому успеют считаться.

В итоге, все ошибки практически пропали. Спасибо, друзья!

Не <>0, а >0. Иначе возможны зависания.

capzap
08.09.2016, 06:32
Ну да. Он же есть в б-ке. Зачем самостоятельно очищать его ? Или я чего то не понял ?

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