Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: CRC16 для modbus
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
demiurg_spb
Я наткнулся недавно на очередные грабли.
Суть такая:
Есть алгоритм для подсчёта 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 модуль и забыл об этом.
Rst7
Цитата
По доке на modbus сдвиг нужно делать до анализа младшего бита crc,


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

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

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

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

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

Угу навыдумывали какие-то "LoHi" нет чтоб полюдски с short работать сразу.
singlskv
Цитата(demiurg_spb @ Oct 10 2008, 16:09) *
Становится понятно, что функция из #include <util/crc16.h> не есть одно и тоже,
а требует обмена байт местамиsmile.gif
Что и требовалось доказать!
По большому счету, это писатели modbus спецификации внесли неясность с перевертыванием
байтов в своем алгоритме. Процы то разные бывают(big/little endian), и результат должен
быть разным.
Самая простая проверка на правильность порядка следования байтов CRC, это то что
CRC всей последовательности включая байты CRC дб == 0.
demiurg_spb
Цитата(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
Сергей Борщ
Цитата(demiurg_spb @ Oct 10 2008, 22:58) *
0x01
0x6C
0x08
0xC6 - СrcL
0x0C - СrcH
В этом случае от перемены мест слагаемых (СrcL<->СrcH) сумма не меняется и никогда не равна 0.
Значит алгоритм, по которому вы их рассчитываете - не CRC. CRC по своей математической природе (остаток от деления полиномов) при добавлении в конец пакета должен давать ноль. Или, что то же самое, если инициализировать crc какими-либо двумя байтами и прогнать через алгоритм эти же два байта в нужном направлении, должен получиться ноль.
singlskv
Цитата(demiurg_spb @ Oct 10 2008, 23:58) *
Блин убил бы за такую документацию!
Сдаётся мне, что эта дока написана для big endian машин.
Применительно к AVR (с его little endian) она требует вдумчивого понимания.
Похоже я облажался с порядком байт CRC в сообщении...
Ага, именно так, в доках они дико все запутали,
еще раз повторюсь
CRC всей последовательности включая байты CRC дб == 0.
aaarrr
Писателям modbus следовало бы оторвать руки за кривейший и неудобнейший протокол с разным эндианизмом в полях. А также оторвать головы тем кроликам, которые купились на приманку - теперь приходится этот ужас поддерживать.
demiurg_spb
Цитата(singlskv @ Oct 11 2008, 00:08) *
CRC всей последовательности включая байты CRC дб == 0.

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

Раз разговор так затянулся, позволю спросить уважаемый All
какой порядок байт используется для передачи float и long?
singlskv
Цитата(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 следовало бы оторвать руки за кривейший и неудобнейший протокол с разным эндианизмом в полях.
Очень хотелось бы хоть кому-нить оторвать голову , за модбас...
Николай Иванович Приходько
Не забудьте, Господа, что CRC имеет "слепые пятна"

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

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

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

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

Назовите ровнее...
Давайте уважать пенсионеров - это все-таки 1979 год. И жив он до сих пор, думаю, потому, что он как раз очень простой, даже примитивный.
http://www.modbus.org/tech.php
http://freemodbus.berlios.de/
Николай Иванович Приходько
Цитата(singlskv @ Oct 11 2008, 00:44) *
Очень хотелось бы хоть кому-нить оторвать голову , за модбас...

Цитата(singlskv @ Oct 11 2008, 01:04) *
Позже попробую

Удачи smile.gif
SpiritDance
Цитата(Andrew2000 @ Oct 11 2008, 01:22) *
Давайте уважать пенсионеров - это все-таки 1979 год. И жив он до сих пор, думаю, потому, что он как раз очень простой, даже примитивный.

Угу вот давайте теперь уважать идиотов только за то что они пенсионеры. RTU-modbus на мыло! Единственный бинарный протокол имеющий право на жизнь - это slip(и подобные), остальное в помойку. И почему так мало кроликов пользует ASCII вариант modbus?
Andrew2000
Цитата
Угу вот давайте теперь уважать идиотов ...

0xFF Применяющих сие в новых разработках, уважать не призывал - я про протокол 1979 года. Но, паровоз не остановить - слишком много промышленных изделий его используют и их приходится поддерживать. И ругать модбас - все равно, что ругать Абак, имея компьютер.
rezident
Цитата(SpiritDance @ Oct 11 2008, 23:16) *
И почему так мало кроликов пользует ASCII вариант modbus?
Потому, что байтовая длина пакета, закодированного в Modbus ASCII, примерно вдвое больше, чем его же в RTU. Вообще ASCII-формат был придуман для связи, использующей модемы. В пакте формата ModBus ASCII присутствуют только "печатные" символы и нет байтов, которые модем способен понять как управляющие (его работой) символы и выйти из "прозрачного" режима трансляции данных, перейдя в командный режим.
А вообще я полностью согласен, что реализовывать поддержку потокола ModBus во вновь разрабатываемых устройствах нужно только в том случае, когда предполагается их использование в сетях совместно с устройствами, поддерживающими только ModBus. От использования ModBus как основного и единственного протокола связи нужно стараться уходить.
sensor_ua
Цитата
А вообще я полностью согласен, что реализовывать поддержку потокола ModBus во вновь разрабатываемых устройствах нужно только в том случае, когда предполагается их использование в сетях совместно с устройствами, поддерживающими только ModBus. От использования ModBus как основного и единственного протокола связи нужно стараться уходить.

Мне вот он тоже не очень нравится. Вот только альтернативы никак не подберу. Нужен поддерживаемый различными SCADA, достаточно документированный и просто стандарт "де-факто" с близкими возможностями, менее перенапряженный проблемами совместимости железа (индейцы, ограничения адресов) у первичных разработчиков, чем Modbus.
demiurg_spb
Цитата(sensor_ua @ Oct 12 2008, 10:27) *
Мне вот он тоже не очень нравится. Вот только альтернативы никак не подберу. Нужен поддерживаемый различными SCADA, достаточно документированный и просто стандарт "де-факто" с близкими возможностями, менее перенапряженный проблемами совместимости железа (индейцы, ограничения адресов) у первичных разработчиков, чем Modbus.

На самом деле, с появлением OPC проблема выбора протокола перестала существовать.
Выбирай что угодно, хоть свой собственный придумывай.
Главное предоставь свой OPC сервер - и дело в шляпе.
И это, на мой взгляд, правильно.
Установить ещё один канал rs485 в PC будет проще, и, возможно, надёжнее...
Да и цены и предложения на адаптеры usb<->rs485 благоприятствуют.
A SCADA системе уже всё равно, сколько физических каналов и какие там ходят протоколы.

Но стоит заметить, что доступность самой документации на OPC и ее доступность для мозга, могут слегка подпортить эту благостную картину...
sensor_ua
Цитата
проблема выбора протокола перестала существовать.
Выбирай что угодно, хоть свой собственный придумывай.
Главное предоставь свой OPC сервер - и дело в шляпе.

Простите, но жонглирование "хотите-нехотите" не приветствую. OPC-сервер как технология, ИМХО, вообще диверсия для поднятия технологии COM от M$ после зарубывания DDE.
Практически у каждой SCADA есть хозяева. Мне приходится интегрировать свои девайсы в чужие системы самых разных пошибов. Чем меньше возникает вопросов от хозяев службе поддержки, тем быстрее решаются вопросы.
Если можете подсказать конкретный протокол с описанными (пусть и весьма расплывчато) свойствами, то, пожалуйста, подскажите.
rezident
demiurg_spb в общем-то правильно заметил. SCADA система не работает напрямую с протоколами. Это функция OPC-сервера. Если OPC-сервер сторонний, тогда ой! sad.gif А если свой собственный, то какая проблема имплементировать туда поддержку хоть десятка различных протоколов? Наш OPC-сервер, например, работает, используя нашу же библиотеку (в виде DLL) с протоколами связи, где кроме нашего собственного протокола еще и ModBus до кучи поддерживается. При конфигурировании в OPC-сервере объектов просто выбирается из списка тот протокол, который поддерживается данным конкретным объектом.
sensor_ua
Цитата
SCADA система не работает напрямую с протоколами. Это функция OPC-сервера.

Да, такое решение известно. Можете считать меня убогим, но это не есть правило.
Цитата
Если OPC-сервер сторонний, тогда ой!

Сторонний. Если на вопрос "какая у вас SCADA?" в ответ получается вопрос "а какой у вас протокол?", то проще сказать "отвечающему" известное для него словоwink.gif Пока слово "Modbus" срабатываетsmile.gif
rezident
sensor_ua, вы меня видимо не понимаете. sad.gif Я не против ModBus в принципе, особенно когда идет разговор о внедрении своих устройств в чужие сети. Я лишь за то, чтобы не ограничиваться только ModBus при реализации протоколов связи. Об этом я собственно уже написал в посте #20.
sensor_ua
Цитата
Я лишь за то, чтобы не ограничиваться только ModBus при реализации протоколов связи

Я за то же, только пока не попалось приемлемого протокола. У нас есть свой, но лучше бы его не былоwink.gif Известны несколько удобных протоколов, кое-какие внедряем, но почему-то их не знают;( (и хуже - ни о каких "своих" слышать не хотят) там, где приходится интегрироваться
demiurg_spb
Цитата(sensor_ua @ Oct 12 2008, 20:50) *
Простите, но жонглирование "хотите-нехотите" не приветствую. OPC-сервер как технология, ИМХО, вообще диверсия для поднятия технологии COM от M$ после зарубывания DDE.
Практически у каждой SCADA есть хозяева. Мне приходится интегрировать свои девайсы в чужие системы самых разных пошибов. Чем меньше возникает вопросов от хозяев службе поддержки, тем быстрее решаются вопросы.

Я настаиваю.
При наличии OPC сервера вопросы бы отсутствовали вовсе.

Цитата(sensor_ua @ Oct 12 2008, 20:50) *
Если можете подсказать конкретный протокол с описанными (пусть и весьма расплывчато) свойствами, то, пожалуйста, подскажите.

К сожалению, посоветовать ничего не могу, ибо не силён в разнообразии протоколов.
Знаю лишь modbus и owen, знаком с profibus и CAN.
Знаю, что у Термодата свой протокол есть, а что они придумали - загадка....
Для военного корабля делали систему на С166-ых с использованием их собственных примитивных ASCII протоколов. Я был поражён их безбашенности (даже CRC не считают, а просто суммируют байты посылки).
Большинство производителей приборов автоматизации в Росии используют modbus - это видимо по инерции и(или) по незнанию хорошей альтернативы...

На самом деле, вопрос, поднимаемый Вами, весьма интересен.

Вспомнил, что давненько уже ничего не слышно о протоколе ПИРАМИДА.
Посмотрите на него - может это оно...
one_man_show
Цитата(sensor_ua @ Oct 13 2008, 01:37) *
Если на вопрос "какая у вас SCADA?" в ответ получается вопрос "а какой у вас протокол?", то проще сказать "отвечающему" известное для него словоwink.gif Пока слово "Modbus" срабатываетsmile.gif

Именно по этой причине продолжаю в ряде железок использовать Modbus, так как это для Заказчика понятная (известная) вещь. Иных протоколов он может и не знать))) Переубеждение не всегда на руку всему проекту в целом, так как используемый протокол не есть самая важная его деталь.

