Вопросы по Modbus

Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Сб сен 12, 2026 18:10:06Это слишком много, что бы считать внутри прерывания, а вытесняющей многозадачности у меня там нет.
А зачем для этого "вытесняющая многозадачность"? Для отделения уровня приёма от уровня обработки, достаточно иметь прерывание + фоновый процесс. Или прерывания двух разных уровней приоритета. Принимающее прерывание имеет более высокий приоритет и прерывает обрабатывающее прерывание или фоновый процесс.

PS: Это уже не говоря о том, что в наше время нет никакого смысла что-то делать на AVR. Когда есть в наличии много дешёвых ARM-ов, на которых нет никаких проблем ни с многозадачностью ни с множеством уровней прерываний.
Да даже на STM8 нет никаких проблем сделать такое разделение уровней.
Реклама
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

jcxz писал(а): Вс сен 13, 2026 10:10:06 А зачем для этого "вытесняющая многозадачность"?
За тем, что основной поток может быть занят другой задачей, и обработка принятого кадра будет ждать её завершения. Это может быть долго, до 3,5T можно не успеть.

Переделал.
По первому принятому байту, определяет мне ли пакет. Если нет - игнорирует остальные байты, по таймауту сразу на прием следующего пакета.
Вся обработка отложена.
Стандарт допускает, но бяку словить можно.

STM32 сделан значительно сложнее, особо разбирается с ним пока желания нет.
Будет заказ за деньги, с требованием точного соответствия - тогда посмотрим.
Реклама
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Вс сен 13, 2026 11:17:01
jcxz писал(а): Вс сен 13, 2026 10:10:06 А зачем для этого "вытесняющая многозадачность"?
За тем, что основной поток может быть занят другой задачей, и обработка принятого кадра будет ждать её завершения. Это может быть долго, до 3,5T можно не успеть.
Тогда читаем моё сообщение немного дальше:
jcxz писал(а): Вс сен 13, 2026 10:10:06 Для отделения уровня приёма от уровня обработки, достаточно иметь прерывание + фоновый процесс. Или прерывания двух разных уровней приоритета. Принимающее прерывание имеет более высокий приоритет и прерывает обрабатывающее прерывание или фоновый процесс.
Про принимающее и обрабатывающее прерывание. Прочитали?
По завершению приёма кадра (обнаружению границы кадра) пингуем обрабатывающее прерывание и выходим. "Пингуем" - т.е. возбуждаем его программно.
Всё. Получили вытеснение фонового процесса + немешание работе принимающего прерывания. Это стандартный способ обработки потока входящих кадров.
Я правда не знаю - есть ли в AVR разделение прерываний по уровням приоритета и возможность программного возбуждения. Но в STM8 всё это есть. AVR не должен быть хуже чем STM8.
~Dimon~ писал(а): Вс сен 13, 2026 11:17:01По первому принятому байту, определяет мне ли пакет. Если нет - игнорирует остальные байты, по таймауту сразу на прием следующего пакета.
Излишне усложнили. Проще всю обработку (и адрес тоже) скинуть на уровнень обработки. Впрочем - можно и так.
~Dimon~ писал(а): Вс сен 13, 2026 11:17:01STM32 сделан значительно сложнее, особо разбирается с ним пока желания нет.
Имхо - весьма просто. Зато потом будете тратить гораздо меньше времени на решение задач. Дорогу осилит идущий. :)
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

