Интерфейс RS-485 в ZETSENSOR

Протокол работы цифровых модулей ZETSENSOR по интерфейсу RS-485

ZETSENSOR BANNER

Управление цифровыми модулями ZETSENSOR по интерфейсу RS-485 осуществляется по протоколу Modbus RTU

Цифровые модули ZETSENSOR выступают в роли ведомых устройств (slave) и отвечают на запросы Modbus RTU, поступающие к ним со стороны ведущего устройства (master). Для этого каждому цифровому модулю назначается уникальный в пределах одной шины RS-485 адрес узла (node) — целое число от 2 до 63.

Формат пакетов Modbus RTU

Запросы и ответы передаются в виде пакетов Modbus RTU

Поле Размер, байты Описание
node 1 Адрес узла (целое число от 2 до 63)
func 1 Код команды
data N Данные
crc 2 Контрольная сумма

                         Формат пакета Modbus RTU

Все поля пакета, за исключением контрольной суммы, указываются в порядке big-endian — старшим байтом вперед.

При работе с цифровыми модулями ZETSENSOR обычно используются следующие команды Modbus:

  • Read Holding Registers (0x03) — для получения текущих настроек цифрового модуля;
  • Read Input Register (0x04) — для получения данных по измерительному каналу;
  • Write Multiple Registers (0x10) — для изменения настроек.

Контрольная сумма вычисляется по алгоритму CRC16 (полином x16+x15+x2+1, начальное значение 0xFFFF).

Команда Read Holding Registers (0x03)

Команда Read Holding Registers (0x03) предназначена для чтения одного или нескольких последовательных регистров хранения. В запросе указываются адрес первого регистра и их количество, а в ответе — сами значения регистров. Количество регистров, выдаваемых в ответе, может быть меньше запрошенного. Значение каждого регистра в ответе указывается в порядке big-endian.

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x03
addr 2 Адрес первого регистра хранения
quant 2 Количество регистров, от 1 до 120
crc 2 Контрольная сумма

                  Формат запроса Read Holding Registers

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x03
len 1 Количество байтов данных, от 0 до (quant * 2)
data len Значения регистров хранения
crc 2 Контрольная сумма

                           Формат ответа Read Holding Registers

Команда Read Input Register (0x04)

Формат команды Read Input Register (0x04) совпадает с форматом команды Read Holding Registers (0x03), только вместо регистров хранения, соответствующих вкладкам, запрашиваются регистры ввода, накапливаемые во внутреннем буфере по каждому каналу.

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x04
addr 2 Адрес регистра ввода
quant 2 Количество регистров, от 1 до 120
crc 2 Контрольная сумма

                     Формат запроса Read Input Register

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x04
len 1 Количество байтов данных, от 0 до (quant * 2)
data len Значения регистра ввода
crc 2 Контрольная сумма

                             Формат ответа Read Input Register

Команда Write Multiple Registers (0x10)

Команда Write Multiple Registers (0x10) предназначена для изменения значений одного или нескольких последовательных регистров хранения.

Значение каждого регистра в запросе указывается в порядке big-endian.

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x10
addr 2 Адрес первого регистра хранения
quant 2 Количество регистров, от 1 до 120
len 1 Количество байтов данных (quant * 2)
data len Значения регистров
crc 2 Контрольная сумма

                    Формат запроса Write Multiple Registers

Поле Размер, байты Описание
node 1 Адрес (Node) — целое число от 2 до 63
func 1 0x10
addr 2 Адрес первого регистра хранения
quant 2 Количество измененных регистров, от 0 quant 
crc 2 Контрольная сумма

                       Формат ответа Write Multiple Registers

Модель данных

Каждое поле внутри устройства хранится в виде числа или массива чисел, в порядке little-endian (младшим байтом вперед)

Тип Размер, байты Описание
char 1 Текстовый символ в кодировке 1251
short / unshort 2 Целое 16-разрядное (знаковое / беззнаковое)
long / unlong 4 Целое 32-разрядное (знаковое / беззнаковое)
float 4 С плавающей запятой одинарной точности (IEEE 754)
time 4 Время в формате UNIX (time_t — количество секунд, прошедших с 01.01.1970 00:00:00)
longlong 8 Целое 64-разрядное

                                                                                                    Основные типы данных

В протоколе Modbus все данные передаются через 16-битные регистры, поэтому, например, для передачи числа типа long требуется два регистра, при этом первый регистр содержит младшие два байта числа, а второй — старшие два байта. Если поле является массивом (количество чисел 2 или более), то все числа массива расположены в памяти друг за другом.

Текстовые символы всегда хранятся не сами по себе, а в составе текстовых строк — массивов, причем количество символов всегда четное. В памяти текстовая строка занимает фиксированное количество байтов (и регистров), но содержащийся в ней текст может быть короче — до первого нулевого символа. Для передачи текстовой строки размером N символов требуется N/2 регистров, при этом каждый следующий регистр содержит очередные два символа строки.

В устройстве организовано две области памяти:

  1. настройки — чтение и запись, доступ через регистры хранения (Holding Registers);
  2. выходные данные — только чтение, доступ через регистры ввода (Input Registers).

Чтение настроек

Все настройки устройства сгруппированы по вкладкам. Каждая вкладка имеет название и состоит из полей. Вкладки расположены друг за другом в одной общей области памяти. Доступ к этой области осуществляется через регистры хранения (Holding Registers), с помощью команд Read Holding Registers и Write Multiple Registers.

Адресация регистров начинается с нуля, количество регистров, соответствующих каждой вкладке, зависит от ее размера.

Адрес регистра Содержимое
0x0000 Заголовок первой вкладки.

Общий размер вкладки — N0 байтов.

0x0001
0x0002
0x0003
0x0004 Содержимое первой вкладки.
N0 / 2 — 1
N0/ 2 Заголовок второй вкладки.

Общий размер вкладки — N1 байтов.

N0/ 2 + 1
N0/ 2 + 2
N0/ 2 + 3
N0/ 2 + 4 Содержимое второй вкладки.
N0/ 2 + N1/ 2 — 1

              Соответствие регистров и вкладок

Как видно из таблицы, каждая вкладка начинается с заголовка, который занимает четыре регистра.

Номер регистра Тип поля Содержимое
0x0000 unshort Размер вкладки в байтах (в младших 12 битах)
0x0001 unshort Зарезервировано
0x0002 unshort Переключатель записи write_enable
0x0003 unshort Контрольная сумма

                                        Формат заголовка вкладки

Состав вкладок зависит от типа цифрового модуля (а зачастую и от версии прошивки). Полную и актуальную таблицу регистров конкретного устройства можно получить с помощью программы «Сервисная работа с ZET7xxx».

Примеры сгенерированных таблиц доступны на файловом ресурсе ZETLAB на FTP сервере.

Например, для цифрового модуля ZET 7060 таблица регистров может выглядеть следующим образом: Таблица адресов модуля ZET 7060

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

Поле Значение, hex Комментарий
node 03 Адрес (Node) — во всех примерах взят за 3
func 03 Команда Read Holding Registers
addr 00 86 Адрес первого регистра поля (0x86)
quant 00 02 Два регистра (так как тип — float)
crc 24 00 Контрольная сумма

Пример запроса чтения текущего значения по четвертому каналу ZET 7160

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Ответ Read Holding Registers
len 04 Четыре байта
quant 00 00 40 A0 5.0 (в двоичном виде 0x40A00000)
crc E8 4B Контрольная сумма

Пример ответа на запрос чтения текущего значения по каналу ZET 7160

Изменение настроек

Для изменения настроек недостаточно просто отправить команду записи соответствующих регистров. Вместо этого требуется выполнить так называемую транзакцию изменения, состоящую из трех или более команд Write Multiple Registers. Это необходимо для обеспечения целостности вкладок.

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

Транзакция изменения состоит из следующих этапов:

  1. начало транзакции (одна команда Write Multiple Registers);
  2. тело транзакции (одна или несколько команд Write Multiple Registers);
  3. завершение транзакции (одна команда Write Multiple Registers).

