Перейти к содержанию
    

Протокол modbus. Вопросы по интерфейсу

Прально, зачем ждать на светофоре зелёного, если на дороге никого нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ничего что для этого придется обзавестись большим буфером памяти, зато реакция на прерывание будет быстрой. И ради чего? Разница ведь в нано-, микро- секундах исчисляется. Снижаем реакцию на прерывание просто ради получения минимальной реакции?! Дык с тем же успехом можете просто разрешить другие прерывания в обработчике текущего, реакция будет еще быстрее.
Я же написал для чего это мне нужно было. Да, для этого требуется буфер на максимальную длину пакета. Но точно такой же буфер требуется и для формирования ответа, если запрошены данные максимального размера. Так что размер буфера тут рояли не играет. Ловить микросекунды по связи у меня не было задачи. Во-первых, связь была малоприоритетным фоновым процессом. Во-вторых, в реализации протокола были сразу заложены настраиваемые задержки и таймауты ожидания выдачи ответа. Так что и сверхбыстрый ответ по связи не играл никакой роли. В-третьих, я уже ответил, что не отвергаю ваших доводов по поводу реализации канального уровня, но всякому сверчку свой шесток. Я не вижу необходимости свербыстродействующей реакции на запрос мастером журнальной записи. Для измерительных же целей целесообразность выбора modbus как протокола связи уже обсудили, повторяться не стоит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На досуге я тут кое-что обдумывал и вернулся вот к этой части сообщения

Разные уровни обмена не нужно смешивать разумеется. Только вот идеи на которые Вы ссылаетесь примерно такого плана: а давайте чтобы сократить время реакции прерывания разобъем канальный уровень на два подуровня, первый подуровень канального уровня будем обслуживать в прерывании, а второй - постобработкой. Ничего что для этого придется обзавестись большим буфером памяти, зато реакция на прерывание будет быстрой. И ради чего? Разница ведь в нано-, микро- секундах исчисляется. Снижаем реакцию на прерывание просто ради получения минимальной реакции?! Дык с тем же успехом можете просто разрешить другие прерывания в обработчике текущего, реакция будет еще быстрее.

defunct, поясните, пожалуйста, как вы обеспечиваете реентерабельность вашей функции канального уровня, описанной в посте #44? То бишь в каком там месте можно поставить оператор, разрешающий вложенные прерывания? Моя ИМХА почему-то упорно мне доказывает, что это прерывание (от UART) делать вложенным нельзя, если только заранее неизвестно, что сумма всех возможных вложенных прерываний не превысит величины символьного интервала для максимальной скорости передачи (т.е., например, скорость передачи достаточно низкая).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

defunct, поясните, пожалуйста, как вы обеспечиваете реентерабельность вашей функции канального уровня, описанной в посте #44? То бишь в каком там месте можно поставить оператор, разрешающий вложенные прерывания?

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

Конкретное положение зависит от требований к латентности в конкретной системе, для обеспечения минимальной латентности - целесообразно - в asm обертке (до сохранения всей кучи регистров в стек). Если минимальная латентность не обязательна - то сразу после вычитки данных из UART регистра.

 

Моя ИМХА почему-то упорно мне доказывает, что это прерывание (от UART) делать вложенным нельзя,
Ваша ИМХА верна, само прерывание "байт принят" просто сделать вложенным нельзя. Необходимо либо перед тем как разрешать все остальные, - замаскировать текущее, либо - см. ниже.

 

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

Справедливо если UART буферизирует только 1 символ. Но если UART обладает RX FIFO (16550 и подобные) требования к интервалу снижаются в FIFO size раз.

Это FIFO можно организовать и программно (и оно потребует куда меньше памяти чем толстый приемный буфер обрабатываемый в background)

 

__interrupt RxHandler(void)
{
     static int write_index = 0;
     static int read_index = 0;
     static char lock = 0;
     static char fifo_buf[ сумма всех прерываний / символьный интервал ];

     fifo_buf[ write_index++ ] = UART_RX_REG;
     if (write_index >= sizeof( fifo_buf))
         write_index = 0; 

     if (lock)
        return;
     else     
        lock = 1;
     // здесь можем разрешать вложенные прерывания включая текущее
     __enable_interrupt();
          
     while (read_index != write_index)
     {
           rx_cb( fifo_buf[ read_index++ ])
           ...
     } 
     lock = 0;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну вот и консенсус наметился :) Считайте, что у меня реализован программный буфер FIFO. Только память, выделяемая под него, не только канальным уровнем используется, но и уровнем приложения. Для сокращения расхода RAM и потому, что ответов с микросекундной задержкой не требуется по условиям моего задания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

:)

Только объем буфера в десятки-сотни раз больший требуется, чем величина fifo.

 

 

Для сокращения расхода RAM и потому, что ответов с микросекундной задержкой не требуется по условиям моего задания.

