了解 Modbus RTU CRC

问题描述

现在正在研究Modbus RTU和Modbus RTU的CRC16。

关于校验CRC的源代码,在High-Order Byte Table和Low-Order Byte Table中,不知道这个值是怎么生成的?在 Modbus 协议的文档中,这样解释:“一个数组包含 16 位 CRC 字段的高字节的所有 256 个可能的 CRC 值,另一个数组包含低字节的所有值。”>

我想怎么取这个值?帮帮我,非常感谢。

Low-Order Byte Table
High-Order Byte Table

解决方法

该 pdf 文件中包含对面向位的 CRC 实现的描述,但它可能需要进行一些清理:

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. Save the LSB for step 4. Shift the CRC register one bit to the right 
   (toward the LSB),zero–filling the MSB.

4. (If the saved LSB is 0): Continue to step 5.
   (If the saved LSB is 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,store the low order byte first,and the
   high order byte next.   (msg ...,crclo,crchi).

https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf

请注意,在使用表格的示例代码中,CRC 的字节被交换,因此被描述为“低字节”的实际上是“高字节”,反之亦然。这样做是为了允许将 CRC 作为 16 位值存储在大端处理器上的消息中。在小端处理器(例如 PC 上的 X86)上不需要交换字节,因为小端存储将交换字节(存储低字节、高字节)。

生成表格的示例代码:

#include <stdio.h>

typedef unsigned char    uint8_t;
typedef unsigned short  uint16_t;

uint8_t crclo[256];
uint8_t crchi[256];

    void gentbls(void)
    {
    uint16_t crc;
    uint16_t b;
    uint16_t c;
    uint16_t i;
        for(c = 0; c < 0x100; c++){
            crc = c;
            for(i = 0; i < 8; i++){
                b = crc & 1;
                crc >>= 1;
                if(b != 0) crc ^= 0xa001;
            }
            crchi[c] = crc & 0xff;
            crclo[c] = crc >> 8;
        }
    }

int main()
{
int i,j;
    gentbls();
    for(j = 0; j < 17; j++){
        for(i = 0; i < 15; i++)
            printf("%02x ",crclo[j*15+i]);
        printf("\n");
    }
    printf("%02x\n\n",crclo[255]);
    for(j = 0; j < 17; j++){
        for(i = 0; i < 15; i++)
            printf("%02x ",crchi[j*15+i]);
        printf("\n");
    }
    printf("%02x\n",crchi[255]);
    return 0;
}

输出:

00 c0 c1 01 c3 03 02 c2 c6 06 07 c7 05 c5 c4 
04 cc 0c 0d cd 0f cf ce 0e 0a ca cb 0b c9 09 
08 c8 d8 18 19 d9 1b db da 1a 1e de df 1f dd 
1d 1c dc 14 d4 d5 15 d7 17 16 d6 d2 12 13 d3 
11 d1 d0 10 f0 30 31 f1 33 f3 f2 32 36 f6 f7 
37 f5 35 34 f4 3c fc fd 3d ff 3f 3e fe fa 3a 
3b fb 39 f9 f8 38 28 e8 e9 29 eb 2b 2a ea ee 
2e 2f ef 2d ed ec 2c e4 24 25 e5 27 e7 e6 26 
22 e2 e3 23 e1 21 20 e0 a0 60 61 a1 63 a3 a2 
62 66 a6 a7 67 a5 65 64 a4 6c ac ad 6d af 6f 
6e ae aa 6a 6b ab 69 a9 a8 68 78 b8 b9 79 bb 
7b 7a ba be 7e 7f bf 7d bd bc 7c b4 74 75 b5 
77 b7 b6 76 72 b2 b3 73 b1 71 70 b0 50 90 91 
51 93 53 52 92 96 56 57 97 55 95 94 54 9c 5c 
5d 9d 5f 9f 9e 5e 5a 9a 9b 5b 99 59 58 98 88 
48 49 89 4b 8b 8a 4a 4e 8e 8f 4f 8d 4d 4c 8c 
44 84 85 45 87 47 46 86 82 42 43 83 41 81 80 
40

00 c1 81 40 01 c0 80 41 01 c0 80 41 00 c1 81 
40 01 c0 80 41 00 c1 81 40 00 c1 81 40 01 c0 
80 41 01 c0 80 41 00 c1 81 40 00 c1 81 40 01 
c0 80 41 00 c1 81 40 01 c0 80 41 01 c0 80 41 
00 c1 81 40 01 c0 80 41 00 c1 81 40 00 c1 81 
40 01 c0 80 41 00 c1 81 40 01 c0 80 41 01 c0 
80 41 00 c1 81 40 00 c1 81 40 01 c0 80 41 01 
c0 80 41 00 c1 81 40 01 c0 80 41 00 c1 81 40 
00 c1 81 40 01 c0 80 41 01 c0 80 41 00 c1 81 
40 00 c1 81 40 01 c0 80 41 00 c1 81 40 01 c0 
80 41 01 c0 80 41 00 c1 81 40 00 c1 81 40 01 
c0 80 41 01 c0 80 41 00 c1 81 40 01 c0 80 41 
00 c1 81 40 00 c1 81 40 01 c0 80 41 00 c1 81 
40 01 c0 80 41 01 c0 80 41 00 c1 81 40 01 c0 
80 41 00 c1 81 40 00 c1 81 40 01 c0 80 41 01 
c0 80 41 00 c1 81 40 00 c1 81 40 01 c0 80 41 
00 c1 81 40 01 c0 80 41 01 c0 80 41 00 c1 81 
40