先上一下 CRC校验 的源代码:
void crc_check(unsigned char *ptr,unsigned int len) //crc为开源函数
{unsigned long wcrc=0XFFFF;//预置16位crc寄存器,初值全部为1unsigned char temp;//定义中间变量int i=0,j=0;//定义计数for(i=0;i<len;i++)//循环计算每个数据{temp=*ptr&0X00FF;//将八位数据与crc寄存器亦或ptr++;//指针地址增加,指向下个数据wcrc^=temp;//将数据存入crc寄存器for(j=0;j<8;j++)//循环计算数据的{if(wcrc&0X0001)//判断右移出的是不是1,如果是1则与多项式进行异或。{wcrc>>=1;//先将数据右移一位wcrc^=0XA001;//与上面的多项式进行异或}else//如果不是1,则直接移出{wcrc>>=1;//直接移出}}}temp=wcrc;//crc的值L_CRC=wcrc;//crc的低八位H_CRC=wcrc>>8;//crc的高八位
}
这个函数通常用于执行 CRC 校验,即对输入的数据指针 ptr
和长度 len
进行 循环冗余校验(Cyclic Redundancy Check)。CRC 是一种非常常见且高效的数据完整性校验算法,广泛应用于:
- 通信协议(如 Modbus、CAN、USB)
- 存储设备(校验文件完整性)
- 嵌入式系统(数据包校验)
CRC 校验概念
CRC 校验的作用:检测数据在传输或保存过程中是否被篡改或损坏
原理:将数据看作一个二进制多项式,除以一个“生成多项式”,取余数作为CRC校验码
常见类型:
CRC8
CRC16(如 Modbus)
CRC32(如以太网、ZIP)
我们这里以标准的 Modbus CRC16 为例,对详细代码进行解析:
uint16_t CRC16_Modbus(uint8_t *data, uint16_t len) {uint16_t crc = 0xFFFF; // 初始值for (uint16_t i = 0; i < len; i++) {crc ^= data[i]; // 将每个字节与 CRC 当前值异或for (uint8_t j = 0; j < 8; j++) {if (crc & 0x0001) {crc >>= 1;crc ^= 0xA001; // 多项式:0x8005 反转为 0xA001} else {crc >>= 1;}}}return crc;
}
逐步解析:
- 初始化 CRC:
Modbus协议规定初始值为 0xFFFF
:
uint16_t crc = 0xFFFF;
- 遍历整个数据区:
每个字节与当前 CRC 值低字节异或:
for (i = 0; i < len; i++) {crc ^= data[i];...
}
- 对每个字节进行 8 次移位处理:
如果最低位是1,就右移并异或“生成多项式”;
否则直接右移。
for (j = 0; j < 8; j++) {if (crc & 0x0001) {crc >>= 1;crc ^= 0xA001;} else {crc >>= 1;}
}
需要注意的是:
CRC 校验是 无错误纠正能力 的,只能检测错误。
必须和接收端使用相同的 CRC 算法、多项式、初始值。
对于不同协议(如 Modbus、CAN),CRC 实现略有不同,例如,本文最初提供的便是 Modbus 协议的 CRC校验。
CRC 的多项式
CRC类型 | 多项式 | 初始值 | 结果异或 | 应用 |
---|---|---|---|---|
CRC-8 | 0x07 | 0x00 | 0x00 | SMBus |
CRC-16 | 0x8005 | 0xFFFF | 0x0000 | Modbus |
CRC-CCITT | 0x1021 | 0xFFFF | 0x0000 | XMODEM |
CRC-32 | 0x04C11DB7 | 0xFFFFFFFF | 0xFFFFFFFF | Ethernet, ZIP |
简单来说,函数 crc_check(unsigned char *ptr, unsigned int len)
是用来对指定内存块执行 CRC 校验的工具函数, 常用于嵌入式通信中验证数据完整性。
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!