找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

394

积分

0

好友

52

主题
发表于 昨天 10:17 | 查看: 7| 回复: 0

在嵌入式通信中,你一定见过 0x1021, 0x8005, CRC-16/MODBUS, CRC-CCITT 这些术语。 你可能也经历过这种绝望:明明代码逻辑是对的,但算出来的 CRC 值就是和上位机对不上!

CRC(循环冗余校验)是数据完整性的守护神,但它也是无数嵌入式工程师的噩梦,因为它的参数组合多到让人怀疑人生。

核心原理:CRC 的本质不是“加法和”,而是一个除法余数。但这个除法不是我们小学学的除法,而是基于 模2运算(异或运算) 的除法,属于计算机基础中的核心数学逻辑之一。

为什么 Checksum(累加和)不够用?

最简单的校验是将所有字节加起来(Checksum)。

缺陷:如果数据中的 0x01 变成了 0x00,而另一个 0x02 变成了 0x03,总和是不变的。累加和无法检测出这种“多位反转”的错误,也无法感知数据的顺序(AB 和 BA 的和一样)。

CRC 利用多项式除法,对数据位的变化极其敏感。哪怕只变了一个 bit,算出来的 CRC 结果也会发生剧烈变化(雪崩效应)。

核心概念:多项式 (Poly)

CRC 算法的核心就是选一个除数。这个除数通常用多项式表示。 比如 CRC-16 的常见多项式 x^16 + x^12 + x^5 + 1。 把它转换成二进制:

  • 第 16 位是 1
  • 第 12 位是 1
  • 第 5 位是 1
  • 第 0 位是 1
  • 其他位是 0

得到二进制串:1 0001 0000 0010 0001 (17位)。 由于最高位总是 1,通常省略不写,剩下的 16 位就是 Generater Polynomial (生成多项式),即 0x1021。

这就是你在代码里看到的那个神秘数字 0x1021 或 0x8005 的由来。

实现方式:移位法 vs 查表法

方式一:逐位移位法 (Bit-Shifting)
这是最原始的算法,模拟硬件电路的移位寄存器。

逻辑:把数据和 CRC 寄存器异或,然后左移一位。如果移出的是 1,就和多项式(Poly)再异或一次。

优点:代码极短,省空间。
缺点:极其慢。每算一个字节要循环 8 次,如果是 1KB 数据,要循环 8000 次,大量消耗 CPU。

方式二:查表法 (Table-Lookup)
结合上一期讲的“空间换时间”思想。 既然一个字节只有 256 种可能,我们预先算出 0x00 到 0xFF 对应的 CRC 值,存入一个 const uint16_t table[256]

逻辑:CRC = (CRC << 8) ^ Table[(CRC >> 8) ^ NewByte]

优点:快如闪电。每处理一个字节只需一次查表和异或。
缺点:占用 Flash 空间(CRC-16 表需 512 字节,CRC-32 表需 1KB)。但在工程中,查表法是标准选择。

避坑指南:CRC 的“参数地狱”

这是本期的重点。为什么你的 CRC 算不对?因为 CRC 并不是只有一个标准,它有 5 个关键参数必须对齐。只要这 5 个参数有一个不一样,结果就完全不同。

  1. Width (宽度):是 8位、16位 还是 32位?
  2. Poly (多项式)
    • Modbus 用的是 0x8005。
    • XModem 用的是 0x1021。
    • USB 用的是 0x8005(但反转了)。
  3. Init (初始值)
    • 算法开始前,CRC 寄存器里填什么?
    • Modbus 是 0xFFFF。
    • XModem 是 0x0000。
    • 有些协议是 0x1D0F。
  4. RefIn / RefOut (输入/输出反转):这是最大的坑!
    • 有些标准规定,字节输入时要低位在前 (LSB First),比如 UART 传输就是低位先出。这就要求我们在计算前把字节里的 bit 倒序(Reflect)。
    • STM32 的硬件 CRC 单元通常默认是 MSB First,如果用来算 Modbus(要求 LSB),你就得手动反转数据,反而比软件算还慢。这个细节对于理解网络/系统中串行通信的数据处理方式至关重要。
  5. XorOut (结果异或)
    • 计算结束后,是否要和某个数异或一下再输出?
    • CRC-32 通常要异或 0xFFFFFFFF。
    • Modbus 是 0x0000(不异或)。

实战案例:CRC-16/MODBUS 标准模型

  • Poly: 0x8005
  • Init: 0xFFFF
  • RefIn: True (输入字节按位反转)
  • RefOut: True (输出结果按位反转)
  • XorOut: 0x0000

软硬件差异

很多单片机(如 STM32)带有 Hardware CRC Unit。

理想:数据扔进去,结果出来,不占 CPU。
现实:STM32 F1/F4 的硬件 CRC 默认配置是为 Ethernet CRC-32 设计的(Poly=0x04C11DB7, Init=0xFFFFFFFF)。

如果你要算 Modbus CRC-16,你发现它的 Poly 是固定的(或者是受限可配),位数对不上,反转属性对不上。

结论:除非你的通信协议完全匹配芯片的硬件 CRC 参数,否则不如老老实实写软件查表法。通用的软件代码移植性更强。

总结

  • CRC 本质:模2除法的余数。
  • 工程选择:首选查表法(256字节的表),兼顾速度和空间。
  • 调试技巧:当结果对不上时,不要瞎猜。去搜 “CRC Online Calculator”(CRC在线计算器),把上面 5 个参数填进去对比,看看到底是初始值错了,还是输入没反转。

希望本文能帮你理清CRC校验的脉络。更多深入的计算机基础与工程实践讨论,欢迎访问云栈社区进行交流。




上一篇:MySQL底层原理详解:语法、数据存储与ACID事务实现机制
下一篇:西数HC620 14T硬盘是加密盘?企业级下架硬盘的NAS使用体验与风险分析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-18 18:11 , Processed in 0.275468 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表