Продолжаем подробно разбирать механизмы передачи данных. И героем сегодняшней статьи станет протокол Modbus. Впервые спецификация Modbus была опубликована ни много ни мало, а в 1979 году, но до сих пор не утратила своей актуальности. Итак, приступаем.
Modbus - самый широко распространенный промышленный протокол для организации обмена данными между различными устройствами (межмашинное взаимодействие, Machine-to-Machine, M2M). Популярность объясняется многими факторами, среди которых простота реализации, отсутствие необходимости использовать дополнительные микросхемы, и, конечно же, открытость протокола.
Основные плюсы:
- надежный контроль ошибок.
- массовая распространенность.
- открытость.
- возможность включения в сеть большого количества подчиненных устройств с последующим обращением к любому из них при помощи выделенного ему адреса.
В то же время протокол Modbus не избавлен от некоторых недостатков:
- организация обмена данными по модели ведущий-подчиненный. При этом подчиненное устройство не может само инициировать отправку данных ведущему, а должно в обязательном порядке ожидать запроса.
- отсутствует механизм для определения обрыва линии связи.
- в одном запросе можно прочитать значения только последовательных регистров. Для чтения произвольных регистров необходимо отправлять несколько запросов, что увеличивает нагрузку на сеть.
Обсуждая достоинства и недостатки протокола мы частично забежали вперед, так что теперь давайте разберем все поэтапно.
Как уже было упомянуто, протокол Modbus использует обмен данными по модели ведущий-подчиненный (Master - Slave). Ведущий отправляет запросы, на которые могут отвечать подчиненные. Slave-устройство не может само начать обмен данными, только по команде от master'a.
В качестве физического уровня стандарт предусматривает использование интерфейсов RS-232, RS-422 и RS-485. Также существует реализация для TCP/IP - Modbus TCP. Но этот вариант мы сегодня будем затрагивать в меньшей степени.
Сеть Modbus может состоять из нескольких slave-устройств (от 1-го до 247-ми), но master должен быть только один. Каждое из подчиненных устройств имеет свой собственный адрес, соответственно, ведущий может адресовать свое сообщение или запрос конкретному slave-устройству.
Кроме того, поддерживаются широковещательные сообщения, которые принимают все подчиненные. Но разница заключается в том, что опознав сообщение, адресованное именно ему, подчиненное устройство отправляет ведущему ответ. На широковещательные же запросы подчиненные отвечать не могут.
На адреса slave накладываются некоторые ограничения:
- диапазон допустимых адресов - 1...247. Значения адресов от 248 до 255 являются зарезервированными, а адрес 0 используется для передачи широковещательных сообщений.
- master адреса не имеет, он в сети и так один.
- два подчиненных устройства не могут иметь одинаковые адреса.
Вот как может выглядеть один из вариантов подключения устройств с использованием RS-485:
Здесь у нас присутствует один master и три slave-устройства с адресами от 0x03 до 0x05.
Переходим дальше... Различают несколько логических уровней протокола:
Modbus RTU | RS-232 RS-422 RS-485 | Двоичный вариант кодирования данных. Разделителем между пакетами служит временной интервал. То есть сообщение должно начинаться и заканчиваться паузой в течение определенного промежутка времени. Это время не должно быть меньше, чем время передачи 3.5 символов при использующейся скорости передачи данных. Кроме того, в процессе передачи пакета данных не должно быть пауз длительностью, превышающей время передачи 1.5 символов. Соответственно, Modbus RTU очень критичен к временным задержкам, но зато размер пакетов меньше, чем в Modbus ASCII. |
Modbus ASCII | RS-232 RS-422 RS-485 | В данном случае для обмена данными используются исключительно ASCII символы. И в отличие от Modbus RTU начало и конец сообщений определяются специальными символами. Начало пакета - ASCII символ ":" (0x3A), конец - "CR + LF" (0x0D + 0x0A). |
Modbus TCP | TCP/IP | Протокол используется при передаче данных с использованием TCP/IP. |
Сегодня, в первую очередь, будем подробно разбирать Modbus RTU и Modbus ASCII. Структура пакетов выглядит следующим образом:
В стандарте Modbus принята следующая терминология:
- ADU (Application Data Unit) - полный пакет данных.
- PDU (protocol data unit) - часть пакета, содержащая непосредственно полезные данные.
Для расчета контрольной суммы используются разные алгоритмы: для Modbus RTU - CRC16, для ASCII - LRC8. В обоих случаях под контрольную сумму задействованы два байта.
Коды функций можно разделить на три группы:
- Стандартные коды команд, описанные в Modbus-IDA.
- Задаваемые пользователем (user-defined function codes) - 65...72, 100...110. Эти коды не описаны в спецификации стандарта и могут использоваться в конкретных изделиях для собственных функций.
- Зарезервированные (reserved). В эту группу входят коды 9, 10, 13, 14, 41, 42, 90, 91, 125, 126 и 127.
Но прежде, чем перейти к рассмотрению базовых/стандартных команд, необходимо рассмотреть использующуюся модель данных. Выполнение команд протокола Modbus подразумевает чтение и запись данных в регистры устройства. И различают 4 типа регистров, организованных в 4 таблицы данных:
Таблица | Тип элемента |
---|---|
Дискретные входы (Discrete Inputs) | 1 бит |
Дискретные выходы (регистры флагов, Coils) | 1 бит |
Регистры ввода (Input Registers) | 16-битное слово |
Регистры хранения (Holding Registers) | 16-битное слово |
Дискретные входы и регистры ввода доступны только для чтения данных, а, соответственно, дискретные выходы и регистры хранения - для чтения и записи.
Доступ к регистрам таблицы осуществляется при помощи 16-ти битного адреса. Первому элементу таблицы соответствует адрес 0. Таким образом, каждая из этих 4-х таблиц может включать в себя вплоть до 65536 регистров (адреса 0...65535 - 16 бит).
Вот теперь давайте рассмотрим конкретные команды из группы стандартных:
- 0x01 (1) - чтение значений из нескольких регистров флагов - Read Coil Status.
- 0x02 (2) - чтение значений из нескольких дискретных входов - Read Discrete Inputs.
- 0x03 (3) - чтение значений из нескольких регистров хранения - Read Holding Registers.
- 0x04 (4) - чтение значений из нескольких регистров ввода - Read Input Registers.
- 0x05 (5) - запись значения одного флага - Force Single Coil.
- 0x06 (6) - запись значения в один регистр хранения - Preset Single Register.
- 0x07 (7) - чтение сигналов состояния - Read Exception Status.
- 0x08 (8) - диагностика - Diagnostic.
- 0x0B (11) - чтение счетчика событий - Get Com Event Counter.
- 0x0C (12) - чтение журнала событий - Get Com Event Log.
- 0x0F (15) - запись значений в несколько регистров флагов - Force Multiple Coils.
- 0x10 (16) - запись значений в несколько регистров хранения - Preset Multiple Registers.
- 0x11 (17) - чтение информации об устройстве - Report Slave ID.
- 0x14 (20) - чтение из файла - Read File Record.
- 0x15 (21) - запись в файл - Write File Record.
- 0x16 (22) - запись в один регистр хранения с использованием маски "И" и маски "ИЛИ" - Mask Write Register.
- 0x18 (24) - чтение данных из очереди - Read FIFO Queue.
- 0x2B (43) - Encapsulated Interface Transport.
И, конечно же, мы не можем не разобрать конкретные примеры запросов и ответов при работе по Modbus.
Протокол Modbus. Примеры команд.
Первым делом займемся чтением данных - коды функций 0x01, 0x02, 0x03, 0x04. В общем виде запросы ведущего и ответы подчиненного выглядят следующим образом (здесь мы рассматриваем только часть пакета - PDU):
Обратите внимание, что в запросе передается количество элементов(!), то есть ячеек таблиц данных (регистров). А в ответе для указания размера данных используются уже байты. Значения адреса и количество элементов передаются в виде 16-битных слов, при этом старший байт передается первым.
Пойдем дальше обобщенного описания формата и проанализируем команды для конкретного устройства. В качестве этого устройства я использую сервопривод серии ASDA-A2, который для обмена данными использует как раз-таки протокол Modbus, причем поддерживает и Modbus RTU, и Modbus ASCII.
Пусть нам надо прочитать данные, расположенные по адресам 0x0200 и 0x0201, slave-устройства с адресом 0x01. Запрос master'а для Modbus RTU будет таким:
Здесь у нас в запросе, указано, что мы хотим прочитать значение двух элементов (регистров). И ответ slave:
А в ответе уже видим, что прочитано 4 байта, поскольку значение одного регистра - это 2 байта (16 бит), а регистров у нас тоже 2. Таким образом, полученные значения:
- Адрес 0x0200 - 0x00B1
- Адрес 0x0201 - 0x1F40
CRC Low и CRC High - это соответственно младший и старший байты 16-ти битной контрольной суммы.
В Modbus ASCII все несколько иначе, запрос выглядит так (в кавычках указаны ASCII символы):
Ответ подчиненного:
И в первом и во втором случае запрашиваем значения одних и тех же регистров и, соответственно, получаем в ответ одинаковые данные.
При чтении битов регистров флагов или дискретных входов запрос выглядит точно также, а вот байты данных ответного сообщения иначе:
Здесь одно значение флага или дискретного входа занимает один бит. И все эти биты упакованы в байты, а если число запрошенных флагов/входов не распределяется по байтам (не кратно 8), то "лишние" биты заполняются нулями. Как в последнем байте на этой схеме.
Переходим к записи. Для записи одного значения используются команды с кодами 0x05 и 0x06. Запрос в целом похож на уже рассмотренный (при чтении данных), только вместо количества элементов для чтения передаются данные, которые будут записаны:
При записи значений флагов или дискретных входов число 0xFF00 соответствует включенному состоянию, 0x0000 - выключенному. В случае успешного выполнения запроса подчиненное устройство отправляет ведущему точную копию этого запроса.
С этим разобрались 👍 Но необходимо рассмотреть еще и случай записи нескольких значений (коды команд 0x10 и 0x0F). В общем виде формат запроса такой:
И, в обязательном порядке, рассмотрим практический пример. Пусть нам требуется записать значения 0x0BB8 и 0x0000 по адресам, начинающимся с 0x0112. Запрос и ответ Modbus RTU:
И для Modbus ASCII:
Довольно большая получилась статья, хотя мы не затронули, например, алгоритмы расчета контрольных сумм, возможно в одной из будущих статей разберемся и с этим ) А заодно и рассмотрим практическую реализацию.
Спасибо!
Николай, а будут практические примеры использования modbus rtu на stm32 ??
Павел, привет! Если нужно, то сделаю конечно)
Надо расти дальше. Очень хочется реализовать)
В ближайшие недели постараюсь сделать) Времени так всегда катастрофически не хватает на все задумки...
А Вы не хотите в сообществе опубликовать какие-нибудь наработки/опыт? Интересно было бы почитать)
Большая часть моего опыта работы с микроконтроллерами STM32 взята как раз с этого сайта) моих знаний маловато для публикации чего то стоящего .
Это уже немало )
Мне бы тоже хотелось посмотреть простейший пример для RTU Slave. Просто запрос от ПК к STM-ке через QModbus например. Чтение ,скажем , input reg с записанным заранее числом. Существующие примеры слишком запутанны для начинающих.
Эх, ну придется делать статью, что сказать )
Я за вариант modbus rtu с использованием микросхемы sp3485
Первую часть опубликовал - https://microtechnics.ru/modbus-rtu-slave-primer-realizaczii-na-mikrokontrollere-stm32/
Здравствуйте! Вопрос по модбасу: устройство на базе ATMega16, программа на основе freemodbus 1.5, отказывается связываться с ПК (RS485-USB) на скорости 1200 бод. На скоростях выше все работает. С чем это может быть связано? Может подскажете в какую сторону смотреть?
Доброго времени суток!
Можно сразу локализовать приблизительно, всего несколько вариантов получается - либо данные не доходят до софта, либо доходят, но ответ на команду не отправляется, либо ответ отправляется, но ATMega его не принимает )
Проверил тестовой программкой. Данные с устройства на ПК через USART проходят на скорости 1200. Похоже проблема в библиотеке freemodbus
Нашел )) Единственная скорость из моего списка при которой задействован регистр UBRRH. В библиотеке только UBRRL применялся
Отлично!