Modbus CRC校验代码+原理理解

1. 代码及过程

inline quint16 CalculateCRC(QByteArray buffer)
{
    quint16 crc = 0xffff;

    for(int i = 0; i < buffer.length(); i++){
        crc = crc ^ (unsigned char)buffer.at(i);

        for(int j = 0; j < 8; j++){
            quint16 temp = crc & 0x01;
            crc = crc >> 1;
            if(temp == 1){
                crc = crc ^ 0xA001;
            }
        }
    }

    return crc;
}

这段代码实现了一个 CRC-16/MODBUS 校验算法(多项式 0xA001),用于计算给定数据缓冲区(QByteArray)的循环冗余校验值(CRC)。以下是逐行逻辑解析:


1. 初始化 CRC 寄存器

quint16 crc = 0xffff;
  • 初始化一个 16 位的 CRC 寄存器,初始值为 0xFFFF(这是 MODBUS CRC 的标准初始值)。

2. 遍历输入数据的每个字节

for(int i = 0; i < buffer.length(); i++) {
    crc = crc ^ (unsigned char)buffer.at(i);
  • 对缓冲区中的每个字节进行按位异或(XOR)操作:
    • (unsigned char)buffer.at(i) 确保当前字节是无符号的(避免符号扩展问题)。
    • crc ^ byte 将当前字节与 CRC 寄存器的低 8 位进行异或。

3. 对每个字节的 8 位进行位处理

for(int j = 0; j < 8; j++) {
    quint16 temp = crc & 0x01;
    crc = crc >> 1;
    if(temp == 1) {
        crc = crc ^ 0xA001;
    }
}
  • 位处理逻辑
    1. 提取最低位temp = crc & 0x01 获取 CRC 寄存器的最低位(LSB)。
    2. 右移 CRC 寄存器crc = crc >> 1 将 CRC 右移 1 位(相当于除以 2)。
    3. 多项式异或:如果最低位是 1,则与多项式 0xA001(即 0x8005 的位反射形式)进行异或。
      • 这是 CRC-16/MODBUS 的核心算法步骤。

4. 返回最终的 CRC 值

return crc;
  • 返回计算完成的 16 位 CRC 值(低字节在前,高字节在后,符合 MODBUS 协议要求)。

示例验证

假设输入数据为 {0x01, 0x02}

  1. 初始 CRC = 0xFFFF
  2. 异或 0x01 → CRC = 0xFFFF ^ 0x01 = 0xFFFE
  3. 处理 8 位后,CRC 可能变为 0xXXXX(具体值取决于位运算过程)。
  4. 最终结果需与实际 MODBUS CRC 工具对比验证。

2. 为什么当最低位为 1 时要异或 0xA001

这个操作是 CRC-16/MODBUS 算法的核心步骤,其背后的原理涉及 模 2 除法(多项式除法)位反射(Bit Reflection)。下面逐步解释:


1. CRC 的本质:模 2 除法

CRC(循环冗余校验)的本质是 用数据除以一个固定的多项式,取余数作为校验值

  • 多项式:在 MODBUS 中,标准多项式是 0x8005(二进制 1000 0000 0000 0101)。
  • 模 2 除法:计算时不考虑进位,等价于按位异或(XOR)。

2. 为什么右移后要异或 0xA001

(1) 标准 CRC-16 的运算方式

在标准 CRC-16(多项式 0x8005)中,计算过程是 高位先处理(MSB-first)

  1. 数据左移,检查最高位(MSB)是否为 1:
    • 如果是 1,则 左移后异或 0x8005
    • 如果是 0,仅左移。
(2) MODBUS 使用低位先处理(LSB-first)

但 MODBUS 协议使用的是 低位先处理(LSB-first),即:

  • 数据右移,检查最低位(LSB)是否为 1:
    • 如果是 1,则 右移后异或 0xA001
    • 如果是 0,仅右移。
(3) 0xA0010x8005 的位反射
  • 0x8005 的二进制1000 0000 0000 0101(标准 CRC-16 多项式)。
  • 0xA001 的二进制1010 0000 0000 0001(MODBUS CRC 多项式)。

0xA001 实际上是 0x8005 的位反射(Bit-Reversed)形式

  • 0x8005 的 16 位倒过来:
    0x8005: 1000 0000 0000 0101  
    反射后: 1010 0000 0000 0001 → 0xA001
    
  • 由于 MODBUS 先处理 LSB(右移),而标准 CRC 先处理 MSB(左移),所以必须使用反射后的多项式 0xA001 来保持计算一致性。

3. 为什么最低位为 1 时要异或?

右移(LSB-first) 的 CRC 计算中:

  1. 检查最低位(LSB)

    • 如果最低位是 1,说明当前 CRC 值 不能被多项式整除,需要做调整(即异或 0xA001)。
    • 如果最低位是 0,说明当前 CRC 值 可以被多项式整除,直接右移即可。
  2. 异或 0xA001 的作用

    • 相当于在模 2 除法中 减去(XOR)多项式,使得余数(CRC)符合校验规则。

3. 对比标准 CRC-16 和 MODBUS CRC-16

参数标准 CRC-16 (CCITT)MODBUS CRC-16
多项式0x80050xA0010x8005 的反射)
初始值0xFFFF0xFFFF
输入反射无(MSB-first)有(LSB-first)
输出反射
结果异或0x00000x0000

示例计算

假设数据 0x01 的 CRC 计算过程:

  1. 初始 CRC = 0xFFFF
  2. 处理 0x01
    • CRC ^ 0x01 = 0xFFFE
    • 进入 8 次右移循环:
      • 第 1 次0xFFFE 的最低位是 0 → 仅右移 → 0x7FFF
      • 第 2 次0x7FFF 的最低位是 1 → 右移并异或 0xA0010xBFFF
      • …(继续 6 次)
  3. 最终 CRC 可能是 0xXXXX(具体值取决于完整计算)。

4. 总结

  • 0xA0010x8005 的位反射,因为 MODBUS 使用 LSB-first 计算。
  • 最低位为 1 时异或 0xA001,相当于在模 2 除法中调整余数。
  • 该算法确保 MODBUS CRC 校验与标准 CRC-16 数学等效,只是计算方向不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值