一份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
