Работа ModBus в проектах FLProg

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#1

Сообщение Sancho » 28.11.2019{, 12:40}

Как часто замечаю, иногда пользователи не до конца понимают как устроена работа обмена по модбас, и, соответственно, требуют не реализуемые задачи.
ilusha писал(а):
24.11.2019{, 01:04}
Почему работает Очень медленно modbus?
Очень медленно.
Это так медленно, если я делаю
не правильно..
Синхронизация включена на 1 миллисекунду
И для общего понимания. Не спеша.
Слэйв модбас rtu. Slave ModBus RTU.
При создании кода для IDE FLProg перед первой платой вставляет дополнительные, необходимые для работы, переменные, функции.
Одной из них является функция, отвечающая за обмен по модбас. Выполняется она, как Вы догадались, один раз в цикле.
Сама по себе состоит из других, вложенных функций, отвечающих за разные задачи.
В примере - минимальный вариант - значение из регистра на выход ШИМ. Время цикла - минимальное.

Итак, вначале проверяется, есть ли что либо, а именно байты, в буфере serial( или Softserial - не принципиально).
1-й цикл. Если ответ пусто, 0, функция прерывается, программа выполняется дальше.
2-й цикл. Если есть, запоминается текущее количество доступных для считывания байт( в _modbusSlaveLastRec),
запоминается текущее время( в _modbusSlaveTime), функция прерывается, программа выполняется дальше.
3-й цикл. Если значение количества последних и сейчас доступных для считывания байт не совпадает, значит данные всё еще поступают в буфер serial.
Запоминается текущее количество доступных для считывания байт(_modbusSlaveLastRec),
запоминается текущее время(_modbusSlaveTime), функция прерывается, программа выполняется дальше.
N-й цикл. Значение количества доступных для считывания из буфера serial байт и старое значение одинаковы, то проверяем, сколько прошло времени,
между последним изменением, вдруг байт где-то заплутал. Если время ожидания получения байта ещё не вышло - прерываем функцию.
N+n цикл. Время вышло, больше байтов нет, начинаем обрабатывать полученное.
Замечу - время ожидания приёма байта зависит от скорости(раньше), у Автора сейчас составляем 5 мс....
обнуляем _modbusSlaveLastRec.
Загружаем все данные из буфера serial в буфер программы, попутно подсчитывая количество байт и следя за переполнением.
/*
Здесь у Автора нет защиты от переполнения буфера и получения нежданчиков:
если в моей библиотеке serial размер буфера больше стандартного, буфер модбаса переполнится,
выставится только флаг - bBuffOverflow, но данные продолжат записываться в ячейки, которые буферу не принадлежат...
Я в таких случаях просто вызываю read(), опустошая буфер serial. Легко чинится в коде перестановкой строк....
*/
Если количество полученных байт >= минимальной посылки, проверяем, нам ли она адресована. Если нет - функция прерывается, программа выполняется дальше.
Проверяем контрольную сумму, если не правильная - функция прерывается, программа выполняется дальше.
Далее, исходя из номера функции, делаем необходимые действия - записываем данные при необходимости и готовим ответ.
Передаём ответ мастеру.
Функция закончена, обмен проведён, программа выполняется дальше.

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

ecoins
Полковник
Сообщения: 2921
Зарегистрирован: 12.02.2016{, 11:40}
Репутация: 452
Откуда: Шатура
Имя: Энвер

Работа ModBus в проектах FLProg

#2

Сообщение ecoins » 28.11.2019{, 18:34}

Sancho писал(а):
28.11.2019{, 12:40}
Далее, исходя из номера функции, делаем необходимые действия - записываем данные при необходимости и готовим ответ.
Передаём ответ мастеру.
Функция закончена, обмен проведён, программа выполняется дальше.
-----
И здесь реализация ModBus в FLProg сама становится "тяжело-временным блоком", особенно если скорость обмене не высокая (например 9600) и кол-во переменных больше 10.
Это происходит из-за того, что функция Serialx.write() "тормозит" выполнение всей программы до момента, пока не будет завершен вывод всего сообщения.
Это недостаток Arduino IDE (библиотек) - почему так сделано, не очень ясно. Аппаратно контроллеры UART поддерживают вывод по прерываниям.
Когда-нибудь функцию вывода буфера UART с выставлением флага завершения вывода кем-нибудь будет написана.
Или самим придется написать. :smile453:
-----
Временные диаграммы при обмене через ModBus FLProg можно исследовать логическим анализатором на пинах RX,TX.

