1、基本概念

1.1、循环冗余检测(Cyclic Redundancy Check,CRC)

CRC编码也被称为多项式编码(polynomial code),因为该编码能够将要发送的比特串看作是系数为 0 和 1 的一个多项式。对比特串操作被解释为多项式算术。

1.2、CRC参数

D:D数据拥有 d 比特
G:发送方和接收方需要协商一个 r+1 比特模式,称为生成多项式(G),G 的最高有效位比特(最高位)和 最低有效位比特(最低位)必须为 1
R:发送方选择 r 个附加比特,称为 R(CRC校验码)

(1)计算:R 是数据 D 通过模 2 除法除 G 运算得到的(姑且这么说)余数,这个 R 就是 FCS(检测帧序列),发送时把 R 附加到数据 D 后面。

(2)检验:一共接收有 d+r 个比特,用模 2 算术恰好能够被 G 整除(没有余数),即 (D+R)/ G,如果余数为 0,接收方认为数据正确而被接收,否则接收方知道出现了差错。

1.3、CRC原理解释

所有 CRC 计算采用模 2 算术,即在加法中不进位,在减法中不借位,意味加法和减法是相同的,等价于操作数的按位异或(XOR)运算,而不是需要借位运算。

例如:
D = 10110011,d = 8
G = 11001,r = 4
通过计算得到 R = 0100
在这种情况下传输 12 个比特是 101100110100

1.4、例子(一步一步分析)

CRC标准有8、12、16、32比特生成多项式G,一般采用32比特。
举一个例子使用 CRC-8 算法求 101001110100001 的 CRC。
CRC-8 标准的 h(x) = x8 + x7 + x6 + x4 + x2 + 1,既 g 是9位的二进制串111010101。

  1. 首先需要在被除数A后加 8 个比特位0(标准位数减 1)。
  2. 进行模2除法运算。注意每次都是模2运算,即异或。
  3. 最后得到余数C就是CRC校验值。注意余数位数必须比除数少1位,如果不够前面加0补齐。

如果数据的比对位为 1,就与 h(x) 进行异或运算然后向后移一位
如果数据的比对位为 0,就直接后移一位

直到剩余数据长度与标准相等。

经过运算后,最终得到的r是10001100,这就是 CRC 校验码。

1.5、生成多项式

注意:

  • 位宽:文献中提到的生成多项式经常会说到多项式的位宽(Width,简记为W),这个位宽不是多项式对应的二进制数的位数,而是位数减1。比如CRC8中用到的位宽为8的生成多项式,其实对应得二进制数有九位:100110001。计算出的CRC校验值长度是位宽。
  • 16进制表示:多项式表示和二进制表示都很繁琐,交流起来不方便,因此,文献中多用16进制简写法来表示,
  • 省略最高位:因为生成多项式的最高位肯定为1,最高位的位置由位宽可知,故在简记式中,将最高的1统一去掉了,如CRC32的生成多项式简记为 0x04C11DB7 实际上表示的是 0x104C11DB7这样简记除了方便外,在编程计算时也有它的用处

2、代码编程原理

以 CRC32 为例
数据都是 8 bit(16进制)

声明1:网上有很多代码,可以看到很多例子不一样(大部分应该没有问题),其实只是他们没有声明自己算法顺序

  • 算法顺序
    (1)正向算法:就是一般讲解的多项式,例如 CRC32 = x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 ,2进制就是 100000100110000010001110110110111,16 进制就是 0x104C11DB7,但是会去掉最高位,实际使用用 0x04C11DB7,这样的话就需要高八位判断,然后左移。
    (2)反向算法:反向算法优化的更好,同样 CRC32 是一样的,只是把二进制倒序,2进制就是 11101101101110001000001100100000,16 进制就是 0xedb88320,这样的话就需要低八位判断,然后右移。

声明2:为什么先移位再异或?

  • 原因:因为不论是 CRC 8/16/32,应该是 9/17/33 位,但是二进制只取了 8/16/32 位,即默认省略了最高位的 1,根据规则最高位必须为 1,都统一省略,方便位数声明。(同为 1,异或为 0)

