当前位置: 首页 > ops >正文

Modbus 报文结构与 CRC 校验实战指南(一)

一、引言

**

在工业通信领域,Modbus 协议无疑是最为广泛应用的协议之一。自 1979 年由 Modicon 公司(现属施耐德电气)推出以来,Modbus 凭借其简单易用、开放性以及广泛的兼容性,迅速成为工业自动化系统中设备之间通信的事实标准 。无论是在制造业、能源行业,还是楼宇自动化等场景中,Modbus 都扮演着关键角色,实现了不同设备之间的数据交互与协同工作。

Modbus 报文结构是理解 Modbus 通信的基础,它定义了数据在设备之间传输的格式和规则。通过深入掌握报文结构,开发者能够精确地解析和构建通信数据,确保设备之间的准确通信。而 CRC 校验则是保障 Modbus 通信可靠性的重要机制,它能够有效检测数据在传输过程中是否发生错误,从而避免因数据错误导致的系统故障。

对于从事工业自动化开发、设备维护以及系统集成的开发者而言,深入理解 Modbus 报文结构与 CRC 校验不仅是必备的技能,更是解决实际问题、优化系统性能的关键。在实际项目中,准确解析报文可以帮助快速定位设备状态和故障信息,而可靠的 CRC 校验则能大大提高系统的稳定性和可靠性,减少因通信错误带来的损失。接下来,让我们一起深入探索 Modbus 报文结构与 CRC 校验的奥秘。

二、Modbus 报文结构探秘

2.1 Modbus 协议概述

Modbus 协议诞生于 1979 年,由 Modicon 公司(现属施耐德电气)推出 ,旨在解决工业自动化领域中设备之间通信不兼容的问题。在当时,不同厂家生产的设备各自采用独特的通信方式,导致设备之间难以互联互通,严重制约了工业自动化系统的集成与扩展。Modbus 协议的出现,犹如一场及时雨,为工业设备提供了一种统一、标准化的通信方式,使得不同品牌、不同类型的设备能够顺畅地进行数据交互。

随着时间的推移,Modbus 协议凭借其简单易用、开放性强以及广泛的兼容性,迅速在工业自动化领域中占据了重要地位。它不仅实现了设备之间的无缝通信,还促进了工业系统的集成化和智能化发展。如今,Modbus 协议已经成为工业自动化领域中最为流行的通信协议之一,被广泛应用于制造业、能源行业、楼宇自动化、交通运输等众多领域。

在制造业中,Modbus 协议可实现生产线中各种设备,如 PLC、传感器、执行器等之间的数据传输与协同工作,从而提高生产效率和产品质量;在能源行业,它能够对发电设备、电网设备等进行实时监控与管理,确保能源的稳定供应;在楼宇自动化领域,Modbus 协议可实现对空调、照明、电梯等设备的集中控制,提高楼宇的智能化管理水平。

2.2 Modbus 报文的组成部分

一个完整的 Modbus 报文通常由设备地址、功能码、数据和校验码这几个关键部分组成,每一部分都有着独特的含义和重要作用。

  • 设备地址:设备地址用于标识 Modbus 网络中的从设备,它占 1 个字节,取值范围是 0 - 255 。其中,0 通常被保留用于广播通信,即主设备向所有从设备发送消息;1 - 247 为有效的从设备地址,不同的从设备通过唯一的地址进行区分;248 - 255 则被保留用于特殊用途。例如,在一个工业自动化系统中,有多个传感器和执行器作为从设备连接到主设备上,每个传感器和执行器都被分配了一个唯一的设备地址,主设备通过这个地址来准确地与特定的从设备进行通信,实现数据的读取和控制指令的发送。
  • 功能码:功能码占 1 个字节,取值范围是 1 - 255,用于指示主设备请求从设备执行的操作类型 。不同的功能码对应着不同的操作,常见的功能码包括读取线圈状态(功能码 01)、读取离散输入状态(功能码 02)、读取保持寄存器(功能码 03)、读取输入寄存器(功能码 04)、写单个线圈(功能码 05)、写单个保持寄存器(功能码 06)等。例如,当主设备需要读取某个传感器的测量值时,会向该传感器对应的从设备发送功能码为 03 的报文,以请求读取其保持寄存器中的数据。
  • 数据:数据部分的内容和长度取决于功能码的类型 。它包含了主设备请求从设备读取或写入的具体信息。例如,在读取保持寄存器的操作中,数据部分会包含要读取的寄存器起始地址和寄存器数量;在写单个保持寄存器的操作中,数据部分则会包含要写入的寄存器地址和数据值。以读取从设备中地址为 0x0001 开始的 10 个保持寄存器的数据为例,数据部分就会包含起始地址 0x0001 和寄存器数量 0x000A(十进制 10)。
  • 校验码:校验码用于检测数据在传输过程中是否发生错误,以确保数据的完整性和准确性 。Modbus 协议常用的校验方式有 CRC(循环冗余校验)和 LRC(纵向冗余校验),其中 CRC 校验更为常用。CRC 校验码通常占 2 个字节,它是根据报文中的设备地址、功能码和数据等部分通过特定的 CRC 算法计算得出的。接收方在收到报文后,会重新计算 CRC 校验码,并与报文中的校验码进行比对,如果两者一致,则认为数据传输正确;否则,说明数据可能发生了错误,需要重新传输。