mafckz
Рядовой
Сообщения: 12
Зарегистрирован: 31.10.2019{, 06:57}
Репутация: 0
Имя: Роман

Работа ModBus в проектах FLProg

#3

Сообщение mafckz » 01.12.2019{, 23:07}

Прошу подтвердить мой вывод, что FLProg не поддерживает работу с адресами регистров (тегов) больше максимального значения типа integer ("по плюсу" = 32767), т.е. работа с адресом 40969 невозможна?

Arduino IDE при компиляции выдает:
C:\Users\PC_Home10\AppData\Local\Temp\flprog\pr10\pr10.ino:4:42:
warning: narrowing conversion of '40969l' from 'long int' to 'int' inside { } [-Wnarrowing]

И при проверке через MasterOPC Universal Modbus Server выдает ошибки связи

******** Update:
А нет, у меня проблемы в другой причине - не понимаю почему - в MasterOPC при указании тегов отличных от Coils выдает ошибки
Вложения
2019-12-02_014437.png
Последний раз редактировалось mafckz 01.12.2019{, 23:52}, всего редактировалось 2 раза.

Аватара пользователя
Rovki
Полковник
Сообщения: 4872
Зарегистрирован: 22.04.2016{, 17:25}
Репутация: 269
Откуда: Чехов
Имя: Анатолий
Контактная информация:

Работа ModBus в проектах FLProg

#4

Сообщение Rovki » 01.12.2019{, 23:31}

так 4 или 3 в переди слева это номер функции(области) и при указании адреса отбрасывается
Электронщик до мозга костей и не только

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#5

Сообщение Sancho » 02.12.2019{, 10:11}

mafckz, А зачем Вы такие номера прописываете? Для чего?
Но Вы правы - адреса регистров в флпрог имеют тип int, а не word, почему - незнаю:
int _modbusSlaveAddresTable_4[10]....
мой ник в нете и почте omelchuk890, если что. запомните на всякий. многие знают номер тлф.

mafckz
Рядовой
Сообщения: 12
Зарегистрирован: 31.10.2019{, 06:57}
Репутация: 0
Имя: Роман

Работа ModBus в проектах FLProg

#6

Сообщение mafckz » 02.12.2019{, 10:22}

Извиняюсь за предыдущий пост - совсем не в тему.
Оказывается, если использовать регистры в MasterOPC, которых нет в слейве - то будут ошибки как на скрине выше.
А зачем Вы такие номера прописываете? Для чего?
Такой адрес регистра использует nсерийный контроллер Pixel для передачи значения аварий приточно-вытяжной установки.
Думаю, если не получится, то на стороне HMI макросами передам значение в более меньший регистр (пустой)

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#7

Сообщение Sancho » 05.12.2019{, 17:56}

ecoins писал(а):
28.11.2019{, 18:34}
И здесь реализация ModBus в FLProg сама становится "тяжело-временным блоком", особенно если скорость обмене не высокая (например 9600) и кол-во переменных больше 10.
Это происходит из-за того, что функция Serialx.write() "тормозит" выполнение всей программы до момента, пока не будет завершен вывод всего сообщения.
Это недостаток Arduino IDE (библиотек) - почему так сделано, не очень ясно. Аппаратно контроллеры UART поддерживают вывод по прерываниям.
Когда-нибудь функцию вывода буфера UART с выставлением флага завершения вывода кем-нибудь будет написана.
Или самим придется написать.
Энвер, как я убедился, Вы доверяете только фактам.
Для Вас провёл специальный эксперимент, простой.
Всё нормально.
Вопрос в реализации в флпрог-модбас: Автор скорее всего намеренно сделал так, используя функцию flush (), и прицепив к этому делу пин включения передатчика.
Вы можете запросто переделать под свои нужды, заменив указанную функцию на другую, availableForWrite с небольшими доработками кода.
По скринам - пауза в тактах - скорее всего загрузка буфера
СпойлерПоказать
screenshot_19-12-05_17-43-10 - копия.png
screenshot_19-12-05_17-44-32 - копия.png
СпойлерПоказать

