ModBus AVR. Стандарт против протоколов-самоделок.

Передо мной встала задача организации обмена данными между ведущим устройством и подчиненными, построенными на базе контроллеров AVR ATmega (16,32,162,128).
Большинство встраиваемых систем обмениваются данными с внешними устройствами. На великих просторах рунета очень много «полезных» советов, о том, как организовать обмен данными. Как правило, такие рекомендации сопровождаются советами типа: ….назначьте ID устройства, введите коды команд, ширину пакета данных, стартовые и стоповые байты. Особо продвинутые предлагают проверку контрольной суммы и прочие прелести, а зачастую на месте таких тем разгорается скандал о том, какая скорость обмена лучше и как сэкономить 10 рублей, сделав преобразователь UART-RS232 на 2х транзисторах. Здесь этого не будет! Основная мысль проста: найди стандарт передачи данных, то есть надежный протокол, поддерживаемый большим спектром программного обеспечения и пригодный к различным аппаратным платформам: UART, ZigBee и прочее. И таким является ModBus. По секрету скажу, есть случаи, когда ModBus не удовлетворяет потребностям, и, как правило, это связано с быстродействием протокола. В данной статье не будет рассмотрен ModBus TCP.
Modbus был разработан компанией Modicon (в настоящее время принадлежит Schneider Electric) для использования в её контроллерах с программируемой логикой. Впервые спецификация протокола была опубликована в 1979 году Адаптированное к пониманию описание пакета протокола:

адрес ведомого устройства| код функции| данные| блок обнаружения ошибок где:
- адрес ведомого устройства — адрес подчинённого устройства, к которому адресован запрос. Ведомые устройства отвечают только на запросы, поступившие в их адрес. Ответ также начинается с адреса отвечающего ведомого устройства, который может изменяться от 1 до 247. Адрес 0 используется для широковещательной передачи, его распознаёт каждое устройство, адреса в диапазоне 248…255 — зарезервированы;
- номер функции — это следующее однобайтное поле кадра. Оно говорит ведомому устройству, какие данные или выполнение какого действия требует от него ведущее устройство;
-  данные — поле содержит информацию, необходимую ведомому устройству для выполнения заданной мастером функции или содержит данные, передаваемые ведомым устройством в ответ на запрос ведущего. Длина и формат поля зависит от номера функции;
- блок обнаружения ошибок — контрольная сумма для проверки отсутствия ошибок в кадре.
Информация, к которой может быть получен доступ посредством ModBus представляется в виде:

Таблица Тип элемента Тип доступа
Дискретные входы (Discrete Inputs) один бит только чтение
Регистры флагов (Coils) один бит чтение и запись
Регистры ввода (Input Registers) 16-битное слово только чтение
Регистры хранения (Holding Registers) 16-битное слово чтение и запись

Физически эти регистры – ячейки памяти ОЗУ контроллера. Как вы увидите позже, в начале программы происходит выделение памяти под них в зависимости от их количества. Стоит отметить, что число регистров каждого типа не должно превышать 100. Число регистров сильно влияет на свободное место в ОЗУ контроллера. В больших проектах рационализация числа регистров – один из способов уместить переменные в ОЗУ. Размер каждого регистра – 16 бит. По типу данных для 8 битного контроллера это Unsigned ShortInt.
Modbus ASCII — для обмена используются только ASCII символы. Для проверки целостности используется алгоритм en:Longitudinal redundancy check. Сообщение разделяется на столбцы с помощью символа «:» и заканчивается символами новой строки CR/LF.
Modbus RTU – каждый байт представляется в виде 2х 16разрядных чисел.
В RTU режиме сообщение должно начинаться и заканчиваться интервалом тишины — временем передачи не менее 3.5 символов при данной скорости в сети. А теперь внимание. Для отсчета интервала частоты на контроллере используется таймер. В коде для AVR это Timer1. Так что, держите его свободным. Так, к примеру, длительность молчания составит: 4мс для скорости 9600. Для скоростей более 19200 бод допускается использовать интервалы 1,75 и 0,75 мс. Хочу отметить, мне не удалось поднять протокол на скоростях выше 57600. И еще, длительность посылки одного пакета (11 байт) на скорости 57600 составит 10.35мс, из которых более 75% составит вот это самое пресловутое молчание. Таким образом, на лицо медлительность протокола. Большую часть времени протокол молчит!

