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


Особенности протокола Modbus ASCII - Блог Домашнего Программиста

Пришло время рассмотреть еще одну вариацию протокола 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 ASCII

В протоколе 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.

 

Примеры Modbus RTU и Modbus ASCII запросов

Что бы лучше понять, как все это работает, посмотрите пару простых примеров.

Возьмем наш запрос на чтение регистров #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

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

void crc_calculating(unsigned char puchMsg, unsigned short usDataLen) /*##Расчёт контрольной суммы##*/ { { unsigned char uchCRCHi = 0xFF ; /* Инициализация последнего байта CRC */ unsigned char uchCRCLo = 0xFF ; /* Инициализация первого байта CRC */ unsigned uIndex ; /* will index into CRC lookup table */ while (usDataLen--) /* pass through message buffer */ { uIndex = uchCRCHi ^ *puchMsg++ ; /* Расчёт CRC */ uchCRCLo = uchCRCLo ^ auchCRCHi[uIndex] ; uchCRCHi = auchCRCLo[uIndex] ; } return (uchCRCHi << 8 | uchCRCLo) ; /* Table of CRC values for high–order byte */ static unsigned char auchCRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 }; /* Table of CRC values for low–order byte */ static char auchCRCLo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 }; } }

habr.com

5) Поле данных содержит информацию, необходимую sLi для выпол-нения указанной функции, или содержит данные sLi, собранные для ответа на запрос.

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


Смотрите также