Код: Выделить всё

unsigned long _stou1 = 0UL;
void setup()
{
Serial.begin(19200);
_stou1 = millis();
DDRB |=B00000100;
}
void loop()
{
//Плата:1
if (!(0)){ if (_isTimer(_stou1, 200 )) {Serial.println(String("0123456789")); _stou1 = millis();}} else {_stou1 = millis();}

PORTB^=B00000100;// инвертируем пин, зелёная линия на скринах
}
bool _isTimer(unsigned long startTime, unsigned long period )
  {
  return (period <=millis() - startTime);
  }
Отправлено спустя 2 минуты 33 секунды:
Да, кстати, как смотрится новая функция таймера?
мой ник в нете и почте omelchuk890, если что. запомните на всякий. многие знают номер тлф.

ecoins
Полковник
Сообщения: 2921
Зарегистрирован: 12.02.2016{, 11:40}
Репутация: 452
Откуда: Шатура
Имя: Энвер

Работа ModBus в проектах FLProg

#8

Сообщение ecoins » 12.12.2019{, 20:39}

Sancho писал(а):
05.12.2019{, 17:59}
Энвер, как я убедился, Вы доверяете только фактам.
Для Вас провёл специальный эксперимент, простой.
Всё нормально.
Вопрос в реализации в флпрог-модбас: Автор скорее всего намеренно сделал так, используя функцию flush (), и прицепив к этому делу пин включения передатчика.
Вы можете запросто переделать под свои нужды, заменив указанную функцию на другую, availableForWrite с небольшими доработками кода.
По скринам - пауза в тактах - скорее всего загрузка буфера
Функция availableForWrite возвращает количество байтов (символов), доступных для записи в последовательный буфер, не блокируя операцию записи.
Но функции Serial.println() и Serial.write() выполняются без прерывания и выход из них происходит только после передачи всего буфера в линию. То есть процессор в это время кроме вывода в буфер ничем не занят ( только обработчиками аппаратных прерываний).
Осциллограммы не показывают загруженность процессора.
-------------------------
По таймеру. Хорошо написан. Но проблема переполнения регистра millis() через 71 582мин (49 дней) остается (... по моему мнению).

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#9

Сообщение Sancho » 13.12.2019{, 08:39}

ecoins писал(а):
12.12.2019{, 20:39}
То есть процессор в это время кроме вывода в буфер ничем не занят ( только обработчиками аппаратных прерываний).
Осциллограммы не показывают загруженность процессора.
Я прошу прощения - Вам сложно увидеть в коде, что в момент передачи происходит инверсия выхода, как указанно в цикле loop ? Строка 13. Увы. Думал осциллограммы Вам помогут. Там, кстати, видно, как выполнение loop приостанавливается в момент загрузки байта из буфера в выходной регистр.

Отправлено спустя 25 минут 30 секунд:
По поводу Ваших сомнений с таймером - файл.
Вложения
Заготовка Atmel_02_uint32 .flp
(112.94 КБ) 94 скачивания
мой ник в нете и почте omelchuk890, если что. запомните на всякий. многие знают номер тлф.

ecoins
Полковник
Сообщения: 2921
Зарегистрирован: 12.02.2016{, 11:40}
Репутация: 452
Откуда: Шатура
Имя: Энвер

Работа ModBus в проектах FLProg

#10

Сообщение ecoins » 13.12.2019{, 16:24}

