PDA

Просмотр полной версии : Последовательный опрос устройств по Modbus RTU



MTven
27.03.2018, 15:53
Здравствуйте, пишу сюда впервые и очень надеюсь на вашу помощь. Задача в последовательном опросе устройств по Modbus RTU.
Пытаюсь произвести последовательный опрос 3 устройств и испробовал разные способы, Good practice к сожалению не знаю в этом вопросе.
Вот мои попытки:


//STEP - переменная для шага, по умолчанию 1
//пользовательский ФБ запроса по RTU
MB_R1(
START := STEP = 1,
SLAVE := 1,
FUNC := 4,
ADDR := 5000,
NUMBER := 12,
ERR_TIME := 1000);

MBR1 := MB_R1.READY;
COUNTER1 := MB_R1.CNT;
//сокращенная форма IF(IN, VAL1, VAL2) - если IN = TRUE, то возвращает VAL1, иначе VAL2
STEP := IFI(MB_R1.READY, 2, STEP);

MB_R2(
START := STEP = 2,
SLAVE := 1,
FUNC := 4,
ADDR := 5050,
NUMBER := 12,
ERR_TIME := 1000);

MBR2 := MB_R2.READY;
COUNTER2 := MB_R2.CNT;
STEP := IFI(MB_R1.READY, 3, STEP);

MB_R3(
START := STEP = 3,
SLAVE := 1,
FUNC := 4,
ADDR := 5100,
NUMBER := 12,
ERR_TIME := 1000);

MBR3 := MB_R3.READY;
COUNTER3 := MB_R3.CNT;
STEP := IFI(MB_R3.READY, 1, STEP);

В итоге запрос виснет только в одном из ФБ, сюда привел самый простой и понятный вариант из написанного, пробовал уже и с передним фронтом и с таймером, но все равно не работает. Может есть какой-то типовой алгоритм для опроса по Modbus RTU? Сил уже нет

capzap
27.03.2018, 16:05
отвлеченный вопрос, а почему не понравилось использовать SEL и написали свой IFI?
а по поводу опроса, если брать библиотеку modbus.lib нужно дождаться прихода COMPLETE чтоб перейти к о просу следующего модуля. Для кокой цели служит Ваш READY не известно потому что не пишите что за бибка и где её брали

Newcomer
27.03.2018, 16:07
Спорягин Кирилл выкладывал универсальный диспетчер для Modbus.lib: http://www.owen.ru/forum/showthread.php?t=25112&highlight=%D1%EF%EE%F0%FF%E3%E8%ED+%CA%E8%F0%E8%EB %EB

MTven
28.03.2018, 07:01
отвлеченный вопрос, а почему не понравилось использовать SEL и написали свой IFI?
а по поводу опроса, если брать библиотеку modbus.lib нужно дождаться прихода COMPLETE чтоб перейти к о просу следующего модуля. Для кокой цели служит Ваш READY не известно потому что не пишите что за бибка и где её брали

Для использования SEL необходимо будет каждый раз добавлять NOT в условии, а мне это показалось неудобным (использую ПЛК TREI и среду UNIMOD PRO). По поводу второго вопроса, вот мой ФБ:


MB_R_F1(
START,
IDDRV,
SLAVE,
FUNC,
ADDR,
NUMBER);

FAULT:=MB_R_F1.FAULT;

//таймер на сработку ошибки
TON_1(
IN := NOT (MB_R_F1.READY AND FAULT = 0),
PT := ERR_TIME);

//в случае отсутствия ошибок считываем регистры в промежуточный массив, либо обнуляем его
IF NOT TON_1.Q THEN
MB_R_FARR[0]:= MB_R_F1.REG01;
MB_R_FARR[1]:= MB_R_F1.REG02;
MB_R_FARR[2]:= MB_R_F1.REG03;
MB_R_FARR[3]:= MB_R_F1.REG04;
MB_R_FARR[4]:= MB_R_F1.REG05;

//счетчик успешно выполненных запросов
CNT := CNT + 1;
READY := TRUE;
ELSE
FOR I := 0 TO 4 DO
MB_R_FARR[I]:= 0.0;
END_FOR;
CNT := 0;
READY := FALSE;
END_IF;

FLOAT_00 := MB_R_FARR[0];
FLOAT_01 := MB_R_FARR[1];
FLOAT_02 := MB_R_FARR[2];
FLOAT_03 := MB_R_FARR[3];
FLOAT_04 := MB_R_FARR[4];
END_IF;

capzap
28.03.2018, 07:31
можно NOT поставить в первый аргумент, а можно оставшиеся два аргумента местами поменять, как будто по Вашему IFI понятно, какой аргумент отвечает за истину, а какой за фальш, только писать про комментарий не надо, он куда длиннее чем поставить NOT

Что касается выложенного куска кода, выполнение таймера зачем?

MTven
28.03.2018, 09:06
можно NOT поставить в первый аргумент, а можно оставшиеся два аргумента местами поменять, как будто по Вашему IFI понятно, какой аргумент отвечает за истину, а какой за фальш, только писать про комментарий не надо, он куда длиннее чем поставить NOT

Что касается выложенного куска кода, выполнение таймера зачем?

:rolleyes: про поменять местами аргументы SEL я почему-то не подумал...
Таймер нужен для того чтобы отсекать периодические ошибки запроса, почему-то от ПЛК проскакивает ошибка платы модуля (сейчас мы уточняем данный момент у производителя).

