CRC冗余校验详解

来源:互联网 发布:红蜘蛛软件怎么卸载 编辑:程序博客网 时间:2024/05/16 12:01

一CRC简介
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码的基本原理:

任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x^6+x^4+x^2+x^1+1,而多项式为x^5+x^3+x^2+x^1+1对应的代码101111。
转换方法:
这里写图片描述

注意:1可以看作1*x^0

标准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

三 CRC原理

1 算术上的除法:

120÷9=13 余 3,120是被除数,9是除数,13是商,3是余数。念作120除以9,或者9除120,或者9去除120!(除法的过程就不写了) 这个除法计算机当然会做,但是做起来很麻烦,因为减法有借位,很耗时间和指令! 所以,计算CRC也是除法,但是用XOR来代替减法,这就简单多了! 

2 CRC除法

120÷9=14 余 6,商、余数和算术除法不一定相同!!因为除法用的是XOR,而不是真正的减法。 以二进制模拟这个计算过程:(过程详见参考资料1)可见,除法(XOR)的目的是逐步消掉最高位的1或0! 由于过程是XOR的,所以商是没有意义的,我们不需要。我们要的是余数。余数110是1111000的CRC吗?不是! 余数110是1111(即十进制15)的CRC!!! 为什么?因为CRC是和数据一起传送的,所以数据后面要加上CRC。 数据1111加上CRC110后,变成1111110,再传送。接收机收到1111110后,除以除数1001,余数为000,正确;如果余数不为0,则说明传送的数据有误!这样完成CRC校验。 即发送端要发送1111,先在1111后加000,变成1111000,再除以1001得到余数110,这个110就是CRC,将110加到数据后面,变成1111110,发送出去。 接收端收到1111110,用它除以1001,计算得余数为000,就说明收到的数据正确。 所以原始数据后面要先扩展出3位0,以容纳CRC值! 这个概念后面要用到。所以,实际上,数据是1111,CRC是110。 对于除数1001,我们叫它生成多项式,即生成项,或POLY,即g(x)。 数据1111根据POLY1001,计算得到CRC110。 如果POLY不是1001,而是1011,那得到的CRC也是不同的! 所以生成项不同,得到的CRC也不同。

要预先定义好POLY,发送端和接收端要用一样的POLY!

四 生成项

上面例子中,生成项是1001,共4位比特,最高位的1,实际上在除法的每次XOR时,都要消掉,所以这个1可不做参考,后3位001才是最重要的!001有3位,所以得到的余数也是3位,因为最后一次除法XOR时,最高位消掉了。所以CRC就是3位比特的。 CRC是3比特,表示它的宽度W=3。也就是说,原始数据后面要加上W=3比特的0进行扩展! 生成项的最低位也必须是1,这是规定的。 生成项1001,就等效于g(x)=x^2+1   (原文错误,应该是g(x)=x^3+1)生成项也可以倒过来写,即颠倒过来,写成1001,这里倒过来的值是一样的。 再如CRC32的生成项是: 1 0000 0100 1100 0001 0001 1101 1011 0111 = 0x04C11DB7  (33个比特) 即g(x)= x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 颠倒过来,就可以写成1110 1101 1011 1000 1000 0011 0010 0000 1 = 0XEDB88320一般生成项简写时不写最高位的1,故生成项是0x04C11DB7,颠倒后的生成项是0xEDB88320 CRC32的生成项是33比特,最高位是消掉的,即CRC值是32比特(4个字节),即宽度W=32,就是说,在计算前,原始数据后面要先扩展W=32个比特0,即4个0x00字节。 

注意:我看到网上CRC32的POLY有0x04C10DB7这个值的,它和正规的POLY值不同,需要注意!

五 直接计算法

