zoukankan      html  css  js  c++  java
  • 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  }

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

                

  • 相关阅读:
    jQuery 选择器
    http statusCode(状态码)含义
    JS实现拖拽效果
    Sql Service中的分页
    SQL Server中一些不常见的查询
    游标的基本写法
    doT.js
    关于GridView中控件的问题
    Sql Server创建函数
    ASP.NET中Ajax的用法
  • 原文地址:https://www.cnblogs.com/Fangjq2020/p/13222570.html
Copyright © 2011-2022 走看看