capzap
28.03.2018, 09:33
в первом посте READY предположительно должен работать как импульс, во четвертом посте как мне кажется работа READY должна быть не импульсной, чтоб дать поработать таймеру, поэтому до сих пор не понятно как это всё работает и то что не переходит на следующий опрос прибора вполне вероятно из-за этих не стыковок

aRRma99
12.04.2018, 17:17
Вот кусок из рабочего проекта для ПЛК73. Вкраце что тут происходит...
Два устройства поочередно опрашиваются по 232 и 485. Тут только кусок от 485 оставил. Опрашиваются они через равные промежутки времени!!! Интервал опроса задается в переменной tMessInterval. Как только коммуникационный ФБ выдает COMPLETE по триггеру отключается флаг enableRsSend и коммуникационный ФБ отключается!!! Как только таймер отсчитает tMessInterval и enableRsSend отключен, смениться шаг в цикле CASE и отключится таймер (чтобы заново стал считать) и так по кругу.



(*ТАЙМЕР СМЕНЫ ШАГОВ КОММУНИКАЦИИ*)
tmrStepRs(IN:= com_ready1 AND com_ready2, PT:= tMessInterval); (*как только открылись оба порта запускаем отсчет шагов опроса*)

(*ОТПРАВКА И ЧТЕНИЕ КОМАНД RS232 и RS485*)
IF (com_ready1 = TRUE AND com_ready2 = TRUE)THEN
CASE stepRs OF
1: бла бла бла
2:
(*ПЕРЕДАЧА ЧАСТОТЫ ПЧВ*)
send1_modbus(
Enable := enableRsSend,
Mode := MB_RTU,
DevAddr := 2,
RegAddr := 50009, (*РЕГИСТР ЗАДАНИЯ ПО ИНТЕРФЕЙСУ*)
Value := freq1, (*ЧАСТОТА*)
ComHandle := Settings1.Port,
TimeOut := tWorkBlock,
Complete => cmpl3,
Exception => err3
);
rt_step_2(CLK:= cmpl3); (*По завершению работы ФБ включаем триггер*)
IF rt_step_2.Q THEN
enableRsSend := FALSE;
END_IF (*Если завершили отключаем работу ФБ*)

IF tmrStepRs.Q AND enableRsSend = FALSE THEN (*Как только досчитал таймер смены шага и блок отправки уже отработал*)
stepRs := 3; (*Меняем шаг*)
tmrStepRs(IN:= FALSE); (*Отключаем таймер*)
IF enableRsSend = FALSE THEN enableRsSend := TRUE; END_IF (*Ставим флаг для запуска следующего блока*)
END_IF

3:
(*ОТПРАВКА КОМАНДНОГО СЛОВА*)
IF freq < 0.09 THEN (*если частота ниже 1Hz то двигатель отключен*)
pcv_com_word := 1268; (*двигатель отключен (торможение с выбегом)*)
IF v1 < 2500 THEN
pcv_com_word := 1212; (*если скорость ниже 25м/c то используем быструю остановку*)
END_IF
ELSE
pcv_com_word := 1148 ; (*запуск двигателя*)
END_IF
send1_modbus(
Enable := enableRsSend,
Mode := MB_RTU,
DevAddr := 2,
RegAddr := 49999, (*РЕГИСТР КОМАНДНОГО СЛОВА ПРИВОДА*)
Value := pcv_com_word, (*КОМАНДНОЕ СЛОВО*)
ComHandle := Settings1.Port,
TimeOut := tWorkBlock,
Complete => cmpl4,
Exception => err4
);
rt_step_3(CLK:= cmpl4);
IF rt_step_3.Q THEN
enableRsSend := FALSE;
END_IF

IF tmrStepRs.Q AND enableRsSend = FALSE THEN
stepRs := 4;
tmrStepRs(IN:= FALSE);
IF enableRsSend = FALSE THEN enableRsSend := TRUE; END_IF
END_IF

4:
(*ЗАПРОС СОСТОЯНИЯ ЭЛЕКТРОДВИГАТЕЛЯ*)
get3_modbus(
Enable := enableRsSend,
Mode := MB_RTU,
DevAddr := 2,
FirstAddr := 50199, (*РЕГИСТР СЛОВА СОСТ. ПРИВОДА*)
Quantity := 1,
ComHandle := Settings1.Port,
TimeOut := tWorkBlock,
Buffer := buffer1,
Complete => cmpl5,
Exception => err5,
ByteCnt => dataSize1
);
rt_step_4(CLK:= cmpl5);
IF rt_step_4.Q THEN
IF err5 = 0 THEN
pcv_state_word := BYTE_TO_WORD(buffer1[1]) OR SHL (BYTE_TO_WORD(buffer1[0]),8);
END_IF
enableRsSend := FALSE;
END_IF

IF tmrStepRs.Q AND enableRsSend = FALSE THEN
stepRs := 1;
tmrStepRs(IN := FALSE);
IF enableRsSend = FALSE THEN enableRsSend := TRUE; END_IF
END_IF
END_CASE

Еще следует помнить что в библиотеке модбаса есть ошибка из-за которой через пачку в порт мусор идет. Для ее починки следует сделать следующее


(*
В БИБЛИОТЕКЕ OWEN MODBUS.LIB В ФБ MB_UNI_IO ЗАМЕНЯЕМ СТРОКУ 7 НА
WHILE SysComRead(ComHandle, ADR(DataBuf)+DataSize, SIZEOF(DataBuf)-DataSize, 0) <> 0 DO;
инче в случае получения ошибки таймаута FF в след пачке пойдем мусор.
Также для скорости 9600 необходимо установить значение таймера T_FRTU = ~5ms
*)