Можно ножку программно дернуть, на которой аппаратное прерывание.
Вроде на AVR даже вложенные прерывания возможны, типа sei() внутри обработчика дернуть, я просто не пробовал еще.
Но это имеет смысл, только при точном таймере, а их на AVR всегда не хватает :(
Реклама
Эиком - электронные компоненты и радиодетали
Это не хвост, это антенна
Сообщения: 1364
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

~Dimon~ писал(а):Можно ножку программно дернуть, на которой аппаратное прерывание.
Есть третий способ.
Если программа имеет архитектуру суперцикла, то можно просто в конце суперцикла обработать пришедший фрейм. И никакой многозадачности не нужно.
Реклама
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

while(2) {
task1();
task2();
task3();

sleep_cpu();
}

Вот так примерно делаю. Это если упрощенно представить. На самом деле там могут быть задачи, запускающиеся с разной регулярностью (раз в секунду, 1000 раз в секунду, и т.д.).
Даже если понавтыкать там везде, проверки необходимости перейти к срочной задаче, все равно текущая задача должна завершить свое выполнение, только потом произойдет переход к срочной. А ждать этого может придется недопустимо долго.

Вот еще интересная ситуация возникла...
Есть 32-битное значение, два соседних Input register, читать их по отдельности бессмысленно, или например запись только одного, приведет к нарушению нормального функционирования устройства.
Пока сделал возврат (0x02) ILLEGAL DATA ADDRESS в таких случаях, но не знаю "общепринятой практики" на это счет...
Реклама
Это не хвост, это антенна
Сообщения: 1364
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

~Dimon~ писал(а):А ждать этого может придется недопустимо долго.
"Долго"- это сколько?
Всё имеет численное выражение, требования ТЗ или здравого смысла. Я ведь не зря несколько раз упоминал про таймауты узлов. И пример с вольтметрами приводил. У нас на том объекте было несколько контроллеров, которые нужно было опрашивать быстро, и несколько десятков вольтметров, не критичных с темпу опроса. Поэтому для правильной работы проложили две ветки EIA-485, чтобы медленные волтметры не мешали быстрым контроллерам.
Пока сделал возврат (0x02) ILLEGAL DATA ADDRESS
Так и делают.
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

tonyk писал(а): Пн сен 14, 2026 17:17:00 "Долго"- это сколько?
Да кто ж знает? Может и 2 секунды.
Это "библиотечная" реализация RTU, на (почти) все случаи жизни.

В целом идея мне понравилась, можно "почти бесплатно" разделить задачи не на 2 а на 3 приоритета:
1. Неотложные (прерывания).
2. Срочные.
3. Обычные.
Запуск более приоритетной, немедленно приостанавливает выполнение менее приоритетной.
Только не дергать другое прерывание, а разрешить другие прерывания внутри обработчика.
После этого, весь последующий код (внутри обработчика) превращается из "Неотложного" в "Срочный", и может быть приостановлен для обработки другого прерывания.

Вот тут про реализацию этого трюка на AVR подробней, после цитат:
viewtopic.php?p=1348441#p1348441
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Пн сен 14, 2026 14:41:45Но это имеет смысл, только при точном таймере
Почему? При чём тут таймер?

Это имеет смысл при наличии широковещательных кадров. Когда новый кадр может начаться пока ещё не успел обработаться старый. Если у вас нет потока широковещательных кадров, то не нужно.
~Dimon~ писал(а): Пн сен 14, 2026 16:37:53 Есть 32-битное значение, два соседних Input register, читать их по отдельности бессмысленно, или например запись только одного, приведет к нарушению нормального функционирования устройства.
Пока сделал возврат (0x02) ILLEGAL DATA ADDRESS в таких случаях, но не знаю "общепринятой практики" на это счет...
Что из себя представляют эти регистры? Это регистры какой-то периферии? Или данные в обычной памяти?
~Dimon~ писал(а): Пн сен 14, 2026 17:51:14 В целом идея мне понравилась, можно "почти бесплатно" разделить задачи не на 2 а на 3 приоритета:
1. Неотложные (прерывания).
2. Срочные.
3. Обычные.
Запуск более приоритетной, немедленно приостанавливает выполнение менее приоритетной.
Это общепринятый подход. Так называемая "псевдо-многозадачность".
~Dimon~ писал(а): Пн сен 14, 2026 17:51:14Только не дергать другое прерывание, а разрешить другие прерывания внутри обработчика.
Другие прерывания - это другие прерывания. Они и так должны иметь разные приоритеты и более приоритетные должны прерывать менее приоритетные.
А то, о чём я писал, это именно запуск псевдозадачи для обработки события (возникшего в прерывании) на более низком приоритете. Не мешая прерыванию продолжать работать дальше. Используется при всяких потоковых обработках. Когда нет РТОС. В случае с РТОС в этом месте пингуется какой-либо объект РТОС (на котором ждёт обрабатывающая задача), а случае "без РТОС", роль обрабатывающей задачи выполняет программно-возбуждаемое прерывание. Позволяет в некоторых случаях обходиться без РТОС и иметь несколько таких псевдо-задач. С некоторыми ограничениями конечно.
Последний раз редактировалось jcxz Пн сен 14, 2026 18:08:22, всего редактировалось 1 раз.
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

jcxz писал(а): Пн сен 14, 2026 17:55:42 Это имеет смысл при наличии широковещательных кадров.
Не только.
Быстрый ответ другого ведомого. Если я не успею к его началу - приму "битый" пакет.
В случае тихого игнора, это не страшно. Но если у меня есть счетчики ошибок - они будут ложно накручиватся.
jcxz писал(а): Пн сен 14, 2026 17:55:42 Что из себя представляют эти регистры? Это регистры какой-то периферии? Или данные в обычной памяти?
Что угодно. Если вы прочитаете их по отдельности, двумя запросами, то между запросами данные там могли поменяться, получите чепуху, половинки двух разных значений. При считывании одним запросом, целостность данных гарантируется.
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Пн сен 14, 2026 18:08:09 Что угодно. Если вы прочитаете их по отдельности, двумя запросами, то между запросами данные там могли поменяться, получите чепуху, половинки двух разных значений. При считывании одним запросом, целостность данных гарантируется.
Что за "запросы"? Вы имеете в виду запросы через протокол обмена (Modbus-RTU)?
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

jcxz писал(а): Пн сен 14, 2026 17:55:42 программно-возбуждаемое прерывание
На AVR можно просто в какой то точке обработчика прерывания, разрешить реагировать на другие прерываний.
Дальнейший (долгий) код будет выполнен до возврата в основной поток, при этом другие прерывания будут обрабатываться, все довольны.

Или вы хотите запускать программное прерывание из основного потока? Зачем? Смысл?
jcxz писал(а): Пн сен 14, 2026 18:10:25 Что за "запросы"? Вы имеете в виду запросы через протокол обмена (Modbus-RTU)?
Да, 32 битное значение лежит в двух соседних 16-бит регистрах, вы можете прочитать оба одним запросом к ведомому, и я гарантирую целостность данных.
Можете сделать к ведомому два запроса, на чтение сперва одного, затем второго 16-бит регистра, но в этом случае рискуете получить чепуху.
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Пн сен 14, 2026 18:22:57 На AVR можно просто в какой то точке обработчика прерывания, разрешить реагировать на другие прерываний.
Дальнейший (долгий) код будет выполнен до возврата в основной поток, при этом другие прерывания будут обрабатываться, все довольны.
Это не то. Во-первых: этот долгий код тогда будет выполняться в контексте этого прерывания и заблокирует его работу до завершения. Также заблокирует работу всех прочих приоритетных прерываний.
~Dimon~ писал(а): Пн сен 14, 2026 18:22:57Или вы хотите запускать программное прерывание из основного потока?
Нет конечно. Я же писал: возбуждается из приёмного прерывания. Т.е. - программно генерируется запрос прерывания. Которое имеет более низкий приоритет и которое сработает (войдёт в ISR) сразу, как только произойдёт выход из приёмного приоритетного ISR. Точнее - когда произойдёт выход из всех вложенных ISR, с уровнями приоритета выше, чем программного.
~Dimon~ писал(а): Пн сен 14, 2026 18:22:57 Да, 32 битное значение лежит в двух соседних 16-бит регистрах, вы можете прочитать оба одним запросом к ведомому, и я гарантирую целостность данных.
Можете сделать к ведомому два запроса, на чтение сперва одного, затем второго 16-бит регистра, но в этом случае рискуете получить чепуху.
В таком случае (как писали выше) - или выдавать ошибку на частичное чтение.
Или можно сделать такой механизм: При чтении скажем младших 16 бит - сделать атомарное защёлкивание всего 32-битного значения. Старшие 16 бит положить в теневую копию, младшие - вернуть в ответе. И последующее чтение старших 16 бит - читать из этой теневой копии. И обязать всегда читать в такой последовательности: младшие 16 бит, затем старшие 16 бит.
Так делают даже в некоторых аппаратных регистрах.
Это не хвост, это антенна
Сообщения: 1364
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

~Dimon~ писал(а):Если я не успею к его началу - приму "битый" пакет.
Успеете. Уже устал объяснять, почему кадры не слипнутся. Возьмите для примера настройки _любого_ ОРС-сервера для протокола Modbus/RTU. В любом из них в том или ином виде есть настройки периодов запросов и _таймаутов_ для ответа ведомого. И пока сервер не_получит ответ от ведомого или не_закончится таймаут, в течении которого сервер ждёт ответ от ведомого, следующий запрос от сервера в сеть не_уйдёт. Задача того, кто настраивает опрос, указать правильное значение этого таймаута.
https://cloud.mail.ru/public/LxUF/ecoSTrEgg
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

jcxz писал(а): Пн сен 14, 2026 18:39:17 Во-первых: этот долгий код тогда будет выполняться в контексте этого прерывания и заблокирует его работу до завершения. Также заблокирует работу всех прочих приоритетных прерываний.
Я извиняюсь, но вы что то не поняли, все другие прерывания будут работать.
Даже это же прерывание повторно, сработает, если специально не заблокируете данную возможность.

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

ISR(USART0_RXC_vect) {
    // Короткий код, другие прерывания заблокированы
    if(...) return; // Возврат в основной поток
    sei(); // Разрешение прерываний
    // Долгий код, приостанавливается другими прерываниями (если возникнут)
} // Возврат в основной поток
tonyk писал(а): Пн сен 14, 2026 18:41:42 Успеете.
Если настроите на всех остальных ведомых паузу перед ответом, что бы ни кто не смел отвечать через 3,5T, по тому что я могу не успеть.
Я же все пакеты слушаю, ответы других ведомых то же слышу, пока не пойму что "это не мне", либо до первого байта (адрес ведомого), либо до проверки CRC.

Я ведомого делаю, если вы вдруг что то другое подразумевали.
Это не хвост, это антенна
Сообщения: 1364
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

~Dimon~ писал(а):Если настроите на всех остальных ведомых паузу перед ответом, что бы ни кто не смел отвечать через 3,5T, по тому что я могу не успеть.
Приплыли. Вы так и не поняли, _как_ работает Модбас.
Уясните уже простые вещи, лежащие в основе протокола: отвечает только тот ведомый, которого спросили. Остальные молчат. Это раз. На широковещательные запросы от ведущего ведомые не_отвечают. Это два.
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

Все верно, но вы недосмотрели ситуацию о которой я говорю.
Ведомый вынужден слушать ВЕСЬ трафик, в том числе чужие ответы (других ведомых), по той причине, что у него нет надежного способа определить, этот пакет от ведущего или другого ведомого?
Когда другой ведомый начинает отвечать ведущему, я должен быть готов словить начало этого пакета, по тому что не знаю от кого он, вдруг от ведущего? От куда я могу знать что нет?
Я должен уже слушать линию в момент 3,5T, а не заниматься в это время расчетом CRC предыдущего пакета, и чем то там еще.

Про разрешение прерываний внутри обработчика - даже не трик оказывается, Atmel сам предлагает так делать если надо :)
Мудрый кот
Сообщения: 1826
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