Sancho писал(а):
13.12.2019{, 09:05}
Я прошу прощения - Вам сложно увидеть в коде, что в момент передачи происходит инверсия выхода, как указанно в цикле loop ? Строка 13. Увы. Думал осциллограммы Вам помогут. Там, кстати, видно, как выполнение loop приостанавливается в момент загрузки байта из буфера в выходной регистр.
Посмотрел повнимательнее и после проверил на логическом анализаторе.
Тест_Sancho.JPG
Тест_Sancho_2.JPG
Тест_Sancho_3.JPG
----
Действительно функция Serial.println(и видимо Serial.write) выдают в линию из буфера по прерываниям.
Любопытно, в явной форме это вроде нигде не описано - не видел.
Отлично, Sancho - спасибо большое.
Теперь проще будет работать с UART, с реализацией ModBus FLProg надо будет что-то подумать.
Попозже займемся, может и автор поправит...
---Спасибо.

Аватара пользователя
Nikan
Майор
Сообщения: 1194
Зарегистрирован: 29.12.2016{, 00:49}
Репутация: 100
Откуда: москва

Работа ModBus в проектах FLProg

#11

Сообщение Nikan » 14.12.2019{, 01:53}

.
Последний раз редактировалось Nikan 24.03.2023{, 01:29}, всего редактировалось 1 раз.

Аватара пользователя
Alias
Лейтенант
Сообщения: 481
Зарегистрирован: 27.11.2017{, 13:15}
Репутация: 36
Откуда: Rus44
Имя: Michael
Контактная информация:

Работа ModBus в проектах FLProg

#12

Сообщение Alias » 08.02.2020{, 17:50}

Все так интересно! Давно хотел уточнить, но думал, что закидаете тапками. Ладно, кидайте!..
Почти уже ввожу в эксплуатацию свой второй проект. Тоже на Меге2560. Собираюсь теперь дома построить сеть на Modbus, но вот именно некоторого понимания нет.
Например, насколько критично для надёжного выполнения основной программы большое количество передаваемых тегов?
Сейчас у меня 14 переменных byte ужаты в 7 регистров int16, и еще в одном - 14 переменных bool. Под КаСкаду. Хотелось бы и еще добавить, но максимальный период опроса в ней не может быть больше секунды, если я опять ничего не путаю.
Понимаю, что процессор не переключается на Modbus полностью и машинное время под основную программу остается, но вот много ли? Стоит ли стараться увеличивать период опроса, скажем, до 5 секунд, если реально ничего там критичного нет, или Modbus фоновая подпрограмма и не зацикливаться на этом? И скорость лучше поднять, да?
Мне уже отвечали, что у кого-то и под сотню тегов гуляет, но хочется несколько большего понимания.
Если что - порт железный, не софт.
PS Наверное, я еще сокращу количество тегов, "динамической" упаковкой: в регистр int в младшей "половинке" прописывать одну из 8 переменных byte, а в старшей - бит на соответствующем ей месте.

Аватара пользователя
Rovki
Полковник
Сообщения: 4872
Зарегистрирован: 22.04.2016{, 17:25}
Репутация: 269
Откуда: Чехов
Имя: Анатолий
Контактная информация:

Работа ModBus в проектах FLProg

#13

Сообщение Rovki » 08.02.2020{, 18:25}

Немного путаете- период опроса это не тайм аут и не пауза . Тайм аут это время отклика слейва на команду мастера , пауза в каскаде - это задежка на отправку посылки слейву .Обычно на отправку пакета уходит 30-60мс , умножим на 100 и получим ЗАДЕРЖКУ 3-6сек
которая визуально воспринимается как- нажал кнопку ,а лампа от нее загорелась через 3-6сек. Но это при обмене с устройством регистрами - одна переменная \одна посылка ,если использовать групповое чтение\запись то время сократиться . Это то что касается каскады . При этом в контроллере все процессы должны быть организованы правильно - это работа по прерываниям с учетом их важности(приоритета) итд..итп. Например при работе с контроллерами ПР200 и использованием Овенлоджик (FBD) , нет необходимости думать пользователю о прерываниях при работе с аналоговыми входами,таймерами и модбасом , там конфликты исключены на программном уровне , да ценой быстродействия - быстродействие по входам не более 500гц (мин.время цикла 1мс)...
Электронщик до мозга костей и не только

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#14

