一文讲透CRC校验码Word文档下载推荐.docx
《一文讲透CRC校验码Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《一文讲透CRC校验码Word文档下载推荐.docx(22页珍藏版)》请在冰豆网上搜索。
这里需要知道几个组成部分或者说计算概念:
多项式公式、多项式简记式、数据宽度、初始值、结果异或值、输入值反转、输出值反转、参数模型。
1、多项式公式
对于CRC标准除数,一般使用多项式(或二项式)公式表示,如下图中除数11011(poly值为0x1b)的二项式为G(X)=X4+X3+X+1,X的指数就代表了该bit位上的数据为1,(最低位为0)。
这里特别注意一下位数问题,除数的位数为二项式最高次幂+1(4+1=5),这个很重要。
2、多项式简记式
通过对CRC的基本了解我们知道,多项式的首尾必定为1,而这个1的位置在下一步计算一定为0,所以就把前面这个1给省略掉了,出现了一个叫简记式的东西,如上例中除数11011的简记式为1011,很多看过CRC高级语言源码的人会知道,对于CRC_16标准下G(X)=X16+X15+X2+1(16#18005)的poly值实际上是8005,这里使用的就是简记式。
后面会对这个用法做一个说明。
3、数据宽度
数据宽度指的就是CRC校验码的长度(二进制位数),知道了CRC的运算概念和多项式,就可以理解这个概念了,CRC长度始终要比除数位数少1,与简记式长度是一致的。
以上三个数据就是我们经常能够用到的基本数据
4、初始值与结果异或值
在一些标准中,规定了初始值,则数据在进行上述二项式运算之前,需要先将要计算的数据与初始值的最低字节进行异或,然后再与多项式进行计算。
而在结果异或值不为零的情况下,则需要将计算得到的CRC结果值再与结果异或值进行一次异或计算,得到的最终值才是我们需要的CRC校验码。
这里可以看出,初始值与结果值的位数要求与数据宽度一致。
5、输入值反转与输出值反转
输入值反转的意思是在计算之前先将二项式反转,然后再用得到的新值和数据进行计算。
如对于G(X)=X16+X15+X2+1(16#18005),其正向值为11000000000000101,反转值则为10100000000000011
输出值反转则是将最终得到的CRC结果反转。
通常,输入值反转后的结果值也会是反转的,所以这两个选项一般是同向的,我们只有在在线CRC计算器中会看到自由选择正反转的情况存在。
三、常见的CRC算法
虽然CRC可以任意定义二项式、数据长度等,但没有一个统一的标准的话,就会让整个计算变得非常的麻烦。
但实际上,不同的厂家经常采用不同的标准算法,这里列出了一些国际常用的模型表:
名称多项式表示法应用举例
CRC-8X8+X2+X+10X107
CRC-12X12+X11+X3+X2+X+10X180Ftelecomsystems
CRC-16X16+X15+X2+10X18005Bisync,Modbus,USB,ANSIX3.28,SIADC-07,manyothers;
alsoknownasCRC-16andCRC-16-ANSI
CRC-CCITTX16+X12+X5+10X11021ISOHDLC,ITUX.25,V.34/V.41/V.42,PPP-FCS
CRC-32X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+10x104C11DB7ZIP,RAR,IEEE802LAN/FDDI,IEEE1394,PPP-FCS
CRC-32CX32+X28+X27+X26+X25+X23+X22+X20+X19+X18+X14+X13+X11+X10+X9+X8+X6+10x11EDC6F41iSCSI,SCTP,G.hnpayload,SSE4.2,Btrfs,ext4,Ceph
四、CRC校验算法前置知识
在学习CRC校验算法之前,先复习一下CRC会涉及的主要几个主要的算法。
1.异或
异或,就是不同为1,相同为0,运算符号是^。
0^0=0
0^1=1
1^1=0
1^0=1
异或运算存在如下几个规律,需要了解。
0^x=x即0异或任何数等于任何数
1^x=~x即1异或任何数等于任何数取反
x^x=0即任何数与自己异或,结果为0
a^b=b^a交换律
a^(b^c)=(a^b)^c结合律
2.模2加法
模2加法相对于普通的算术加法,主要的区别在模2加法,不做进位处理。
具体结果如下。
0+0=00+1=11+1=01+0=1我们发现模2加法的计算结果,同异或运算结果一模一样。
进一步推演,我们会发现,异或运算的5个规律,同样适合于模2加法。
这里,就不在一一列举了。
3.模2减法
模2减法相对于普通的算术减法,主要的区别在模2减法,不做借位处理。
0-0=00-1=11-1=01-0=1我们发现模2减法的计算结果,同模2加法,以及异或的运算结果一模一样。
进一步推演,我们会发现,异或运算的5个规律,同样适合于模2减法。
4.模2除法
模2除法相对于普通的算术除法,主要的区别在模2除法,它既不向上位借位,也不比较除数和被除数的相同位数值的大小,只要以相同位数进行相除即可。
五、CRC原理
CRC原理:
在K位信息码(目标发送数据)后再拼接R位校验码,使整个编码长度为N位,因此这种编码也叫(N,K)码。
通俗的说,就是在需要发送的信息后面附加一个数(即校验码),生成一个新的发送数据发送给接收端。
这个数据要求能够使生成的新数据被一个特定的数整除。
这里的整除需要引入模2除法的概念。
那么,CRC校验的具体做法就是
(1)选定一个标准除数(K位二进制数据串)
(2)在要发送的数据(m位)后面加上K-1位0,然后将这个新数(M+K-1位)以模2除法的方式除以上面这个标准除数,所得到的余数也就是该数据的CRC校验码(注:
余数必须比除数少且只少一位,不够就补0)
(3)将这个校验码附在原m位数据后面,构成新的M+K-1位数据,发送给接收端。
(4)接收端将接收到的数据除以标准除数,如果余数为0则认为数据正确。
注意:
CRC校验中有两个关键点:
一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串(或多项式);
二是把原始帧与上面选定的除进行二进制除法运算,计算出FCS。
前者可以随机选择,也可按国际上通行的标准选择,但最高位和最低位必须均为“1”
六、循环冗余的计算
实例:
由于CRC-32、CRC-16、CCITT和CRC-4的编码过程基本一致,只有位数和生成多项式不一样,下面就举例,来说明CRC校验码生成过程。
对于数据11100101(16#E5),以指定除数11011求它的CRC校验码,其过程如下:
使用上面计算的校验和和消息数据,可以创建要传输的码字。
有时候,我们需要填充checksum到制定的位置,这就涉及到字节序问题,建议用memcpy()进行拷贝。
七、代码实现
实现算法参考网络相关代码,进行整理并验证,可直接使用。
crc.c
/*
*一口Linux
*2021.6.21
*version:
1.0.0
*/
#include"
crc.h"
#include<
stdio.h>
typedefenum{
REF_4BIT=4,
REF_5BIT=5,
REF_6BIT=6,
REF_7BIT=7,
REF_8BIT=8,
REF_16BIT=16,
REF_32BIT=32
}REFLECTED_MODE;
uint32_tReflectedData(uint32_tdata,REFLECTED_MODEmode)
{
data=((data&
0xffff0000)>
>
16)|((data&
0x0000ffff)<
<
16);
0xff00ff00)>
8)|((data&
0x00ff00ff)<
8);
0xf0f0f0f0)>
4)|((data&
0x0f0f0f0f)<
4);
0xcccccccc)>
2)|((data&
0x33333333)<
2);
0xaaaaaaaa)>
1)|((data&
0x55555555)<
1);
switch(mode)
{
caseREF_32BIT:
returndata;
caseREF_16BIT:
return(data>
16)&
0xffff;
caseREF_8BIT:
24)&
0xff;
caseREF_7BIT:
25)&
0x7f;
caseREF_6BIT:
26)&
caseREF_5BIT:
27)&
0x1f;
caseREF_4BIT:
28)&
0x0f;
}
return0;
}
uint8_tCheckCrc4(uint8_tpoly,uint8_tinit,boolrefIn,boolrefOut,uint8_txorOut,
constuint8_t*buffer,uint32_tlength)
uint8_ti;
uint8_tcrc;
if(refIn==true)
crc=init;
poly=ReflectedData(poly,REF_4BIT);
while(length--)
crc^=*buffer++;
for(i=0;
i<
8;
i++)
if(crc&
0x01)
crc>
=1;
crc^=poly;
else
returncrc^xorOut;
crc=init<
4;
poly<
=4;
0x80)
crc<
return(crc>
4)^xorOut;
uint8_tCheckCrc5(uint8_tpoly,uint8_tinit,boolrefIn,boolrefOut,uint8_txorOut,
poly=ReflectedData(poly,REF_5BIT);
3;
=3;
3)^xorOut;
uint8_tCheckCrc6(uint8_tpoly,uint8_tinit,boolrefIn,boolrefOut,uint8_txorOut,
poly=ReflectedData(poly,REF_6BIT);
2;
=2;
2)^xorOut;
uint8_tCheckCrc7(uint8_tpoly,uint8_tinit,boolrefIn,boolrefOut,uint8_txorOut,
poly=ReflectedData(poly,REF_7BIT);
1;
1)^xorOut;
uint8_tCheckCrc8(uint8_tpoly,uint8_tinit,boolrefIn,boolrefOut,uint8_txorOut,
uint32_ti=0;
uint8_tcrc=init;
crc^=ReflectedData(*buffer++,REF_8BIT);
if(refOut==true)
crc=ReflectedData(crc,REF_8BIT);
uint16_tCheckCrc16(uint16_tpoly,uint16_tinit,boolrefIn,boolrefOut,uint16_txorOut,
uint16_tcrc=init;
crc^=ReflectedData(*buffer++,REF_8BIT)<
crc^=(*buffer++)<
0x8000)
crc=ReflectedData(crc,REF_16BIT);
uint32_tCheckCrc32(uint32_tpoly,uint32_tinit,boolrefIn,boolrefOut,uint32_txorOut,
uint32_tcrc=init;
24;
0x80000000)
crc=ReflectedData(crc,REF_32BIT);
uint32_tCrcCheck(CRC_TypecrcType,constuint8_t*buffer,uint32_tlength)
switch(crcType.width)
case4:
returnCheckCrc4(crcType.poly,crcType.init,crcType.refIn,crcType.refOut,
crcType.xorOut,buffer,length);
case5:
returnCheckCrc5(crcType.poly,crcType.init,crcType.refIn,crcType.refOut,
case6:
returnCheckCrc6(crcType.poly,crcType.init,crcType.refIn,crcType.refOut,
case7:
returnCheckCrc7(crcType.poly,crcType.init,crcType.refIn,crcType.refOut,
case8:
returnCheckCrc8(crcType.poly,crcType.init,crcType.refIn,crcType.refOut,
case16:
returnCheckCrc16(crcType.poly,crcType.init,crcType.refIn,crcT