循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。具体算法原理可以参考百度,简单说就是用来检测传输数据是否出错。
正好要用到CRC7,学习之余记录下来,以备查阅。本文主要参考:http://www.cnblogs.com/esestt/archive/2007/08/09/848856.html ,实现查表法快速计算CRC7.
CRC7应用在SD、MMC的数据校验,生成多项式x^7 + x^3 + 1,生成的校验值为7bit.很容易想到数据以 1byte 为单位进行计算比较方便.
1、算法示意:
假设现在要计算的数据是0x10 (00010000b),后面要填7个0,生成多项式可以表示为0x89(10001001b)。
过程如下:(数据为0不需要计算,直接跳到1的位置与生成多项式系数异或操作)
计算过程总结为:
(1)从高位到低位依次扫描每个bit
(2)如果当前bit为1,则以当前位置开始于生成多项式进行异或操作
(3)将得到的余数作为新的置重复第(1)步到8bit结束
(4)最后剩余的就是这个字节的校验码
从这个过程可以看到生成多项式最高位为1,与当前的bit进行异或,结果一定是0,所以生成多项式的最高位可以省略,记为0x09。
对比红色字体的运算结果与黄底色生成多项式系数,生成结果是生成多项式移位后异或得到的,生成多项式是已知的,所以结果可以提前计算出来,这就是查表法的原理。
再考虑多字节的情况,前一个字节计算结果(红色字体部分)会移位到下一字节,即下一个字节的计算与前一个字节的余数相关,两者异或后才是下一字节计算的开始。
上面的结论即查表法的原理:1byte的数据总共有256个,每个数据都可以提前计算相应的校验码放到表中,计算时直接查表;对于多字节的情况,第n个字节查表的结果Table[Data[n]],第n+1个字节的结果是Table[Data[n+1]^ Table[Data[n]]],第1个字节前面没有数据,所以需要一个初始值,可以为0x00或0xFF。
2、代码实现:
根据前面的结论用代码很容易实现CRC7计算.
(1)生成码表
#define TAB_LEN 256
#define ALPHA 0x09
int table_gen8(unsigned char *buf){
unsigned int alpha = ALPHA; //x^7+x^3+1
int i,j;
unsigned char tmp;
for(i=0;i<TAB_LEN;i++){
tmp = i;
for(j=0;j<8;j++){
if(tmp & 0x80)
tmp ^= alpha;
tmp <<= 1;
}
buf[i] = tmp>>1; /*余数为7bit,计算中用了8bit,结尾多一位0要去掉*/
}
return 0;
}
(2) 校验码计算
unsigned char get_crc7(unsigned char start, const unsigned char *buff, int len, unsigned char *table){
unsigned char accu = start;
unsigned int i= 0;
for (i=0; i < len; i++) {
accu = table[(accu << 1) ^ buff[i]];
}
return accu;
}
(3)测试代码
int main(void){
unsigned char data[TAB_LEN] = {0};
int i,j;
printf("CRC7 table:\n");
table_gen8(data);
for(i=0;i<TAB_LEN/8;i++){
for(j=0;j<8;j++)
printf("0x%02x ",data[i*8+j]);
printf("\n");
}
printf("\n");
/*Test*/
const unsigned char testdat[10] = "0123456789";
unsigned char result;
result = get_crc7(0, testdat, 10, data);
printf("get_crc7:0x%02x\n",result);
return 0;
}
运行结果如下:
3、总结
通过查表法计算CRC7快速简洁,只需占用一些存储空间保存预先计算好的码表。其他系数的CRC计算也可以用类似方法,不过本文没有用到,未做验证。