Где ж тут сокращение расхода RAM? Когда наоборот.

У меня командная консолько и та разбита на канальный уровень, на канальном уровне обслуживаются CR, LF, UP, DOWN, CTRL-H, CTRL-C, CTRL-... служебные символы.

Самой консольке же идут только осмысленные команды. Требования к буферу - строго детерминированны макс длиной команды, а если просто складывать все принятое подряд какой размер буфера брать?..

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только объем буфера в десятки-сотни раз больший требуется, чем величина fifo.
Не преувеличивайте. Размер буфера определяется максимальной длиной фрейма ответа. Для случая ModBus RTU это 256 байт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не преувеличивайте. Размер буфера определяется максимальной длиной фрейма ответа. Для случая ModBus RTU это 256 байт.

А где преувеличение? Сколько байт fifo потребуется? 2? 4? 8? 256 / 8 = 32. В 32 раза.

 

Следуя вашей схеме - сетевые адреса в прерывании у вас не обслуживаются и таймауты тоже - значит приемный буфер должен покрывать два RTU пакета - а это 256x2 байт.

А что с консолькой? Что с PPP? и там и там управляющие символы есть. Даже при известном MRU наличие упр. символов увеличивают требования к буферу - 2*MRU, а если учесть что ранжирование на пакеты делается приложением получается требования к буферу 4 * MRU.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Следуя вашей схеме - сетевые адреса в прерывании у вас не обслуживаются и таймауты тоже - значит приемный буфер должен покрывать два RTU пакета - а это 256x2 байт.
Это ваша схема выходит, а не моя. Зачем 2*256? Пока уровень приложения обрабатывает запрос, переданный ему с канального уровня, прием вообще запрещен. По истечение времени, выделенного уровню приложения на обработку запроса, если ответ еще не готов, то автоматически формируется типовой ответ "BUSY". Для него большого буфера не нужно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это ваша схема выходит, а не моя.

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

или все-таки сетевые адреса и таймауты у вас обслуживаются в прерывании? Тогда Вы противоречите сами себе.

 

Зачем 2*256?

Если не распознавать адрес и не обслуживать таймаут в прерывании.

То как Вы определяете, когда прекращать прием?

 

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

Не вопрос пусть так, но ведь в RX прерывании складываете все подряд, в т.ч. пакеты адресованные другому слейву и ответы других слейвов.

 

По истечение времени, выделенного уровню приложения на обработку запроса, если ответ еще не готов, то автоматически формируется типовой ответ "BUSY". Для него большого буфера не нужно.

Это же TX буфер, а мы вроде как про RX...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

или все-таки сетевые адреса и таймауты у вас обслуживаются в прерывании? Тогда Вы противоречите сами себе.

В прерывании. Только не в UARTовом, а в таймерном. Например, 1мс-ном или 1-секундном. Не принципиально. Зависит от требуемой точности определения паузы.

Если не распознавать адрес и не обслуживать таймаут в прерывании.

То как Вы определяете, когда прекращать прием?

Если в RTU, то как только между двумя (или большим кол-вом, в зависимости от требуемой длительности паузы) 1мс-ыми прерываниями буфер перестал пополняться, то значит нужно считать CRC и при совпадении можно передавать указатель на начало фрейма в буфере на уровень приложения. Если другой формат. то ищутся маркеры начала/конца фрейма в буфере. И адресация тоже определяется нормально. Если адрес был получен не свой, то в таймерном прерывании буфер каждый раз очищается до тех пор, пока не обнаружится начало следующего фрейма.

Не вопрос пусть так, но ведь в RX прерывании складываете все подряд, в т.ч. пакеты адресованные другому слейву и ответы других слейвов.
Нет. Зачем мне пакеты, адресованные другому слейву, если только у меня не организовано маркерное кольцо для доступа к шине?

Это же TX буфер, а мы вроде как про RX...
При формате обмена запрос-ответ второй буфер не нужен. Слейв не будет обрабатывать второй и последующий запросы до тех пор, пока не готов ответ на первый или выделенное ему для формирования ответа время не вышло.

И еще вопрос-уточнение. А как вы обеспечиваете атомарность при проверке условия

while (read_index != write_index)

? Ведь если атомарность не будет обеспечена, то последний байт, попавший в буфер в прерывании, может быть пропущен функцией обработки канального уровня.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет. Зачем мне пакеты, адресованные другому слейву, если только у меня не организовано маркерное кольцо для доступа к шине?

Но вы же их принимаете и складываете их данные в буфер... Анализируете свой-чужой - уже в приложении.

 

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

Т.е. отдельный TX буфер не нужен? А куда будем складывать отложенный ответ (тот который формируется, в момент когда уже шлется busy)?

После отправки busy, не будем ждать еще одного запроса мастера, чтобы отдать сформированный ответ?

 

