一份C代码搞定所有CRC校验
0赞一份C代码搞定所有CRC---by Koomee
写在前面:
CRC校验有很多种,有CRC4/5/6/7/8/16/32,每一种的多项式也有很多种变化,
* 并不是一成不变的;主要有这几个参数决定计算方法
* 1>Poly:是多项式的值(Gx)
* 2>Init: Init 的位数和Poly的位数相同,它的值为全0或者全F,
当全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0,
然后就可以进行后续计算了。
当全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或
(即:前CRC位数的值按位取反), 再在后面补上CRC位数个0,才进行后续计算。
* 3>RefIn和Refout:它们要么全为False,要么全为True,
当RefIn为False表示,输入的原始数据的每个字节的第7位作为最高有效位,
第0为作为最低有效位,即正常计算即可
当RefIn为True时,输入的原始数据的每个字节需要做个逆序的处理,
注意:针对的每个字节,而不是整个数据,
当Refout为False时,输出不做处理,
当Refout为True,需要对输出数据做一次整个数据的逆序处理,
注意:这里做的逆序和RefIn不同,它不是按字节逆序,而是整个逆序,
以CRC-32为例来说明,最后的数据为32位,当Refout为True时,翻转如下
* bit31-bit30-bit29........bit3-bit2-bit1-bit0
* -> bit0-bit1-bit2-bit3...........bit29-bit30-bit31
* 4->XorOut:表示根据上面参数计算完后,和这个数再进行一次异或。
以上叙述摘自本网站wuyage的博文。
看代码前先看看本站大牛写的这篇文章:《你真的明白CRC的计算过程吗》
地址:http://blog.chinaaet.com/wuyage/p/5100049902
弄明白CRC的计算输入输出条件后,来看看代码怎么实现。
下面看代码:
Filename:CRC_Calculate.c
#include <stdio.h> #include <stdlib.h> /************************************************************************************************* 写在前面: 代码前先看看本站大牛写的这篇文章 http://blog.chinaaet.com/wuyage/p/5100049902 CRC校验有很多种,有CRC4/5/6/7/8/16/32,每一种的多项式也有很多种变化, * 并不是一成不变的;主要有这几个参数决定计算方法 * 1>Poly:是多项式的值 * * 2>Init: Init 的位数和Poly的位数相同,它的值为全0或者全F, * 当全为0时,在算法开始前对数据(这个数据是根据RefIn的值得到的)后面补上CRC位数个0后就可以进行后续计算了。 * 当全为1时,表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或(即:前CRC位数的值按位取反), * 再在后面补上CRC位数个0,才进行后续计算。 * 3>RefIn和Refout:它们要么全为False,要么全为True, * 当RefIn为False表示,输入的原始数据的每个字节的第7位作为最高有效位,第0为作为最低有效位,即正常计算即可 * 当RefIn为True时,输入的原始数据的每个字节需要做个逆序的处理,注意:针对的每个字节,而不是整个数据, * 当Refout为False时,输出不做处理,当 * Refout为True,需要对输出数据做一次整个数据的逆序处理, * 注意:这里做的逆序和RefIn不同,它不是按字节逆序,而是整个逆序, * 以CRC-32为例来说明,最后的数据为32位,当Refout为True时,翻转如下 * bit31-bit30-bit29........bit3-bit2-bit1-bit0 * -> bit0-bit1-bit2-bit3...........bit29-bit30-bit31 * * 4->XorOut:表示根据上面参数计算完后,和这个数再进行一次异或。 *************************************************************************************************************************************** ///Atention at 64bit machine unsigned int is 64 bitwidth typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; typedef struct uCRC_Parameter { u8 BitWidth; //4,5,6,7,8,16,32 or you define u32 Poly; //Polynomial with BitWidth u32 Init_Value; //BitWidth bit "0"or "1" u8 Refin_Bool; //same Time Refin=Refout True Or False u8 Refout_Bool; u32 XorOut; //Before last verify data to putout,shuld XOR with XorOut u32 ValidBit; //BitWidth bit 1 }CRC_PARAM; static unsigned int CRC_table[256]; //位逆转 static unsigned int mf_Bitrev(u32 input, u8 bitwidth) { u8 i; u32 var; var = 0; for(i=0; i<bitwidth; i++) { if(input & 0x01) { var |= 1<<(bitwidth - 1 - i); } input >>= 1; } return var; } void CRC_CreateTable(CRC_PARAM uCRC_PARAM) { u16 i; u8 j; u32 tmp_poly; u32 tmp_CrcReg; u32 tmp_MaxBit; tmp_poly=uCRC_PARAM.Poly; tmp_MaxBit=(1<<(uCRC_PARAM.BitWidth-1)); if(1==uCRC_PARAM.Refin_Bool) { tmp_poly = mf_Bitrev(tmp_poly, uCRC_PARAM.BitWidth); for(i=0; i<256; i++) { tmp_CrcReg = i; for (j=0; j<8; j++) { if((tmp_CrcReg & 1)!=0) { tmp_CrcReg=tmp_poly ^ (tmp_CrcReg >> 1); } else { tmp_CrcReg=(tmp_CrcReg >> 1); } } CRC_table[i] = (uCRC_PARAM.ValidBit&tmp_CrcReg); } } else { if(uCRC_PARAM.BitWidth<8) { tmp_poly=uCRC_PARAM.Poly<<(8-uCRC_PARAM.BitWidth); tmp_MaxBit=(1<<7); } for(i=0; i<256; i++) { tmp_CrcReg = (i<<uCRC_PARAM.BitWidth-8); if(uCRC_PARAM.BitWidth<8) { tmp_CrcReg = (i<<0); } for (j=0; j<8; j++) { if((tmp_CrcReg & tmp_MaxBit)!=0) { tmp_CrcReg=(tmp_poly ^ (tmp_CrcReg<<1)) ; } else { tmp_CrcReg=(tmp_CrcReg << 1); } } CRC_table[i] = (uCRC_PARAM.ValidBit&tmp_CrcReg); } } } //计算 CRCu32 CRC_Calculate(CRC_PARAM uCRC_PARAM, u8* uInput, u16 len) { int i; u8 index; u32 tmp_crc; u8 tmp_crcHB; tmp_crc=uCRC_PARAM.Init_Value; if(1==uCRC_PARAM.Refin_Bool) { for(i=0; i<len; i++) { index = (uCRC_PARAM.ValidBit&(uInput[i]^ tmp_crc)); tmp_crc = (uCRC_PARAM.ValidBit&((tmp_crc >> 8) ^ CRC_table[index])); } //tmp_crc=mf_Bitrev(tmp_crc, uCRC_PARAM.BitWidth); } else { if(uCRC_PARAM.BitWidth>8) { for(i=0; i<len; i++) { tmp_crcHB=(tmp_crc>>(uCRC_PARAM.BitWidth-8)); tmp_crc=(tmp_crc<<8); tmp_crc = (uCRC_PARAM.ValidBit&(tmp_crc ^ CRC_table[tmp_crcHB ^ uInput[i]])); } } else { if(uCRC_PARAM.BitWidth<8) { tmp_crc=(tmp_crc<<(8-uCRC_PARAM.BitWidth)); } for(i=0; i<len; i++) { tmp_crc = CRC_table[tmp_crc ^ uInput[i]]; } } } if((uCRC_PARAM.BitWidth<8)&&(uCRC_PARAM.Refin_Bool==0)) { return (tmp_crc>>(8-uCRC_PARAM.BitWidth))^uCRC_PARAM.XorOut; } else { return tmp_crc^uCRC_PARAM.XorOut; } } int main() { int i; unsigned int res; CRC_PARAM tmp_CRC_Param; unsigned char TestData[]= {0xAA,0xAA,0x06,0x01,0x01,0x03,0xFB,0xEC,0x88,0x99}; tmp_CRC_Param.BitWidth=7; tmp_CRC_Param.Poly=0x09; tmp_CRC_Param.Init_Value=0x00; tmp_CRC_Param.Refin_Bool = 0; tmp_CRC_Param.Refout_Bool = 0; tmp_CRC_Param.XorOut=0x00; tmp_CRC_Param.ValidBit=0xFF; CRC_CreateTable(tmp_CRC_Param); for(i=0;i<256;i++) { if(i%8==0) { printf("\n"); } printf(" 0x%08X",CRC_table[i]); } printf("\n"); res=CRC_Calculate(tmp_CRC_Param,TestData,9); printf("\n CRC Result=%08X! \n",res); printf("Hello world!\n"); return 0; }
写在后面
有网友提问
问题1、为什么不直接计算,而是要先计算一个表/
答:如果直接计算,则每计算一个数据字节,都要重复算Table的256次循环,时间复杂度增加。建议在MCU等的应用中直接将Table值拷贝,不计算,节省功耗嘛!当然看应用自己权衡,时间复杂度和空间复杂度。
问题2、为什么在CRC_CreateTable(CRC_PARAM uCRC_PARAM)函数和
CRC_Calculate(CRC_PARAM uCRC_PARAM, u8* uInput, u16 len)函数中是先判断在for循环?
代码冗余了好多,。为何不是一个For循环里面判断。
答:我这里写这份代码的时候主要是用在单片机里的c代码,考虑了代码的执行效率,所以写成了先判断,后for循环,这里先判断后循环,看似增加了代码的复杂度,其实细看会发现,真正计算的时候只有一路For循环被执行。同事也是只执行一次判断跳转。如果改为一个for循环里面判断几个分支,则每一次循环都要if判断跳转,对于单片机来说这样的跳转指令执行效率不高。当然如果你愿意,也可以改成你喜欢的样子。
写在 最后:
如果有疑问或这建议请发E-mail到Koomee77@qq.com