CRC16-Modbus 校验 C语言

项目中DSP 28335需要和上位机西门子PLC通过485串口进行SCI通讯,采用Modbus协议(PLC可直接调用相应模块,很方便),

信息帧需要CRC16-Modbus进行校验。因为之前项目多是自己定的通信协议,采用奇偶校验,或者不校验,借着编写DSP通讯程

序的机会学习一下CRC16-Modbus校验。

根据Modbus协议,常规485通讯的信息发送形式如下:

                                                                                       地址    功能码    数据信息   校验码

                                                                                       1byte    1byte       nbyte         2byte

CRC校验是前面几段数据内容的校验值,为一个16位数据,发送时,低8位在前,高8为最后。

例如:信息字段代码为: 01 10 12 34 56 78(十六进制),校验字段为:01 10 12 34 56 78 。

发送方:发出的传输字段为: 01 10 12 34 56 78 BB 3D , 其中 BB 3D 就是CRC16校验代码(计算原理后面详述)。

接收方:使用相同的计算方法计算出信息字段的校验码,对比接收到的实际校验码,如果相等及信息正确,不相等则信息错误。

计算原理     

1. 预置 1 个 16 位的寄存器为十六进制FFFF(即全为 1) , 称此寄存器为 CRC寄存器。

2. 把第一个 8 位二进制数据 (通信信息帧的第一个字节) 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器。

3. 把 CRC 寄存器的内容右移一位( 朝低位)用 0 填补最高位, 并检查右移后的移出位。

4. 如果移出位为 0, 重复第 3 步 ( 再次右移一位); 如果移出位为 1, CRC 寄存器与多项式A001 ( ‭1010 0000 0000 0001) 进行异或。

5. 重复步骤 3 和步骤 4, 直到右移 8 次,这样整个8位数据全部进行了处理。

6. 重复步骤 2 到步骤 5, 进行通信信息帧下一个字节的处理。

7. 将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换。

8. 最后得到的 CRC寄存器内容即为 CRC码。

注:以上计算步骤中的多项式A001是8005按位颠倒后的结果。

按照这个思路,不难用C语言实现算法:

 1 int CRC16Modbus(void)
 2 {
 3     unsigned short tmp = 0xffff;
 4     unsigned short ret1 = 0;
 5     unsigned char buff[6] = {0};
 6     buff[0] = 0x01;
 7     buff[1] = 0x10;
 8     buff[2] = 0x12;
 9     buff[3] = 0x34;
10     buff[4] = 0x56;
11     buff[5] = 0x78;
12   
13     for(int n = 0; n < 6; n++)  //此处的6 -- 要校验的位数为6个
14      {
15         tmp = buff[n] ^ tmp;
16         for(int i = 0;i < 8;i++)  //此处的8 -- 指每一个char类型又8bit,每bit都要处理
17         {   
18             if(tmp & 0x01)
19              {
20                 tmp = tmp >> 1;
21                 tmp = tmp ^ 0xA001;
22              }   
23             else
24              {
25                 tmp = tmp >> 1;
26              }   
27         }   
28     }   
29     
30     ret1 = tmp >> 8;   //将CRC校验的高低位对换位置
31     ret1 = ret1 | (tmp << 8); 
32     
33     return ret1;
34 }

如果验证上述程序,可得返回值ret1为 “0xBB3D” 。

另外再给出一个查找资料时搜到的比较简洁的CRC16-Modbus实现程序,不同于前一种算法类似于计算步骤直译,这种算法基于查表法:

 1   const uint16_t crctalbeabs[] = { 
 2       0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 
 3       0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400 
 4   };
 5   
 6   uint16_t crc16tablefast(uint8_t *ptr, uint16_t len) 
 7   {
 8       uint16_t crc = 0xffff; 
 9       uint16_t i;
10      uint8_t ch;
11   
12      for (i = 0; i < len; i++) {
13          ch = *ptr++;
14          crc = crctalbeabs[(ch ^ crc) & 15] ^ (crc >> 4);
15         crc = crctalbeabs[((ch >> 4) ^ crc) & 15] ^ (crc >> 4);
16      } 
17      
18      ret1 = crc>> 8;
19      ret1 = ret1 | (crc<< 8); 
20     
21      return ret1 ;
22  }

两段代码计算结果相同,需要时直接调用两者中的任意一种程序即可。

            

原文地址:https://www.cnblogs.com/Fangjq2020/p/13222570.html