CRC校验字节型算法查表法解读

时间:2021-02-05 11:47:18

CRC校验字节型算法查表法解读(备忘)

本文本人很喜欢,系转载转自:http://blog.csdn.net/suding666/article/details/8078708,若转载对作者本人有侵犯,请作者及时与本人联系,定将删除。

以下为转载:    http://hi.baidu.com/zhangshe/blog/item/0805e95c2a649647fbf2c0f4.html

CRC校验

    crc算法已经有成熟和比较经典的现成代码可供我们利用。CRC计算可以靠专用的硬件来实现,但是对于低成本的微控制器系统,在没有硬件支持下实现CRC检验,关键的问题就是如何通过软件来完成CRC计算,也就是CRC算法的问题。CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。

1.生成多项式。
16位的CRC码产生的规则是先将要发送的二进制序列数左移16位(既乘以
)后,再除以一个多项式,最后所得到的余数既是CRC码。任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。

标准CRC生成多项式如下表:

   名称        生成多项式              简记式*   标准引用
   CRC-4       x4+x+1                  3         ITU G.704
   CRC-8       x8+x5+x4+1              0x31                   
   CRC-8       x8+x2+x1+1              0x07                   
   CRC-8       x8+x6+x4+x3+x2+x1       0x5E
   CRC-12      x12+x11+x3+x+1          80F
   CRC-16      x16+x15+x2+1            8005      IBM SDLC
   CRC16-CCITT x16+x12+x5+1            1021      ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS
   CRC-32      x32+x26+x23+...+x2+x+1 04C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS
   CRC-32c     x32+x28+x27+...+x8+x6+1 1EDC6F41 SCTP
//叶子:这里不知道问什么省略了,有些迷惑哦。要是生成多项式要是都省了,那还怎么校验?我猜想可能是中间的全为一吧。
                              
   生成多项式的最高位固定的1,故在简记式中忽略最高位1了,如0x1021实际是0x11021。
I、基本算法(人工笔算):
   以CRC16-CCITT为例进行说明,CRC校验码为16位,生成多项式17位。假如数据流为4字节:BYTE[3]、BYTE[2]、BYTE[1]、BYTE[0];
数据流左移16位,相当于扩大256×256倍,再除以生成多项式0x11021,做不借位的除法运算(相当于按位异或),所得的余数就是CRC校验码。
发送时的数据流为6字节:BYTE[3]、BYTE[2]、BYTE[1]、BYTE[0]、CRC[1]、CRC[0];

II、计算机算法1(比特型算法):
1)将扩大后的数据流(6字节)高16位(BYTE[3]、BYTE[2])放入一个长度为16的寄存器;
2)如果寄存器的首位为1,将寄存器左移1位(寄存器的最低位从下一个字节获得),再与生成多项式的简记式异或;
    否则仅将寄存器左移1位(寄存器的最低位从下一个字节获得);
3)重复第2步,直到数据流(6字节)全部移入寄存器;
4)寄存器中的值则为CRC校验码CRC[1]、CRC[0]。

III、计算机算法2(字节型算法):256^n表示256的n次方
    把按字节排列的数据流表示成数学多项式,设数据流为BYTE[n]BYTE[n-1]BYTE[n-2]、、、BYTE[1]BYTE[0],表示成数学表达式为BYTE[n]×256^n+BYTE[n-1]×256^(n-1)

+...+BYTE[1]*256+BYTE[0],在这里+表示为异或运算。设生成多项式为G17(17bit),CRC码为CRC16。
    则,CRC16=(BYTE[n]×256^n+BYTE[n-1]×256^(n-1)+...+BYTE[1]×256+BYTE[0])×256^2/G17,即数据流左移16位,再除以生成多项式G17。
    先变换BYTE[n-1]、BYTE[n-1]扩大后的形式,
    CRC16=BYTE[n]×256^n×256^2/G17+BYTE[n-1]×256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
         =(Z[n]+Y[n]/G17)×256^n+BYTE[n-1]×256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
         =Z[n]×256^n+{Y[n]×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
         =Z[n]×256^n+{(YH8[n]×256+YHL[n])×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
         =Z[n]×256^n+{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
    这样就推导出,BYTE[n-1]字节的CRC校验码为{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17},即上一字节CRC校验码Y[n]的高8位(YH8[n])与本字节BYTE[n-1]异或,

该结果单独计算CRC校验码(即单字节的16位CRC校验码,对单字节可建立表格,预先生成对应的16位CRC校验码),所得的CRC校验码与上一字节CRC校验码Y[n]的低8位(YL8[n])

乘以256(即左移8位)异或。然后依次逐个字节求出CRC,直到BYTE[0]。
    字节型算法的一般描述为:本字节的CRC码,等于上一字节CRC码的低8位左移8位,与上一字节CRC右移8位同本字节异或后所得的CRC码异或。   
    字节型算法如下:
    1)CRC寄存器组初始化为全"0"(0x0000)。(注意:CRC寄存器组初始化全为1时,最后CRC应取反。)
    2)CRC寄存器组向左移8位,并保存到CRC寄存器组。
    3)原CRC寄存器组高8位(右移8位)与数据字节进行异或运算,得出一个指向值表的索引。
    4)索引所指的表值与CRC寄存器组做异或运算。
    5)数据指针加1,如果数据没有全部处理完,则重复步骤2)。
    6)得出CRC。