Использую оба варианта протокола, то есть и ASC тоже, а именно, для отладки, так как на стадии отладки всё, что творится в канале можно увидеть "глазами" без специального софта, подключив параллельно дополнительный преобразователь 232-485 и ноутбук с обычным терминалом.

Кроме того, когда у тебя есть реализованные и проверенные годами библиотеки для различных аппаратных платформ, конечно не заморачиваешься, а начинаешь именно со стандарта дефакто. И еще, когда используешь самодельный протокол и одновременно с этим приходится использовать самодельный софт верхнего уровня, то когда начинают вылезать косяки, да еще на территории заказчика, да еще через через какое-то время после запуска.....куча вопросов тогда возникает. И хотя авторы протокола в таком случае под рукой, от этого не легче. В случае применения известного протокола, чем он более "засижен", тем лучше, так как многие косяки его реализации выгребли еще лет 10-20 назад. И использовать самодельный софт верхнего уровня не приходится, берешь любую СКАДу, даже без лицензии, в демо-режиме, и спокойно и быстро делаешь тестовый софт (канальную часть) - имитатор софта Заказчика, так как в любой СКАДе всегда есть Модбас ))) Пока есть)))
sensor_ua
Цитата
о протоколе ПИРАМИДА

Знаем и по-чуть-чутьwink.gif применяем, но более в исследовательских целях, чем по делу.
Мне удобны протоколы, у которых в шапке есть длина сообщения, а также адрес оптравителя и получателя - такие, которые можно было бы аппаратно разгребать (мне аппаратно не нужно, но логика транспорта в таком варианте прямоугольная) - типа UDP, SimpliciTI, Virtual Wire.
ReAl
Цитата(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() ).
demiurg_spb
crc16_1 - "неправильная" функция из доки на modbus, она даёт результат в big-endian формате.
Цитата(ReAl @ Oct 18 2008, 01:33) *
Это если ещё и в поток давать инверсию от рассчитанной CRC, то тогда независимо от содержимого блока при правильной CRC рассчёт CRC для блока вместе с той инвертированной CRC даст константу, не равную нулю (т.е. проверять CRC от всего надо не на нуль, а на константу, 0x01B0 для приведенной crc16_1() ).

