Интерфейс RS-485 в ZETSENSOR
Протокол работы цифровых модулей ZETSENSOR по интерфейсу RS-485
Управление цифровыми модулями 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 регистров, при этом каждый следующий регистр содержит очередные два символа строки.
В устройстве организовано две области памяти:
- настройки — чтение и запись, доступ через регистры хранения (Holding Registers);
- выходные данные — только чтение, доступ через регистры ввода (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
Изменение настроек
Для изменения настроек недостаточно просто отправить команду записи соответствующих регистров. Вместо этого требуется выполнить так называемую транзакцию изменения, состоящую из трех или более команд Write Multiple Registers. Это необходимо для обеспечения целостности вкладок.
Транзакция изменения всегда затрагивает только одну вкладку. Если требуется изменить настройки из нескольких вкладок, то нужно выполнить как минимум по одной транзакции на каждую вкладку.
Транзакция изменения состоит из следующих этапов:
- начало транзакции (одна команда Write Multiple Registers);
- тело транзакции (одна или несколько команд Write Multiple Registers);
- завершение транзакции (одна команда Write Multiple Registers).
На всю транзакцию отводится не более 10 секунд. Если после начала транзакции отведенное время истечет до ее завершения, то вся транзакция будет отменена.
Начало транзакции — это запись значения 0x0001 в поле write_enable . Чтобы получить адрес регистра write_enable, нужно к адресу самой вкладки прибавить 0x02.
Тело транзакции — это запись самих настроек. Изменять можно как одно поле вкладки, так и несколько, как одной командой записи, так и несколькими, но все в пределах одной вкладки.
Завершение транзакции — это запись значения 0x0003 в поле write_enable, а также нового значения контрольной суммы (поле crc в заголовке).
Контрольная сумма рассчитывается по тому же алгоритму CRC16, что используется в пакетах Modbus RTU, но в «перевернутом» виде — старшим байтов вперед.
В расчете контрольной участвуют три последовательности байтов:
- восемь байтов серийного номера;
- первые шесть байтов заголовка вкладки (весь заголовок кроме поля crc);
- новое содержимое вкладки после заголовка (с восьмого по последний байты вкладки)
Как видно, для выполнения транзакции требуется знать серийный номер и содержимое вкладки. Серийный номер достаточно знать заранее или вычитать один раз перед началом работы с цифровым модулем. Серийный номер имеет тип 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 (отправляем новые значения write_enable (0x03) и контрольной суммы в одной команде записи Modbus).
Контрольную сумму можно отправить и перед записью write_enable, отдельной командой.
Устройство проверяет целостность вкладки при получении write_enable = 0x03 (завершение транзакции), поэтому корректная контрольная сумма должна быть записана до выполнения проверки.
Поле | Значение, 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 секунд последних данных (или даже меньше — при достаточно большой частоте выдачи данных). Устаревшие данные будут потеряны, если скорость чтения данных будет ниже, чем скорость их накопления.