直接计算法”就是直接模拟上面的除法的过程,来得到余数即CRC! 上面的例子中,除数是4位,但最高位是要一直消掉的,所以我们只需要一个3位的寄存器就好了。 计算过程: 待测数据后扩展W=3个比特0,变成1111000; 寄存器初始化置0; 先在寄存器中移入数据111; 寄存器左移一位,并且右边移入下一位数据1。这样最高位1移出,由于最高位是1,故本次的商是1,要用除数1001来进行XOR,最高位肯定XOR得0,故不管它,只要用低3位001来进行XOR就可以,即001对寄存器进行XOR,寄存器中得到110,即第一次XOR后的结果(相当于是数据1111与生成项1001进行了一次XOR,并把最高位0消掉了)。 如果移出的最高位是0,则用0000来进行XOR(0 XOR 后,得到的还是原值)。 一直重复这个过程,就能得到最后余数了。 总共处理次数=商的位数=待测数据的位数-生成项位数+1+宽度W=待测数据的位数=4次。 假设有一个4 bits的寄存器,通过反复的移位和进行CRC的除法,最终该寄存器中的值就是我们所要求的余数。              3   2   1   0   Bits            +---+---+---+---+  Pop   <-- |   |   |   |   | <----- Augmented message(已加0扩张的原始数据)            +---+---+---+---+         1    0   0   1   1   = The Poly 生成项    依据这个模型,我们得到了一个最最简单的算法:    把register中的值置0.    把原始的数据后添加w个0.    While (还有剩余没有处理的数据)           Begin           把register中的值左移一位,读入一个新的数据并置于register最低位的位置。           If (如果上一步的左移操作中的移出的一位是1)              register = register XOR Poly.           End 实际上就是模拟XOR除法的过程,即被测数据一位一位放到寄存器中来做除法。     比如生成项是10011,则生成的余数是4位XXXX,所以寄存器是4位。     待测数据是1101 0110 11,后面加上0000,即扩张4位,以容纳余数。     只要与生成项的0011做XOR就好了,最高位经过XOR肯定出0,可不用最高位。    过程如下: 待测数据先移4位即1101到寄存器中,准备开始除法。第1次除法:寄存器中是1101,先从寄存器移出最高位1,移进下一位待测数据位0,则寄存器中是1010,由于移出的位是1,则需要与生成项的0011做XOR,得到1001,即做了第1次除法后,寄存器中是1001,这个就是余数。 第2次除法:寄存器中是1001,从寄存器移出最高位1,移进下一位待测数据位1,则寄存器中是0011,由于移出的位是1,则需要与生成项的0011做XOR,得到0000,即做了第2次除法后,寄存器中是0000,这个就是余数。 第3次除法:寄存器中是0000,从寄存器移出最高位0,移进下一位待测数据位1,则寄存器中是0001,由于移出的位是0,则需要不做XOR,直接下一步移位。也可以等同于:本次的商是0,0*生成项=0,即是0000与寄存器做XOR,得到寄存器的数不变,还是0001,即做了第3次除法后,寄存器中是0001,这个就是余数。 第4次除法:寄存器中是0001,从寄存器移出最高位0,移进下一位待测数据位0,则寄存器中是0010,由于移出的位是0,则需要不做XOR,直接下一步移位。 第5次除法:移位,不用做XOR,得到寄存器中是0101 第6次除法:移位,不用做XOR,得到寄存器中是1011 第7次除法:移位,移出的位是1,又要与生成项做XOR了 注意: 这个算法,计算出的CRC32值,与WINRAR计算出来的不一样,为什么?算法是正确的,不用怀疑!只是CRC32正式算法还涉及到数据颠倒和初始化预置值等,后述。 程序实现:
#include<iostream>using namespace std;unsigned char POLY=0X13;  //生成项 0x13=10011unsigned short data=0x035B;  //待测数据 0x035B=11 0101 1011unsigned short regi=0x0000;  //寄存器 初始化值为0x0000void GetCrc();int main(){    printf("data=%x\n",data);    data=data<<4;  //在data后面追加4个0    GetCrc();    return 0;}void GetCrc(){    for ( int cur_bit = 15; cur_bit >= 0; -- cur_bit )   //处理16次,前4次实际上只是加载数据     {         printf("cur_bit=%d\n",cur_bit);        if ( (  ( regi >> 4 )  & 0x0001 ) == 0x1 )              {                regi = regi ^ POLY;                 printf("regi^POLY=%x\n",regi);            }           regi <<= 1;          printf("regi<<1=%x\n",regi);        unsigned short tmp = ( data >> cur_bit ) & 0x0001;  //加载待测数据1比特到tmp中,tmp只有1比特         printf("cur_bit=%d\n",cur_bit);        printf("data>>cur_bit=%x\n",(data>>cur_bit));        printf("data=%x\n",data);        printf("tmp=%x\n",tmp);        regi |= tmp;    //这1比特加载到寄存器中         printf("regi=%x\n",regi);        printf("\n\n");    }     if ( ( ( regi >> 4 ) & 0x0001 ) == 0x1 )   regi = regi ^ POLY;    //做最后一次XOR    //这时, regi中的值就是CRC     printf("%x\n",regi);}
上面程序所加的输出只是为了方便观察运行中的一些中间变量

