数据校验与CRC
内存泄漏
2026年8月22日 00:19
河南
在小说阅读器读本章
去阅读
以下文章来源于嵌入式软件客栈
,作者Psyducking
嵌入式软件客栈
.
嵌入式开发加速器,作为嵌入式、Linux、物联网、C/C++等技术分享平台,提供更多实战指南,让嵌入式技术应用更简单高效。欢迎关注,一起交流探索无限可能!
校验不是可选项,是嵌入式通信的基本防线。
为什么和校验不够
最朴素的办法是把所有字节相加,取低字节附在帧尾。实现简单,却挡不住一类常见错误:两个字节同时出错且增减相互抵消,或者数据整体移位。
// 和校验:易实现,但漏检率高
uint8_t checksum(uint8_t *p, int n)
{
uint8_t s = 0;
for (int i = 0; i < n; i++) s += p[i];
return s;
}
和校验只覆盖\"加法\"维度,对突发错误(burst error,线路干扰常连续错几位)几乎无能为力。工业通信(Modbus、CAN、以太网)普遍选择 CRC,因为它对连续多位错误有极强的检出能力。
CRC 到底在算什么
CRC 不是\"循环冗余\"这个名字体现的数学玄学,本质是把数据当作一个很大的二进制多项式,除以一个固定的生成多项式,取余数。余数就是校验值。接收方用同样的多项式除一遍,余数不为 0 即说明传输有误。
生成多项式决定了检错能力。Modbus RTU 用 CRC-16/MODBUS(多项式 0x8005,初始值 0xFFFF,输入反转、输出反转、异或输出 0x0000);CAN 用 CRC-15;以太网用 CRC-32。多项式不同,同一段数据算出的 CRC 完全不同——两端必须用同一个参数定义,否则永远对不上。
参数陷阱
当照搬一段 CRC-16 代码,和对方设备对不上,排查半天发现是参数没对齐。CRC 有六个关键参数,缺一不可:
- • Width:校验位宽(16/32)
- • Poly:生成多项式
- • Init:寄存器初始值
- • RefIn:每个输入字节是否按位反转
- • RefOut:输出余数是否整体反转
- • XorOut:最终异或值
// Modbus RTU 的 CRC-16,参数缺一不可
uint16_t crc16_modbus(uint8_t *data, int len)
{
uint16_t crc = 0xFFFF; // Init
for (int i = 0; i < len; i++) {
crc ^= data[i]; // 这里隐含了 RefIn=true(逐字节处理时等价)
for (int b = 0; b < 8; b++) {
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001; // 0xA001 是 0x8005 的反转形式
else
crc >>= 1;
}
}
return crc; // XorOut=0x0000,无需再异或
}
注意 0xA001 不是随便写的,它是多项式 0x8005 按位反转后的结果——这一项就对应 RefIn/RefOut 都为 true 的约定。如果对方设备 RefOut 为 false,你就该用 0x8005 直接算,结果会不同。对接任何 CRC 设备,先把这六个参数和白皮书逐一对齐,不要凭\"都是 CRC-16\"就假设一致。
字节
算出的 16 位 CRC 在帧尾有两个字节,先放高字节还是低字节?Modbus 规定低字节在前(little-endian on the wire)。
// 发送:低字节在前
frame[tail] = crc & 0xFF; // 低字节
frame[tail+1] = (crc >> 8) & 0xFF; // 高字节
// 接收端校验:取出后按同样顺序重组再算
uint16_t rx_crc = frame[tail] | (frame[tail+1] << 8);
uint16_t calc = crc16_modbus(frame, tail);
if (rx_crc != calc) { /* 帧错误,丢弃 */ }
字节序搞反是最常见的对接故障,现象是\"本地自算自校验 OK,和对方设备一通信就全错\"。先核对协议文档里 CRC 的传输顺序,再写收发代码。
用硬件 CRC 外设加速
软件逐位计算在大批量数据下很占 CPU。STM32 等 MCU 内置 CRC 外设,一条指令算完一个字,且是 CRC-32(以太网多项式)。
// 使能并配置硬件 CRC(以 STM32 HAL 为例)
__HAL_RCC_CRC_CLK_ENABLE();
hcrc.Instance = CRC;
HAL_CRC_Init(&hcrc); // 默认 CRC-32 0x04C11DB7
// 按 32 位字计算,注意数据需 4 字节对齐
uint32_t calc = HAL_CRC_Calculate(&hcrc, (uint32_t *)buf, len_words);
硬件 CRC 多固定为某一种参数(常是 CRC-32),和 Modbus 的 CRC-16 不是一回事,不能直接替代 Modbus 校验。它适合做固件完整性检查、Flash 数据校验这类场景;对接标准 Modbus 仍用上面软件实现的 CRC-16/MODBUS。别为了\"用硬件\"硬套不匹配的多项式。
校验在通信帧里的位置
否
是
应用数据
组帧: 地址+功能码+数据
计算 CRC 附帧尾
发送
接收 收齐一帧
用同参重算 CRC
一致?
丢弃/请求重传
提交上层
校验的边界要划在\"整帧有效载荷\"上,不能只覆盖数据段漏掉地址和功能码——否则从机地址被干扰成别的设备,校验却通过,会造成错误响应。CRC 计算范围必须和协议定义完全一致,包括帧头里的每个字段。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论