На всю транзакцию отводится не более 10 секунд. Если после начала транзакции отведенное время истечет до ее завершения, то вся транзакция будет отменена. 

Начало транзакции — это запись значения 0x0001 в поле write_enable . Чтобы получить адрес регистра write_enable, нужно к адресу самой вкладки прибавить 0x02.

Тело транзакции — это запись самих настроек. Изменять можно как одно поле вкладки, так и несколько, как одной командой записи, так и несколькими, но все в пределах одной вкладки.

Завершение транзакции — это запись значения 0x0003 в поле write_enable, а также нового значения контрольной суммы (поле crc  в заголовке).

Контрольная сумма рассчитывается по тому же алгоритму CRC16, что используется в пакетах Modbus RTU, но в «перевернутом» виде — старшим байтов вперед.

В расчете контрольной участвуют три последовательности байтов:

  1. восемь байтов серийного номера;
  2. первые шесть байтов заголовка вкладки (весь заголовок кроме поля crc);
  3. новое содержимое вкладки после заголовка (с восьмого по последний байты вкладки)

Как видно, для выполнения транзакции требуется знать серийный номер и содержимое вкладки. Серийный номер достаточно знать заранее или вычитать один раз перед началом работы с цифровым модулем. Серийный номер имеет тип longlong и доступен по адресу 0x06.

Содержимое вкладки можно либо считать один раз и хранить в памяти программы (рекомендуется), либо просто считывать перед каждым изменением. Для чтения вкладки обычно требуется две команды чтения: первой командой считывается размер вкладки в байтах (это самый первый регистр вкладки, точнее, его младшие 12 битов), а второй командой — вся вкладка.

Пример изменения настроек

В качестве примера возьмем цифровой модуль ZET 7060 и попробуем изменить в нем вкладку «Порт» (адрес 0x100), а именно — установить частоту дискретизации 10 Гц (адрес поля — 0x104).

Запрос № 1. Считываем серийный номер устройства.

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Команда Read Holding Registers
addr 00 06 Адрес первого регистра поля «Серийный номер»
quant 00 04 Четыре регистра (так как тип —- longlong)
crc A5 EA Контрольная сумма

                         Пример запроса чтения серийного номера

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Ответ Read Holding Registers
len 08 Восемь байтов
quant 13 0F 69 41 5D B4 35 85 Серийный номер
crc 90 39 Контрольная сумма

         Пример ответа на запрос чтения серийного номера

Получаем серийный номер устройства — 0x35855DB46941130F

Запрос № 2. Считываем размер вкладки «Порт»

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Команда Read Holding Registers
addr 01 00 Адрес первого регистра вкладки (0x100)
quant 00 01 Один регистр
crc 84 14 Контрольная сумма

              Пример запроса чтения размера вкладки «Порт»

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Ответ Read Holding Registers
len 02 Два байта
data 40 2C Размер равен (0x402C & 0x0FFF) = 44
crc F1 99 Контрольная сумма

          Пример ответа на запрос размера вкладки «Порт»

Получаем размер вкладки — 44 байта, или 22 регистра.

Запрос № 3. Считываем содержимое вкладки «Порт».

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Команда Read Holding Registers
addr 01 00 Адрес первого регистра вкладки (0x100)
quant 00 16 22 регистра (44 байта)
crc C4 1A Контрольная сумма

              Пример запроса чтения всей вкладки «Порт»

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 03 Ответ Read Holding Registers
len 2C 44 байта
data 40 2C 00 7E 00 00 62 96

00 00 3F 80

00 01 00 00

00 01 00 00

00 01 00 00

00 01 00 00

00 00 00 00

00 00 00 00

00 00 00 00

00 00 00 00

Заголовок вкладки

Частота дискретизации (float = 1 Гц)

Маска управления портом 0 (int = 1)

Маска управления портом 1 (int = 1)

Маска управления портом 2 (int = 1)

Маска управления портом 3 (int = 1)

Значение на порту 0 (int = 0)

Значение на порту 1 (int = 0)