Ниже будет выложен код, который полностью работоспособен для указанных моделей и позволяет оперировать со всем, кроме дискрет. Основа кода – примеры c сайта http://freemodbus.berlios.de/. Если вы зайдете туда и заглянете в пример для AVR, вы с удивлением обнаружите, что отсутствуют функции записи рабочих регистров и дискрет, кроме того, полностью набрать файлы проекта вам придется вручную. Также этот код не поддерживает ATmega162. (Чтобы добавить поддержку требуется банальный редефайн регистров, можете проделать это с файлом port.h, где собственно рекомендуется это делать для поддержки других моделей, и вы с удивлением обнаружите, что код не работает, а виной этому – код дописывался множеством авторов и один из них добавил дополнительный исключающий редефайн регистров, причем в файл С, это ж оупен сырс).

А теперь пробежимся по самому коду:

  eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN )

 

– инициализация протокола

 MB_RTU

– режим передачи

 0x0A

 

– адреса устройства ( в данном случае все микроконтроллеры в сети – слэйвы, а этот их адрес)

 38400

 

– скорость

MB_PAR_EVEN

– с битом четности

 eMBEnable (void)

 

– старт протокола

  ( void )eMBPoll( )

 

– эту функцию требуется периодически вызывать в основном цикле контроллера.

Из особенностей, стоит отметить:
- PORTB,0 занят под бит разрешения ввода\вывода для RS-485. По умолчанию инициализирован на выход.
- прошивка в режиме экономии места требует 6 Кб памяти программ.
- существует возможность располагать переменные большего размера сразу в нескольких рабочих регистрах.
К статье будут приложены конструктор форм для тестирования ModBus, простенькая программа для быстрой проверки работоспособности ModPoll и код в виде проекта AVR Studio, а также документация протокола. Самодельные устройства использующие протокол ModBus активно использовались нами в соревнованиях Евробот 2011.

ModBus и RS-485.

Итак, данный код вполне можно использовать для передачи данных с использованием RS-485, протокол ModBus - сетевой.  Для этого делаем следующее:

-открываем port.h, для вашей модели контроллера в разделе объявлений добавляем

 #define RTS_ENABLE

- далее открываем portserial.c  и вверху сразу после инклудов ставим  тоже

 #define RTS_ENABLE

- По умолчанию, ножка, разрешающая  передачу с контроллера - это PORTB,0.  Изменить это вы можете также в port.h.

Я использовал  MAX483, MAX485. Ниже будет представлена схема, обратите внимание. На правой картинке резистор 120 Ом - терминаторный, ставится только на начальном и замыкающем звене. Резисторы 560 Ом создают смещение на линиях, их номинал вкупе с терминаторами рассчитан на напряжение питания цифровой части 5 вольт.  RX,TX - это ножки UART контроллера.

 

 

 

 

 

 

 

 

 

 

 

 

Скачать: ModBusAVR



4 Comments

  1. fonin:

    MODBUS хороший протокол, но он из серии "pull" протоколов (такого же плана и DCON). Недостаток в необходимости цикла опроса, из-за чего нельзя достичь высоких значений реакции системы.

    Вы случаем не знаете популярные "push" протоколы, когда slave-устройство может инициировать посылку на мастер (например при изменении уровня на вводе) ?

  2. Актуальный вопрос, у нас такая проблема не вставала, в связи с этим я не знаю "push" протоколов, но я понял вашу проблему. Мы только начали использовать промышленные протоколы. Мне кажется, вам нужен протокол, где инициировать передачу может любой узел сети, то есть вам нужна равноправная сеть. Поэтому вас скорее всего устроит CAN.

  3. Macduf:

    Добрый день!
    Много времени прошло с написания данной статьи, но прошу помочь мне. Реализация, которую вы приложили к статье отлично работает на ATmega32, но напрочь отказывается работать на ATmega64. Для начала решил просто добавить дефайн в port.h и portserial.c, но после компиляции вылезло 125 варнингов о двойном дефайне регистров...убрал дефайны которе добавил и просто переименовал в коде нужные мне регистры. В итоге варнинги исчезли...код скомпилировался, но....контроллер все равно молчит на запросы мастера...Данные принимаются, но в ответ контроллер ничего не передает. В чем может быть причина? на что следует обратить внимание?

  4. Macduf:

    Поторопился с вопросом)...все оказалось в неправильных фьюзах настройка кварца...вопрос снят, все работает) Спасибо за статью!)

Leave a Reply

You must be logged in to post a comment.