Сообщение Sancho » 08.02.2020{, 22:06}

Alias, тут, немного выше, писал.
мой ник в нете и почте omelchuk890, если что. запомните на всякий. многие знают номер тлф.

Аватара пользователя
Alias
Лейтенант
Сообщения: 481
Зарегистрирован: 27.11.2017{, 13:15}
Репутация: 36
Откуда: Rus44
Имя: Michael
Контактная информация:

Работа ModBus в проектах FLProg

#15

Сообщение Alias » 09.02.2020{, 00:07}

Да, я все это видел и неоднократно возвращался сюда снова. Мне не нужны детали, понимай я в них, сам бы себе и ответил. Мне бы понять происходящее на пальцах, можно тремя фразами. Понимаю, опираясь на первое сообщение, примерно так, по-непрограммистски:
Начиная с 1 по N+n цикл основная программа успевает отрабатывать в полном объеме, причем в зависимости от сложности вычислений по несколько циклов и вероятность пропустить что-то важное, например, превышение контролируемого значения тока, невелика. То есть мы в любом случае полный цикл пройдем вместе с тем самым блоком слежения за током.
Затем начинается перегрузка из буфера в буфер, отработка команды, ответ мастеру. Это понятно. И вот тут немного бы уточнить, как это происходит.
Если все ресурсы отдаются этой процедуре сразу, то есть по входу в какую-то библиотеку, из которой пока все не выполнится, не выйти и наш пользовательский код не выполняется, то сколько времени на это уходит? Мне ведь нужно смотреть за током постоянно, ну хотя бы 1-2 раза в секунду, боюсь пропустить превышение и не отреагировать. Зависит ли это время "невыхода" из "библиотеки" от количества тегов, передаваемых по Модбас? Или между передаваемыми тегами пользовательская программа тоже исполняется?
От этого зависит моя тактика:
Если моя программа не исполняется, пока передаются данные, я буду изыскивать все возможности по сокращению тегов, чтобы гарантированно не проспать событие.
Если во время передачи тегов мастеру моя программа тоже будет исполняться, хотя бы пару циклов в секунду, я буду перегонять больше данных и не скупиться, а работать комфортно. Естественно, тоже упаковывая, как только смогу.
Вот, собственно, что меня беспокоит.
Ну и вопрос, почему при количестве тегов больше 10 Ардуино начинает тормозить и есть ли разница, что это - Nano или Mega?
И еще: а если в программе очень большие формулы и сложные расчёты стыковки с МКС, Модбас не пропустит поступление очередного байта или бита?Программа ведь может не успеть передать управление к блокам Модбас, тем, что до первой платы.
Rovki писал(а):
08.02.2020{, 18:25}
Немного путаете- период опроса это не тайм аут и не пауза
Да, Вы говорили это в Экспрессе, но я имею в виду период опроса, вернее, период синхронизации в терминологии FlProg, там у Мастера в настройках этот период можно выбирать. Далеко не все мои слейвы будут работать на КаСкаду, скорее всего только один, поэтому я и хочу разобраться, как часто я могу их опрашивать без потери контроля над ключевыми параметрами. В некоторых случаях мне достаточно даже 10 и более секунд между опросами.
Если я правильно понимаю термин "Период", означающий отрезок времени, в течение которого циклический процесс совершает полный цикл, Мастер запрашивает данные от слейва не по отсчету времени от предыдущего ответа, а по своим часам, иначе этот период может плавать. То есть без умножения на 100. Кстати, а что это? Это только в КаСкаде, а где почитать? Хотелось бы заранее все понять и не наступать на грабли. Но это уже другая тема.

Аватара пользователя
Rovki
Полковник
Сообщения: 4872
Зарегистрирован: 22.04.2016{, 17:25}
Репутация: 269
Откуда: Чехов
Имя: Анатолий
Контактная информация:

Работа ModBus в проектах FLProg

#16

Сообщение Rovki » 09.02.2020{, 00:36}