Значение на порту 2 (int = 0)

Значение на порту 3 (int = 0)

crc 66 32 Контрольная сумма

                 Пример ответа на запрос вкладки «Порт»

Получаем содержимое вкладки «Порт» (в виде последовательности байтов):

2C 40 7E 00 00 00 96 62 00 00 80 3F 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Видно также, что текущее значение частоты дискретизации — 1 Гц.

Запрос № 4. Начинаем транзакцию изменения.

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 10 Команда Write Multiple Registers
addr 01 02 Адрес регистра write_enable (0x102)
quant 00 01 Один регистр
len 02 Или два байта
data 00 01 Значение write_enable = 0x0001
crc 6F D2 Контрольная сумма

Пример начала транзакции для вкладки «Порт» (адрес 0x100)

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

Запрос № 5. Выполняем тело транзакции — устанавливаем необходимое значение.

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 10 Команда Write Multiple Registers
addr 01 04 Адрес регистра «Частота дискретизации» (0x104)
quant 00 02 Два регистра (так как тип поля — float)
len 04 Или четыре байта
data 00 00 41 20 Значение 10.0 в двоичном виде — 0x41200000
crc C5 FC Контрольная сумма

   Пример установки частоты дискретизации 10 Гц на вкладке «Порт»

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

Запрос № 6. Завершаем транзакцию.

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

№ п/п Начальное значение Входная последовательность, hex Выходное значение Комментарий
1 0xFFFF 0F 13 41 69 B4 5D 85 35 0x3765 Серийный номер
2 0x3765 2C 40 7E 00 03 00 0x8AE2 Заголовок вкладки

с новым значением 

write_enable = 0x0003

3 0x8AE2 00 00 20 41

01 00 00 00 01 00 00 00

01 00 00 00 01 00 00 00

00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00

0xD728 Содержимое вкладки

после заголовка

с новым значением частоты

дискретизации

0x41200000 = 10.0

                                                                                  Пример расчета контрольной суммы

Получившуюся в итоге контрольную сумму 0xD728 «переворачиваем» (0x28D7) и отправляем вместе с кодом write_enable = 0x0003.

Поле Значение, hex Комментарий
node 03 Адрес (Node)
func 10 Команда Write Multiple Registers
addr 01 02 Адрес регистра write_enable (0x102)
quant 00 02 Два регистра: write_enable и скс
len 04 Или четыре байта
data 00 03

28 D7

Значение write_enable = 0x0003

Значение crc= 0x28D7

crc DA 00 Контрольная сумма

Пример завершения транзакции для вкладки «Порт» (адрес 0x100)

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

Если все запросы были сделаны правильно и за отведенное время, то при следующих запросах чтения вкладки «Порт» будет считываться установленное значение частоты дискретизации — 10 Гц. Если же транзакция был отклонена по какой-либо причине, то считываться будет старое значение — 1 Гц.

Кроме этого, так как установленное значение влияет на частоту выдачи данных по каналам, то во вкладках «Канал 1», «Канал 2», «Канал 3» и «Канал 4» поле «Частота обновления выходного сигнала» также примет новое значение 10 Гц.

Получение выходных данных

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

Для получения выходных данных используется команда Read Input Register, при этом каждому каналу (буферу) отводится один регистр ввода. Адрес регистра ввода совпадает с адресом регистра хранения, соответствующего полю «Текущее значение канала». Обычно адрес регистра ввода для первого канала равен 0x14, для второго канала, если он есть, — 0x3A (0x14 + 0x26), для третьего — 0x60 (0x3A + 0x26), и так далее.

В одной команде можно запросить до 60 чисел float (или 120 регистров), при этом в ответе будет выдано столько чисел, сколько накоплено во внутреннем буфере к моменту запроса. Если буфер пуст, то будет выдан пустой ответ (len = 0).

Буфер рассчитан на хранение не более 15 секунд последних данных (или даже меньше — при достаточно большой частоте выдачи данных). Устаревшие данные будут потеряны, если скорость чтения данных будет ниже, чем скорость их накопления.