一 概述
CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
二 CRC-8原理
模2除法
模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。
关键点
CRC校验中有两个关键点,一是预先确定一个发送送端和接收端都用来作为除数的二进制比特串(或多项式),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1;二是把原始帧与上面计算出的除数进行模2除法运算,计算出CRC码。
具体步骤
选择合适的多项式,确定除数。
看选定多项式的二进制位数,然后将要发送的数据上面加上这个位数-1位的0,然后用得到的数据以模2除法的方式除上面确定的除数,得到的余数就是该数的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。
示例
现假设我们使用的多项式为:G(X) = X8+X2+X^1+1,要求出0x1A的CRC-8校验码。下面是具体的计算过程:
将多项式转化为二进制序列,由G(X) = X8+X2+X^1+1可知二进制一种有9位,第8位、第2位、第1位和第0位分别为1,则序列为100000111。
原来要计算的数据为1 1010,多项式的最高次为8,则在数据的后面加上8位0,数据变为1 1010 0000 0000,然后使用模2除法除以除数100000111,最终得到的除不尽的余数,变为我们要求的CRC-8结果。
三、CRC-8软件实现
CRC-8的计算软件上可以采用循环计算的方法,也可以采用查表法,下面的代码都是以多项式G(X) = X8+X2+X^1+1来实现,其他的多项式也类似。
- 循环计算法
从上面的计算过程可以看到,多项式最高位为1,遇到需要异或数据最高位为1时, 才进行异或计算,并且异或后,最高位就为0了,最高位为0,下次也不需要异或了, 这样需要采用代码计算的方式,就可以把最高位去掉,不需要异或,最后结果也是一样的。
#define FACTOR (0x107 & 0xFF) //多项式因子(取低8bit)
unsigned char calcCRC(unsigned char *pdat, unsigned int len)
{
unsigned char j;
unsigned char crc = 0x00;
while(len--)
{
crc ^= (*pdat++);//前一字节计算CRC后的结果异或上后一字节,再次计算CRC
for (j=8; j>0; j--)
{
crc <<= 1;
if (crc & 0x80)//高位为1,需要异或;否则,不需要
{
crc ^= FACTOR;
}
}
}
return crc;
}
- 查表法
查表法实际上就是采用法1中的方法,对0x00~0xFF计算CRC8,然后按顺序保存到数组里面,在需要计算CRC8的时候,通过查找这个数组得到CRC8。
// 按照多项式 X^8+X^2+X^1+1 生成。
static const unsigned int crc8Table[256] =
{
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
// 计算CRC
unsigned char calcCRC(unsigned char *data, unsigned int len)
{
unsigned char crc8 = 0;
while (len --)
{
crc8 = crc8 ^ (*data++);
crc8 = crc8Table[crc8];
}
return crc8;
}