Передача \прием это асинхронный процесс в модбасе , поэтому исполнение всех команд (запись\чтение тэгов) идет не по часам ,а последовательно- дал команду одному слейву ,прочитал (записал) данные , переходит к другому регистру идт по кругу (период) . Но время периода в каскаде влияет только на задержку ...Ответ от слейва всегда плавает и стало бы и весь период так же . А плавает ответ ,потому что слейв занят еще и вычислительными задачами и работу по другим интерфесам и все эти процессы асинхронны.
Электронщик до мозга костей и не только

Аватара пользователя
Alias
Лейтенант
Сообщения: 481
Зарегистрирован: 27.11.2017{, 13:15}
Репутация: 36
Откуда: Rus44
Имя: Michael
Контактная информация:

Работа ModBus в проектах FLProg

#17

Сообщение Alias » 09.02.2020{, 00:44}

То есть период синхронизации в flp это тоже пауза по факту?
А что такое 100?

Аватара пользователя
Rovki
Полковник
Сообщения: 4872
Зарегистрирован: 22.04.2016{, 17:25}
Репутация: 269
Откуда: Чехов
Имя: Анатолий
Контактная информация:

Работа ModBus в проектах FLProg

#18

Сообщение Rovki » 09.02.2020{, 08:52}

Alias писал(а):
09.02.2020{, 00:44}
То есть период синхронизации в flp это тоже пауза по факту?
А что такое 100?
100- это 100 регистров (сетевых регистров в примере). Умножаю на 100 исходя из последовательной обработки их...
Электронщик до мозга костей и не только

Аватара пользователя
Sancho
Полковник
Сообщения: 4066
Зарегистрирован: 25.12.2015{, 17:32}
Репутация: 590
Откуда: Ярославль.
Имя: Александр
Контактная информация:

Работа ModBus в проектах FLProg

#19

Сообщение Sancho » 18.12.2020{, 16:36}

Ну что.
Новые рассуждения.
Приветствую всех форумчан и гостей.
Давно читаю на нашем форуме о проблемах и непонимании в работе
serial порта и модбаса, rtu в частности.
Попытаюсь высказать моё видение данных моментов.
Не претендую ни на учебник, ни на предположение - просто опус.
Начну с serial, hrdware, а не soft.
Оба буфера uart - кольцевые, т.е. обращение к 1-му байту будет сразу после последнего.
Буфер работает с того места, где остановился, даже если это уже другая задача, индекс переменной не сбрасывается.
Как работает приём. Названия переменных - из библиотеки.
Буфер пуст, индексы _rx_buffer_head и _rx_buffer_tail равны, голова и хвост, разница 0.
В режиме приёма при поступлении очередного байта в аппаратный регистр вызывается прерывание,
в котором его обработчик копирует байт в буфер порта, _rx_buffer[SERIAL_RX_BUFFER_SIZE],
по индексу _rx_buffer_head, при этом увеличивая его значение на единицу.
При этом основная программа, которая всё это время работала, приостанавливается на время
выполнения этого копирования.
По окончании приёма функцией available мы можем узнать, сколько байт приняли,
т.е расстояние от головы до хвоста :)
При чтении, read, мы получаем значение из буфера с индексом _rx_buffer_tail, одновременно
увеличивая его на 1, постепенно подтягивая к голове, _rx_buffer_head :)
Как только значения головы и хвоста станут равными, попытка чтения вернёт -1.
Передача.
Для примера массив.
Отправляем его Serial.write(buf, len); // buf - сам массив, len - сколько байт отправить
Что происходит: данные копируются в буфер uart, байты по одному начинают попадать в сам
передатчик данных. При этом функция availableForWrite() может вернуть нам, сколько
свободных ячеек осталось в буфере передачи.
По окончания передачи очередного байта вызывается прерывание, обработчик
которого кладёт следующий байт данных из буфера в передатчик.
Так продолжается, до тех пор, пока буфер не опустеет, при этом availableForWrite вернёт
размер SERIAL_TX_BUFFER_SIZE-1.
Вот так, в моём видении, происходит работа HardwareSerial.
Есть ещё функции, но о них позже.

