РЕШЕНО Modbus без парсинга и ошибок.

Тема в разделе "Глядите, что я сделал", создана пользователем Алопеций Шнифт, 4 мар 2026.

  1. Вы опять предлагаете собственные решения, причём весьма конкретные. Они, скорее всего, работать будут, но с чего Вы взяли что они единственные? И уж тем более, что они наиболее правильные из десятков возможных вариантов подобных решений?
    Система ЕВ (единого времени) в контроллере конечно же должна присутствовать.. Мне, например, оттуда нужен только программный счётчик. Другой без железного таймера жить не может.
    И то, и другое работает.
    Кстати, поясните. Таймер ведь у Вас только для того, чтобы во время ожидания не жрать процессорное время? То есть программные задержки могут его полностью заменить без ущерба для алгоритма, хотя ценой понижения производительности системы?
     
    Последнее редактирование: 17 мар 2026
  2. parovoZZ

    parovoZZ Гуру

    они не собственные, а взяты из стандарта на протокол. Собственно, если говорить про MCU/CPU, то у других разработчиков всё примерно тоже. Если говорить про ПЛК, то подход там чуть иной.

    Оно всё так и работает.

    Мы что говорим? Что ОЗУ потребляем как можно меньше. Так вот запись
    превращается в монструозную конструкцию, т.к. просто так взять целиком и сравнить эти числа нельзя. Необходимо куда-то эти числа положить. Куда? В ОЗУ. Затем взять первые байты у обоих чисел. Если совпадают, то второй и так далее. И так надо делать на каждый принятый байт. Условная строчка же
    Код (C++):
    if (*my_addr = *r_addr) {
    }
    не потребляет ОЗУ вообще. Всё происходит сразу на регистрах (my_addr взяли из EEPROM, r_addr из UART). Если совпало, ушли в подпрограмму конечного автомата разбора остальной части пакета. Собственно, у первых микроконтроллеров ОЗУ и не было. Только внешнее, при желании.

    таймер для того, чтобы поднять флажок признака свободной линии. Речь исключительно про приём пакета. Передавать можно сразу после приёма. Современные мастера на приём переключаются сразу же после отправки пакета. Но лучше всё же выждать полтора символа или 0.75 мс, если скорость выше 19200.
     
  3. ???
    То есть компиляторам Вы не доверяете? Я, в общем-то, неплохо представляю, в какую именно конструкцию развернётся эта строчка на процессорах разной разрядности. Ничего особо страшного даже для восьмибиток. Строчка со сдвигом значительно страшнее, но до монструозности тоже довольно далеко.
    Вообще уже говорил, но повторю ещё раз: восьмибайтовый ключ - частный случай. Минимальный ключ - 2 байта. Ну или даже 1, если линия чистая.
     
  4. parovoZZ

    parovoZZ Гуру

    примерно так:

    Код (Text):
        lds  r18, 0x0100    ; Загружаем 1-й байт первого числа
        lds  r19, 0x0108    ; Загружаем 1-й байт второго числа
        cp   r18, r19       ; Сравниваем
        brne _end_if        ; Если не равны, выходим
    и так восемь раз.


    вот мы постепенно и пришли к тому, что некий ключ, в общем-то, и не нужен.
     
  5. parovoZZ

    parovoZZ Гуру

    Подход, описанный в алгоритме, примерно и используется в промышленных ПЛК, т.к. там прямого доступа к оборудованию НЕТ. А есть некий цикл задачи, внутри которой мы обращаемся к системной функции, которая отдаёт всё принятое и накопленное, пока задача спала. Но на CPU/MCU такой проблемы не существует. Даже поверх RTOS.
    Поэтому решение - мы сперва копим, - а потом сравниваем накопленное, выглядит как спагетти код. Почему нельзя сравнивать сразу же, по приходу?
     
  6. DetSimen

    DetSimen Гуру

    Нет, это

    разворачивается совершенно в другой код, без всяких сравнений
     
  7. parovoZZ

    parovoZZ Гуру

    Деда, сугроб растаял?
     
  8. DetSimen

    DetSimen Гуру

    присваивание вижу, сравнение - нет.
     
  9. parovoZZ

    parovoZZ Гуру

    вот же ж:

    Код (C++):
        lds  r18, 0x0100    ; Загружаем байт адреса
        lds  r19, UDR0    ; Загружаем байт из UART
        cp   r18, r19       ; Сравниваем
        brne _end_if        ; Если не равны, выходим
    И не надо ничего нигде накапливать.
     
  10. Ну почему не нужен? Очень даже нужен. Другое дело, что его размер может в промежутке от 8 до 1 байта. Меньше не имеет смысла.
    Кстати, именно для аврок я бы предпочёл именно восьмибитный ключ.Надёжность останется стандартной для Модбаса, но так как мы решили, что линия идеальная, то это не важно.
    Вообще я же не библиотеку 'продаю'. И даже не набор макросов.
    А голую идею, которая может быть реализована в нескольких вариантах, каждый из которых лучше заточен под что-то одно.
     
  11. Уважаемые господа!
    Ниже архив, в котором проект VS, реализующий вторую версию алгоритма с 32-хбитным ключом в виде консольного приложения Вин..
    Под VS - потому что стоит практически у всех, кто имеет хоть какое-то отношение к написанию кода.
    А алгоритм чисто математический и к железу не привязан никак.
    Там можно, играясь со входным потоком, попытаться сбить алгоритм.
    Сразу предупреждаю: помещение в поток данных корректного 64-х битного ключа его собьёт. Но только так.
    Блин, не грузит почему-то
    Ладно, вот ссылка на рлоцман, я туда тоже выложил. Форум, вроде, считается относительно приличным, так что подстав быть не должно.
    Но если кто-то опасается (что правильно), берите только файл main.c (он самодостаточен), и вставляйте в консольное приложение сами.
    https://www.rlocman.ru/forum/attachment.php?attachmentid=51349&d=1773738600
     
  12. parovoZZ

    parovoZZ Гуру

    мы же отдаём не статическую информацию, а какие-то значения каких-то параметров. Соответственно, CRC считать надо. А раз надо, то и заранее его считать для всех запросов смысла особого нет.
    я пишу очень много кода, но VS у меня нет.

    Неоднократно сталкивался с радиомодемами, которые начинали отдавать информацию на половине принятого пакета. Т.е. даже промышленные микросхемы работают так, как я описывал.
    Идея принимать байты, куда-то их складывать, а только когда-то потом их анализировать нужна тогда, когда протоколом предусмотрено исправление ошибок. Или же когда у нас транзакции и разные куски единого целого могут прийти в разное время. Но это всё не про модбас.
     
  13. Надо. И что?


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

    parovoZZ Гуру

    как я уже и говорил - идея для закрытого применения годная. Но для полной совместимости с модбас не подходит - при росте сложности адресного пространства растёт и сложность её внедрения с неминуемым возникновением ошибок на ровном месте. Чего не должно быть в принципе.
     
  15. Если Вы решили меня запутать, то это удалось: я ничего не понял.
    Ни о каком адресном пространстве вообще идёт речь, ни почему его сложность должна расти, ни уж тем более как это отражается на сложности внедрения и неминуемости ошибок.
    Единственное, с чем согласен полностью, так это с последним предложением. Но так как остальное ни хера не понятно, могу ошибиться.
     
  16. Код (Text):
    slider:|07 |05 |a6 |5a |12 |34 |86 |04 |  error -1
    slider:|05 |a6 |5a |12 |34 |86 |04 |d5 |
    slider:|a6 |5a |12 |34 |86 |04 |d5 |d3 |
    slider:|5a |12 |34 |86 |04 |d5 |d3 |c9 |
    slider:|12 |34 |86 |04 |d5 |d3 |c9 |ff |
             0   1   2   3   4   5   6   7 |
    slider:|34 |86 |04 |d5 |d3 |c9 |ff |07 |
    slider:|86 |04 |d5 |d3 |c9 |ff |07 |06 |
    slider:|04 |d5 |d3 |c9 |ff |07 |06 |a5 |
    slider:|d5 |d3 |c9 |ff |07 |06 |a5 |5a |
    slider:|d3 |c9 |ff |07 |06 |a5 |5a |12 |
    slider:|c9 |ff |07 |06 |a5 |5a |12 |34 |
    slider:|ff |07 |06 |a5 |5a |12 |34 |86 |
    slider:|07 |06 |a5 |5a |12 |34 |86 |04 |  *** OK! ***
    slider:|06 |a5 |5a |12 |34 |86 |04 |07 |
    slider:|a5 |5a |12 |34 |86 |04 |07 |06 |
    slider:|5a |12 |34 |86 |04 |07 |06 |a6 |
    slider:|12 |34 |86 |04 |07 |06 |a6 |5a |
    slider:|34 |86 |04 |07 |06 |a6 |5a |12 |
    slider:|86 |04 |07 |06 |a6 |5a |12 |34 |
    slider:|04 |07 |06 |a6 |5a |12 |34 |86 |
    slider:|07 |06 |a6 |5a |12 |34 |86 |04 |  error -2
    slider:|06 |a6 |5a |12 |34 |86 |04 |55 |
    slider:|a6 |5a |12 |34 |86 |04 |55 |aa |
    slider:|5a |12 |34 |86 |04 |55 |aa |ff |
    slider:|12 |34 |86 |04 |55 |aa |ff |07 |
    slider:|34 |86 |04 |55 |aa |ff |07 |06 |
    slider:|86 |04 |55 |aa |ff |07 |06 |a5 |
    slider:|04 |55 |aa |ff |07 |06 |a5 |5a |
    slider:|55 |aa |ff |07 |06 |a5 |5a |12 |
    slider:|aa |ff |07 |06 |a5 |5a |12 |34 |
    slider:|ff |07 |06 |a5 |5a |12 |34 |86 |
    slider:|07 |06 |a5 |5a |12 |34 |86 |04 |  *** OK! ***
    slider:|06 |a5 |5a |12 |34 |86 |04 |07 |
    slider:|a5 |5a |12 |34 |86 |04 |07 |05 |
    slider:|5a |12 |34 |86 |04 |07 |05 |a6 |
    slider:|12 |34 |86 |04 |07 |05 |a6 |5a |
    slider:|34 |86 |04 |07 |05 |a6 |5a |12 |
             0   1   2   3   4   5   6   7 |
    slider:|86 |04 |07 |05 |a6 |5a |12 |34 |
    slider:|04 |07 |05 |a6 |5a |12 |34 |86 |
    slider:|07 |05 |a6 |5a |12 |34 |86 |04 |  error -1
    slider:|05 |a6 |5a |12 |34 |86 |04 |d5 |
    slider:|a6 |5a |12 |34 |86 |04 |d5 |d3 |
    slider:|5a |12 |34 |86 |04 |d5 |d3 |c9 |
    slider:|12 |34 |86 |04 |d5 |d3 |c9 |ff |
    slider:|34 |86 |04 |d5 |d3 |c9 |ff |07 |
    slider:|86 |04 |d5 |d3 |c9 |ff |07 |06 |
    slider:|04 |d5 |d3 |c9 |ff |07 |06 |a5 |
    slider:|d5 |d3 |c9 |ff |07 |06 |a5 |5a |
    slider:|d3 |c9 |ff |07 |06 |a5 |5a |12 |
    slider:|c9 |ff |07 |06 |a5 |5a |12 |34 |
    slider:|ff |07 |06 |a5 |5a |12 |34 |86 |
    slider:|07 |06 |a5 |5a |12 |34 |86 |04 |  *** OK! ***
    slider:|06 |a5 |5a |12 |34 |86 |04 |07 |
    slider:|a5 |5a |12 |34 |86 |04 |07 |06 |
    slider:|5a |12 |34 |86 |04 |07 |06 |a6 |
    slider:|12 |34 |86 |04 |07 |06 |a6 |5a |
    slider:|34 |86 |04 |07 |06 |a6 |5a |12 |
    slider:|86 |04 |07 |06 |a6 |5a |12 |34 |
    slider:|04 |07 |06 |a6 |5a |12 |34 |86 |
    slider:|07 |06 |a6 |5a |12 |34 |86 |04 |  error -2
    slider:|06 |a6 |5a |12 |34 |86 |04 |55 |
    slider:|a6 |5a |12 |34 |86 |04 |55 |aa |
    slider:|5a |12 |34 |86 |04 |55 |aa |ff |
    slider:|12 |34 |86 |04 |55 |aa |ff |07 |
    slider:|34 |86 |04 |55 |aa |ff |07 |06 |
    slider:|86 |04 |55 |aa |ff |07 |06 |a5 |
    slider:|04 |55 |aa |ff |07 |06 |a5 |5a |
    slider:|55 |aa |ff |07 |06 |a5 |5a |12 |
    slider:|aa |ff |07 |06 |a5 |5a |12 |34 |
             0   1   2   3   4   5   6   7 |
    slider:|ff |07 |06 |a5 |5a |12 |34 |86 |
    slider:|07 |06 |a5 |5a |12 |34 |86 |04 |  *** OK! ***
    slider:|06 |a5 |5a |12 |34 |86 |04 |07 |
    slider:|a5 |5a |12 |34 |86 |04 |07 |05 |
     
    ===========
    Так выглядит вывод алгоритма v.2 на тестовом потоке.
    Должно прояснить.
     
    Последнее редактирование: 22 мар 2026
  17. parovoZZ

    parovoZZ Гуру

    На примере всего 10 регистров. Мастер может запросить первый регистр, второй и так до десятого. А может запросить со второго и до десятого за один раз ( вдруг в первом регистре лежит версия устройства, версия прошивки или что-то, что не меняется вообще ). А может запросить два последних. Устройство, которое поддерживает спецификацию модбас, на все эти запросы должно отдать информацию. Если следовать предложенному алгоритму, то необходимо нагенерить добрую сотню 'ключей' А зачем?
     
  18. А, вот Вы о чём! Чего бы сразу не написать по-русски?
    Ссылкой на стандарт не порадуете? В смысле, откуда Вы это извлекли?
    Если слейв чего-то там не поддерживает, то он
    а) отражает это в технической спецификации и
    б) отвечает исключением, если мастер по каким-то там причинам обращается куда-то вне спецификации.
    И всё.
    То есть. Если слейв отдаёт только 120 регистров и только одним куском, о чём честно предупреждает в доках, к нему нет вопросов.
    Разве нет?
     
  19. parovoZZ

    parovoZZ Гуру

    Для этого в спецификации модбас выделены специальные коды пользовательских функций. Также есть коды ошибок. И там уже назначай любые правила, какие хочешь.
     
  20. parovoZZ

    parovoZZ Гуру

    Я читаю стандарт версии 1.1b3. Свежее не видел.
    Исхожу из того, что в стандарте нет правила: читайте вот отсюда и столько-то. К тому же, в стандарте нет специальной ошибки для такого случая. Есть, правда, 03 illegal data value, но она немного про другое. Мало того, в комментарии к ошибке 02 прямо так и указано - если читаете регистры внутри диапазона, то данная ошибка не возникает. Если запрос выходит за диапазон, то необходимо возвращать такую ошибку.