CRC校验原理及其C语言实现

[复制链接]
查看939 | 回复0 | 2011-10-12 19:47:52 | 显示全部楼层 |阅读模式
<
CRC校验码的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+ r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
      在数据存储和数据通讯领域,CRC无处不在:著名的通讯协议X.25的FCS(帧检错序列)采用的是CRC. CCITT,ARJ、LHA等压缩工具软件采用的是CRC32,磁盘驱动器的读写采用了CRC16,通用的图像存储格式GIF、TIFF等也都用CRC作为检错手段。
      CRC的本质是模-2除法的余数,采用的除数不同,CRC的类型也就不一样。通常,CRC的除数用生成多项式来表示。最常用的CRC码的生成多项式有CRC16,CRC32.
      以CRC16为例,16位的CRC码产生的规则是先将要发送的二进制序列数左移16位(既乘以2^16)后,再除以一个多项式,最后所得到的余数既是 CRC码,如下式所示,其中K(X)表示n位的二进制序列数,G(X)为多项式,Q(X)为整数,R(X)是余数(既CRC码)。
K(X)>>16=G(x)Q(x)+R(x)
      求CRC码所采用模2加减运算法则,既是不带进位和借位的按位加减,这种加减运算实际上就是逻辑上的异或运算,加法和减法等价,乘法和除法运算与普通代数式的乘除法运算是一样,符合同样的规律。生成CRC码的多项式如下,其中CRC-16和CRC-CCITT产生16位的CRC码,而CRC-32则产生的是32位的CRC码
PLC资料
      接收方将接收到的二进制序列数(包括信息码和CRC码)除以多项式,如果余数为0,则说明传输中无错误发生,否则说明传输有误,关于其原理这里不再多述。用软件计算CRC码时,接收方可以将接收到的信息码求CRC码,比较结果和接收到的CRC码是否相同。
      CCITT推荐的高级数据链路控制规程HDLC的帧校验序列FCS中,使用CCITT-16即CRC16,其生成多项式为G(x)=x16+x12+x5 +1, CRC-32的生成多项式为G(x)=x32+x26+x23+x22+x16+x11+x10+x16+x8+x7+x5+x4+x2+x+1
      以上是摘录的一段介绍,是不是觉得很玄?我第一次看也是觉得很晕,后来查询了一些规约和协议的实现资料以后,才恍然大悟。以下是我实现的一段C++代码,只实现了CRC16,CRC32的实现基本类似,网上有很多资源。其实实现起来非常简单,就是查表、异或运算而已!查表法实现起来比较简单,耗资源也比较少,速度较快。只要通信双方采用的同样的余数表,就可以正确进行CRC校验。余数表并没有硬性规定。许多程序还把余数表做成自动生成,那样适用性就更好了。
//CRC计算主函数:
//内容:
#include "CrcCode.h"
PLC
using namespace std;
unsigned short int CrcCode::CrcCode16(const char *CrcSrcCode,const size_t CodeLength)
{
      unsigned short CRC_Code[]={
              0x0673,0x1652,0x2631,0x3610,0x46F7,0x56D6,0x66B5,0x7694,0x877B,0x975A,//10
              0xA739,0xB718,0xC7FF,0xD7DE,0xE7BD,0xF79C,0x1442,0x0463,0x3400,0x2421,//20
              0x54C6,0x44E7,0x7484,0x64A5,0x954A,0x856B,0xB508,0xA529,0xD5CE,0xC5EF,//30
              0xF58C,0xE5AD,0x2211,0x3230,0x0253,0x1272,0x6295,0x72B4,0x42D7,0x52F6,//40
              0xA319,0xB338,0x835B,0x937A,0xE39D,0xF3BC,0xC3DF,0xD3FE,0x3020,0x2001,//50
              0x1062,0x0043,0x70A4,0x6085,0x50E6,0x40C7,0xB128,0xA109,0x916A,0x814B,//60
PLC资料网
              0xF1AC,0xE18D,0xD1EE,0xC1CF,0x4EB7,0x5E96,0x6EF5,0x7ED4,0x0E33,0x1E12,//70
              0x2E71,0x3E50,0xCFBF,0xDF9E,0xEFFD,0xFFDC,0x8F3B,0x9F1A,0xAF79,0xBF58,//80
              0x5C86,0x4CA7,0x7CC4,0x6CE5,0x1C02,0x0C23,0x3C40,0x2C61,0xDD8E,0xCDAF,//90
              0xFDCC,0xEDED,0x9D0A,0x8D2B,0xBD48,0xAD69,0x6AD5,0x7AF4,0x4A97,0x5AB6,//100
              0x2A51,0x3A70,0x0A13,0x1A32,0xEBDD,0xFBFC,0xCB9F,0xDBBE,0xAB59,0xBB78,//110
              0x8B1B,0x9B3A,0x78E4,0x68C5,0x58A6,0x4887,0x3860,0x2841,0x1822,0x0803,//120
              0xF9EC,0xE9CD,0xD9AE,0xC98F,0xB968,0xA949,0x992A,0x890B,0x97FB,0x87DA,//130
PLC
              0xB7B9,0xA798,0xD77F,0xC75E,0xF73D,0xE71C,0x16F3,0x06D2,0x36B1,0x2690,//140
              0x5677,0x4656,0x7635,0x6614,0x85CA,0x95EB,0xA588,0xB5A9,0xC54E,0xD56F,//150
              0xE50C,0xF52D,0x04C2,0x14E3,0x2480,0x34A1,0x4446,0x5467,0x6404,0x7425,//160
              0xB399,0xA3B8,0x93DB,0x83FA,0xF31D,0xE33C,0xD35F,0xC37E,0x3291,0x22B0,//170
              0x12D3,0x02F2,0x7215,0x6234,0x5257,0x4276,0xA1A8,0xB189,0x81EA,0x91CB,//180
              0xE12C,0xF10D,0xC16E,0xD14F,0x20A0,0x3081,0x00E2,0x10C3,0x6024,0x7005,//190
              0x4066,0x5047,0xDF3F,0xCF1E,0xFF7D,0xEF5C,0x9FBB,0x8F9A,0xBFF9,0xAFD8,//200 PLC资料网
              0x5E37,0x4E16,0x7E75,0x6E54,0x1EB3,0x0E92,0x3EF1,0x2ED0,0xCD0E,0xDD2F,//210
              0xED4C,0xFD6D,0x8D8A,0x9DAB,0xADC8,0xBDE9,0x4C06,0x5C27,0x6C44,0x7C65,//220
              0x0C82,0x1CA3,0x2CC0,0x3CE1,0xFB5D,0xEB7C,0xDB1F,0xCB3E,0xBBD9,0xABF8,//230
              0x9B9B,0x8BBA,0x7A55,0x6A74,0x5A17,0x4A36,0x3AD1,0x2AF0,0x1A93,0x0AB2,//240
              0xE96C,0xF94D,0xC92E,0xD90F,0xA9E8,0xB9C9,0x89AA,0x998B,0x6864,0x7845,//250
              0x4826,0x5807,0x28E0,0x38C1,0x08A2,0x1883                               //256
PLC
      };                                                     //CRC_Code[]数组就是一个余数表
          unsigned short int n_CrcCode16=0;
          unsigned short int location=0;
          for(size_t i=0;i!=CodeLength;i++)
          {
              location=(n_CrcCode16 & 0xff)^(CrcSrcCode[i] & 0xff);             //查表,事实上就是获取该余数在CRC_Code[]数组的中的位置
              n_CrcCode16=((n_CrcCode16>>8) & 0xff) ^ CRC_Code[location];       //从表中取值计算
PLC资料网
          }
          std::cout<<"The CrcCode you need is:"<<n_CrcCode16<<endl;
          return n_CrcCode16;
}
      当然了,你要做的就是给它传递参数,也就是你要计算的数据,char类型。一般的串口通信、网络通信都是字符型通信,更适用于LINUX里面的字符型设备。这个类是在Suse10.1下面用Kdevelop写的,全部使用ANSI C/C++编写,在Linux和Windows均可以编译通过并且正确运行。