Теперь о ModBus RTU. В FlProg.
Год назад писал первый пост темы
Процитирую:
СпойлерПоказать
Sancho писал(а):
28.11.2019{, 12:40}
Как часто замечаю, иногда пользователи не до конца понимают как устроена работа обмена по модбас, и, соответственно, требуют не реализуемые задачи.
ilusha писал(а):
24.11.2019{, 01:04}
Почему работает Очень медленно modbus?
Очень медленно.
Это так медленно, если я делаю
не правильно..
Синхронизация включена на 1 миллисекунду
И для общего понимания. Не спеша.
Слэйв модбас rtu. Slave ModBus RTU.
При создании кода для IDE FLProg перед первой платой вставляет дополнительные, необходимые для работы, переменные, функции.
Одной из них является функция, отвечающая за обмен по модбас. Выполняется она, как Вы догадались, один раз в цикле.
Сама по себе состоит из других, вложенных функций, отвечающих за разные задачи.
В примере - минимальный вариант - значение из регистра на выход ШИМ. Время цикла - минимальное.

Итак, вначале проверяется, есть ли что либо, а именно байты, в буфере serial( или Softserial - не принципиально).
1-й цикл. Если ответ пусто, 0, функция прерывается, программа выполняется дальше.
2-й цикл. Если есть, запоминается текущее количество доступных для считывания байт( в _modbusSlaveLastRec),
запоминается текущее время( в _modbusSlaveTime), функция прерывается, программа выполняется дальше.
3-й цикл. Если значение количества последних и сейчас доступных для считывания байт не совпадает, значит данные всё еще поступают в буфер serial.
Запоминается текущее количество доступных для считывания байт(_modbusSlaveLastRec),
запоминается текущее время(_modbusSlaveTime), функция прерывается, программа выполняется дальше.
N-й цикл. Значение количества доступных для считывания из буфера serial байт и старое значение одинаковы, то проверяем, сколько прошло времени,
между последним изменением, вдруг байт где-то заплутал. Если время ожидания получения байта ещё не вышло - прерываем функцию.
N+n цикл. Время вышло, больше байтов нет, начинаем обрабатывать полученное.
Замечу - время ожидания приёма байта зависит от скорости(раньше), у Автора сейчас составляем 5 мс....
обнуляем _modbusSlaveLastRec.
Загружаем все данные из буфера serial в буфер программы, попутно подсчитывая количество байт и следя за переполнением.
/*
Здесь у Автора нет защиты от переполнения буфера и получения нежданчиков:
если в моей библиотеке serial размер буфера больше стандартного, буфер модбаса переполнится,
выставится только флаг - bBuffOverflow, но данные продолжат записываться в ячейки, которые буферу не принадлежат...
Я в таких случаях просто вызываю read(), опустошая буфер serial. Легко чинится в коде перестановкой строк....
*/
Если количество полученных байт >= минимальной посылки, проверяем, нам ли она адресована. Если нет - функция прерывается, программа выполняется дальше.
Проверяем контрольную сумму, если не правильная - функция прерывается, программа выполняется дальше.
Далее, исходя из номера функции, делаем необходимые действия - записываем данные при необходимости и готовим ответ.
Передаём ответ мастеру.
Функция закончена, обмен проведён, программа выполняется дальше.

Что я изменяю:
При приёме, особенно, когда присутствует большое количество слэйвов, каждому приходится переваривать пакеты,
отправленные не ему. А пакеты ответа других слэйвов не флпрог мастеру могут иметь приличный размер.
Однако, после завершения приёма посылки в буфер UART(N+n цикл. из цитаты сверху), прежде чем копировать в буфер самой программы,
можно посмотреть на первый байт, адрес устройства. Это peek(). И если мы убедимся, что это не нам, просто сбрасываю буфер.
Для этого пришлось дописать в библиотеку ещё одну простейшую функцию:
в хедер
virtual void reset(void);
в реализацию
void HardwareSerial::reset(){
_rx_buffer_head = _rx_buffer_tail;
}
Всё. Нет необходимости копировать без надобности.