И еще вопрос-уточнение. А как вы обеспечиваете атомарность при проверке условия

while (read_index != write_index)

? Ведь если атомарность не будет обеспечена, то последний байт, попавший в буфер в прерывании, может быть пропущен функцией обработки канального уровня.

Не пропушен (пропущен - это равнозначно потерян), а отложен до следующего прерывания! Да в приведенном варианте есть проблема последнего символа, но она тоже решается, например, запретом прерываний перед очередной проверкой индексов:

 

     while (read_index != write_index)
     {
           __enable_interrupt();
           rx_cb( fifo_buf[ read_index++ ])
           ...
           __disable_interrupt();
     } 
     ...

 

Можно и другим путем.

 

Вот что более интересно, а вам не кажется, что:

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

относится и к обычному (невложенному обработчику) в точно такой же мере? Приоритет прерывания UART'а в системе как правило далеко не самый высокий, т.о. необходимо чтобы сумма всех возможных и более приоритетных прерываний не превышала символьного интервала для макс скорости передачи, что есть практически одно и тоже с суммой всех возможных вложенных прерываний.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но вы же их принимаете и складываете их данные в буфер... Анализируете свой-чужой - уже в приложении.
Нет. Анализ уже во втором слое канального уровня, как вы его охарактеризовали, происходит. Та функция которая в 1мс прерывании вызывается и работает с буфером UART.

Т.е. отдельный TX буфер не нужен? А куда будем складывать отложенный ответ (тот который формируется, в момент когда уже шлется busy)?
Дык в тот же буфер. Или в другой. Маленький, но отдельный.

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

Да в приведенном варианте есть проблема последнего символа, но она тоже решается, например, запретом прерываний перед очередной проверкой индексов:
Вот. Все-таки не так все просто, как вы вначале описали. ;)

Вот что более интересно, а вам не кажется, что:

 

относится и к обычному (невложенному обработчику) в точно такой же мере?

К которому именно обработчику? Если к тому, который таймерный, то я там такую же фишку с обходом вызова функции применяю. Почему вам можно, а мне нельзя?

И еще. У меня точно также как и у вас канальный уровень абстрагирован от железа. Но в HAL я уложил еще и буфер UART и атомарность доступа к буферу я обеспечиваю там же - в функции работы с буфером UART. При этом решается проблема как с наличием аппаратного FIFO, так и с "отложенными" байтами.

P.S. на всякий случай напомню с чего началась дискуссия и ваши возражения.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет. Анализ уже во втором слое канального уровня, как вы его охарактеризовали, происходит. Та функция которая в 1мс прерывании вызывается и работает с буфером UART.

Понял. Применительно к modbus-slave я тоже так делал (выделял пакет в обработчике таймера). Но потом отказался от этого пути, потому что один уровень получается размазанным по нескольким обработчикам. И что самое печальное не все протоколы можно эффективно втиснуть в такую модель. Потом получается сложнее портировать код между МК.

 

Вот. Все-таки не так все просто, как вы вначале описали. ;)

Ну а в чем сложность? Сорри допустил ошибку вчера (еще забыл static переменные объявить как volatile), - каюсь :(

Но код же ж не стал после исправления ошибки, много сложнее? И эффективность ведь не потерялась.

 

К которому именно обработчику?

К обработчику UART'а разумеется.

 

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

Функция в таймере которая обрабатывает весь пакет - у вас много сложнее моей, обслуживающей строго 1 символ. Поэтому если Вы выделяете целый пакет из кучи принятых байт, вам не то что можно-нельзя, а просто необходимо поступать так и только так, т.к. время выполнения функции будет диким! А вот мне можно и обойтись без вложенности вовсе.

 

Ну а вторая причина - вы про таймер не рассказывали. Я думал у вас обработка делается в аппликейшн.

 

P.S. на всякий случай напомню с чего началась дискуссия и ваши возражения.
Да да, я помню. По прежнему стою на том, что выделять пакеты удобнее всего в обработчике посимвольного приема, т.к. это упрощает разбивку и снижает расход памяти.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Функция в таймере которая обрабатывает весь пакет - у вас много сложнее моей, обслуживающей строго 1 символ. Поэтому если Вы выделяете целый пакет из кучи принятых байт, вам не то что можно-нельзя, а просто необходимо поступать так и только так, т.к. время выполнения функции будет диким! А вот мне можно и обойтись без вложенности вовсе.
Ничуть не сложнее. Обработка зависит от темпа поступления данных. Если темп такой же как частота вызова таймерного прерывания, то также посимвольно получится. Почему нельзя обойтись без вложенности я уже писал.

Да да, я помню. По прежнему стою на том, что выделять пакеты удобнее всего в обработчике посимвольного приема, т.к. это упрощает разбивку и снижает расход памяти.
С вашего разрешения по второму кругу дискуссию пускать не буду :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...