CRC校验(循环冗余校验 Cyclic Redundancy Check)(以Modbus通信为例)(注意:CRC16先发低位再发高位)

CRC校验在Modbus通信中的应用

介绍

循环冗余校验(Cyclic Redundancy Check,CRC)是一种在数据通讯领域广泛使用的技术,用于检测数据传输或存储过程中的错误。在工业通信协议中,特别是Modbus协议中,CRC校验起到了非常关键的作用,它保证了数据从一个设备传输到另一个设备时的完整性和准确性。

CRC校验的基本原理

校验原理

CRC校验是基于二进制数据进行的。它通过将数据视为一个长的二进制数,并通过特定的多项式进行除法运算,从而得到一个固定长度的余数,即CRC值。这个余数就作为校验码附加到原始数据后面。接收方在接收到数据后,将包括CRC在内的完整数据再次通过同一个多项式进行除法运算,如果余数为零,则认为数据在传输过程中未发生错误。

核心多项式

在Modbus协议中,通常使用的CRC多项式是0xA001(反向多项式),这是根据CRC-16标准的多项式0x8005进行位序反转得到的。

CRC在Modbus通信中的实际应用

数据格式

在Modbus协议中,数据包的格式通常包括设备地址、功能码、数据和CRC校验码。CRC校验码是整个数据包的最后两个字节,用于验证数据包的完整性。

校验流程

1. 发送方:计算数据包(不包括CRC校验码)的CRC校验码,然后将校验码附加到数据包的末尾。
2. 接收方:收到数据包后,提取出CRC校验码,并对包括校验码在内的整个数据包进行CRC校验。如果计算结果为零,说明数据在传输过程中未被篡改或损坏。

CRC校验的编程实现

CRC计算函数

下面是一个在Python中实现Modbus CRC16校验的示例代码:

def crc16(data: bytes):
    crc = 0xFFFF
    for pos in data:
        crc ^= pos
        for i in range(8):
            if (crc & 1) != 0:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc

# 示例数据
data = b'\x01\x03\x00\x00\x00\x01'  # 设备地址为1, 功能码为3, 请求读取寄存器地址为0x0000, 长度为1
crc_value = crc16(data)
print(f"CRC Value: {crc_value:04X}")

数据包的构造与解析

在实际的Modbus通信中,发送方需要将计算得到的CRC校验码附加到数据包末尾,并发送到网络中。接收方需要从接收到的数据包中提取出CRC校验码,并验证整个数据包的完整性。

CRC校验的优势与局限

优势

  1. 高效性:CRC校验的计算效率高,适用于实时数据传输环境。
  2. 检错能力强:CRC校验对常见的错误如位错误有很高的检测率。

局限

1. 非修复性:CRC校验只能用于错误检测,无法修复错误。
2. 有限的保护:在极端情况下,如数据遭到复杂篡改,CRC校验可能会失效。

在某些极端情况下,如果数据被有意识地、复杂地篡改,并且篡改者也计算出新的CRC校验码来替换原始的CRC校验码,那么接收方在检验数据时可能无法察觉到这种篡改。因为从技术上讲,新的数据和其对应的CRC校验码仍然是匹配的,CRC校验会认为数据是没有错误的。

CRC校验主要是用来检测数据在传输或存储过程中由于错误而产生的无意的改动,如噪声干扰所导致的位错误。它并不是设计来防御有意的数据篡改,这类攻击通常需要更复杂的安全措施,如使用加密和数字签名等方法来保证数据的完整性和真实性。

3. CRC校验码并不是与数据唯一对应的

CRC校验码并不是与数据唯一对应的。虽然CRC校验码是根据数据内容计算得出的,使得它在很大程度上反映了原始数据的特征,但它不是一个完全唯一的指纹。理论上,不同的数据可以产生相同的CRC校验码,这种情况称为“碰撞”。

CRC校验码的长度通常较短(如16位或32位),这意味着只有有限的可能的CRC值。因此,随着处理的数据量增加,不同数据产生相同CRC校验码的可能性也随之增加。尽管如此,对于大多数实际应用来说,CRC提供了足够的错误检测能力,特别是在偶发错误或非有意篡改的环境中。

如果应用对冲突的敏感度非常高,可能需要考虑使用更复杂的校验算法或结合其他形式的校验和安全措施来减少冲突的可能性和提高数据的安全性。

结论

CRC校验在Modbus通信中扮演着至关重要的角色,通过提供一种高效且可靠的错误检测机制,它帮助确保了工业通信的稳定性和数据的完整性。尽管CRC校验有其局限性,但在多数工业应用场景中,它仍然是一种非常有效的解决方案。

20240718 注意:CRC16先发低位再发高位

在Modbus协议中,对于多字节数据字段(如地址、寄存器值、CRC等),通常遵循大端字节序(Big-Endian)的规则,即先发送数据的高位字节,然后是低位字节。这意味着在发送例如16位的地址或值时,首先应发送高位(更高的字节),然后发送低位(较低的字节)。

然而,对于CRC16,Modbus标准特别规定要先发送低字节(LSB,最低有效字节),然后发送高字节(MSB,最高有效字节)。这种对CRC的特殊处理与数据字段的处理方式不同。

在你的代码中,寄存器地址、寄存器个数等字段,如果是16位的,则应该遵守先高位后低位的顺序发送。我看到你在构建请求数据包时,对于这些字段已经正确地按照大端序进行了处理:

tbuf[index++] = 0x04; // 寄存器地址高位
tbuf[index++] = 0x0E; // 寄存器地址低位
tbuf[index++] = 0x00; // 寄存器个数高位
tbuf[index++] = 0x01; // 寄存器个数低位

确保这些字段遵循正确的字节顺序是很重要的,这有助于确保符合Modbus协议的正确性和互操作性。对于CRC16,按照标准的要求,我们已经正确地设置为先发送低位再发送高位。如果有特殊的硬件或软件要求,需要检查具体实现或文档,以确认是否有特定的字节序要求。

以下是我的完整数据示例:

uint8_t index = 0;
uint8_t tbuf[128]; // 局部请求缓冲区

// 构建请求数据包
tbuf[index++] = ClientInfor.devaddr; // 地址(设备地址)
tbuf[index++] = 0x10;                // 功能码(对于此声光报警器控制板:把设置的数值写入指定的连续寄存器)
tbuf[index++] = 0x04;                // 寄存器地址(高位)
tbuf[index++] = 0x0E;                // 寄存器地址(低位)
tbuf[index++] = 0x00;                // 寄存器个数(高位)
tbuf[index++] = 0x05;                // 寄存器个数(低位)
tbuf[index++] = 0x0A;                // 寄存器字节数(一个寄存器——高位+低位,有四个16进制数,等于两个字节)
tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器状态
tbuf[index++] = 0x03;                // 修改数据(低位)
tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器音量
tbuf[index++] = 0x1E;                // 修改数据(低位)
tbuf[index++] = 0x01;                // 修改数据(高位)—— 报警器音调
tbuf[index++] = 0x01;                // 修改数据(低位)
tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器播放模式
tbuf[index++] = 0x01;                // 修改数据(低位)
tbuf[index++] = 0x01;                // 修改数据(高位)—— 警示灯闪烁模式(调试发现只支持默认模式,即 0x01 0x01)
tbuf[index++] = 0x01;                // 修改数据(低位)

// Calculate CRC16,注意CRC16先发低位再发高位
uint16_t crc = Calculate_CRC16(tbuf, index);
tbuf[index++] = crc & 0xFF;        // CRC16(低位)
tbuf[index++] = (crc >> 8) & 0xFF; // CRC16(高位)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dontla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值