~Dimon~ писал(а): Пн сен 14, 2026 19:01:16 Я извиняюсь, но вы что то не поняли, все другие прерывания будут работать.
Даже это же прерывание повторно, сработает, если специально не заблокируете данную возможность.
Возможно. Я не знаю как устроена система прерываний в AVR.
Там нет приоритетов?
Все прерывания ходят на одном уровне приоритета?
Автоматически запрещаются ВСЕ прерывания на входе в ISR и автоматически разрешаются на выходе из ISR?
И можно разрешить только все сразу прерывания (с возможным повторным входном в тот же ISR)?
Если всё так, то это что-то очень примитивное.

На Cortex-M при входе в ISR блокируются только прерывания текущего и всех нижележащих уровней. Глобальный запрет/разрешение прерываний на это не влияют. Соответственно - пока не выйдешь из ISR, ни прерывания более низких приоритетов ни того же самого приоритета не могут вызваться. Они ждут. Только более приоритетные могут прервать текущий ISR.
На STM8 примерно так же сделано. Только там сильно проще - уровней всего 3 (в отличие от Cortex-M, где куча уровней приоритета).
Поэтому и там и там можно создавать псевдозадачи (низкоприоритетные ISR), которые можно активизировать из высокоприоритетных ISR (обслуживания периферии).