unsigned short GetCrc_16(unsigned char * pData, int nLength)
//函数功能:计算数据流* pData的16位CRC校验码,数据流长度为nLength
{
    unsigned short cRc_16 = 0x0000;    // 初始化
   
    while(nLength>0)
    {
        cRc_16 = (cRc_16 << 8) ^ cRctable_16[((cRc_16>>8) ^ *pData) & 0xff]; //cRctable_16表由函数mK_cRctable生成
        nLength--;
        pData++;
    }
   
    return cRc_16;   
}


void mK_cRctable(unsigned short gEnpoly)
//函数功能:生成0-255对应的16CRC校验码,其实就是计算机算法1(比特型算法)
//gEnpoly为生成多项式
//注意,低位先传送时,生成多项式应反转(低位与高位互换)。如CRC16-CCITT为0x1021,反转后为0x8408
{
unsigned short cRc_16=0;
unsigned short i,j,k;

for(i=0,k=0;i<256;i++,k++)
{
      cRc_16 = i<<8;
      for(j=8;j>0;j--)
      {
if(cRc_16&0x8000)                 //反转时cRc_16&0x0001
             cRc_16=(cRc_16<<=1)^gEnpoly; //反转时cRc_16=(cRc_16>>=1)^gEnpoly
         else
             cRc_16<<=1;                   //反转时cRc_16>>=1
      }
      cRctable_16[k] = cRc_16;
}
}

2:CRC码集选择的原则

若设码字长度为N,信息字段为K位,校验字段为R位(N=K+R),则对于CRC码集中的任一码字,存在且仅存在一个R次多项式g(x),使得

V(x)=A(x)g(x)=xRm(x)+r(x);

其中:    m(x)为K次信息多项式, r(x)为R-1次校验多项式,

         g(x)称为生成多项式:

g(x)=g0+g1x+ g2x2+...+g(R-1)x(R-1)+gRxR

发送方通过指定的g(x)产生CRC码字,接收方则通过该g(x)来验证收到的CRC码字。

3、CRC校验码软件生成方法:

    借助于多项式除法,其余数为校验字段。

例如:信息字段代码为: 1011001;对应m(x)=x6+x4+x3+1

      假设生成多项式为:g(x)=x4+x3+1;则对应g(x)的代码为: 11001

      x4m(x)=x10+x8+x7+x4 对应的代码记为:10110010000;

采用多项式除法: 得余数为: 1010     (即校验字段为:1010)

发送方:发出的传输字段为: 1 0 1 1 0 0 1 1 0 10

                          信息字段       校验字段

接收方:使用相同的生成码进行校验:接收到的字段/生成码(二进制除法), 如果能够除尽,则正确。

 

 

以上字节型算法(红字部分)解读:

      比特型算法较易理解,这里不做解释.一般我们应用中更多的是用字节型的算法,而大部字节型算法为了提高效率是利用查表法实现的,对于查表法,表是如何产生的,有时迷惑,所以在此对字节型查表法推导过程的理解做一个备忘:

     由等式一到等式二的推导:Z[n] 和Y[n]分别为BYTE[N]/G17的商和余子式,所以可以进行转换;其它的BYTE暂不变;

     由等式二到等式三的推导:由于只关心余子式,所以可以不理Z[n],对Y[n]与后一字节进行进一步的结合处理,提取256^(n-1);

     由等式三到等式四的推导:把Y[n]进一步分解成高低8位Y[n]×256/G17 = (YH8[n]×256+YL8[n])×256/G17;

     由等式四到等式五的推导:重新组合得{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17};

     这样就推导出,BYTE[n-1]字节的CRC校验码为{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17},即上一字节CRC校验码Y[n]的高8位(YH8[n])与本字节BYTE[n-1]异或,该结果单独计算CRC校验码(即单字节的16位CRC校验码,对单字节可建立表格,预先生成对应的16位CRC校验码),所得的CRC校验码与上一字节CRC校验码Y[n]的低8位(YL8[n])乘以256(即左移8位)异或。然后依次逐个字节求出CRC,直到BYTE[0]。

    表的生成则是对0~255每个数进行求CRC得到的!

(END)

转自:http://blog.csdn.net/suding666/article/details/8078708