CRC8检验算法(C语言实现)
CRC校验算法,简而言之就是把需要校验的数据与多项式进行循环异或(XOR),XOR的方式与数据传输方式的高位先行(MSB)和低位先行(LSB)有关。那么什么情况算是对齐呢?除数poly的左边的高位的作用其实是给人看的(实际上参与运算的是0011),目的是干掉当前最高位的被除数,本质上是让poly和被除数对齐,然后开始XOR运算。上面的crc计算是纯采用逻辑运行的方式,可以看到,需要的运行量也是不少
本文仅用于本人学习记录,参考摘录了以下大佬的文章:
CRC8算法_算法_简单的过客-GitCode 开源社区 (csdn.net)
CRC算法原理、推导及实现 - QIYUEXIN - 博客园 (cnblogs.com)
目录
1.CRC8标准生成多项式
CRC-8 x8+x5+x4+1 0x31(0x131)
CRC-8 x8+x2+x1+1 0x07(0x107)
CRC-8 x8+x6+x4+x3+x2+x1 0x5E(0x15E)
注:由于多项式的最高为都为1,并且在代码的crc8计算中,最高位也是不使用的,所以在多项式记录时都去掉了最高位。
2.CRC校验算法
CRC校验算法,简而言之就是把需要校验的数据与多项式进行循环异或(XOR),XOR的方式与数据传输方式的高位先行(MSB)和低位先行(LSB)有关。
对于MSB:XOR从数据的高位开始,暂时定义为顺序异或;
对于LSB:XOR从数据的低位开始,暂时定义为反序异或;
两种不同的异或方式,即使对应相同的多项式,计算出来的结果也不一样。
3.算法推导
CRC的算法是基于除法,但不能直接使用除法运算,由于待校验的数据可能会很长,而MCU可能并没有这么大的寄存器;
3.1 仿人算方法
现在我们假设一个消息数据为1101011011,选取除数为10011,使用CRC算法将消息除以poly:
1100001010 = Quotient (nobody cares about the quotient)
_______________
10011 ) 11010110110000 = Augmented message (1101011011 + 0000)
=Poly 10011,,.,,..|.
-----,,.,,|...
10011,.,,|.|.
10011,.,,|...
-----,.,,|.|.
10110...
10011.|.
-----...
010100.
10011.
-----.
01110
00000
-----
1110 = Remainder = THE CHECKSUM!!!!
除数poly的左边的高位的作用其实是给人看的(实际上参与运算的是0011),目的是干掉当前最高位的被除数,本质上是让poly和被除数对齐,然后开始XOR运算。
那么什么情况算是对齐呢? 从例子上看,当被除数和除数的最高位都是1时,就算是对齐了。因此当多项式为0x131(100110001)时,实际参与运算的是0x31(00110001)
转换成算法的思路就是,你也可以理解成一长串的数据不断的从右边移位到寄存器中,当寄存器最左边溢出的数值是1的时候,那么当前寄存器的数据就可以和poly异或运算了,用算法表示,大概是这样:
3 2 1 0 Bits
+---+---+---+---+
Pop! <-- | | | | | <----- Augmented message
+---+---+---+---+
1 0 0 1 1 = The Poly
4.直接计算法:
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
#define CRC8_POLYNOMIAL 0x31
/*计算多个字节CRC8校验值的函数*/
uint8_t cal_bytes_crc8(uint8_t *data, uint8_t len){
uint8_t crc8 = 0x00; /* 计算的初始crc值 */
uint8_t i ;
while(len--){
crc8 ^= *data++; /* 每次先与需要计算的数据异或,计算完指向下一数据 */
for (i = 8; i > 0; --i) { /* 下面这段计算过程与计算一个字节crc一样 */
crc8 = ( crc8 & 0x80 )
? (crc8 << 1) ^ CRC8_POLYNOMIAL
: (crc8 << 1);
}
}
return crc8 ;
}
上面的crc计算是纯采用逻辑运行的方式,可以看到,需要的运行量也是不少的,每一个字节都需要进行8次判断、移位、或异或操作。可以采用查表法,大大减少计算量,先计算0x00~0xFF每一个字节的crc校验结果,后面就可以通过表来查出每个字节的crc结果,大大减少计算量。
5.查表法
uint8_t cal_table_high_first(uint8_t value)
{
uint8_t i, crc=0x00;
crc = value;
/* 数据往左移了8位,需要计算8次 */
for (i=8; i>0; --i){
crc = ( crc & 0x80 ) /* 判断最高位是否为1 */
/* 最高位为1,不需要异或,往左移一位,然后与0x31异或 */
/* 0x31(多项式:x8+x5+x4+1,100110001),最高位不需要异或,直接去掉 */
? (crc << 1) ^ CRC8_POLYNOMIAL
/* 最高位为0时,不需要异或,整体数据往左移一位 */
: (crc << 1);
}
return crc;
}
// 下面是一个表生成程序:
void create_crc_table(void)
{
uint16_t i;
uint8_t j;
for (i=0; i<=0xFF; i++)
{
if (0 == (i%16))
printf("\n");
j = i&0xFF;
printf("0x%.2x, ", cal_table_high_first (j)); /*依次计算每个字节的crc校验值*/
}
}
// 按照多项式 X^8+X^2+X^1+1 生成。
static const unsigned int crc8_table[256] =
{
0x00, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0xb9, 0x88, 0xdb, 0xea, 0x7d, 0x4c, 0x1f, 0x2e,
0x43, 0x72, 0x21, 0x10, 0x87, 0xb6, 0xe5, 0xd4, 0xfa, 0xcb, 0x98, 0xa9, 0x3e, 0x0f, 0x5c, 0x6d,
0x86, 0xb7, 0xe4, 0xd5, 0x42, 0x73, 0x20, 0x11, 0x3f, 0x0e, 0x5d, 0x6c, 0xfb, 0xca, 0x99, 0xa8,
0xc5, 0xf4, 0xa7, 0x96, 0x01, 0x30, 0x63, 0x52, 0x7c, 0x4d, 0x1e, 0x2f, 0xb8, 0x89, 0xda, 0xeb,
0x3d, 0x0c, 0x5f, 0x6e, 0xf9, 0xc8, 0x9b, 0xaa, 0x84, 0xb5, 0xe6, 0xd7, 0x40, 0x71, 0x22, 0x13,
0x7e, 0x4f, 0x1c, 0x2d, 0xba, 0x8b, 0xd8, 0xe9, 0xc7, 0xf6, 0xa5, 0x94, 0x03, 0x32, 0x61, 0x50,
0xbb, 0x8a, 0xd9, 0xe8, 0x7f, 0x4e, 0x1d, 0x2c, 0x02, 0x33, 0x60, 0x51, 0xc6, 0xf7, 0xa4, 0x95,
0xf8, 0xc9, 0x9a, 0xab, 0x3c, 0x0d, 0x5e, 0x6f, 0x41, 0x70, 0x23, 0x12, 0x85, 0xb4, 0xe7, 0xd6,
0x7a, 0x4b, 0x18, 0x29, 0xbe, 0x8f, 0xdc, 0xed, 0xc3, 0xf2, 0xa1, 0x90, 0x07, 0x36, 0x65, 0x54,
0x39, 0x08, 0x5b, 0x6a, 0xfd, 0xcc, 0x9f, 0xae, 0x80, 0xb1, 0xe2, 0xd3, 0x44, 0x75, 0x26, 0x17,
0xfc, 0xcd, 0x9e, 0xaf, 0x38, 0x09, 0x5a, 0x6b, 0x45, 0x74, 0x27, 0x16, 0x81, 0xb0, 0xe3, 0xd2,
0xbf, 0x8e, 0xdd, 0xec, 0x7b, 0x4a, 0x19, 0x28, 0x06, 0x37, 0x64, 0x55, 0xc2, 0xf3, 0xa0, 0x91,
0x47, 0x76, 0x25, 0x14, 0x83, 0xb2, 0xe1, 0xd0, 0xfe, 0xcf, 0x9c, 0xad, 0x3a, 0x0b, 0x58, 0x69,
0x04, 0x35, 0x66, 0x57, 0xc0, 0xf1, 0xa2, 0x93, 0xbd, 0x8c, 0xdf, 0xee, 0x79, 0x48, 0x1b, 0x2a,
0xc1, 0xf0, 0xa3, 0x92, 0x05, 0x34, 0x67, 0x56, 0x78, 0x49, 0x1a, 0x2b, 0xbc, 0x8d, 0xde, 0xef,
0x82, 0xb3, 0xe0, 0xd1, 0x46, 0x77, 0x24, 0x15, 0x3b, 0x0a, 0x59, 0x68, 0xff, 0xce, 0x9d, 0xac
};
//采用查表法计算crc代码如下:
uint8_t cal_crc_table(uint8_t *data, uint8_t len){
uint8_t crc8 = 0x00;
while (len --){
crc8 = crc8_table[crc8 ^ *data++];
}
return crc8 ;
}
更多推荐



所有评论(0)