|
CRC16 для modbus, не повторяйте ошибок |
|
|
|
Oct 17 2008, 21:33
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(demiurg_spb @ Oct 10 2008, 22:58)  Подтвердите Ваши слова на практике: 0x01 0x6C 0x08 0xC6 - СrcL 0x0C - СrcH В этом случае от перемены мест слагаемых (СrcL<->СrcH) сумма не меняется и никогда не равна 0. Что-то мне кажется, что где-то вкралась ошибка. buf2[] = { 0x01, 0x6C, 0x08, 0x0C, 0xC6 };приведенным Вами же исходником (цитатой из модбаса?) crc = crc16_1(buf2, sizeof(buf2));даёт нулевую CRC. Это если ещё и в поток давать инверсию от рассчитанной CRC, то тогда независимо от содержимого блока при правильной CRC рассчёт CRC для блока вместе с той инвертированной CRC даст константу, не равную нулю (т.е. проверять CRC от всего надо не на нуль, а на константу, 0x01B0 для приведенной crc16_1() ).
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Oct 18 2008, 05:48
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
crc16_1 - "неправильная" функция из доки на modbus, она даёт результат в big-endian формате. Цитата(ReAl @ Oct 18 2008, 01:33)  Это если ещё и в поток давать инверсию от рассчитанной CRC, то тогда независимо от содержимого блока при правильной CRC рассчёт CRC для блока вместе с той инвертированной CRC даст константу, не равную нулю (т.е. проверять CRC от всего надо не на нуль, а на константу, 0x01B0 для приведенной crc16_1() ). Вы меня окончательно запутали что на что надо проверять  Я хотел понять как получить 0 в данном конкретном случае и наконец понял. В протоколе сказано, что приёмник должен посчитать CRC посылки без учёта последних двух байт и сравнить их с принятой crc, подсчитанной передатчиком (последние два байта посылки). О инверсиях и нулях в данном мануале речи не ведётся. Цитата(ReAl @ Oct 18 2008, 01:33)  Что-то мне кажется, что где-то вкралась ошибка. Сделал как Вы предлагаете: считать CRC для всей посылки. Действительно было непонимание, а сайчас получилось, что CRC для всей последовательности байт (с учётом двух байт CRC) = 0. Так даже проще, но несколько дольше, чем предлагается в описании протокола modbus, т.к. длиннее на 2 байта. ReAl, спасибо за помощь!
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Oct 18 2008, 07:22
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(demiurg_spb @ Oct 18 2008, 08:48)  crc16_1 - "неправильная" функция из доки на modbus, она даёт результат в big-endian формате. Это ничего не меняет - я привёл уже последовательнсть байт, а нуль остаётся нулём и при перестановке байт. big-endian-ность crc16_1() приводит только к тому, что в конце формирования буфера приходится написать Код *ptr++ = (uint8_t)((crc) >> 8); *ptr = (uint8_t)crc; а не Код *ptr++ = (uint8_t)crc; *ptr = (uint8_t)((crc) >> 8); Цитата(demiurg_spb @ Oct 18 2008, 08:48)  Вы меня окончательно запутали что на что надо проверять  Если бы в конце CRC "от crc16_1()" заносилась так: Код *ptr++ = (uint8_t) ~((crc) >> 8); *ptr = (uint8_t) ~crc; и если бы на приёме прогонять через crc16_1() и данные, и контрольный код, как "в случае с нулём" выше, то результат выходил бы не всегда 0x0000, а всегда 0x01B0, что есть остатком от деления 0xFFFF на полином CRC (с учётом endian-ности crc16_1() ). Записывая в последние два байта инверсию CRC мы добавляем это к остатку от деления всего "полинома сообщения" на CRC и таким образом получаем (CRC ^ ~CRC) = 0xFFFF. Потом на приёме мы вместо сравнения полученной CRC с инверсией двух последних байт обрабатываем на два байта больше и получаем эту константу 0x01B0. Но это так, к слову, просто тут было упомянуто, что в некоторых случаях в поток заносится не CRC, а инверсия, ну так в этом случае в конце будет не 0 :-).
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Oct 18 2008, 10:40
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(demiurg_spb @ Oct 18 2008, 10:33)  Осталось только узнать в чём практическая польза этого трюка? Принят ошибочный пакет, состоящий из одних нулей. Без применения инверсии CRC сойдется и придется делать дополнительную проверку на ноль содержимого всего пакета для отлова именно этого конкретного случая, а при использовании инверсии проверка не даст ожидаемую константу и пакет будет отброшен уже на стадии проверки CRC. Польза от инициализации начального значения отличной от нуля константой: Если таки действительно пакет из одних нулей может присутствовать в обмене, то в случае инициализации нулем потеря части этих нулей или вкрапление лишних из-за ошибок синхронизации никак не отразится на CRC. В случае инициализации константой CRC уже не сойдется.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 18 2008, 20:02
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(demiurg_spb @ Oct 10 2008, 23:58)  Подтвердите Ваши слова на практике: Цитата Вы меня окончательно запутали что на что надо проверять Я хотел понять как получить 0 в данном конкретном случае и наконец понял. В протоколе сказано, что приёмник должен посчитать CRC посылки без учёта последних двух байт и сравнить их с принятой crc, подсчитанной передатчиком (последние два байта посылки). Чего-то Вы в итоге похоже сами напутали. Возьмем последовательность вот от сюда: http://www.simplymodbus.ca/FC15.htmи возмем такой код: Код #include <util/crc16.h>
/* Типа пример!
Force Multiple Coils (FC=15)
Request
This command is writing the contents of a series of 10 discrete coils from #20 to #29 to the slave device with address 17.
11 0F 0013 000A 02 CD01 BF0B
11: The Slave Address (17 = 11 hex) 0F: The Function Code (Force Multiple Coil, 15 = 0F hex) 0013: The Data Address of the first coil. (coil# 20 - 1 = 19 = 13 hex) 000A: The number of coils to written (10 = 0A hex) 02: The number of data bytes to follow (10 Coils / 8 bits per byte = 2 bytes) CD: Coils 20 - 27 (1100 1101) 01: Coils 27 - 29 (0000 0001) BF0B: The CRC (cyclic redundancy check) for error checking. */
unsigned char buff[11] = { 0x11, 0x0F, 0x00, 0x13, 0x00, 0x0A, 0x02, 0xCD, 0x01, 0xBF, 0x0B };
volatile unsigned short CRC = 0xFFFF; volatile unsigned short CRC1;
int main(void) { unsigned char i;
for (i = 0; i < 9; i++) { CRC = _crc16_update(CRC, buff[i]); } CRC1 = CRC;
asm("nop"); // <-------- вот здесь CRC1 == 0x0BBF
CRC = _crc16_update(CRC, buff[9]); CRC = _crc16_update(CRC, buff[10]);
asm("nop"); // <--------- вот здесь CRC == 0
while (1); return 0; } То есть при формировании пакета с CRC мы должны сначала положить CRCLo == 0xBF, затем CRCHi == 0x0B. Полный CRC пакета дает 0.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|