




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1.生成多项式。16位的CRC码产生的规则是先将要发送的二进制序列数左移16位(既乘以216)后,再除以一个多项式,最后所得到的余数既是CRC码。任意一个由二进制位串组成的代码都可以和一个系数仅为0和1取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。标准CRC生成多项式如下表:名称生成多项式简记式*标准引用CRC-4x4+x+10x31 0011CRC-8x8+x5+x4+10x311 0011 0001CRC-8x8+x2+x1+10x071 0000 0111CRC-8x8+x6+x4+x3+x2+x10x5E1 0101 1110CRC-12x12+x11+x3+x+10x80F1 1000 0000 1111CRC-16x16+x15+x2+10x80051 1000 0000 0000 0101CRC16-CCITTx16+x12+x5+10x10211 0001 0000 0010 0001CRC-32x32+x26+x23+.+x2+x+10x04C11DB71 0000 0100 1100 0001 0001 1101 1011 0111CRC-32cx32+x28+x27+.+x8+x6+10x1EDC6F411 0001 1110 1101 1100 0110 1111 0100 0001I、基本算法(人工笔算):以CRC16-CCITT为例进行说明,CRC校验码为16位,生成多项式17位。假如数据流为4字节:BT3、BT2、BT1、BT0;数据流左移16位,相当于扩大256256倍,再除以生成多项式0x11021,做不借位的除法运算(相当于按位异或),所得的余数就是CRC校验码。发送时的数据流为6字节:BT3、BT2、BT1、BT0、CRC1、CRC0;II、计算机算法1(比特型算法):1)将扩大后的数据流(6字节)高16位(BT3、BT2)放入一个长度为16的寄存器;2)如果寄存器的首位为1,将寄存器左移1位(寄存器的最低位从下一个字节获得),再与生成多项式的简记式异或;否则仅将寄存器左移1位(寄存器的最低位从下一个字节获得);3)重复第2步,直到数据流(6字节)全部移入寄存器;4)寄存器中的值则为CRC校验码CRC1、CRC0。III、计算机算法2(字节型算法):256n表示256的n次方,左移一个字节的位数(8bit)即为乘以256!把按字节排列的数据流表示成数学多项式,设数据流为:BTnBTn1BTn2 BT1BT0表示成数学表达式为:BTn256nBTn-1256(n-1)BT1256BT0在这里表示为异或运算。设生成多项式为G17(17bit),CRC码为CRC16,则有CRC16(BTn256nBTn-1256(n-1).BT1256BT0)2562/G17即数据流左移16位,再除以生成多项式G17。先变换BTn-1、BTn-1扩大后的形式CRC16BTn256n2562/G17BTn-1256(n-1)2562/G17.BT12562562/G17BT02562/G17(ZnYn/G17)256nBTn-1256(n-1)2562/G17.BT12562562/G17BT02562/G17Zn256nYn256/G17BTn-12562/G17256(n-1).BT12562562/G17BT02562/G17Zn256n(YH8n256YHLn)256/G17BTn-12562/G17256(n-1).BT12562562/G17BT02562/G17Zn256nYHLn256/G17(YH8nBTn-1)2562/G17256(n-1).BT12562562/G17BT02562/G17这样就推导出,BTn-1字节的CRC校验码为YHLn256/G17+(YH8n+BTn-1)2562/G17,即上一字节CRC校验码Yn的高8位(YH8n)与本字节BTn-1异或,该结果单独计算CRC校验码(即单字节的16位CRC校验码,对单字节可建立表格,预先生成对应的16位CRC校验码),所得的CRC校验码与上一字节CRC校验码Yn的低8位(YL8n)乘以256(即左移8位)异或。然后依次逐个字节求出CRC,直到BT0。字节型算法的一般描述为:本字节的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。unsignedshortGetCrc_16(unsignedchar*pData,intnLength)/函数功能:计算数据流*pData的16位CRC校验码,数据流长度为nLengthunsignedshortcRc_16=0x0000;/初始化while(nLength0)cRc_16=(cRc_168)*pData)&0xff;/cRctable_16表由函数mK_cRctable生成nLength-;pData+;returncRc_16;voidmK_cRctable(unsignedshortgEnpoly)/函数功能:生成0255对应的16CRC校验码,其实就是计算机算法1(比特型算法)/gEnpoly为生成多项式/注意,低位先传送时,生成多项式应反转(低位与高位互换)。如CRC16-CCITT为0x1021,反转后为0x8408unsignedshortcRc_16=0;unsignedshorti,j,k;for(i=0,k=0;i256;i+,k+)cRc_16=i0;j-)if(cRc_16&0x8000)/反转时cRc_16&0x0001cRc_16=(cRc_16=1)gEnpolyelsecRc_16=1cRctable_16k=cRc_16;CyclicRedundancyCheck循环冗余检验,是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。算法原理假设数据传输过程中需要发送15位的二进制信息g=101001110100001,这串二进制码可表示为代数多项式g(x)=x14+x12+x9+x8+x7+x5+1,其中g中第k位的值,对应g(x)中xk的系数。将g(x)乘以xm,既将g后加m个0,然后除以m阶多项式h(x),得到的(m-1)阶余项r(x)对应的二进制码r就是CRC编码。h(x)可以自由选择或者使用国际通行标准,一般按照h(x)的阶数m,将CRC算法称为CRC-m,比如CRC-32、CRC-64等。国际通行标准可以参看/wiki/Cyclic_redundancy_checkg(x)和h(x)的除运算,可以通过g和h做xor(异或)运算。比如将11001与10101做xor运算:明白了xor运算法则后,举一个例子使用CRC-8算法求101001110100001的效验码。CRC-8标准的h(x)=x8+x7+x6+x4+x2+1,既h是9位的二进制串111010101。经过迭代运算后,最终得到的r是10001100,这就是CRC效验码。通过示例,可以发现一些规律,依据这些规律调整算法:1.每次迭代,根据gk的首位决定b,b是与gk进行运算的二进制码。若gk的首位是1,则b=h;若gk的首位是0,则b=0,或者跳过此次迭代,上面的例子中就是碰到0后直接跳到后面的非零位。2.每次迭代,gk的首位将会被移出,所以只需考虑第2位后计算即可。这样就可以舍弃h的首位,将b取h的后m位。比如CRC-8的h是111010101,b只需是11010101。3.每次迭代,受到影响的是gk的前m位,所以构建一个m位的寄存器S,此寄存器储存gk的前m位。每次迭代计算前先将S的首位抛弃,将寄存器左移一位,同时将g的后一位加入寄存器。若使用此种方法,计算步骤如下:蓝色表示寄存器S的首位,是需要移出的,b根据S的首位选择0或者h。黄色是需要移入寄存器的位。S是经过位移后的S。查表法同样是上面的那个例子,将数据按每4位组成1个block,这样g就被分成6个block。下面的表展示了4次迭代计算步骤,灰色背景的位是保存在寄存器中的。经4次迭代,B1被移出寄存器。被移出的部分,不我们关心的,我们关心的是这4次迭代对B2和B3产生了什么影响。注意表中红色的部分,先作如下定义:B23=00111010b1=00000000b2=01010100b3=10101010b4=11010101b=b1xorb2xorb3xorb44次迭代对B2和B3来说,实际上就是让它们与b1,b2,b3,b4做了xor计算,既:B23xorb1xorb2xorb3xorb4可以证明xor运算满足交换律和结合律,于是:B23xorb1xorb2xorb3xorb4=B23xor(b1xorb2xorb3xorb4)=B23xorbb1是由B1的第1位决定的,b2是由B1迭代1次后的第2位决定(既是由B1的第1和第2位决定),同理,b3和b4都是由B1决定。通过B1就可以计算出b。另外,B1由4位组成,其一共24有种可能值。于是我们就可以想到一种更快捷的算法,事先将b所有可能的值,16个值可以看成一个表;这样就可以不必进行那4次迭代,而是用B1查表得到b值,将B1移出,B3移入,与b计算,然后是下一次迭代。可看到每次迭代,寄存器中的数据以4位为单位移入和移出,关键是通过寄存器前4位查表获得,这样的算法可以大大提高运算速度。上面的方法是半字节查表法,另外还有单字节和双字节查表法,原理都是一样的事先计算出256或216个b的可能值,迭代中使用寄存器前8位或16位查表获得b。反向算法之前讨论的算法可以称为正向CRC算法,意思是将g左边的位看作是高位,右边的位看作低位。G的右边加m个0,然后迭代计算是从高位开始,逐步将低位加入到寄存器中。在实际的数据传送过程中,是一边接收数据,一边计算CRC码,正向算法将新接收的数据看作低位。逆向算法顾名思义就是将左边的数据看作低位,右边的数据看作高位。这样的话需要在g的左边加m个0,h也要逆向,例如正向CRC-16算法h=0x4c11db8,逆向CRC-16算法h=0xedb88320。b的选择0还是h,由寄存器中右边第1位决定,而不是左边第1位。寄存器仍旧是向左位移,就是说迭代变成从低位到高位。以下的源程序全部以CCITT为例。其实本质都是一样,搞明白一种,其他的都是小菜。图1,图2说明了CRC校验中CRC值是如何计算出来的,体现的多项式正是X16+X12+X5+1。SerialData即是需要校验的数据。从把数据移位开始计算,将数据位(从最低的数据位开始)逐位移入反向耦合移位寄存器(这个名词我也不懂,觉得蛮酷的,就这样写了,嘿)。当所有数据位都这样操作后,计算结束。此时,16位移位寄存器中的内容就是CRC码。图中进行XOR运算的位与多项式的表达相对应。X5代表Bit5,X12代表Bit12,1自然是代表Bit0,X16比较特别,是指移位寄存器移出的数据,即图中的DATAOUT。可以这样理解,与数据位做XOR运算的是上次CRC值的Bit15。根据以上说明,可以依葫芦画瓢的写出以下程序。(程序都是在keilC7.10下调试的)typedefunsignedcharuchar;typedefunsignedintuint;codeucharcrcbuff=0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3;uintcrc;/CRC码voidmain(void)uchar*ptr;crc=0;/CRC初值ptr=crcbuff;/指向第一个BT数据crc=crc16l(ptr,8);while(1);uintcrc16l(uchar*ptr,ucharlen)/ptr为数据指针,len为数据长度uchari;while(len-)for(i=0x80;i!=0;i=1)if(crc&0x8000)!=0)crc=1;crc=0x1021;1-1elsecrc=1;1-2if(*ptr&i)!=0)crc=0x1021;1-3ptr+;return(crc);执行结果crc=0xdbc0;程序1-1,1-2,1-3可以理解成移位前crc的Bit15与数据对应的Bit(*ptr&i)做XOR运算,根据此结果来决定是否执行crc=0x1021。只要明白两次异或运算与原值相同,就不难理解这个程序。很多资料上都写了查表法来计算,当时是怎么也没想通。其实蛮简单的。假设通过移位处理了8个bit的数据,相当于把之前的CRC码的高字节(8bit)全部移出,与一个BT的数据做XOR运算,根据运算结果来选择一个值(称为余式),与原来的CRC码再做一次XOR运算,就可以得到新的CRC码。不难看出,余式有256种可能的值,实际上就是0255以X16+X12+X5+1为权得到的CRC码,可以通过函数crc16l来计算。以1为例。codetest=0x01;crc=0;ptr=test;crc=crc16l(ptr,1);执行结果crc=1021,这就是1对应的余式。进一步修改函数,我这里就懒得写了,可得到X16+X12+X5+1的余式表。codeuintcrc_ta256=/X16+X12+X5+1余式表0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0;根据这个思路,可以写出以下程序:uinttable_crc(uchar*ptr,ucharlen)/字节查表法求CRCucharda;while(len-!=0)da=(uchar)(crc/256);/以8位二进制数暂存CRC的高8位crc=8;/左移8位crc=crc_tada*ptr;/高字节和当前数据XOR再查表ptr+;return(crc);本质上CRC计算的就是移位和异或。所以一次处理移动几位都没有关系,只要做相应的处理就好了。下面给出半字节查表的处理程序。其实和全字节是一回事。codeuintcrc_ba16=0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,;uintban_crc(uchar*ptr,ucharlen)ucharda;while(len-!=0)da=(uchar)(crc/256)/16;crc=4;crc=crc_bada(*ptr/16);da=(uchar)(crc/256)/16);crc=4;crc=crc_bada(*ptr&0x0f);ptr+;return(crc);crc_ba16和crc_ta256的前16个余式是一样的。其实讲到这里,就已经差不多了。反正当时我以为自己是懂了。结果去看别人的源代码的时候,也是说采用CCITT,但是是反相的。如图3反过来,一切都那么陌生,faint.吐血,吐血。仔细分析一下,也可以很容易写出按位异或的程序。只不过由左移变成右移。uintcrc16r(unsignedchar*ptr,unsignedcharlen)unsignedchari;while(len-!=0)for(i=0x01;i!=0;i=1;crc=0x8408;elsecrc=1;if(*ptr&i)!=0)crc=0x8408;ptr+;return(crc);0x8408就是CCITT的反转多项式。套用别人资料上的话“反转多项式是指在数据通讯时,信息字节先传送或接收低位字节,如重新排位影响CRC计算速度,故设反转多项式。”如code
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论