2.1、CRC 校验规则

(1)如果仅使用上面所介绍的规则,这显然不够。
如果在字符串前面加0,并不影响校验值,这就不符合我们的预期了。比如,我们想校验的1字节1001 1100,现在在前面补1字节0,变成2字节0000 0000 1001 1100,结果两个得到的校验值是一样的。所以在实际应用中,CRC校验过程做了一些改变:增加了“余数初始值”、“结果异或值”、“输入数据反转”、“输出数据反转”四个概念。

(2)工程中常用CRC校验规则

  • 余数初始值:即在计算开始前,先给变量CRC赋的初值。
  • 结果异或值:即在计算结束后,得到的变量CRC与这个值进行异或操作,就得到了最终的校验值。
  • 输入数据反转:即在计算开始前,将需要校验的数据反转,如数据位1011,反转后为1101。
  • 输出数据反转:即在计算结束后,与结果异或值异或之前,计算值反转,如计算结果为1011,反转后为1101。


接下来以 CRC-32 校验为例,讲解工程中使用的 CRC 校验编程实现。具体实现时,以字节为单位进行计算。const CRC_32 crc_32 = { 0x04c11db7,0xffffffff,0xffffffff,TRUE,TRUE };
数据 1 字节 = 8 bit

  1. 预置 1 个 32 位的变量 CRC,存放校验值,首先赋初值 0xffffffff
  2. 查看规则判断第一个数据是否需要反转,若需要,则按反转,若不需要,直接进入第3步。这里需要反转;
  3. 把第1个数据按照步骤2处理后,与 32 位的变量CRC的高8位相异或,把结果放于变量CRC,低 24 位数据不变;
  4. 检查左移后的移出位;
  5. 如果移出位为 0,左移一位;如果移出位为 1,变量 CRC 左移一位后与多项式 0x04c11db7 进行异或;
  6. 重复步骤4和5,直到左移 8 次(和数据长度一致),这样整个 8 位数据全部进行了处理;
  7. 重复步骤2到步骤6,进行通讯信息帧下一个数据(字节)的处理;
  8. 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的 32 位变量CRC,查看规则是否需要反转,这里需要反转;
  9. 最后,与结果异或值异或,得到的变量 CRC 即为 CRC 校验值;

这里有两种方法实现,基础都是一样的,分为:按位计算法查表法

2.2、按位计算法

数据虽然是一个一个读取的,但是每次都会移位 8 bit,即下一位刚好在上一位移出时接上下一位,而且异或 8 次,结果还是本身,所以无论是拼接到一起还是 8 bit 计算结果是一样的。

就是根据规则一遍又一遍的计算得到 CRC 即可。

分为正向计算和反向计算,正向计算容易理解,反向计算仔细想想,结果是一样的