Вы меня окончательно запутали что на что надо проверятьsmile.gif
Я хотел понять как получить 0 в данном конкретном случае и наконец понял.
В протоколе сказано, что приёмник должен посчитать CRC посылки без учёта последних двух байт и сравнить их с принятой crc, подсчитанной передатчиком (последние два байта посылки).

О инверсиях и нулях в данном мануале речи не ведётся.

Цитата(ReAl @ Oct 18 2008, 01:33) *
Что-то мне кажется, что где-то вкралась ошибка.

Сделал как Вы предлагаете: считать CRC для всей посылки.

Действительно было непонимание, а сайчас получилось, что CRC для всей последовательности байт (с учётом двух байт CRC) = 0.
Так даже проще, но несколько дольше, чем предлагается в описании протокола modbus,
т.к. длиннее на 2 байта.

ReAl, спасибо за помощь!
ReAl
Цитата(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) *
Вы меня окончательно запутали что на что надо проверятьsmile.gif
Если бы в конце 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 :-).
demiurg_spb
Цитата(ReAl @ Oct 18 2008, 11:22) *
Но это так, к слову, просто тут было упомянуто, что в некоторых случаях в поток заносится не CRC, а инверсия, ну так в этом случае в конце будет не 0 :-).

Как я понял, случаи бывают разныеsmile.gif
Осталось только узнать в чём практическая польза этого трюка?
Сергей Борщ
Цитата(demiurg_spb @ Oct 18 2008, 10:33) *
Осталось только узнать в чём практическая польза этого трюка?
Принят ошибочный пакет, состоящий из одних нулей. Без применения инверсии CRC сойдется и придется делать дополнительную проверку на ноль содержимого всего пакета для отлова именно этого конкретного случая, а при использовании инверсии проверка не даст ожидаемую константу и пакет будет отброшен уже на стадии проверки CRC.

Польза от инициализации начального значения отличной от нуля константой: Если таки действительно пакет из одних нулей может присутствовать в обмене, то в случае инициализации нулем потеря части этих нулей или вкрапление лишних из-за ошибок синхронизации никак не отразится на CRC. В случае инициализации константой CRC уже не сойдется.
singlskv
Цитата(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
Цитата(singlskv @ Oct 19 2008, 00:02) *
Чего-то Вы в итоге похоже сами напутали.
То есть при формировании пакета с CRC мы должны сначала положить CRCLo == 0xBF,
затем CRCHi == 0x0B.
Полный CRC пакета дает 0.

Всё правильно. Я уже со всем разобрался.
Спасибо.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.