2.3 不同功能码的报文结构解析

Modbus 协议定义了多种功能码,每种功能码对应的报文结构都有其特定的规则。下面以读取线圈状态(功能码 01)和读取保持寄存器(功能码 03)这两种常见的功能码为例,详细分析主站询问报文和从站响应报文的结构。

读取线圈状态(功能码 01)
  • 主站询问报文结构:[设备地址(1 字节)][功能码 01(1 字节)][起始地址(2 字节)][线圈数量(2 字节)][CRC 校验码(2 字节)]。例如,主站向地址为 0x01 的从设备发送询问报文,请求读取从地址 0x0000 开始的 10 个线圈的状态,其报文内容可能为:0x01 0x01 0x00 0x00 0x00 0x0A [CRC 校验码]。其中,0x01 为设备地址,0x01 表示读取线圈状态的功能码,0x00 0x00 是起始地址,0x00 0x0A 表示要读取的线圈数量为 10 个,最后的 [CRC 校验码] 是根据前面的内容计算得出的。
  • 从站响应报文结构:[设备地址(1 字节)][功能码 01(1 字节)][字节数(1 字节)][数据(n 字节)][CRC 校验码(2 字节)] 。从站在接收到主站的询问报文后,会返回响应报文。例如,从站地址为 0x01,功能码为 0x01,返回的字节数为 0x02(表示后面的数据占 2 个字节),数据为 0x30 0x00(假设这 10 个线圈的状态对应的二进制数据为 0011 0000 0000 0000),CRC 校验码为 [具体校验码值],则响应报文为:0x01 0x01 0x02 0x30 0x00 [CRC 校验码]。这里的数据部分,每个线圈的状态用 1 位二进制表示,8 个线圈的状态组成 1 个字节,不足 8 位的高位补 0。
读取保持寄存器(功能码 03)
  • 主站询问报文结构:[设备地址(1 字节)][功能码 03(1 字节)][起始地址(2 字节)][寄存器数量(2 字节)][CRC 校验码(2 字节)] 。例如,主站向地址为 0x02 的从设备发送询问报文,请求读取从地址 0x0001 开始的 5 个保持寄存器的值,其报文内容可能为:0x02 0x03 0x00 0x01 0x00 0x05 [CRC 校验码]。其中,0x02 为设备地址,0x03 表示读取保持寄存器的功能码,0x00 0x01 是起始地址,0x00 0x05 表示要读取的寄存器数量为 5 个。
  • 从站响应报文结构:[设备地址(1 字节)][功能码 03(1 字节)][字节数(1 字节)][数据(n*2 字节)][CRC 校验码(2 字节)] 。由于每个保持寄存器的数据通常是 16 位(2 字节),所以从站响应报文中数据部分的长度为寄存器数量的两倍。例如,从站地址为 0x02,功能码为 0x03,返回的字节数为 0x0A(表示后面的数据占 10 个字节,即 5 个寄存器 * 2 字节 / 寄存器),数据为 0x12 0x34 0x56 0x78 0x9A 0xBC 0xDE 0xF0 0x11 0x22(假设这 5 个保持寄存器的值分别为 0x1234、0x5678、0x9ABC、0xDEF0、0x1122),CRC 校验码为 [具体校验码值],则响应报文为:0x02 0x03 0x0A 0x12 0x34 0x56 0x78 0x9A 0xBC 0xDE 0xF0 0x11 0x22 [CRC 校验码]。

三、CRC 校验深度剖析

3.1 CRC 校验的基本概念

CRC 校验,即循环冗余校验(Cyclic Redundancy Check) ,是数据通信领域中最常用的一种查错校验码。它通过对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。CRC 校验的目的在于检测数据在传输过程中是否发生错误,确保接收端接收到的数据与发送端发送的数据一致。

在数据传输过程中,由于受到噪声干扰、信号衰减等因素的影响,数据可能会发生错误。例如,在工业自动化系统中,传感器采集的数据需要通过通信网络传输到控制器,如果数据在传输过程中发生错误,控制器可能会做出错误的决策,导致设备故障或生产事故。因此,采用有效的校验方法来保证数据的准确性和完整性至关重要。

与其他常见的校验方法,如奇偶校验、海明校验相比,CRC 校验具有显著的优势。奇偶校验是一种简单的校验方法,它通过在数据中添加一个校验位,使得数据中 “1” 的个数为奇数(奇校验)或偶数(偶校验) 。接收端通过检查 “1” 的个数是否符合奇偶性来判断数据是否正确。然而,奇偶校验只能检测出奇数位的错误,对于偶数位的错误则无法检测。例如,当数据中的两位同时发生错误时,奇偶校验无法发现这些错误。

海明校验则是一种更为复杂的校验方法,它通过在数据中添加多个校验位,不仅能够检测出错误,还能够纠正一些错误 。海明校验的实现相对复杂,需要较多的校验位,增加了数据传输的开销。而且,海明校验的纠错能力也受到一定限制,对于多个错误同时发生的情况,可能无法正确纠错。

相比之下,CRC 校验能够检测出多种类型的错误,包括突发错误和多位错误 。它通过特定的多项式计算,生成一个校验码,该校验码与数据之间存在着紧密的关联。即使数据中发生了多位错误,CRC 校验也能够有效地检测出来。此外,CRC 校验的计算效率较高,实现相对简单,在数据通信和存储领域得到了广泛应用。

3.2 CRC 校验的工作原理

CRC 校验基于多项式除法的数学原理,它将数据视为一个多项式,并使用预设的生成多项式进行除法运算,最终余数作为 CRC 校验值。在 CRC 校验中,数据被表示为一个系数为 0 或 1 的多项式序列,例如,数据 1011 可以表示为多项式\(x^3 + x^1 + x^0\) 。生成多项式则是一个预先定义的多项式,它的选择对 CRC 算法的错误检测能力有重要影响。

以一个简单的例子来说明 CRC 码的生成和校验步骤。假设要发送的数据为 1010,生成多项式为\(G(X) = X^3 + X + 1\)(对应的二进制数为 1011) 。

  1. 数据左移:将数据 1010 左移 3 位(生成多项式的次数),得到 1010000。这是因为在 CRC 校验中,需要在数据后面添加与生成多项式次数相同数量的 0,以便进行多项式除法运算。
  1. 模 2 除法:用左移后的数据 1010000 除以生成多项式 1011,这里的除法采用模 2 除法,即每一位除的结果不影响其他位,不向上借位,相当于二进制中的逻辑异或运算。具体计算过程如下:
    • 首先,将 1010000 的前 4 位 1010 与 1011 进行异或运算,得到 0001。
    • 然后,将 0001 与下一位 0 组合成 00010,再与 1011 进行异或运算,得到 1001。
    • 接着,将 1001 与下一位 0 组合成 10010,继续与 1011 进行异或运算,得到 0011。
    • 最后,将 0011 与下一位 0 组合成 00110,再与 1011 进行异或运算,得到 110,这就是余数。
  1. 生成 CRC 码:将得到的余数 110 作为 CRC 码,附加在原始数据 1010 后面,得到完整的发送数据 1010110。