При передаче. FLProg. пока весь буфер не будет отправлен - программа не выполняется, т.к. после
Serial.write( _modbusMasterBuffer, _modbusMasterBufferSize );
есть
delay(1);
и
Serial.flush();
Последняя функция крутится в цикле, пока все данные из буфера uart не будут отправлены, соответственно, цикл стоит на паузе!
Мог предположить, что это нужно при управлении пином передачи, т.е. перед передачей включаем, потом, после окончания, выкл.
Однако сейчас я вижу:

Код: Выделить всё

UCSR0A=UCSR0A |(1 << TXC0);
digitalWrite(2, HIGH );
  delay (1);
Serial.write( _modbusSlaveBuffer, _modbusSlaveBufferSize );
 while (!(UCSR0A & (1 << TXC0)));
  delay (1);
digitalWrite(2, LOW );
Serial.flush();
Ну да ладно...
Представляете, у Вас 55 байт на скорости 9600 приостанавливают программу на время около 50мс. А если битрэйт медленнее?
Теперь вспомним о availableForWrite() .
Немного поколдуем, дописав в библиотеку новую функцию:

Код: Выделить всё

virtual boolean finishWrite(void);
boolean HardwareSerial::finishWrite(void)
{
return (SERIAL_TX_BUFFER_SIZE - 1) == availableForWrite();
}
Что нам это даёт? Мы можем включить пин передачи, отправить буфер в uart, а в теле программы отслеживать, пока пин в 1,
что возвращает finishWrite(). Как только возвращает 1, можно с задержкой, равной времени передачи двух байт, выключить пин упр.
Почему нужна задержка - возможно буфер пуст, но передатчик отправляет последний байт.
Как в оружии - магазин вроде и пуст, но патрон в патроннике может быть.
Необходимая задержка по натуральным тестам - на 9600 - 2000 мкс, на 115200 - 170.
Пример для теста:

Код: Выделить всё

if (_gtv1 == 1) {
  digitalWrite(9, 1);
  Serial.println("qwrtyrafghgzsjhgkuygbzkjvhggzgb");
}

if(digitalRead(9)){
  if (Serial.finishWrite()){ 
    if(_isTimerMicros(qwe,170)){digitalWrite(9, 0);}
    }
  else qwe=micros();
}
Ну вот вроде и всё, касательно uart и modbus в роли слэйва, из того, чем хотел поделиться.
Надеюсь, кому-то это пригодится.
О режиме modbus master rtu постараюсь написать в ближайшее время. Опишу своё видение, откуда ноги растут у "тормоза".
Ну, и как я с этим борюсь.
Извиняюсь замногабукв, за возможную скомканность.
мой ник в нете и почте omelchuk890, если что. запомните на всякий. многие знают номер тлф.

ecoins
Полковник
Сообщения: 2921
Зарегистрирован: 12.02.2016{, 11:40}
Репутация: 452
Откуда: Шатура
Имя: Энвер

Работа ModBus в проектах FLProg

#20

Сообщение ecoins » 18.12.2020{, 19:24}

Sancho писал(а):
18.12.2020{, 16:36}
Ну вот вроде и всё, касательно uart и modbus в роли слэйва, из того, чем хотел поделиться.
Надеюсь, кому-то это пригодится.
О режиме modbus master rtu постараюсь написать в ближайшее время. Опишу своё видение, откуда ноги растут у "тормоза".
Отлично и подробно.
Как бы проблему решить на уровне ПБ FLProg и новой библиотеки?
Может возьмется кто-нибудь.
У нас в планах стоит эта задача, но пока отвлекают другие системные задачи.
Готовы объединить усилия - обсуждении, написании отдельных частей кода, отладке.
На наш взгляд такая библиотека должна UART выбирать по его номеру (сейчас некоторые контроллеры
имеют до 9 UART + SoftSerial +TCP) и должна быть релевантна к разным архитектурам без какой-либо дополнительной адаптации (#define и т.п.).

Ответить

Вернуться в «Обучающие примеры работы в FLProg»