Пришло время рассмотреть еще одну вариацию протокола Modbus – Modbus ASCII. Эта версия протокола использует для передачи данных только символы ASCII, которыми кодирует шестнадцатеричное представление бинарных данных. Немного не понятно и запутано? Это ничего, welcome под кат и давайте рассмотрим, с чем же мы имеем дело.
Первое отличие протокола Modbus ASCII от Modbus RTU – у него есть разделитель между пакетами. Если в Modbus RTU все пакеты шли один за одним (практически, там должна быть небольшая задержка на линии между пакетами, порядка 2-5мс), то в Modbus ASCII каждый новый пакет должен начинаться со специального символа разделителя.
По стандарту Modbus RTU между пакетами нужна задержка в 3.5 символа (это время, которое нужно для передачи 3.5 символов по линии связи, зависит от скорости передачи). Эта задержка используется, что бы детектировать новый запрос от мастера. Т.е. эта задержка указывает начало нового запроса. Но когда стали использовать модемы, это перестало работать. На модеме невозможно выдержать нужное время. Поэтому решили использовать новый вариант протокола — Modbus ASCII. Этот вариант устраняет многие неудобства при работе с модемом: есть специальный символ разделитель пакетов и используются только видимые символы ASCII.
Так вот, таким символом начала пакета служит символ двоеточие с шестнадцатеричным кодом 0x3A. А конец каждого пакета помечается символами новой строки и перевода каретки – 0x0D 0x0A. Таким образом, из протокола полностью убирается зависимость от задержек между байтами. Т.е. если модем задержит байт, это не вызовет недопонимания на стороне клиента. И он будет ждать окончания пакета байтами 0x0D 0x0A. А если встретит символ разделителя 0х3А – сбросит буфер и начнем формировать пакет заново. Кроме того нет необходимости в экранировании спец символов модема, так как данные не используют символы из начальной секции ASCII таблицы.
В Modbus ASCII протоколе каждый байт данных представлен в виде 2 байтов. Каждый байт представляет собой ASCII символ в шестнадцатеричном представлении. Что бы легче было понять, приведем пример:
Modbus Протокол | Данные (ASCII) | Данные (HEX) | Данные (Двоичные) |
Modbus RTU | # | 23 | 0010 0011 |
Modbus ASCII | 2, 3 | 32, 33 | 0011 0010, 0011 0011 |
Например, нам нужно передать байт данных, который хранит символ #. Этот символ имеет в таблице ASCII шестнадцатеричный код 0x23. В протоколе Modbus RTU мы просто передаем байт со значением 0x23.
Если мы хоти передать тот же символ через протокол Modbus ASCII, нам нужно уже передавать 2 байта. На первом этапе мы получаем шестнадцатеричный код символа, 0x23. На втором этапе мы кодируем это значение при помощи двух символов ASCII – 2 и 3. И на третьем этапе мы передаем два байта данных, первый — это шестнадцатеричное значение символа 2, второй байт — это шестнадцатеричное значение символа 3.
Таким образом, диапазон значений для байта данных в протоколе Modbus RTU – 0 .. 0xFF
Диапазон значений для байта данных в протоколе Modbus ASCII – только символы, необходимые для отображения шестнадцатеричных цифр, т.е. 0 – 9, A, B, C, D, E, F (все заглавные).
В протоколе Modbus RTU используется 2 байтная контрольная сумма, которая помогает детектировать поврежденные запросы. В протоколе Modbus ASCII так же есть контрольная сумма – LRC (Longitudinal Redundancy Check).
Вычисление LRC намного проще, чем вычисление CRC. Что бы высчитать LRC вам нужно сделать следующие:
Таким образом, если затем сложить все байты пакета данных и байт LRC мы получим в результате 0. Это и есть самая быстрая проверка корректности пакета данных.
Ниже приведен пример вычисления LRC для конкретного запроса Modbus ASCII.
Для примера возьмем запрос на чтение регистров #40108 — #40110 с устройства с адресом 17
Запрос: 11 03 00 6B 00 03Данные (Десятичные) | Данные (HEX) | Данные (Двоичные) |
17 | 11 | 0001 0001 |
3 | 03 | 0000 0011 |
0 | 00 | 0000 0000 |
107 | 6B | 0110 1011 |
0 | 00 | 0000 0000 |
3 | 03 | 0000 0011 |
Теперь посчитаем сумму всех байт
Данные (Десятичные) | Данные (HEX) | Данные (Двоичные) |
130 | 82 | 1000 0010 |
Следующий шаг – сделаем полученную сумму отрицательной
Данные (Десятичные) | Данные (HEX) | Данные (Двоичные) |
-130 | 7E | 0111 1110 |
Вот это отрицательное число (-130 или 0x7E) и есть LRC запроса.
Эта контрольная сумма добавляется к запросу в виде 2 ASCII символов – 7 и E.
Т.е. в конце запроса нужно добавить 2 байта со значением 37 и 45.
Что бы лучше понять, как все это работает, посмотрите пару простых примеров.
Возьмем наш запрос на чтение регистров #40108 — #40110 с устройства с адресом 17
Запрос: 11 03 00 6B 00 03Это Modbus RTU запрос без последних двух байтов CRC. Теперь преобразуем этот запрос из Modbus RTU в Modbus ASCII. Для этого добавляем в начало запроса символ двоеточия, в конец запроса символ перевода строки и возврата каретки, а каждый байт представим в виде ASCII символов, соответствующих шестнадцатеричному представлению каждого байта запроса. В итоге у нас получиться такой запрос (в виде ASCII символов, или попросту в виде текстовой строки). Так же в конец запроса добавляем LRC.
: 1 1 0 3 0 0 6 B 0 0 0 3 7 E CR LFТеперь просто нужно передать данный запрос в порт, используя коды ASCII символов. В бинарном виде запрос будет выглядеть так:
3A 3131 3033 3030 3642 3030 3033 3745 0D 0AИндекс байта | Значение HEX | ASCII | Описание |
0 | 3A | : | Символ начала |
1-2 | 31 31 | 11 | Адрес устройства |
3-4 | 30 33 | 03 | Код команды |
5-8 | 30 30 36 42 | 00 6B | Адрес HOLDING регистра, с которого нужно начинать чтение. В данном случае 0х006B = 107. Но это не адрес, а смещение от адреса 40001. Т.е. реальный адрес = 107+ 40001 = 40108. |
9-12 | 30 30 30 33 | 00 03 | Количество регистров, которые нужно прочитать. 0х0003 = 3. Т.е. читать нужно регистры 40108– 40110. |
13 – 14 | 37 45 | 7E | LRC запроса |
15 | CR | 0D | Символ перевода каретки |
16 | LF | 0A | Символ новой строки |
www.siv-blog.com
habr.com
7) Поле контрольной суммы позволяет MS и SL проверять сообще-ние на наличие ошибок. Сообщения с ошибками и SL, и MS игнорируют. В режиме ASCII в поле контрольной суммы используется LRC, а в режиме RTU – CRC.
LRC (Longitudinal Redundancy Check) - контрольная сумма представ-ляется 8-ми разрядным числом, передаваемым двумя ASCII символа (hex). Контрольная сумма образуется путем конвертирования всех символов в двоичные числа, сложением этих чисел без учета переноса, и вычислением дополнительного кода полученного числа. В приемнике LRC заново рас-считывается и сравнивается с полученым LRC. При вычислении LRC двоеточие, CR, LF и любой другой не ASCII символ отбрасывается.
CRC-16 (Cyclic Redundancy Check) рассматривает все сообщение (то-лько биты данных без учета старт/стоповых бит и бит четности) как одно последовательное двоичное число, у которого старший значащий бит пере-дается первым. Сообщение защищается полиномом вида: Х16+Х15+Х2+1.
11.1.3. Пользовательский уровнь Modbus определяет то, каким об-разом с помощью данного интерфейса могут решаться производственные задачи.
1) Система команд, предназначенных для взаимодействия в сети Modbus, представлена в табл. 11.3.
В процессе взаимодействия MS с SL в дополнении к тем ситуациям, когда на канальном уровне индицируется ошибка по контрольной сумме, SL в коде функции может устанавливать значение S=1 при выявлении сле-дующих исключительных пользовательских ситуаций:
– функция в принятом сообщении не поддерживается на данном SL;
– значения в поле данных не допустимы для данного SL;
– адрес в поле данных не допустим для данного SL;
– значения в поле данных не допустимы для данного SL;
– SL не может ответить на запрос или произошла авария;
– SL принял запрос и начал выполнять долговременную операцию про-граммирования;
– сообщение принято без ошибок, но SL в данный момент выполняет долговременную операцию программирования. Запрос необходимо ретран-слировать позднее;
– функция программирования не может быть выполнена.
Т а б л и ц а 11.3.
Код | Название | Действие |
01 | READCOIL STATUS | СЧИТАТЬтекущее состояние (ON/OFF) группы логических ячеек. |
02 | READINPUTSTATUS | СЧИТАТЬтекущее состояния (ON/OFF) группы дискретных входов. |
03 | READHOLDING .REGISTERS | СЧИТАТЬтекущие значения одного/нескольких регистров хранения. |
04 | READ INPUT REGISTERS | СЧИТАТЬтекущие значения одного/нескольких входных регистров. |
05 | FORCE SINGLE COIL | ЗАПИСАТЬсостояниеONилиOFFлогической ячейки. |
06 | FORCE SINGLE REGISTER | ЗАПИСАТЬновые значения в регистр хранения. |
15 | FORCE MULTIPLE COILS | ЗАПИСАТЬ состояние (ON/OFF) нескольких логических ячеек. |
16 | FORCEMULTIPLE REGISTERS | ЗАПИСАТЬновые значения нескольких последовательных регистров. |
17 | REPORT SLAVE I.D. | СЧИТАТЬ тип адресуемогоSLи его рабочее состояние. |
19 | RESET COMMUNI-CATIONS LINK | СБРОС SLв известное состояние после неустранимой ошибки. Сбрасывает счетчик принятых байт. |
11 | FETCH EVENT COUNTER COMMUNICATIONS | Позволяет MSпутем последовательной посылки одного сообщения определить выполнение операции. |
12 | FETCH COMMUNI-CATIONS EVENT LOG | Позволяет MSполучить из журнала связи ин-формацию о каждой передаче. Если передача не выполнена, в журнале фиксируется ошибка. |
13 | PROGRAM | Позволяет MSпрограммироватьSL. |
Остальные коды функций зарезервированы для развития . |
Рассмотрим примеры исполнения функций протокола ( числа пред-ставлены в hex-формате).
2) Чтение данных (функция -03) осуществляется сообщениями в форматах RTU и ASCII в соответствии с табл.11.4.
Т а б л и ц а 11.4.
Чтение данных из SL с адресом 06, из 3-х регистров, начиная с адреса 6B. | ||||||
ЗАПРОС | RTU | ASCII | ||||
Заголовок | : | |||||
Адрес | 0000 | 0110 | 0 | 6 | ||
Функция | 0000 | 0011 | 0 | 3 | ||
Начальный адрес | H.O. | 0000 | 0000 | 0 | 0 | |
L.O. | 0110 | 1011 | 6 | В | ||
Количество требуемых регистров | H.O. | 0000 | 0000 | 0 | 0 | |
L.O. | 0000 | 0011 | 0 | 3 | ||
Поле контрольной суммы | 0111 | 0101 | 8 | 9 | ||
1010 | 0000 | |||||
Завершение | CR | LF | ||||
ОТВЕТ | RTU | ASCII | ||||
Заголовок | : | |||||
Адрес | 0000 | 0110 | 0 | 6 | ||
Функция | 0000 | 0011 | 0 | 3 | ||
Количество байт данных | 0000 | 0110 | 0 | 6 | ||
Данные | Регистр 1 | H.O | 0000 | 0010 | 0 | 2 |
L.O. | 0010 | 1011 | 2 | B | ||
Регистр 2 | H.O. | 0000 | 0000 | 0 | 0 | |
L.O. | 0000 | 0000 | 0 | 0 | ||
Реги-стр 3 | H.O. | 0000 | 0000 | 0 | 0 | |
L.O. | 0110 | 0011 | 6 | 3 | ||
Контрольная сумма | CRC | 6 | 1 | |||
Завершение | CR | LF |
Адресуемый SL посылает: свой адрес, код выполненной функции с указанием S=0/1 и информационное поле, которое содержит: 2 байта, оп-ределяющих количество байт данных. Длина каж-ого регистра данных – 2 байта. Первый байт данных в посылке является старшим, второй – млад-шим. Некоторые SL ограничивают количество регистров, передаваемых за один запрос. В этом случае для получения, большего числа регистров, необходимо выполнить несколько последовательных запросов.
3) Чтение дискретных входов (функция 02) позволяет пользователю получить состояние (ВКЛ/ОТКЛ) дискретных входов 197-218 из SL с деся-тичным адресом 17.
Ответное сообщение включает адрес SL, код функции, количество байт данных, байты данных и поле контрольной суммы. Данные упако-ваны по биту на каждый вход (1 = ON, 0 = OFF). Младший бит первого байта содержит значение первого адресуемого входа, за которым следуют остальные. Если количество запрошенных входов не кратно 8, то осталь-ные биты заполняются нулями. Количество байт данных всегда определяется как количество RTU данных.
Запрос | ||||||
Адрес | Команда да | Размер поля данных | Контр. сумма | |||
Номер первой ячейки | Количество ячеек | |||||
Ст. байт | Мл. байт | Ст. байт | Мл. байт | |||
11 | 02 | 00 | С4 | 00 | 16 | 13 |
Данные или буфер под данные - отсутствуют |
Ответ | ||||||
Адрес | Команда да | Размер поля данных | Поле данных | Контр. сумма | ||
Дискр. входы 197-204 | Дискр. входы 205-212 | Дискр. входы 213-218 | ||||
Кол. байт | Байт 1 | Байт 2 | Байт 3 | |||
11 | 02 | 03 | AC | DB | 35 | 2E |
Статус входов 197…204 =ACh = 1010 1100. Читая слева направо, видим, что входы 204, 202, 200 и 199 в состоянии ON. Все остальные байты данных распаковываются аналогично. Так как было запрошено 22 линии, последний байт данных (35h =0011 0101) содержит только 6 входов (213…218) вместо 8-ми. Два последних бита заполняются нулями.
4) Запись одной ячейки (функция 05) позволяет модифицировать содержание одной логической ячейки. Ячейки нумеруются с нуля (ячейка 1= 0, ячейка 2 = 1 и т.д.). Число FF00h устанавливает ячейку в 1, а число 00 00– в 0. Другие числа не влияют на содержимое ячейки. Функция может использоваться в широковещательном режиме.
Запись «1» в ячейку с адресом 0173:
Адрес | Команда | Размер поля данных | Контр. сумма | |||
Номер первой ячейки | Индикатор/ управление | |||||
Ст. байт | Мл. байт | Ст. байт | Мл. байт | |||
11 | 05 | 00 | AC | FF | 00 | 3F |
Данные или буфер под данные - отсутствуют |
Ответ: ответное сообщение полностью совпадает с запросом.
studfiles.net