这里写图片描述

e就是求得的CRC校验值(注意:e是十六进制输出,换算为二进制为e=1110)

程序2:通用CRC计算程序:

#include<iostream>using namespace std;__int64 POLY=0X11021;  //生成项 0x11021=1 0001 0000 0010 0001__int64 data=0x035B;  //待测数据 0x035B=11 0101 1011__int64 regi=0x0;  //寄存器 初始化值为0x0000int crcbitnumber=16; //CRC是16bitint databitnumber=16; //待测数据是16位void GetCrc();int main(){    data<<=crcbitnumber;    GetCrc();    return 0;}void GetCrc(){    for ( int cur_bit = databitnumber+crcbitnumber-1; cur_bit >= 0; -- cur_bit )   //处理16次,前4次实际上只是加载数据     {         if ( (  ( regi >> crcbitnumber )  & 0x0001 ) == 0x1 )          {                regi = regi ^ POLY;          }        regi <<= 1;          unsigned short tmp = ( data >> cur_bit ) & 0x0001;  //加载待测数据1比特到tmp中,tmp只有1比特         regi |= tmp;    //这1比特加载到寄存器中      }    if ( ( ( regi >> crcbitnumber ) & 0x0001 ) == 0x1 )   regi = regi ^ POLY;    //做最后一次XOR    //这时, regi中的值就是CRC     printf("%x\n",regi);}

这里写图片描述

后记:
1 参考资料来自百度文库
http://wenku.baidu.com/link?url=Re2BWyWtcC2LXKNMyOM7eQX_qXOhCOn9sNacY8sLSK6LI-TmVmIDI1I46IjlaV-eOmNBPzuX7YAHhemtq3aXzLUCaCSXMpuxe8_u85Wfa_W
强烈推荐这一片文章
本文基本上是参照这一篇文章的思路来写,只不过原文作者有写的不太清楚的地方我有加了一些补充。另外原文还有很多内容,我只是截取了其中的一小部分,关于程序当然还可以进一步优化,具体的程序实现可以参考上面连接部分的文章,最后向作者表示深深的谢意。

2 百度知道
http://zhidao.baidu.com/link?url=tVyb1oCHklQOqYqxnWfDBDVaCdeA4A0Y7wchyNQwhtB5rmudtUpQHphqT1tLBxyRZjYnQGJqOT46AssO9WQaPeH07iwkHAR_5yhiFe0minq

3 http://www.cnblogs.com/FPGA_DSP/archive/2010/05/08/1730529.html

4 推荐一个可以计算CRC16的小计算器
这里写图片描述
下载地址:
http://download.csdn.net/detail/qq_27312943/9644783

小计算器是我从网上下载的,后来又传上来,侵删。

0 0