CRC для modbus RTU

 

TEvg

аксакал

админ. бан
Есть устройства работающие по протоколу modbus RTU. Информация которая передается - понятна. За исключением контрольной суммы CRC.
Мне надо самому подергать устройства, а также эмулировать их работу, а для этого сформировать соответсвующие пакеты. И всё было бы хорошо, если засунуть туда верные значения CRC. И вот тут у меня вылезла пиписька. Вроде бы и не сложно, но числа которые я генерирую своими процедурами, отличаются от тех, что генерируются устройствами. Как следствие устройства посылают меня по ошибке CRC.

Вот простейший запрос к устройству:
1 - это ID
3 - это код команды
0
0
0
40 - это количество параметров (регистров) которые надо получить
69 - это байт CRC
212 - это тоже байт CRC

Это верный запрос. Если его послать устройству, то оно пришлет портянку с ответом.
Но у меня никак не получается вычислить эти самые байты 69 и 212. (слово CRC=54341)
Я учитывал, что младший байт CRC идет первым, да и вообще проверка не инвертированы ли байты в результате - одно из моих первых действий. Увы, ничто не помогло.
 3.6.133.6.13

TEvg

аксакал

админ. бан
Вот так описывается сам алгоритм:

Контрольная сумма CRC состоит из двух байт. Контрольная сумма вычисляется передающим устройством и добавляется в конец сообщения. Принимающее устройство вычисляет контрольную сумму в процессе приема и сравнивает ее с полем CRC принятого сообщения.
Счетчик контрольной суммы предварительно инициализируется числом FF hex. Только восемь бит данных используются для вычисления контрольной суммы CRC. Старт и стоп биты, бит паритета, если он используется, не учитываются в контрольной сумме.
Во время генерации CRC каждый байт сообщения складывается по исключающему ИЛИ с текущим содержимым регистра контрольной суммы. Результат сдвигается в направлении младшего бита, с заполнением нулем старшего бита. Если младший бит равен 1, то производится исключающее ИЛИ содержимого регистра контрольной суммы и определенного числа. Если младший бит равен 0, то исключающее ИЛИ не делается.
Процесс сдвига повторяется восемь раз. После последнего (восьмого) сдвига, следующий байт складывается с текущей величиной регистра контрольной суммы, и процесс сдвига повторяется восемь раз как описано выше. Конечное содержание регистра и есть контрольная сумма CRC.

Но его реализация дает у меня совсем другие значения, которые устройствами с негодованием отбрасываются как ошибочные.
 3.6.133.6.13

Mishka

модератор
★★★
В протоколах обычно используется network order, т.е. старшее значение первым, младшее последним.Но не в этом случае. Кроме того существует множество разных полиномов для вычисления CRC-16(CRC-17 на самом деле) — Cyclic redundancy check - Wikipedia, the free encyclopedia — посмотри. Конкретный полином подбирается дизайнером для конкретных нужд. В частности, тип шума в канале является одним из факторов, определяющих полином. Но спешу тебя успокоить — ты выбрал правильный.
TEvg> 69 - это байт CRC
69 == 0x45

TEvg> 212 - это тоже байт CRC
212 == 0xD4

TEvg> Это верный запрос. Если его послать устройству, то оно пришлет портянку с ответом.
TEvg> Но у меня никак не получается вычислить эти самые байты 69 и 212. (слово CRC=54341)
54341=0xD445


TEvg> Я учитывал, что младший байт CRC идет первым, да и вообще проверка не инвертированы ли байты в результате - одно из моих первых действий. Увы, ничто не помогло.
Неправильно учитывал, поменяй байты местами и будет тебе счастье.
 

Mishka

модератор
★★★
TEvg>> Но у меня никак не получается вычислить эти самые байты 69 и 212. (слово CRC=54341)
Mishka> 54341=0xD445

А, понял. Это не твоё число. Какое начальное значение ты выбрал? И какие числа у тебя получаются? Подожди, я притопаю на работу и гляну. У меня там разные программки для разных CRC уже написаны.
 

Mishka

модератор
★★★
TEvg>>> Но у меня никак не получается вычислить эти самые байты 69 и 212. (слово CRC=54341)
Mishka>> 54341=0xD445
Mishka> А, понял. Это не твоё число. Какое начальное значение ты выбрал? И какие числа у тебя получаются? Подожди, я притопаю на работу и гляну. У меня там разные программки для разных CRC уже написаны.



On-line CRC calculation and free library


On-line CRC calculation sheet. Free CRC routines downloadable. Covers CRC-16, 32, CCITT, DNP and Sick routines.

// www.lammertbies.nl
 

О, нашёл. Выбираешь HEX, вводишь твою строку 010300000028 и жмешь кнопку "calculate crc". И видишь, что там используется CRC-16 (Modbus). Дальше либо книжку в руки, либо интернет. Та же статья в вике, на которую я ссылался даёт достаточно инфы.

Кстати, в том онлайн калькулаторе можно почитать теорию и есть полиномы для вычислений.
 

Mishka

модератор
★★★
Выдрал из библиотечки и немного изменил для тебя.