#include <stdio.h>
#include <malloc.h>typedef unsigned char  u8;
typedef unsigned short  u16;
typedef unsigned int    u32;
typedef unsigned long   u64;//data 是要CRC计算的数据都是 8 bit 的 16 进制
u8 data[] = {0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,       //目的 MAC 地址 0x54, 0xe1, 0xad, 0x7e, 0xc5, 0x11,     //源 MAC 地址 0x08, 0x00,                              //帧类型 0x45,                                 //版本号 4,首部长度 20 字节 ==> 50x00,                                   //服务类型,默认0x00, 0x2e,                             //总长度(首部 + 数据)0x12, 0x34,                            //16 标识位 0x40, 0x00,                                //3 比特标志不分片 010 ,13 比特片偏移 0x40,                                  //生存时间 64 0x11,                                 //上层协议,以 UDP 为例 170xa4, 0x43,                                //IP 首部检验和 0xc0, 0xa8, 0x01, 0x51,                  //源 IP 地址       192.168.1.81 0xc0, 0xa8, 0x01, 0xa6,                    //目的地 IP 地址     192.168.1.1660x17, 0xc1,                                //源端口 0x1b, 0xa9,                               //目的地端口 0x00, 0x1a,                             //UDP 长度 0x6c, 0x2c,                                //UDP 检验和 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,       //传输数据 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,0x51, 0x51, 0x51, 0x51, 0x51, 0x51
};//方法一
u8 reverse8(u8 data) {u8 i;u8 temp = 0;for (i = 0; i < 8; i++)                       // 8 bit反转temp |= ((data >> i) & 0x01) << (7 - i);return temp;
}
u32 reverse32(u32 data) {u8 i;u32 temp = 0;for (i = 0; i < 32; i++)                  // 32 bit反转temp |= ((data >> i) & 0x01) << (31 - i);return temp;
}
//正向计算
u32 crc32(u8* addr, int num) {u8 data;u32 crc = 0xffffffff;                        //初始值int i;for (; num > 0; num--) {data = *addr++;data = reverse8(data);                 //字节反转crc = crc ^ (data << 24);              //与crc初始值高8位异或 for (i = 0; i < 8; i++) {              //循环8位if (crc & 0x80000000)             //左移移出的位为1,左移后与多项式异或crc = (crc << 1) ^ 0x04c11db7;else {crc <<= 1;                     //否则直接左移}}}crc = reverse32(crc);                       //字节反转crc = crc ^ 0xffffffff;                      //最后返与结果异或值异或return crc;                                 //返回最终校验值
}//反向计算
u32 crc32_reverse(u8 array[], int len) {u8 data;u32 crc = 0xffffffff;for (int i = 0; i < len; i++) {data = array[i];crc = crc ^ data;for (int bit = 0; bit < 8; bit++) {             //循环8位if (crc & 0x00000001)             //右移移出的位为1,右移后与多项式异或crc = (crc >> 1) ^ 0xedb88320;else {crc >>= 1;                     //否则直接右移}}}return crc ^ 0xffffffff;
} int main() {//方法一 int data_len = sizeof(data) / sizeof(data[0]);u32 crc = crc32(data, data_len);printf("crc         检验和为:%x\n", crc);u32 crc_reverse = crc32_reverse(data, data_len);printf("crc_reverse 检验和为:%x\n", crc_reverse);return 0;
}

2.3、查表法(如果想弄其他表字节对应修改异或值、是否反转、输入输出前异或值即可)

如果每次像上面这样计算,当数据很长时性能就不好,每个都要从头计算一边,所以就有了查表法,因为都是 8 bit 数据,那么 28 最多只有 256 种情况,所以先计算出这张表。(正向表和方向表不一样,所以网上不一样,但是结果一样)

就是把第二个循环(中间那个循环)单独列出来,计算得到对应表。

#include <stdio.h>
#include <malloc.h>typedef unsigned char  u8;
typedef unsigned short  u16;
typedef unsigned int    u32;
typedef unsigned long   u64;//data 是要CRC计算的数据都是 8 bit 的 16 进制
u8 data[] = {0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,       //目的 MAC 地址 0x54, 0xe1, 0xad, 0x7e, 0xc5, 0x11,     //源 MAC 地址 0x08, 0x00,                              //帧类型 0x45,                                 //版本号 4,首部长度 20 字节 ==> 50x00,                                   //服务类型,默认0x00, 0x2e,                             //总长度(首部 + 数据)0x12, 0x34,                            //16 标识位 0x40, 0x00,                                //3 比特标志不分片 010 ,13 比特片偏移 0x40,                                  //生存时间 64 0x11,                                 //上层协议,以 UDP 为例 170xa4, 0x43,                                //IP 首部检验和 0xc0, 0xa8, 0x01, 0x51,                  //源 IP 地址       192.168.1.81 0xc0, 0xa8, 0x01, 0xa6,                    //目的地 IP 地址     192.168.1.1660x17, 0xc1,                                //源端口 0x1b, 0xa9,                               //目的地端口 0x00, 0x1a,                             //UDP 长度 0x6c, 0x2c,                                //UDP 检验和 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,       //传输数据 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,0x51, 0x51, 0x51, 0x51, 0x51, 0x51
};//方法二:查表法
//生成正向计算表
u32 crc32_table[256];
void create_crc32_table() {u32 c;for (int i = 0; i < 256; i++) {c = (u32)i << 24;for (int bit = 0; bit < 8; bit++) {if (c & 0x80000000) {c = (c << 1) ^ 0x04c11db7;       //CRC32正向二进制}else {c = c << 1;}}crc32_table[i] = c;}
}
u32 use_crc32_table(u8 array[], int len) {u8 data;u32 crc = 0xffffffff;                        //初始值for (int i = 0; i < len; i++) {data = reverse8(array[i]);               //是否反转u8 n = (crc >> 24) ^ data;crc = crc32_table[n] ^ (crc << 8);}crc = reverse32(crc);                     //是否反转return crc ^ 0xffffffff;                  //输出前异或
}//生成反向计算表
u32 crc32_table_reverse[256];
void create_crc32_table_reverse() {u32 c;for (int i = 0; i < 256; i++) {c = (u32)i;for (int bit = 0; bit < 8; bit++) {if (c & 1) {c = (c >> 1) ^ (0xedb88320);}else {c = c >> 1;}}crc32_table_reverse[i] = c;}
}
u32 use_crc32_table_reverse(u8 array[], int len) {u32 crc = 0xffffffff;for (int i = 0; i < len; i++) {crc = crc32_table_reverse[(crc ^ array[i]) & 0xff] ^ (crc >> 8);}return crc ^ 0xffffffff;
}int main() {//方法二 create_crc32_table();u32 crc32 = use_crc32_table(data, data_len);printf("crc_table         检验和为:%x\n", crc32);create_crc32_table_reverse();u32 crc32_reverse = use_crc32_table_reverse(data, data_len);printf("crc_table_reverse 检验和为:%x\n", crc32_reverse);//   for (int i = 0; i < 32; i++) {//      for (int j = 0; j < 8; j++) {//          printf("0x%08x,", crc32_table[i * 8 + j]);
//      }
//      printf("\n");
//  }
//  printf("\n\n");
//  for (int i = 0; i < 32; i++) {//      for (int j = 0; j < 8; j++) {//          printf("0x%08x,", crc32_table_reverse[i * 8 + j]);
//      }
//      printf("\n");
//  }return 0;
}

2.4、结果及CRC32生成表

2.4.1、结果

2.4.2、正向 CRC32 生成表

2.4.3、反向 CRC32 生成表

3、通用正向按位计算代码

记录了常见的 CRC8/16/32 多项式及规则:
https://crccalc.com/
http://www.ip33.com/crc.html

3.1、各种声明——type.h

#ifndef __TYPE_H__
#define __TYPE_H__
#include <stdio.h>/*****************************************************************************
*function: 自定义字符长度
*type    : 8 bit/ 16 bit/ 32 bit/ 64 bit
******************************************************************************/
typedef unsigned char        u8;
typedef unsigned short       u16;
typedef unsigned int         u32;
typedef unsigned long        u64;#define FALSE                0
#define TRUE                 1#endif

3.2、函数接口定义和CRC 参数模型定义——crc.h

#ifndef __CRC_H__
#define __CRC_H__
#include "type.h"/*****************************************************************************
*function: 定义结构体
*type    : 8
******************************************************************************/
typedef struct {u8   Poly;          //多项式u8   InitValue;     //初始值u8   XorOut;        //结果异或值bool InputReverse;  //(是否)输入反转bool OutputReverse; //(是否)输出反转
} CRC_8;
/*****************************************************************************
*function: 定义结构体
*type    : 16
******************************************************************************/
typedef struct {u16  Poly;          //多项式u16  InitValue;     //初始值u16  XorOut;        //结果异或值bool InputReverse;  //(是否)输入反转bool OutputReverse; //(是否)输出反转
} CRC_16;
/*****************************************************************************
*function: 定义各种类型的结构体
*type    : 32
******************************************************************************/
typedef struct {u32  Poly;          //多项式u32  InitValue;     //初始值u32  XorOut;        //结果异或值bool InputReverse;  //(是否)输入反转bool OutputReverse; //(是否)输出反转
} CRC_32;/*****************************************************************************
*function: 常见 CRC 参数模型(16进制)
*first   : 多项式
*second  : 初始值
*third   : 结果异或值
*fourth  : 输入反转
*fifth   : 输出反转
******************************************************************************/
const CRC_8 crc_8          = {0x07, 0x00, 0x00, FALSE, FALSE};
const CRC_8 crc_8_CDMA2000 = {0x9B, 0xff, 0x00, FALSE, FALSE};
const CRC_8 crc_8_DARC     = {0x39, 0x00, 0x00, TRUE, TRUE};
const CRC_8 crc_8_DVB_S2   = {0xD5, 0x00, 0x00, FALSE, FALSE};
const CRC_8 crc_8_EBU      = {0x1D, 0xff, 0x00, FALSE, FALSE};
const CRC_8 crc_8_ITU      = {0x07, 0x00, 0x55, FALSE, FALSE};
const CRC_8 crc_8_I_CODE   = {0x1D, 0xFD, 0x00, FALSE, FALSE};
const CRC_8 crc_8_MAXIM    = {0x31, 0x00, 0x00, TRUE, TRUE};
const CRC_8 crc_8_ROHC     = {0x07, 0xff, 0x00, TRUE, TRUE};
const CRC_8 crc_8_WCDMA    = {0x9B, 0x00, 0x00, TRUE, TRUE};const CRC_16 crc_16_A           = {0x1021, 0xC6C6, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_ARC         = {0x8005, 0x0000, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_AUG_CCITT   = {0x1021, 0x1D0F, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_BUYPASS     = {0x8005, 0x0000, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_CCITT       = {0x1021, 0x0000, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_CCITT_FALSE = {0x1021, 0xffff, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_CDMA2000    = {0xC867, 0xFFFF, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_DDS110      = {0xC867, 0xFFFF, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_DECT_R      = {0x0589, 0x0000, 0x0001, FALSE, FALSE};
const CRC_16 crc_16_DECT_X      = {0x0589, 0x0000, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_DNP         = {0x3d65, 0x0000, 0xffff, TRUE, TRUE};
const CRC_16 crc_16_EN13757     = {0x3D65, 0x0000, 0xFFFF, FALSE, FALSE};
const CRC_16 crc_16_GENIBUS     = {0x1021, 0xFFFF, 0xFFFF, FALSE, FALSE};
const CRC_16 crc_16_IBM         = {0x8005, 0x0000, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_KERMIT      = {0x1021, 0x0000, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_MAXIM       = {0x8005, 0x0000, 0xffff, TRUE, TRUE};
const CRC_16 crc_16_MCRF4XX     = {0x1021, 0xFFFF, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_MODBUS      = {0x8005, 0xffff, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_RIELLO      = {0x1021, 0xB2AA, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_T10_DIF     = {0x8BB7, 0x0000, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_TELEDISK    = {0xA097, 0x0000, 0x0000, FALSE, FALSE};
const CRC_16 crc_16_TMS37157    = {0x1021, 0x89EC, 0x0000, TRUE, TRUE};
const CRC_16 crc_16_USB         = {0x8005, 0xffff, 0xffff, TRUE, TRUE};
const CRC_16 crc_16_X5          = {0x1021, 0xffff, 0xffff, TRUE, TRUE};
const CRC_16 crc_16_XMODEM      = {0x1021, 0x0000, 0x0000, FALSE, FALSE};const CRC_32 crc_32        = {0x04c11db7, 0xffffffff, 0xffffffff, TRUE, TRUE};
const CRC_32 crc_32_BZIP2  = {0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, FALSE, FALSE};
const CRC_32 crc_32_C      = {0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, TRUE, TRUE};
const CRC_32 crc_32_D      = {0xA833982B, 0xFFFFFFFF, 0xFFFFFFFF, TRUE, TRUE};
const CRC_32 crc_32_JAMCRC = {0x04C11DB7, 0xFFFFFFFF, 0x00000000, TRUE, TRUE};
const CRC_32 crc_32_MPEG2  = {0x04c11db7, 0xffffffff, 0x00000000, FALSE, FALSE};
const CRC_32 crc_32_POSIX  = {0x04C11DB7, 0x00000000, 0xFFFFFFFF, FALSE, FALSE};
const CRC_32 crc_32_Q      = {0x814141AB, 0x00000000, 0x00000000, FALSE, FALSE};
const CRC_32 crc_32_XFER   = {0x000000AF, 0x00000000, 0x00000000, FALSE, FALSE};/*****************************************************************************
*function: CRC 函数声明
*type    : crc8/crc16/crc32
******************************************************************************/
u8  crc8(u8* addr, int num, CRC_8 type);
u16 crc16(u8* addr, int num, CRC_16 type);
u32 crc32(u8* addr, int num, CRC_32 type);/*****************************************************************************
*function: 反转函数声明
*type    : reverse8/reverse16/reverse32
******************************************************************************/
u8  reverse8(u8 data);
u16 reverse16(u16 data);
u32 reverse32(u32 data);#endif

3.3、函数实现——crc.cpp

#include "crc.h"
#include "type.h"
#include <stdio.h>/*****************************************************************************
* @brief : 8 bit反转,如1100 0101 反转后为1010 0011
* @param :
*   data : 8 bit
* @return:
*   temp : 反转后的 8 bit
*****************************************************************************/
u8 reverse8(u8 data) {u8 i;u8 temp = 0;// 字节反转for (i = 0; i < 8; i++) {temp |= ((data >> i) & 0x01) << (7 - i);}return temp;
}
/*****************************************************************************
* @brief : 16 bit反转,如1100 0101 1110 0101反转后为1010 0111 1010 0011
* @param :
*   data : 16 bit
* @return:
*   temp : 反转后的 16 bit
******************************************************************************/
u16 reverse16(u16 data) {u8  i;u16 temp = 0;for (i = 0; i < 16; i++) {temp |= ((data >> i) & 0x0001) << (15 - i);}return temp;
}
/*****************************************************************************
* @brief : 32 bit 反转
* @param :
*   data : 32 bit
* @return:
*   temp : 反转后的 16 bit
******************************************************************************/
u32 reverse32(u32 data) {u8  i;u32 temp = 0;for (i = 0; i < 32; i++) {temp |= ((data >> i) & 0x01) << (31 - i);}return temp;
}/*****************************************************************************
* @brief : CRC 校验,校验值为 8 位
* @param :
*   addr : 数据首地址
*   num  : 数据长度(字节)
*   type : CRC8 的算法类型
* @return:
*   crc  : 8 位校验值
******************************************************************************/
u8 crc8(u8* addr, int num, CRC_8 type) {u8  data;u8  crc = type.InitValue; //初始值int i;for (; num > 0; num--) {data = *addr++;if (type.InputReverse == TRUE) {data = reverse8(data);}//与crc初始值异或crc = crc ^ data;for (i = 0; i < 8; i++) {//左移移出的位为1,左移后与多项式异或if (crc & 0x80) {crc = (crc << 1) ^ type.Poly;} else {crc <<= 1;}}}//满足条件,反转if (type.OutputReverse == TRUE) {crc = reverse8(crc);}crc = crc ^ type.XorOut; //最后返与结果异或值异或return (crc);            //返回最终校验值
}/*****************************************************************************
* @brief : CRC 校验,校验值为 16 位
* @param :
*   addr : 数据首地址
*   num  : 数据长度(字节)
*   type : CRC16 的算法类型
* @return:
*   crc  : 16 位校验值
******************************************************************************/
u16 crc16(u8* addr, int num, CRC_16 type) {u8  data;u16 crc = type.InitValue; //初始值int i;for (; num > 0; num--) {data = *addr++;if (type.InputReverse == TRUE) {data = reverse8(data);}//与crc初始值高8位异或crc = crc ^ (data << 8);for (i = 0; i < 8; i++) {//左移移出的位为1,左移后与多项式异或if (crc & 0x8000) {crc = (crc << 1) ^ type.Poly;} else {crc <<= 1;}}}//满足条件,反转if (type.OutputReverse == TRUE) {crc = reverse16(crc);}crc = crc ^ type.XorOut; //最后返与结果异或值异或return (crc);            //返回最终校验值
}
/*****************************************************************************
* @brief : CRC 校验,校验值为 32 位
* @param :
*   addr : 数据首地址
*   num  : 数据长度(字节)
*   type : CRC32 的算法类型
* @return:
*   crc  : 32 位校验值
******************************************************************************/
u32 crc32(u8* addr, int num, CRC_32 type) {u8  data;u32 crc = type.InitValue; //初始值int i;for (; num > 0; num--) {data = *addr++;if (type.InputReverse == TRUE) {data = reverse8(data);}// 与crc初始值高8位异或crc = crc ^ (data << 24);for (i = 0; i < 8; i++) {// 左移移出的位为1,左移后与多项式异或if (crc & 0x80000000) {crc = (crc << 1) ^ type.Poly;} else {crc <<= 1;}}}//满足条件,反转if (type.OutputReverse == TRUE) {crc = reverse32(crc);}crc = crc ^ type.XorOut; //最后返与结果异或值异或return (crc);            //返回最终校验值
}

3.4、主函数调用——main.cpp

#include <stdio.h>
#include <malloc.h>
#include "type.h"
#include "CRC.h"/*****************************************************************************
*explain: 8 bit数据
******************************************************************************/
//data 是要CRC计算的数据都是 8 bit 的 16 进制
u8 data[] = {0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,       //目的 MAC 地址 0x54, 0xe1, 0xad, 0x7e, 0xc5, 0x11,     //源 MAC 地址 0x08, 0x00,                              //帧类型 0x45,                                 //版本号 4,首部长度 20 字节 ==> 50x00,                                   //服务类型,默认0x00, 0x2e,                             //总长度(首部 + 数据)0x12, 0x34,                            //16 标识位 0x40, 0x00,                                //3 比特标志不分片 010 ,13 比特片偏移 0x40,                                  //生存时间 64 0x11,                                 //上层协议,以 UDP 为例 170xa4, 0x43,                                //IP 首部检验和 0xc0, 0xa8, 0x01, 0x51,                  //源 IP 地址       192.168.1.81 0xc0, 0xa8, 0x01, 0xa6,                    //目的地 IP 地址     192.168.1.1660x17, 0xc1,                                //源端口 0x1b, 0xa9,                               //目的地端口 0x00, 0x1a,                             //UDP 长度 0x6c, 0x2c,                                //UDP 检验和 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,       //传输数据 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,0x51, 0x51, 0x51, 0x51, 0x51, 0x51
};int main() {int len = sizeof(data) / sizeof(data[0]);u32 crc = crc32(data, len, crc_32);printf("%x\n", crc);return 0;
}

4、参考

1、循环冗余检验 (CRC) 算法原理
https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html

2、CRC校验详解(附代码示例)
https://blog.csdn.net/u013073067/article/details/86621770

3、全部代码
计算CRC

5、总结

5.1、数据
这是 UDP 64 字节计算CRC,剔除掉前导码。

99 aa bb cc dd ee 54 e1 ad 7e c5 11 08 00 45 00 00 2e 12 34 40 00 40 11 a4 43 c0 a8 01 51 c0 a8 01 a6 17 c1 1b a9 00 1a 6c 2c 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51

CRC检验码计算——C语言(CRC8/16/32)相关推荐

  1. c语言 crc16 不是8的整数倍,当输入是奇数位(不是字节)时生成CRC8/16的最佳方法?C或Python...

    在前面填充0不应该改变结果.计算CRC本质上是二进制长除法.不幸的是,这涉及到分割每个字节.使用移位运算符和按位or很容易实现.在 结尾处的零填充要容易得多,并且取决于计算CRC的原因,这是一个完全合 ...

  2. c语言16x32点阵显示汉字,点阵16*32滚屏显示汉字

    // matrix2pcs20140821.ino const int SH_CP = 8; const int DS = 9; const int ST_CP = 10; //声明4个数组临时变量 ...

  3. c语言16左移3位,点阵16*32综合(间断、上移、下移、左移、右移、左拉、右拉)C程序...

    点阵16*32综合(间断.上移.下移.左移.右移.左拉.右拉)C程序,硬件电路:行驱动74HC154+S8550三极管,列驱动74HC595,具体原理图祥见:http://www.51hei.com/ ...

  4. CRC校验及C语言实现

    摘自:CRC校验原理及其C语言实现 地址:https://blog.csdn.net/whik1194/article/details/108837493?spm=1001.2014.3001.550 ...

  5. c语言程序判断32位还是64位,c++ 判断是64位还是32位系统的实例

    1.IsWow64Process 确定指定进程是否运行在64位操作系统的32环境(Wow64)下. 语法 BOOL WINAPI IsWow64Process( __in HANDLE hProces ...

  6. Python3 PNG文件格式及根据CRC检验码修复图片高度

    PNG文件头部格式如下: - (固定)八个字节89 50 4E 47 0D 0A 1A 0A为png的文件头 - (固定)四个字节00 00 00 0D(即为十进制的13)代表数据块的长度为13 - ...

  7. python计算n的32次方_获得用户输入的一个整数N,计算并输出N的32次方。_学小易找答案...

    [多选题]材料的力学性质 [单选题]已知在计算机中存储了"大学计算机基础"这样一串汉字,它们所占用的存储空间为( )二进制位. [单选题]以下不是python的注释方式是( ) [ ...

  8. 编写一个计算机程序用来计算一个文件的 16 位效验和(Java实现)

    编写一个计算机程序用来计算一个文件的 16 位效验和(Java实现) 题目 编写一个计算机程序用来计算一个文件的 16 位效验和.最快速的方法是用一个 32 位的整数来存放这个和.记住要处理进位(例如 ...

  9. C语言基础之32个关键字

    C语言总共有32个关键字,它们是C语言中最基础的东西,也是组成C语言程序不可或缺的部分.了解,熟悉并掌握它们,对于我们学习C语言有莫大的好处. auto 声明自动变量,缺省时编译器一般默认为auto ...

  10. 16 16点阵c语言程序,用C语言编程16点阵字库

    用C语言编程16点阵字库 2007-07-31 12:28 #include #include #include #include #include #include #include #define ...

最新文章

  1. 华科提出目标检测新方法:基于IoU-aware的定位改进,简单又有效
  2. 网页中如何显示版权符号
  3. 从Sql server 2000 到 Oracle 10g数据库迁移数据类型转化
  4. DDoS攻击可能损害企业品牌的四种方式
  5. Java联网技术之一HTTP
  6. Airbnb欺诈预测机器学习模型设计:准确率和召回率的故事
  7. Oracle442个应用场景-----------Oracle数据库物理结构
  8. 初学Java开发,有哪些从业方向可以选择?
  9. 项目中使用EasyPOI完成监控设备历史数据的导出
  10. java字节流转字符串_字节流与字符流的区别及相互转换
  11. 为什么现在电脑基本没有病毒了?
  12. ssis 计划任务_SSIS FTP任务概述
  13. 2021-2025年中国独立式梳妆浴缸行业市场供需与战略研究报告
  14. 让没有连续TabOrder的Edit连续获得焦点 [精华]
  15. java jpg转png 模糊_Java JPG转换为Png
  16. 【BJOI 2019】排兵布阵
  17. linux下用户名怎么修改密码,LINUX用户名密码忘记怎么修改用户密码
  18. 浅谈4款低功耗电流测试“神器”
  19. Java设计模式博客全目录
  20. C语言:一元二次方程(输入系数a,b,c输出一元二次方程解)

热门文章

  1. Radius 协议介绍
  2. 有关产品项目管理的ISO/IEC/IEEE标准
  3. android 控制手机音量大小,android 控制手机音量的大小 切换声音的模式
  4. matlab 识别车辆,基于MATLAB的车辆牌照识别系统设计
  5. 计算机三维设计大作业模型图,机械制图作业
  6. EastFax USB SERVER推动天润集团U盾管理提效升级
  7. java合并2个txt文本,Java实现多个文档合并输出到一个文档
  8. Ribbon界面开发(C++)
  9. java 医疗监护_医疗监护仪解决方案
  10. 3D打印技术最全解析:从设计到工艺