реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> CRC16 для modbus, не повторяйте ошибок
demiurg_spb
сообщение Oct 9 2008, 17:05
Сообщение #1


неотягощённый злом
******

Группа: Свой
Сообщений: 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 модуль и забыл об этом.


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Rst7
сообщение Oct 9 2008, 17:50
Сообщение #2


Йа моск ;)
******

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



Цитата
По доке на modbus сдвиг нужно делать до анализа младшего бита crc,


Однако, на следующей странице стандарта проверяется бит переноса после сдвига. Т.е. соответственно util/crc16.h

Налицо неаккуратное описание алгоритма в стандарте. А у Вас надо полечить остальные приборы для совместимости.

PS - лечить именно остальные, до приведения их к состоянию нового прибора, основанного на util/crc16.h


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
singlskv
сообщение Oct 9 2008, 17:57
Сообщение #3


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Rst7 @ Oct 9 2008, 21:50) *
PS - лечить именно остальные, до приведения их к состоянию нового прибора, основанного на util/crc16.h
+1
пользуюсь util/crc16.h для модбаса, проблем нет
и оно соответствует стандартной таблице crc для модбас
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Oct 10 2008, 08:09
Сообщение #4


неотягощённый злом
******

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



Цитата(singlskv @ Oct 9 2008, 21:57) *
+1
пользуюсь util/crc16.h для модбаса, проблем нет
и оно соответствует стандартной таблице crc для модбас

В том-то и дело что ранее у меня все приборы были с табличным алгоритмом (из доки модбаса).
А после перехода на util/crc16.h фигня какая-то происходит.
Попробую разобраться в чем дело...


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Oct 10 2008, 12:09
Сообщение #5


неотягощённый злом
******

Группа: Свой
Сообщений: 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> не есть одно и тоже,
а требует обмена байт местамиsmile.gif

Что и требовалось доказать!


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
defunct
сообщение Oct 10 2008, 14:11
Сообщение #6


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(demiurg_spb @ Oct 10 2008, 15:09) *
Становится понятно, что функция из #include <util/crc16.h> не есть одно и тоже,
а требует обмена байт местамиsmile.gif
Что и требовалось доказать!

Угу навыдумывали какие-то "LoHi" нет чтоб полюдски с short работать сразу.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Oct 10 2008, 14:18
Сообщение #7


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



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


неотягощённый злом
******

Группа: Свой
Сообщений: 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 за сочувствиеsmile.gif


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Oct 10 2008, 20:04
Сообщение #9


Гуру
******

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



Цитата(demiurg_spb @ Oct 10 2008, 22:58) *
0x01
0x6C
0x08
0xC6 - СrcL
0x0C - СrcH
В этом случае от перемены мест слагаемых (СrcL<->СrcH) сумма не меняется и никогда не равна 0.
Значит алгоритм, по которому вы их рассчитываете - не CRC. CRC по своей математической природе (остаток от деления полиномов) при добавлении в конец пакета должен давать ноль. Или, что то же самое, если инициализировать crc какими-либо двумя байтами и прогнать через алгоритм эти же два байта в нужном направлении, должен получиться ноль.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Oct 10 2008, 20:08
Сообщение #10


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(demiurg_spb @ Oct 10 2008, 23:58) *
Блин убил бы за такую документацию!
Сдаётся мне, что эта дока написана для big endian машин.
Применительно к AVR (с его little endian) она требует вдумчивого понимания.
Похоже я облажался с порядком байт CRC в сообщении...
Ага, именно так, в доках они дико все запутали,
еще раз повторюсь
CRC всей последовательности включая байты CRC дб == 0.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Oct 10 2008, 20:09
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Писателям modbus следовало бы оторвать руки за кривейший и неудобнейший протокол с разным эндианизмом в полях. А также оторвать головы тем кроликам, которые купились на приманку - теперь приходится этот ужас поддерживать.
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Oct 10 2008, 20:16
Сообщение #12


неотягощённый злом
******

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



Цитата(singlskv @ Oct 11 2008, 00:08) *
CRC всей последовательности включая байты CRC дб == 0.

А что же это за алгоритм если сумма +CRC != 0 ?
Но modbus называет его именно CRC.

Раз разговор так затянулся, позволю спросить уважаемый All
какой порядок байт используется для передачи float и long?


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Oct 10 2008, 20:44
Сообщение #13


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(demiurg_spb @ Oct 11 2008, 00:16) *
Раз разговор так затянулся, позволю спросить уважаемый All
какой порядок байт используется для передачи float и long?
Ooo... здесь у Вас ровно 4 варианта .... http://www.simplymodbus.ca/FAQ.htm#Order


Цитата(aaarrr @ Oct 11 2008, 00:09) *
Писателям modbus следовало бы оторвать руки за кривейший и неудобнейший протокол с разным эндианизмом в полях.
Очень хотелось бы хоть кому-нить оторвать голову , за модбас...
Go to the top of the page
 
+Quote Post
Николай Иванович...
сообщение Oct 10 2008, 20:57
Сообщение #14


Частый гость
**

Группа: Новичок
Сообщений: 139
Регистрация: 26-09-08
Пользователь №: 40 510



Не забудьте, Господа, что CRC имеет "слепые пятна"

"Начальные и конечные значения
В довершение ко всему нами виденному, CRC алгоритмы различаются еще по моментам: по начальному значению регистра; по значению, которое комбинируется по XOR с окончательным содержимым регистра. Например, алгоритм "CRC " инициализирует регистр значением 0xFFFFFFFF и выполняют операцию XOR окончательного значения также с величиной 0xFFFFFFFF. Большинство CRC алгоритмов инициализируют регистр нулевым значением, однако некоторые предпочитают ненулевое. Теоретически (когда не делается ни каких предположений относительно содержания сообщения) начальной значение не влияет на стойкость CRC, оно лишь является точкой отсчета, с которой алгоритм начинает работать. Однако, на практике некоторые сообщения более вероятны, чем другие, поэтому разумнее будет выбрать такое начальное значение регистра, которое бы не имело "слепых пятен". Под "слепым пятном" мы подразумеваем такую последовательность байтов сообщения, которые не приводят к изменению со держимого регистра. В частности, любые CRC алгоритмы, которые инициализи руют свой регистр нулевым значением, будут иметь "слепое пятно" в отношении нулевых байтов в начале сообщения и не смогут оценить их количество. А так как в начале сообщения нулевые байты встречаются достаточно часто, разумнее ини циализировать регистр значением, отличным от нуля.
©Ross N. Williams

Сам как-то наткнулся на эти грабли, когда использовал CRC для контроля целостности программы во FLASH

Цитата(singlskv @ Oct 11 2008, 00:44) *
Очень хотелось бы хоть кому-нить оторвать голову , за модбас...

Мне тоже. Уж больно "кривой" и запутанный протокол.

Сообщение отредактировал Николай Иванович Приходько - Oct 10 2008, 20:58
Go to the top of the page
 
+Quote Post
singlskv
сообщение Oct 10 2008, 21:04
Сообщение #15


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Позже попробую
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 25th June 2025 - 05:47
Рейтинг@Mail.ru


Страница сгенерированна за 0.01496 секунд с 7
ELECTRONIX ©2004-2016