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

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

|
Я наткнулся недавно на очередные грабли. Суть такая: Есть алгоритм для подсчёта CRC16 для Modbus: Цитата 1. Load a 16–bit register with FFFF hex (all 1’s). Call this the CRC register. 2. Exclusive OR the first 8–bit byte of the message with the low–order byte of the 16–bit CRC register, putting the result in the CRC register. 3. Shift the CRC register one bit to the right (toward the LSB), zero–filling the MSB. Extract and examine the LSB. 4. (If the LSB was 0): Repeat Step 3 (another shift). (If the LSB was 1): Exclusive OR the CRC register with the polynomial value 0xA001 (1010 0000 0000 0001). 5. Repeat Steps 3 and 4 until 8 shifts have been performed. When this is done, a complete 8–bit byte will have been processed. 6. Repeat Steps 2 through 5 for the next 8–bit byte of the message. Continue doing this until all bytes have been processed. 7. The final content of the CRC register is the CRC value. 8. When the CRC is placed into the message, its upper and lower bytes must be swapped as described below. И есть алгоритм для подсчёта CRC16 из WinAvr <util/crc16.h> написанный на ASM и оптимизированный. Код Optimized CRC-16 calculation.
Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)<br> Initial value: 0xffff
This CRC is normally used in disk-drive controllers.
The following is the equivalent functionality written in C.
\code uint16_t crc16_update(uint16_t crc, uint8_t a) { int i;
crc ^= a; for (i = 0; i < 8; ++i) { if (crc & 1) crc = (crc >> 1) ^ 0xA001; else crc = (crc >> 1); }
return crc; } Вроде как одно и тоже, а нет! По доке на modbus сдвиг нужно делать до анализа младшего бита crc, а в приведённой цитате из WinAvr <util/crc16.h> это не так. Я 3 часа долбался - почему это вдруг у моих приборов RS-485 перестал работать. А это я перед отпуском решил "улучшить" CRC модуль и забыл об этом.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
 |
Ответов
|
Oct 9 2008, 17:50
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата По доке на modbus сдвиг нужно делать до анализа младшего бита crc, Однако, на следующей странице стандарта проверяется бит переноса после сдвига. Т.е. соответственно util/crc16.h Налицо неаккуратное описание алгоритма в стандарте. А у Вас надо полечить остальные приборы для совместимости. PS - лечить именно остальные, до приведения их к состоянию нового прибора, основанного на util/crc16.h
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Oct 9 2008, 17:57
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Rst7 @ Oct 9 2008, 21:50)  PS - лечить именно остальные, до приведения их к состоянию нового прибора, основанного на util/crc16.h +1 пользуюсь util/crc16.h для модбаса, проблем нет и оно соответствует стандартной таблице crc для модбас
|
|
|
|
|
Oct 10 2008, 08:09
|

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

|
Цитата(singlskv @ Oct 9 2008, 21:57)  +1 пользуюсь util/crc16.h для модбаса, проблем нет и оно соответствует стандартной таблице crc для модбас В том-то и дело что ранее у меня все приборы были с табличным алгоритмом (из доки модбаса). А после перехода на util/crc16.h фигня какая-то происходит. Попробую разобраться в чем дело...
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Oct 10 2008, 12:09
|

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