PS: Если на AVR такая примитивная система прерываний, что не даёт ничего делать - тем более уходите с AVR!
Вы уже упёрлись там в потолок. А значит - доросли до изучения ARM. :)
Это не хвост, это антенна
Сообщения: 1364
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

~Dimon~ писал(а):Ведомый вынужден слушать ВЕСЬ трафик, в том числе чужие ответы (других ведомых), по той причине, что у него нет надежного способа определить, этот пакет от ведущего или другого ведомого?
Приплыли. Вы внимательно смотрели форматы фреймов? Вы разобрались с ADU и PDU? подсказываю: _все_ фреймы начинаются с адреса ведомого, поэтому у ведомого есть чёткие критерии (не один!) выделения своего фрейма: адрес ведомого и CRC.
Вот иерархия, вникните, наконец-то, в неё:

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

struct MB_PDU
{
    uint8
        func,
        data[ 252 ];


    inline uint8
    putFunc( uint8 _f )
    {
        func = _f;
        return( 1 );
    }

    inline uint8
    getFunc( void )
    {
        return( func );
    }

    inline int8
    putDataLenght( uint8 _l )
    {
        data[ 0 ] = _l;
        return( 1 );
    }

    inline uint8
    getDataLenght( void )
    {
        return( data[ 0 ] );
    }

