-
什么是CRC算法?
CRC(Cyclic Redundancy Check),称循环冗余校验,是根据数据产生简短固定位数校验码的一种信道编码技术,主要用于发送端和接收端检测或校验数据传输前后是否出现错误,它是利用除法及余数的原理来作数据错误侦测的。 -
CRC数据格式
一个完整的数据帧通常由以下部分构成:帧头+数据+校验数据+帧尾
在实际应用中,我们通常只关心 数据+校验数据,即校验数据一般是附加在数据后一起传输,所以数据传输流程如下:
发送端以CRC算法计算出校验数据crcData
发送端将crcData合并到数据data中得到一帧数据data+crcData,发送给接收端
接收端收到data+crcData数据帧后以CRC算法验证crcData的正确性,以检测数据是否出错 -
CRC算法参数模型解释
-
示例(CRC-8/MAXIM算法):
设原始数据为0xD6
(1)原始数据为0xD6,转成二进制为1101 0110,多项式由上图知为x^8+x^5+x^4+1=0x131(可以理解为x=2),上图中的多项式是0x31,去掉了最高位,
至于为啥去掉目前没搞明白,多项式为啥要多1个bit、多项式能不能自己随意定,这些专业知识还得找文献再看看,不过能算出来校验和就OK。
(2)输入反转为true,这里是指高低位顺序反转,例如1000 1000反转后为0001 0001,所以我们要把异或
后的数据0xD6即1101 0110进行反转得到0110 1011即0x6B。
(3)用反转后数据高8位和上图中初始值进行异或运算:0x6B^0x00=0x6B,还是原始数据;初始值可以随意设置,至于为啥是和高8位而不是低8位,也不明白。
(4)规定要在原始数据后补(多项式位数-1)个0,因为多项式为0x131有9个位,所以我们要在0x63后补9-1=8个0,结果为0x6B00,为啥这么规定,也不明白。
(5)把反转后的数据与多项式进行模2除法,其实就是异或运算,我们需要的是运算结果的余数,所以我们要把0x6B00与多项式0x131不断异或,直到余数
位小于等于多项式位数;如果余数位等于多项式位数,那么把它俩异或后的结果就是最终运算结果;如果余数位小于多项式位数,那么余数直接就是
最终运算结果。0x6B00(hex)=0110 1011 0000 0000(bin),去掉高位没用的0结果为**110 1011 0000 0000**;0x131(hex)=0000 0001 0011 0001(bin),
去掉高位没用的0结果为**1 0011 0001**,下面进行运算:
110 1011 0000 0000
^100 1100 01
-------------------
10 0111 0100 0000
^10 0110 001
------------------
1 0110 0000
^1 0011 0001
---------------
101 0001(最终结果0x51)
(6)上图中的“输出结果异或”为true,所以我们需要将结果0x51(0101 0001)反转,结果为1000 1010(0x8A)。
(7)上图中的“结果异或值为0x00”,所以我们需要将最终结果与0x00异或,结果还是0x8A。
(8)将校验数据0x8A添加到数据后面,最终的数据帧为0xD68A,其中D6是原始数据,8A是校验数据。
(9)数据帧发送后,接收端根据同样的算法再算一次,如果校验数据是0x8A,那么数据正确,否则,数据出错。
我们使用工具再次验证校验和没有错误:
下面用C语言实现该算法:
#include <iostream>
using namespace std;
unsigned char width = 8; //宽度
unsigned short int poly = 0x0131; //多项式
unsigned char init = 0x00; //初始值
unsigned char xorout = 0x00; //结果异或
bool refin = true; //输入反转
bool refout = true; //输出反转
/*计算数据data的数据位数*/
unsigned int bitnum(unsigned long long int data)
{
unsigned long long int _data = data;
unsigned int retValue = 0;
while (_data)
{
_data >>= 1;
retValue++;
}
return retValue;
}
/*将数据反转*/
unsigned char reversal(unsigned long long int data)
{
unsigned char _data = (unsigned char)(data);
unsigned char retValue = 1;
for (int i = 0; i < 8; i++)
{
unsigned char temp = _data & 0x01;
if (temp)
retValue |= 0x01;
else
retValue &= ~0x01;
if (i == 7)
break;
retValue <<= 1;
_data >>= 1;
}
return retValue;
}
unsigned int crc(unsigned char data)
{
unsigned long long int _data = (unsigned long long int)(data);
unsigned long long int _poly = (unsigned long long int)(poly);
unsigned long long int temp = 0x00;
_data ^= init; //将原始数据和数据异或
if(refin)
_data = (unsigned long long int)(reversal(_data)); //将数据反转
_data <<= width; //数据左移
do
{
_poly = poly << (bitnum(_data) - bitnum(poly));
_data = _data ^ _poly;
} while (bitnum(_data) > 8);
_data ^= xorout;
if(refout)
_data = reversal(_data);
return (unsigned int)(_data);
}
int main(int argc, char** argv)
{
unsigned int data = 0; //原始数据
while (true)
{
cout << "输入数据(Hex):";
cin >> hex >> data;
unsigned int ret = crc(data);
cout << "校验数据(Hex):" << hex << uppercase << ret << endl << endl;
}
/*输出从0~255的校验数据*/
//for (int i = 0; i <= 0xFF; i++)
//{
// cout << hex << uppercase << i << ":" << crc(i) << endl;
//}
return 0;
}
计算结果展示: