Modbus и stm32
Автор: Артём Тянутов, artem.lab@gmail.com Введение Здравствуйте, коты! Нередко возникает необходимость в общении между каким-либо нашим устройством и компьютером. Самый простой и доступный для этих целей интерфейс - UART, имеется по несколько штук в микроконтроллерах, завсегдатай в компьютерах (на крайний случай как USB адаптер), а если нужна связь на некотором расстоянии - можно использовать RS-485. В простейшем случае можно ограничиться простым stdio (printf, scanf), периодически выводящим, например, текущую температуру, обороты, состояние и т.п. и принимающим текстовые команды (на манер AT-команд модема). По мере роста числа команд обрабатывать всё это становится тем ещё удовольствием, как по удобству, так и скорости, ну и добивает такой метод общения отсутствие проверки целостности посылки (кроме чётности UART) и адресации. В итоге мы так или иначе приходим к необходимости применения какого-либо цифрового протокола, устраняющего все эти недостатки и становимся у развилки - что-то своё или же стандартное. Нелёгкий выбор Можно, конечно, изобрести свой велосипед, но ведь наверняка должно быть что-то готовое и широко применяющееся? Разумеется, есть целый зоопарк протоколов, разобраться в котором довольно не тривиальная задача, поэтому попробуем подобрать что-нибудь, что будет открыто, универсально и с кучей документации. Таким протоколом оказался Modbus - открытый коммуникационный протокол, основанный на архитектуре ведущий-ведомый. Его поддерживает огромное количество всевозможного оборудования, в сети есть большое количество информации, в том числе доступен официальный PDF с исчерпывающей спецификацией. Рассмотрим этот протокол подробнее. Протокол Modbus
Modbus был разработан компанией Modicon для использования в её контроллерах с программируемой логикой. Впервые спецификация протокола была опубликована в 1979 году. Первоначально контроллеры MODICON использовали последовательный интерфейс RS-232. Позднее стал применяться интерфейс RS-485. Modbus - это, как уже было сказано выше, протокол вида "ведущий - ведомый" (master - slave). Только один ведущий может быть подключен к сети и только он может инициировать обмен данными с максимум 247 ведомыми устройствами (адреса 0 и 248-255 зарезервированы). Обмен данными может осуществляться в двух режимах:
Общий для всех физических уровней пакет Modbus - Protocol Data Unit (PDU) состоит из кода функции (1 байт) и данных (до 253 байт): Для передачи данных по физическим линиям связи PDU оборачивается в другой пакет - Application Data Unit (ADU) - который отличается в зависимости от линии. Всего их три вариант - Modbus ASCII (текстовый вариант), Modbus TCP (для передачи через TCP/IP) и интересующий нас Modbus RTU. ADU для Modbus RTU включает в себя 1 байт адреса ведомого устройства и 2 байта контрольной суммы CRC:
Пакеты разделяются паузой в линии: не менее 3.5 символов при данной скорости передачи. Во время передачи пакета не должно быть пауз длительностью более 1.5 символов. Для скоростей более 19200 бод допускается использовать интервалы 1.75 и 0.75 мс, соответственно. Протокол Modbus оперирует четырьмя стандартными типами данных:
Доступ к каждому элементу производится по 16и-битному адресу, указываемому внутри данных функции, начиная с 0, таким образом может существовать до 65536 элементов. Причём что из себя представляет каждый элемент спецификация умалчивает, т.е. это может быть какая-либо переменная, а может быть и бит состояния ножки микроконтроллера. Все многобайтовые поля и данные передаются старшим байтом вперёд. Modbus включает в себя множество стандартных функций. Причём старший бит в номере функции в ответе ведомого указывает на наличие ошибки, таким образом нам доступны номера функций от 1 до 127 (0 не является допустимым номером функции). Остановимся для примера на нескольких базовых. Функции Modbus Чтение регистров флагов (0x1) Читает битовое(ВКЛ/ВЫКЛ) состояние дискретных выходов (они же регистры флагов, coils). Запрос к ведомому определяет начальный адрес флаговых регистров (номер цифрового выхода) и количество для чтения.
Ответ ведомого содержит состояния регистров флагов, запакованные побитово в байты данных (1 - ВКЛ, 0 - ВЫКЛ), в которых младший бит самого первого байта содержит состояние регистра по запрашиваемому адресу. Не используемые биты в последнем байте заполняются нулями. Перед данными передаётся байт, содержащий число передаваемых байт данных с состояниями регистров флагов.
В данном пример байт данных 0x03 (0b00000011) означает, что все два запрашиваемых регистра флагов включены. Чтение цифровых входов (0x2) Формат PDU полностью аналогичен чтению регистров флагов. Чтение регистров хранения (0x3) Читает двухбайтовые значения регистров хранения. Запрос к ведомому определяет начальный адрес регистров хранения и количество регистров для чтения.
Ответ ведомого содержит двухбайтовые значения регистров хранения (старшим байтом вперёд) по начальному адресу в запросе и байт с количеством передаваемых байт данных со значениями регистров хранения.
В данном примере передаваемые данные означают, что по начальному адресу в запросе, два последовательно идущих регистра хранения имеют значения 0x1234 и 0x5678. Чтение регистров ввода (0x4) Формат PDU полностью аналогичен чтению регистров хранения. Установка одного регистра флагов (0x5) Устанавливает битовое (ВКЛ/ВЫКЛ) значение регистра флагов. Запрос к ведомому определяет адрес регистра флагов и его значение. Последнее передаётся в двух байтах, причём ВКЛ соответствует значение 0xff00, а ВЫКЛ - 0x0000.
Ответ ведомого повторяет запрос.
В данном примере осуществляется запись в регистр флагов по адресу 0x50 значения 0x1 (ВКЛ). Установка одного регистра хранения (0x6) Устанавливает двухбайтовое значение регистра хранения. Запрос к ведомому определяет адрес регистра флагов и его значение.
Ответ ведомого повторяет запрос.
В данном примере осуществляется запись в регистр хранения по адресу 0x60 значения 0x1234. Установка нескольких регистров флагов (0xf) Устанавливает битовые (ВКЛ/ВЫКЛ) значения регистров флагов. Запрос к ведомому содержит начальный адрес регистров флагов и количество для записи. Значения регистров флагов упаковываются побитово (1 - ВКЛ, 0 - ВЫКЛ) в байты данных, в которых младший бит самого первого байта содержит значение регистра по заданному адресу. Не используемые биты в последнем байте заполняются нулями. Перед данными передаётся байт, содержащий число передаваемых байт данных со значениями регистров флагов.
Ответ от ведомого содержит начальный адрес регистров флагов и количество установленных значений.
В данном пример устанавливаются значения двух регистров флагов по начальному адресу 0x00f0 как ВКЛ и ВКЛ. Установка нескольких регистров хранения (0x10) Устанавливает значения нескольких регистров хранения. Запрос к ведомому содержит начальный адрес регистров хранения, количество регистров для записи, число байт передаваемых данных значений и двухбайтовые значения регистров хранения.
Ответ от ведомого содержит начальный адрес регистров хранения и количество установленных значений.
В данном пример устанавливаются значения двух регистров хранения по начальному адресу 0x1000 как 0x1234 и 0x5678. Получение идентификатора ведомого устройства (0x11) Получает информацию о ведомом устройстве: идентификатор, состояние и специфичные для конкретного типа устройства данные. Запрос к ведомому содержит лишь номер функции.
Ответ от ведомого содержит количество передаваемых следом байт данных, идентификатор ведомого устройства, состояние ведомого устройства (0x00 - ВЫКЛ, 0xFF - ВКЛ) и данные, зависящие от конкретного типа устройства.
В данном примере получен идентификатор ведомого (0x55), состояние (ВКЛ - 0xff) и дополнительные данные (0x01, 0x02, 0x03). Реализация для микроконтроллера USART Первое что нужно - это сам USART - именно он будет получать и передавать пакеты в линию. Обязательно должен использовать DMA - зачем нам самим передавать байты, если это может сделать кто-то другой? Пакеты Modbus RTU разделяются паузами - можно, конечно, помудрить с таймером, но периферия USART умеет детектировать свободную линию, так что просто предоставим и это периферии, благо это прекрасно работает, приятно когда аппаратура делает работу за нас. Хотя Modbus RTU обеспечивает проверку целостности пакета посредством CRC, неплохо бы обнаруживать ошибки приёма USART, не нужно делать лишнюю работу по приёму и обработке заведомо неправильных данных. Ещё одной приятной особенностью USART в stm32 является возможность ничего не принимать (спать) до детектирования свободной линии, что можно использовать для пропуска сообщений, адресованных не нашему устройству. Modbus RTU К реализации Modbus RTU есть одна, но важная хотелка - удобство - как можно меньше телодвижений для обработки обращений к нашему устройству и минимум затрат для добавления очередного элемента данных. Схема Схема тривиальна, по сути, имея отладочную плату, достаточно соединить крест-накрест Tx и Rx с Rx и Tx преобразователя USB или RS-323 компьютера в UART с пинами микроконтроллера. Светодиод подключен для управления им по Modbus, на моей отладочной плате он висит на ножке 1 порта A. Прошивка Программа "Привет, Мир!" для микроконтроллера - это моргание светодиодом. Поэтому сделаем так, чтобы этим самым светодиодом можно было управлять с ПК по Modbus, задавая значение регистра флагов, заодно сделаем, скажем, счётчик, значение которого будем получать и задавать как регистр хранения. Объявим необходимые переменные. /** //! Шина USART.
Опишем функции для работы с USART. /**
Теперь очередь функционала, отвечающего за работу Modbus. /**
Вот и вся прошивка (опущена реализация библиотек USART и Modbus RTU - ссылки на них в конце статьи, к тому же последняя довольно громоздка), теперь наше устройство будет реагировать на запросы Modbus на интерфейсе USART1. Самое замечательное и удобное, на мой взгляд, это те самые функции обратного вызова (каллбэки) доступа к данным - не нужно заботиться об обработке запроса и правильном заполнении ответа, всё это за нас сделает библиотека, всё что нам нужно - указать какую функцию вызывать для какого типа данных! Размер прошивки составляет всего 6.5 кб (6634 байта). Реализация для компьютера Для компьютера воспользуемся довольно удобной библиотекой libmodbus. Программа Тут всё очень просто, библиотека имеет довольно простой и удобный интерфейс. Переменные. // Контекст Modbus. И обращение к устройству (опустим обработку ошибок). // Создадим контекст Modbus и откроем порт с заданными параметрами. // Режим - RS-232. // Соединимся. // Состояние светодиода. // Выведем на экран. // Инвертируем. // Запишем значение регистра флагов.
// Значение счётчика. // Получим значение регистра хранения. // Выведем на экран. // Обнулим значение регистра хранения.
// Буфер для идентификатора устройства. // Нулевой байт в ответе - идентификатор. // Выведем полученные сведения на экран. Всё, теперь при запуске программы, на устройстве светодиод будет менять своё состояние, счётчик сбрасываться, а в консоль будет выводиться состояние светодиода, значение счётчика и идентификатор ведомого устройства. Пример вывода на консоль: Led status: 1 Заключение Теперь можно довольно просто управлять нашим устройством с компьютера, или даже несколькими устройствами, соединёнными в сеть (например в случае того же умного дома). В будущем также планируется реализовать полноценный режим ведущего устройства. Спасибо за внимание! Исходный код проектов для МК и ПК находится в прикреплённых файлах. Библиотеки USART и Modbus RTU лежат на GitHub.
Файлы: Все вопросы в Форум.
Эти статьи вам тоже могут пригодиться: |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||