    inline int16
    putToData( uint8 _idx, uint16 _value )
    {
        data[ _idx ]     = ( _value >> 8 );
        data[ _idx + 1 ] = ( _value & 0xFF );
        return( 2 );
    }

    inline uint16
    getFromData( uint8 _idx )
    {
        uint16
            value = 0;

        value |= data[ _idx ] << 8;
        value |= data[ _idx + 1 ];

        return( value );
    }
};

//------------------------------------------------------------------------------

struct MB_RTU_ADU
{
    uint8
        node;

    MB_PDU
        pdu;

    uint8
        reserved[ 3 ];  // CRC+guard
};

struct MB_TCP_ADU
{
    uint16
        transactionID,
        protocolID;

    uint16
        lenght;

    uint8
        node;

    MB_PDU
        pdu;
};

union MB_ADU
{
    MB_RTU_ADU rtu;
    MB_TCP_ADU tcp;
};
Вымогатель припоя
Сообщения: 502
Зарегистрирован: Пт окт 28, 2011 16:01:18

Сообщение ~Dimon~ »

Приоритеты есть.
Как сказано в теме по ссылке - приоритет работает только до перехода к обработчику, он только выбирает какое прерывание обслужить первым, в конкурентной ситуации. Настраивать приоритеты нельзя, это в железо вшито.
Аппаратно запрещаются все прерывания при переходе к ISR, разрешаются при выходе из ISR инструкцией RETI. Если вы выйдите по RET, JMP - прерывания останутся запрещены, на других архитектурах, что я видел на уровне ассемблера - точно так же.
Так же, в любой момент можно переключать глобальное разрешение прерываний вручную, изменением бита в регистре.
Если вы разрешили прерывания внутри кода ISR, не успели из нее выйти, и это же прерывание прилетело еще раз - будет reentrant, но такое развитие событий можно заблокировать, временно запретив прерывания от этого источника.

Вообще, как мне кажется, понятие ISR существует только в голове, в камне ISR нет, есть только заданные реакции на аппаратные события и инструкции кода.

С 500-м меня :beer:
Ответить

Вернуться в «Периферия»