/************************************************************
crc16校验程序
uint crc16(uint crcdata,uchar bufdata)
形参:bufdata     需要校验数据
       crcdata     校验后的数据
功能:接收字节crc校验,返回校验值
*************************************************************/
uint crc16(uint crcdata,uchar bufdata)
{
uchar i;
PLC资料网
   
crcdata^=bufdata;
for(i=8;i>0;i--)
{
      crcdata>>=1;
      if(crcdata&0x01)
      crcdata^=0xa001;
     }
return crcdata;
}
VBvbvbvbvbvbvbvvvvvvvvvvvvvvvvvvbbbbbbbbbbbbbbbbbb
Private Sub Command1_Click()
          Dim CRC()       As Byte
          Dim d()       As Byte       '待传输数据
          ReDim d(5) As Byte
          d(0) = 2 '02 03 00 00 00 04    lo44 hi3A
          d(1) = 3
          d(2) = 0
PLC
          d(3) = 0
          d(4) = 0
          d(5) = 4 '02 03 20 40 00 04    hi2E Lo4E
      
          CRC = CRC16(d)         '调用CRC16计算函数
          'CRC(0)为高位
          'CRC(1)为低位
        End Sub
        '注意:在数据传输时CRC的低位可能在前,而高位在后。
   
        Function CRC16(data() As Byte) As String
          Dim CRC16Lo       As Byte, CRC16Hi         As Byte          'CRC寄存器 PLC
          Dim CL       As Byte, CH         As Byte                    '多项式码&HA001
          Dim SaveHi       As Byte, SaveLo         As Byte
          Dim i       As Integer
          Dim Flag       As Integer
          CRC16Lo = &HFF
          CRC16Hi = &HFF
          CL = &H1
          CH = &HA0
          For i = 0 To UBound(data) PLC
            CRC16Lo = CRC16Lo Xor data(i)             '每一个数据与CRC寄存器进行异或
            For Flag = 0 To 7
              SaveHi = CRC16Hi
              SaveLo = CRC16Lo
              CRC16Hi = CRC16Hi \ 2                      '高位右移一位
              CRC16Lo = CRC16Lo \ 2                      '低位右移一位
              If ((SaveHi And &H1) = &H1) Then                 '如果高位字节最后一位为1 PLC资料网
                CRC16Lo = CRC16Lo Or &H80                '则低位字节右移后前面补1
              End If                                 '否则自动补0
              If ((SaveLo And &H1) = &H1) Then                 '如果LSB为1,则与多项式码进行异或
                CRC16Hi = CRC16Hi Xor CH
                CRC16Lo = CRC16Lo Xor CL
PLC资料网
              End If
            Next Flag
          Next i
          Dim ReturnData(1)       As Byte
          ReturnData(0) = CRC16Hi                    'CRC高位
          ReturnData(1) = CRC16Lo                    'CRC低位
          CRC16 = ReturnData
        End Function
热帖推荐
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则