在接收端,对接收到的数据进行同样的 CRC 校验操作。如果计算得到的余数为 0,则说明数据在传输过程中没有发生错误;如果余数不为 0,则说明数据发生了错误,需要进行相应的处理,如请求重发数据。

3.3 CRC-16 算法在 Modbus 中的应用

在 Modbus 协议中,常用的 CRC 校验算法是 CRC-16,它生成 16 位的校验码,能够有效地检测出数据传输中的错误 。CRC-16 算法使用的多项式为\(X^16 + X^15 + X^2 + 1\),对应的十六进制数为 0x8005,初始值为 0xFFFF 。

下面通过一个具体的代码示例,展示 CRC-16 算法在 Modbus 中的实现过程。以 C 语言代码为例:

 

#include <stdio.h>

// 计算CRC-16校验码

unsigned short crc16_modbus(unsigned char *data, int length) {

unsigned short crc = 0xFFFF;

int i, j;

for (i = 0; i < length; i++) {

crc ^= (unsigned short)data[i];

for (j = 0; j < 8; j++) {

if (crc & 0x0001) {

crc >>= 1;

crc ^= 0xA001;

} else {

crc >>= 1;

}

}

}

return crc;

}

int main() {

unsigned char data[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01}; // 示例数据

int length = sizeof(data) / sizeof(data[0]);

unsigned short crc = crc16_modbus(data, length);

printf("CRC-16校验码: 0x%04X\n", crc);

return 0;

}

在上述代码中,crc16_modbus函数实现了 CRC-16 算法。首先,将 CRC 寄存器初始化为 0xFFFF 。然后,遍历数据数组,对每个字节与 CRC 寄存器进行异或操作。接着,对每个字节的 8 位进行处理,如果 CRC 寄存器的最低位为 1,则右移一位并与 0xA001 进行异或;否则,仅右移一位。最后,返回计算得到的 CRC 校验码。

在main函数中,定义了一个示例数据数组,并调用crc16_modbus函数计算 CRC 校验码,然后输出结果。通过这个示例,可以清晰地看到 CRC-16 算法在 Modbus 中的具体实现过程。

http://www.xdnf.cn/news/14715.html

相关文章:

  • leetcode437-路径总和III
  • TVFEMD-CPO-TCN-BiLSTM多输入单输出模型
  • ASP.Net依赖注入!使用Microsoft.Extensions.DependencyInjection配置依赖注入
  • 【ad-hoc】# P12414 「YLLOI-R1-T3」一路向北|普及+
  • MyBatis批量删除
  • 现代 JavaScript (ES6+) 入门到实战(一):告别 var!拥抱 let 与 const,彻底搞懂作用域
  • 数据结构笔记4:数组、链表OJ
  • 华为云 Flexus+DeepSeek 征文|华为云 Flexus 云服务 Dify-LLM 平台深度部署指南:从基础搭建到高可用实践
  • 疏通经脉: Bridge 联通逻辑层和渲染层
  • 使用component封装组件和h函数的用法
  • 数据结构之Map和Set
  • 打造地基: App拉起基础小程序容器
  • linux面试常考
  • 正交视图三维重建 笔记 2d线到3d线
  • 使用deepseek制作“喝什么奶茶”随机抽签小网页
  • Jina-Embeddings-V4:多模态向量模型的革命性突破与实战指南
  • Python生成器表达式最佳实践指南:何时使用与高效选择
  • Flutter基础(控制器)
  • Python基础(吃洋葱小游戏)
  • LINUX628 NFS 多web;主从dns;ntp;samba
  • WOE值:风险建模中的“证据权重”量化术——从似然比理论到FICO评分卡实践
  • SpringMVC系列(五)(响应实验以及Restful架构风格(上))
  • H6-108QB2W QILSTE/旗光
  • WebRTC(十二):DTLS
  • Cesium快速入门到精通系列教程十一:Cesium1.74中高性能渲染上万Polyline
  • 2025第十五届上海生物发酵展:江苏健达干燥盛装赴会
  • 数据结构:最小生成树—Prim(普里姆)与Kruskal(克鲁斯卡尔)算法
  • 使用asyncio构建高性能网络爬虫
  • Linux离线搭建Redis (centos7)详细操作步骤
  • Python助力自动驾驶:深度学习模型优化全攻略