|
Код static const unsigned char PROGMEM auchCRCHi[256]= { 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 };
static const unsigned char PROGMEM auchCRCLo[256]= { 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 };
//============================================================== // Функция подсчета контрольной суммы (CRC modbus RTU). // Максимальная длина пакета 256 байт. //==============================================================
unsigned short crc16_1(unsigned char *p, unsigned short len) { unsigned char crc_hi; unsigned char crc_lo; unsigned char n;
if (len>256U) { return (0); }
n = (unsigned char)len;
crc_hi = 0xFF; // high byte of CRC initialized crc_lo = 0xFF; // low byte of CRC initialized
do { unsigned char i = crc_hi ^ *p++; // will index into CRC lookup table
crc_hi = crc_lo ^ (unsigned char)pgm_read_byte(&auchCRCHi[i]); // calculate the CRC crc_lo = (unsigned char)pgm_read_byte(&auchCRCLo[i]);
} while (--n); // pass through message buffer (max 256 items)
return ((crc_hi << 8) | crc_lo); }
#include <util/crc16.h>
//============================================================== // Функция подсчета контрольной суммы (CRC modbus RTU). // Максимальная длина пакета 256 байт. //==============================================================
unsigned short crc16_2(unsigned char *p, unsigned short len) { unsigned short crc; unsigned char n;
if (len>256U) { return (0); }
n = (unsigned char)len;
crc = 0xffff;
do { crc = _crc16_update(crc, *p++); // uint16_t _crc16_update(uint16_t __crc, uint8_t __data) } while (--n); // pass through message buffer (max 256 items) return (crc); }
unsigned char mess[3] = {1,108,8};
volatile unsigned short res1 = crc16_1(&mess[0],3); volatile unsigned short res2 = crc16_2(&mess[0],3); Результат: res1 = 0x0CC6 res2 = 0xC60C И если прочитать этот кусочек из описания протокола modbus на их функцию CRC16: Цитата Note: This function performs the swapping of the high/low CRC bytes internally. The bytes are already swapped in the CRC value that is returned from the function. Therefore the CRC value returned from the function can be directly placed into the message for transmission. Становится понятно, что функция из #include <util/crc16.h> не есть одно и тоже, а требует обмена байт местами  Что и требовалось доказать!
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Oct 10 2008, 14:18
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(demiurg_spb @ Oct 10 2008, 16:09)  Становится понятно, что функция из #include <util/crc16.h> не есть одно и тоже, а требует обмена байт местами  Что и требовалось доказать! По большому счету, это писатели modbus спецификации внесли неясность с перевертыванием байтов в своем алгоритме. Процы то разные бывают(big/little endian), и результат должен быть разным. Самая простая проверка на правильность порядка следования байтов CRC, это то что CRC всей последовательности включая байты CRC дб == 0.
|
|
|
|
|
Oct 10 2008, 19:58
|

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

|
Цитата(singlskv @ Oct 10 2008, 18:18)  Самая простая проверка на правильность порядка следования байтов CRC, это то что CRC всей последовательности включая байты CRC дб == 0. Подтвердите Ваши слова на практике: 0x01 0x6C 0x08 0xC6 - СrcL 0x0C - СrcH В этом случае от перемены мест слагаемых (СrcL<->СrcH) сумма не меняется и никогда не равна 0. Цитата The CRC field is two bytes, containing a 16–bit binary value. The CRC value is calculated by the transmitting device, which appends the CRC to the message. The receiving device recalculates a CRC during receipt of the message, and compares the calculated value to the actual value it received in the CRC field. If the two values are not equal, an error results. Цитата A procedure for generating a CRC is: ... 8. When the CRC is placed into the message, its upper and lower bytes must be swapped as described below. ... The CRC field is appended to the message as the last field in the message. When this is done, the low–order byte of the field is appended first, followed by the high–order byte. The CRC high–order byte is the last byte to be sent in the message. Блин убил бы за такую документацию! Сдаётся мне, что эта дока написана для big endian машин. Применительно к AVR (с его little endian) она требует вдумчивого понимания и доработки процедуры CRC16!!! Функция из #include <util/crc16.h> - хороша и все мои подозрения на её счёт были ошибочны. Похоже я облажался с порядком байт CRC в сообщении... Спасибо за помощь в поиске истины: Rst7 и singlskv ! Спасибо defunct за сочувствие
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
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.
|
|
|
|
Сообщений в этой теме
demiurg_spb CRC16 для modbus Oct 9 2008, 17:05    defunct Цитата(demiurg_spb @ Oct 10 2008, 15:09) ... Oct 10 2008, 14:11      Сергей Борщ Цитата(demiurg_spb @ Oct 10 2008, 22:58) ... Oct 10 2008, 20:04      singlskv Цитата(demiurg_spb @ Oct 10 2008, 23:58) ... Oct 10 2008, 20:08       demiurg_spb Цитата(singlskv @ Oct 11 2008, 00:08) CRC... Oct 10 2008, 20:16        singlskv Цитата(demiurg_spb @ Oct 11 2008, 00:16) ... Oct 10 2008, 20:44         Николай Иванович Приходько Цитата(singlskv @ Oct 11 2008, 00:44) Оче... Oct 10 2008, 21:22      ReAl Цитата(demiurg_spb @ Oct 10 2008, 22:58) ... Oct 17 2008, 21:33       demiurg_spb crc16_1 - "неправильная" функция из доки... Oct 18 2008, 05:48        ReAl Цитата(demiurg_spb @ Oct 18 2008, 08:48) ... Oct 18 2008, 07:22         demiurg_spb Цитата(ReAl @ Oct 18 2008, 11:22) Но это ... Oct 18 2008, 07:33          Сергей Борщ Цитата(demiurg_spb @ Oct 18 2008, 10:33) ... Oct 18 2008, 10:40 aaarrr Писателям modbus следовало бы оторвать руки за кри... Oct 10 2008, 20:09 Николай Иванович Приходько Не забудьте, Господа, что CRC имеет "слепые п... Oct 10 2008, 20:57 singlskv Позже попробую Oct 10 2008, 21:04 Andrew2000 Цитата...Уж больно "кривой" и запутанный... Oct 10 2008, 21:22  SpiritDance Цитата(Andrew2000 @ Oct 11 2008, 01:22) Д... Oct 11 2008, 17:16   Andrew2000 ЦитатаУгу вот давайте теперь уважать идиотов ...
0... Oct 11 2008, 19:09   rezident Цитата(SpiritDance @ Oct 11 2008, 23:16) ... Oct 11 2008, 23:44 sensor_ua ЦитатаА вообще я полностью согласен, что реализовы... Oct 12 2008, 06:27 demiurg_spb Цитата(sensor_ua @ Oct 12 2008, 10:27) Мн... Oct 12 2008, 10:20 sensor_ua Цитатапроблема выбора протокола перестала существо... Oct 12 2008, 16:50 demiurg_spb Цитата(sensor_ua @ Oct 12 2008, 20:50) Пр... Oct 13 2008, 09:30 rezident demiurg_spb в общем-то правильно заметил. SCADA си... Oct 12 2008, 20:48 sensor_ua ЦитатаSCADA система не работает напрямую с протоко... Oct 12 2008, 21:37 rezident sensor_ua, вы меня видимо не понимаете. Я не прот... Oct 12 2008, 21:47 sensor_ua ЦитатаЯ лишь за то, чтобы не ограничиваться только... Oct 13 2008, 04:02 one_man_show Цитата(sensor_ua @ Oct 13 2008, 01:37) Ес... Oct 13 2008, 10:17 sensor_ua Цитатао протоколе ПИРАМИДА
Знаем и по-чуть-чуть пр... Oct 13 2008, 10:21
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|