code text
  1. #include <stdio.h>
  2.  
  3. // pay attention
  4. // P_16 -- 0xA001 or 1010 0000 0000 0001 is a reversed form
  5. // of the standard polynom 0x8005 or 1000 0000 0000 0101
  6. #define  P_16        0xA001
  7.  
  8. static unsigned short   crc_tab16[ 256 ];
  9.  
  10. /*******************************************************************\
  11. *                                                                   *
  12. *   static void init_crc16_tab( void );                             *
  13. *                                                                   *
  14. *   The function init_crc16_tab() is used  to  fill  the  array     *
  15. *   for calculation of the CRC-16 with values.                      *
  16. *                                                                   *
  17. \*******************************************************************/
  18.  
  19. void init_crc16_tab( void )
  20. {
  21.  
  22.     unsigned int i, j;
  23.     unsigned short crc, c;
  24.  
  25.     for ( i=0; i < 256; ++i )
  26.     {
  27.  
  28.         crc = 0;
  29.         c = i;
  30.  
  31.         for ( j = 0; j < 8; ++j )
  32.         {
  33.  
  34.             if ( ( crc ^ c ) & 0x0001 )
  35.             {
  36.                 crc = ( crc >> 1 ) ^ P_16;
  37.             }
  38.             else
  39.             {
  40.                 crc =   crc >> 1;
  41.             }
  42.  
  43.             c = c >> 1;
  44.         }
  45.  
  46.         crc_tab16[ i ] = crc;
  47.     }
  48.    
  49. }  /* init_crc16_tab */
  50.  
  51.  
  52. /*******************************************************************\
  53. *                                                                   *
  54. *   unsigned short update_crc_16( unsigned short crc, char c );     *
  55. *                                                                   *
  56. *   The function update_crc_16 calculates a  new  CRC-16  value     *
  57. *   based  on  the  previous value of the CRC and the next byte     *
  58. *   of the data to be checked.                                      *
  59. *                                                                   *
  60. \*******************************************************************/
  61.  
  62. unsigned short update_crc_16( unsigned short crc, char c )
  63. {
  64.  
  65.     unsigned short tmp, short_c;
  66.  
  67.     short_c = 0x00ff & ( unsigned short ) c;
  68.  
  69.     tmp =  crc         ^ short_c;
  70.     crc = ( crc >> 8 ) ^ crc_tab16[ tmp & 0xff ];
  71.  
  72.     return crc;
  73.  
  74. }  /* update_crc_16 */
  75.  
  76.  
  77. int main( int argc, char *argv[ ] )
  78. {
  79.     unsigned short crc_16_modbus;
  80.     char input_string[ ] =
  81.       {
  82.         '\x01',
  83.         '\x03',
  84.         '\x00',
  85.         '\x00',
  86.         '\x00',
  87.         '\x28'
  88.       };
  89.  
  90.     init_crc16_tab( );
  91.  
  92.     crc_16_modbus  = 0xffff; // pay attention to initialization
  93.  
  94.     for ( int i = 0; i < sizeof( input_string ); ++i )
  95.     {
  96.         crc_16_modbus = update_crc_16( crc_16_modbus, input_string[ i ] );
  97.  
  98.         printf( "Command Byte %d = %02X (%u)\n",
  99.                 i, input_string[ i ], input_string[ i ] );
  100.     }
  101.    
  102.     printf( "CRC 1 = %02X (%u)\n",
  103.             ( crc_16_modbus & 0xFF ),
  104.             ( crc_16_modbus & 0xFF ) );
  105.     printf( "CRC 2 = %02X (%u)\n",
  106.             ( ( crc_16_modbus >> 8 ) & 0xFF ),
  107.             ( ( crc_16_modbus >> 8 ) & 0xFF ) );
  108.  
  109.     return 0;
  110. }


Результат прогона.
code text
  1. (evstiomv@opal)/<6>crc:117> g++ crc16modbus.C -o crc16modbus
  2. (evstiomv@opal)/<6>crc:118> ./crc16modbus
  3. Command Byte 0 = 01 (1)
  4. Command Byte 1 = 03 (3)
  5. Command Byte 2 = 00 (0)
  6. Command Byte 3 = 00 (0)
  7. Command Byte 4 = 00 (0)
  8. Command Byte 5 = 28 (40)
  9. CRC 1 = 45 (69)
  10. CRC 2 = D4 (212)
  11. (evstiomv@opal)/<6>crc:119>
 5.05.0

TEvg

аксакал

админ. бан
Спасибо большущее Мишка, за помощь!
 

TEvg

аксакал

админ. бан
Алгоритм правильный. Спектрометр не ругается. Написал его эмулятор - тоже принимают за родного.
Переделал Мишкин код на Паскаль.

unit modbus;

interface

function CRC16(buf:pointer;size:word):word;

implementation

uses SysUtils;

const P_16=$A001;

var crc_tab16:array[0..255] of word;

procedure init_crc16_tab;
var i,j,crc,c:word;
begin
for i:=0 to 255 do
begin
crc:=0;
c:=i;
for j:=0 to 7 do begin
if (crc xor c) and 1=1
then crc:=(crc shr 1) xor P_16
else crc:=crc shr 1;
c:=c shr 1;
end;
crc_tab16[i]:=crc;
end;
end;

function update_crc_16(crc,c:word):word;
var tmp, short_c:word;
begin
tmp:=crc xor c;
crc:=(crc shr 8) xor crc_tab16[tmp and $ff];
Result:=crc;
end;

function CRC16(buf:pointer;size:word):word;
var i,crc_16_modbus:word;
begin
init_crc16_tab;
crc_16_modbus:=$ffff;
for i:=0 to size-1 do
crc_16_modbus:=update_crc_16(crc_16_modbus,pbytearray(buf)^[i]);
Result:=crc_16_modbus;
end;

end.
 3.6.133.6.13

Mishka

модератор
★★★
TEvg> Переделал Мишкин код на Паскаль.

Ккод не мой :) — отсюда — On-line CRC calculation and free library. Я только уптостил.
 

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru