Modbus RTU 入门:报文、CRC 与寄存器数值
通过请求与响应示例,了解 Modbus RTU 报文结构、CRC-16、寄存器地址、字节序和常见通信问题。
Modbus RTU 入门:报文、CRC 与寄存器数值
Modbus RTU 通过串行链路传输二进制请求与响应。典型报文包含设备地址、功能码、数据和 CRC-16 校验值。
本教程从这些字段入手,说明寄存器地址和字节序,并介绍如何检查捕获的报文。请配合设备寄存器表使用示例:支持的功能码、缩放关系和串口设置取决于具体设备。
0. 先做一次寄存器读取与解析
没有设备也可以先打开 Modbus RTU 工具,生成和解析下面的示例。选择 FC03、从站地址 1、起始地址 0、数量 2,可得到读取两个保持寄存器的请求:
01 03 00 00 00 02 C4 0B
这里的地址 0 是报文中的偏移地址。如果设备手册采用以 40001 为起点的保持寄存器引用编号,那么 40001 对应偏移 0;先确认手册的编号规则,不能把所有设备地址都直接减去 40001。
假设这两个寄存器按高字在前存储 FLOAT32 数值 1,示例响应为:
01 03 04 3F 80 00 00 F7 CF
01 是从站地址,03 是功能码,04 表示后面有四个数据字节,3F 80 00 00 是寄存器数据,F7 CF 是 CRC。将整帧粘贴到 RTU 工具的解析输入,检查帧结构与 CRC;再 打开这四个字节的 FLOAT32 转换,确认结果为 1。这只是可复算的教学响应,实际设备数值和布局按手册核对。
需要读取自己的设备时,打开 串口调试,按下面顺序操作:
- 在支持 Web Serial 的桌面浏览器中选择串口,授权连接自己的适配器。
- 按设备手册设置波特率、数据位、校验位和停止位;确认从站地址与所读寄存器确实存在。
- 先使用单次读取。若手动发送上面的完整报文,先关闭 Modbus 模式,再选择 HEX 发送,不附加文本换行;修改地址或数量后,先重新生成报文和 CRC。
- 收到响应后核对地址、功能码、字节数和 CRC,再按手册解释数据类型、字序、倍率和单位。没有响应先查串口参数、地址、端口占用和接线,不要把写入命令当作连通性测试。
端口打不开、没有响应或数值不对?查看 RTU 排错步骤。
协议中的 FC03 读取、寄存器偏移和响应格式见 Modbus 应用协议第 6.3 节;RTU 的 CRC 低字节先发送,见 串行链路规范第 2.5.1 节。要处理浮点数、寄存器交换或小数误差,接着看 浮点数与十六进制转换指南。
1. 什么是 Modbus RTU?
Modbus RTU 是 Modbus 协议家族中的一种传输模式,专注于在串行通信(Serial Communication)线路上通过二进制数据格式进行高效的数据交换。它遵循主从(Master-Slave)通信模型:通常有一个主站(Master)设备(如PC或PLC)发起请求,一个或多个从站(Slave)设备(如传感器或控制器)响应该请求。
Modbus RTU 的核心优势在于:
- 简单可靠:协议结构极为精炼,易于理解和在嵌入式系统中实现,出错率低。
- 开放免费:Modbus 协议是完全开放且免授权费的,极大促进了其在工业领域的普及。
- 广泛支持:多种工业产品提供 Modbus RTU 接口;互联时仍需逐项核对寄存器表、功能码和串口设置。
- 高效传输:采用紧凑的二进制格式传输数据,相比 Modbus ASCII 模式,具有更高的传输效率。
2. 物理层:Modbus RTU 的载体
Modbus RTU 协议本身只定义了数据报文的结构,而底层的物理连接则依赖于串行通信标准。最常见的物理层标准是:
2.1 RS-485
RS-485 是 Modbus RTU 在工业应用中最常见的选择,其优势显著:
- 多点通信:支持半双工多点通信,一个主站可以连接多达32个从站(通过中继器可扩展更多)。
- 传输距离远:可用距离取决于线缆、速率、终端和环境,不能把 1200 米作为所有安装条件下的保证。
- 抗干扰能力强:采用差分信号传输,对共模噪声具有良好的抑制能力,适用于工业恶劣环境。
- 布线简单:通常采用两根双绞线(A线和B线)进行连接。
2.2 RS-232
RS-232 是一种较老的串行通信标准,Modbus RTU 也可以在其上运行:
- 点对点通信:仅支持全双工点对点通信,即一个主站只能连接一个从站。
- 传输距离短:传输距离通常限制在15米以内。
- 抗干扰能力弱:采用非差分信号传输,易受噪声干扰。
- 用途:常用于设备与本地计算机的短距离直接连接或调试。
2.3 Modbus RTU over TCP/IP(网络透传)
随着工业物联网的发展,越来越多的厂商开始将传统的 Modbus RTU 协议通过 TCP/IP 网络进行传输,这种方式被称为 "Modbus RTU over TCP" 或 "Modbus RTU 网络透传"。这种实现方式在工业现场非常常见,主要有两种应用场景:
2.3.1 设备直接支持网络功能
一些现代化的工业设备(如智能传感器、变频器、PLC等)直接集成了以太网接口,能够将原本的 Modbus RTU 数据帧封装在 TCP/IP 数据包中进行传输:
- 优势:设备直接连接到企业网络,无需额外的转换设备
- 传输距离:几乎不受限制,可以通过互联网进行远程通信
- 多主站支持:TCP/IP 的特性使得多个主站可以同时访问同一个从站设备
- 应用场景:远程监控、云端数据采集、分布式控制系统
2.3.2 串口转网络设备(透传模块)
对于不具备网络功能的传统 Modbus RTU 设备,可以通过专门的串口转网络设备(如串口服务器、DTU、网关等)实现网络化:
- 工作原理:透传设备一端连接传统的 RS-485/RS-232 串口设备,另一端连接到以太网,将串口数据透明传输到网络
- 设备类型:
- 串口服务器:专业的工业级设备,支持多串口、协议转换等功能
- DTU(数据传输单元):通常支持4G/WiFi等无线网络,适用于远程或移动场景
- 工业网关:集成多种协议转换功能,支持 Modbus RTU/TCP、OPC UA 等
- 配置要求:需要配置设备的 IP 地址、端口号、串口参数(波特率、数据位等)
- 应用优势:
- 成本效益:无需更换现有设备,只需添加转换设备
- 灵活部署:可以将现场设备接入企业网络或云平台
- 集中管理:通过网络实现设备的集中监控和管理
2.3.3 技术特点与注意事项
数据帧格式:
- Modbus RTU over TCP 保持了原有的 RTU 数据帧格式(包括 CRC 校验)
- 数据帧被完整地封装在 TCP 数据包中,不进行任何修改
- 与标准的 Modbus TCP 协议不同,Modbus TCP 使用 MBAP 头部替代了 CRC 校验
时序要求:
- 网络传输中不存在传统串口的 T3.5 帧间静默时间概念
- 透传设备通常会处理帧的分割和重组
- 需要注意网络延迟对通信时序的影响
实际应用建议:
- 选择合适的透传设备:根据现场环境选择有线或无线、单串口或多串口设备
- 网络规划:合理规划 IP 地址和端口,避免冲突
- 安全考虑:在通过互联网传输时,建议使用 VPN 或其他加密手段保护数据安全
- 兼容性测试:在正式部署前,充分测试主站软件与透传设备的兼容性
3. Modbus 数据模型:理解数据存储结构
在深入了解协议结构之前,理解 Modbus 如何组织和存储设备数据是至关重要的。Modbus 协议将设备内部的数据抽象为四个逻辑上独立的、地址空间互不重叠的数据表。每个表存储不同类型的数据,并具有特定的访问权限。
| 数据表 | 类型 | 访问权限 | Modbus协议地址范围 | PLC地址范围 | 典型用途 |
|---|---|---|---|---|---|
| 线圈 (Coils) | 单个位 (Bit) | 读/写 | 0x0000-0xFFFF | 00001-09999 | 控制继电器、开关、指示灯、电机启停等布尔量输出 |
| 离散量输入 (Discrete Inputs) | 单个位 (Bit) | 只读 | 0x0000-0xFFFF | 10001-19999 | 读取数字输入信号、传感器状态(门开/关)、按钮状态等 |
| 保持寄存器 (Holding Registers) | 16位字 (Word) | 读/写 | 0x0000-0xFFFF | 40001-49999 | 存储配置参数、设定值、控制变量、模拟量输出、PLC内部变量 |
| 输入寄存器 (Input Registers) | 16位字 (Word) | 只读 | 0x0000-0xFFFF | 30001-39999 | 读取测量值(温度、压力)、设备状态、传感器数据、模拟量输入 |
重要提示:
- PLC地址是工业自动化领域常用的地址表示方法(基于1的编址),而Modbus协议地址是通信时实际使用的地址(基于0的编址)。两者的转换关系为:Modbus协议地址 = PLC地址 - 基础地址(如40001、30001等)。 例如,参考地址
40003对应协议中的偏移地址0x0002=40003-40001。 - 线圈和离散量输入是位(Bit)操作,而保持寄存器和输入寄存器是字(Word,即16位)操作。
- 每个表的地址空间是独立的,即
0x0001的线圈与0x0001的保持寄存器是完全不同的概念。
4. 协议基础结构:Modbus RTU 数据帧
一个标准的 Modbus RTU 数据帧(或称报文)是主站与从站之间进行通信的基本单位。它包含以下几个关键部分:
4.1 数据帧格式概览
[从站地址] [功能码] [数据字段] [CRC校验]
1字节 1字节 N字节 2字节
4.1.1 从站地址(Slave Address)- 1个字节
- 作用:用于唯一标识网络上的目标从站设备。
- 范围:通常为 1-247。
0是广播地址,发送给所有从站,但从站不响应。248-255为保留地址。
- 要求:每个从站设备在 Modbus 网络中必须配置一个唯一的地址。
4.1.2 功能码(Function Code)- 1个字节
- 作用:定义了主站希望从站执行的操作类型(如读取数据、写入数据)。
- 最常用功能码列表:
| 功能码 (十进制/十六进制) | 名称 | 数据类型 | 访问权限 | 说明 |
|---|---|---|---|---|
| 01 (0x01) | 读取线圈状态 | Bit | 读 | 读取一个或多个输出线圈(Coils)的开/关状态 |
| 02 (0x02) | 读取离散量输入状态 | Bit | 读 | 读取一个或多个离散量输入(Discrete Inputs)的开/关状态 |
| 03 (0x03) | 读取保持寄存器 | Word | 读 | 读取一个或多个保持寄存器(Holding Registers)的值 |
| 04 (0x04) | 读取输入寄存器 | Word | 读 | 读取一个或多个输入寄存器(Input Registers)的值 |
| 05 (0x05) | 强制单个线圈 | Bit | 写 | 设置单个输出线圈的状态(开/关) |
| 06 (0x06) | 预置单个寄存器 | Word | 写 | 设置单个保持寄存器的值 |
| 15 (0x0F) | 强制多个线圈 | Bit | 写 | 设置多个输出线圈的状态 |
| 16 (0x10) | 预置多个寄存器 | Word | 写 | 设置多个保持寄存器的值 |
4.1.3 数据字段(Data Field)- 变长
- 作用:包含执行特定功能所需的具体信息,如起始地址、读取或写入的数量、要写入的值等。
- 长度:根据功能码的不同而变化。
- 读取请求:通常包含起始地址和数量。
- 写入请求:通常包含起始地址和要写入的值。
4.1.4 CRC校验(Cyclic Redundancy Check)- 2个字节
-
作用:提供数据的完整性校验,用于检测在传输过程中是否发生错误。
-
算法:Modbus RTU 使用 CRC-16 算法。
- 发送方计算从从站地址到数据字段末尾的所有字节的 CRC 值,并将其附加在帧的末尾(低字节在前,高字节在后)。
- 接收方接收到数据帧后,以同样的方式对接收到的数据(不含接收到的 CRC)进行 CRC 计算。
- 如果计算结果与接收到的 CRC 值不匹配,则认为数据传输有误,从站通常会丢弃该帧不予处理或返回错误。
-
示例代码
WORD CRC16 (const BYTE *data, WORD length)
{
static const WORD table[] = {
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
BYTE temp;
WORD word = 0xFFFF;
while (length--)
{
temp = *data++ ^ word;
word >>= 8;
word ^= table[temp];
}
return word;
}
4.1.5 帧间静默时间(T3.5)
- 重要性:这是 Modbus RTU 协议中一个非常关键的特性,用于判断一帧数据的开始和结束。
- 定义:在 Modbus RTU 模式下,一个数据帧的发送必须以至少 3.5 个字符传输时间的静默间隔作为结束标志。当接收方检测到连续的 3.5 个字符的静默时间后,就会认为前一帧数据已经完整接收。同样,一个新帧的开始也必须以 3.5 个字符的静默时间作为前导。
- 作用:
- 帧同步:确保接收方能够准确地识别一帧的边界,避免粘包或断帧问题。
- 错误检测:如果两个字符之间的静默时间超过 1.5 个字符但小于 3.5 个字符,则认为这是一个传输错误(可能表示帧被截断)。
- 计算:标准 RTU 每字符为 11 比特,
T3.5 = 3.5 * 11 / 波特率秒,9600 baud 时约为 4.01 ms。超过 19200 baud 后,串行规范建议使用固定的 T1.5 = 750 μs、T3.5 = 1.75 ms。设备特有的串口格式须另行核对。
5. 异常响应:错误处理机制
当主站向从站发送一个有效但从站无法处理的请求时(例如,请求读取一个不存在的寄存器地址,或者数据值超出范围),从站不会返回正常响应,而是会返回一个异常响应帧。
5.1 异常响应帧的格式
[从站地址] [功能码+0x80] [异常码] [CRC校验]
1字节 1字节 1字节 2字节
- 功能码+0x80:异常响应的标志。从站会将原始请求功能码的最高位设置为 1(即原始功能码与
0x80进行按位或操作),以此告诉主站这是一个异常响应。例如,如果请求的功能码是0x03,异常响应的功能码将是0x83。 - 异常码:一个字节,指示错误的具体类型。
5.2 常见异常码及其含义
| 异常码 (十六进制) | 名称 | 说明 |
|---|---|---|
| 01 (0x01) | 非法功能码 (Illegal Function) | 从站不支持该功能码。 |
| 02 (0x02) | 非法数据地址 (Illegal Data Address) | 请求的数据地址(寄存器或线圈地址)在从站中不存在或超出其支持范围。 |
| 03 (0x03) | 非法数据值 (Illegal Data Value) | 请求中包含的数据值对于从站无效(例如,试图写入负数到无符号寄存器,或写入值超出设备限制)。 |
| 04 (0x04) | 从站设备故障 (Slave Device Failure) | 从站在尝试执行操作时发生不可恢复的内部错误。 |
| 05 (0x05) | 确认 (Acknowledge) | 接收请求但需要长时间处理,通常用于复杂操作。 |
| 06 (0x06) | 从站忙 (Slave Device Busy) | 从站正忙于处理其他任务,无法响应请求。 |
| 0A (0x0A) | 网关路径不可用 (Gateway Path Unavailable) | Modbus TCP/IP 网关无法路由请求到物理串行端口。 |
| 0B (0x0B) | 网关目标设备无响应 (Gateway Target Device Failed to Respond) | Modbus TCP/IP 网关未能从目标从站接收到响应。 |
6. 实际应用示例:深入理解通信流程
通过具体的命令和响应示例,我们可以更好地理解 Modbus RTU 的工作方式。
6.1 示例1:读取温度传感器数据(功能码 0x03 - 读取保持寄存器)
假设我们有一个地址为 1 的温度传感器,其当前温度值存储在保持寄存器(Holding Register)的地址 40001 (Modbus协议偏移地址 0x0000)。我们要读取这一个寄存器的值。
发送命令(主站 -> 从站):
01 03 00 00 00 01 84 0A
命令解析:
01:从站地址为 1。03:功能码为 3 (0x03),表示读取保持寄存器。00 00:起始寄存器地址为 0x0000 (对应 Modbus 参考地址 40001)。00 01:请求读取 1 个寄存器。84 0A:CRC-16 校验码。
设备响应(从站 -> 主站):
01 03 02 00 64 B9 30
响应解析:
01:从站地址为 1。03:功能码为 3 (0x03),正常响应。02:数据字节数,表示接下来的数据有 2 个字节(因为读取了 1 个 16 位寄存器,即 2 个字节)。00 64:实际读取到的数据。0x0064转换为十进制是100。B9 30:CRC-16 校验码。
结果:主站成功读取到温度值为 100 (可能是摄氏度、华氏度或带小数点的整数,具体取决于设备约定)。
6.2 示例2:设置输出线圈状态(功能码 0x05 - 强制单个线圈)
要将地址为 2 的设备的输出线圈(Coil)地址 00001 (Modbus协议偏移地址 0x0000) 设置为开启状态。
发送命令(主站 -> 从站):
02 05 00 00 FF 00 8C 3A
命令解析:
02:从站地址为 2。05:功能码为 5 (0x05),表示强制单个线圈。00 00:线圈地址为 0x0000 (对应 Modbus 参考地址 00001)。FF 00:写入的值。0xFF00表示将线圈设置为 ON(开启),0x0000表示设置为 OFF(关闭)。8C 3A:CRC-16 校验码。
设备响应(从站 -> 主站):
02 05 00 00 FF 00 8C 3A
响应解析:
- 对于功能码 0x05,从站的正常响应是将其接收到的请求帧原样返回给主站,以确认操作已成功执行。
02:从站地址为 2。05:功能码为 5。00 00:线圈地址为 0x0000。FF 00:确认线圈被设置为 ON。8C 3A:CRC-16 校验码。
结果:从站的线圈 00001 被成功设置为开启状态。
7. 数据类型和字节序:处理复杂数据
Modbus RTU 以 16 位(2 字节)寄存器作为其基本数据存储单元。然而,实际工业应用中常常需要传输更复杂的数据类型,如 32 位整数或浮点数。这就引入了数据类型转换和字节序(Byte Order)的问题。
7.1 常见数据类型在 Modbus 中的表示
| 数据类型 | 长度 (位) | 占用寄存器数 | 描述 |
|---|---|---|---|
| UINT16 | 16 | 1 | 16位无符号整数 (0 到 65535) |
| INT16 | 16 | 1 | 16位有符号整数 (-32768 到 32767) |
| UINT32 | 32 | 2 | 32位无符号整数 (0 到 4,294,967,295) |
| INT32 | 32 | 2 | 32位有符号整数 (-2,147,483,648 到 2,147,483,647) |
| FLOAT32 | 32 | 2 | 32位单精度浮点数 (遵循 IEEE 754 标准) |
| String | 8n | n | 多个寄存器表示字符串,每个寄存器存储两个ASCII字符 |
7.3 字节序(Byte Order)和字序(Word Order)问题
当一个数据类型需要占用两个或更多寄存器时(如 32 位整数、浮点数),这些寄存器在 Modbus 协议中的排列顺序以及每个寄存器内部的字节顺序就变得至关重要。不同的设备厂商可能采用不同的字节序和字序,这通常是导致通信数据解析错误的主要原因。
假设我们有一个 32 位的值 0x12345678,它占用两个 16 位寄存器:
- 寄存器 A 存储
0x1234 - 寄存器 B 存储
0x5678
以下是几种常见的字节序和字序组合:
-
ABCD (大端序 / Big-Endian):
- 字序:高位字在前 (寄存器 A, 寄存器 B) ->
0x12340x5678 - 字节序:每个字内部高字节在前 ->
12 34 56 78 - 常见于:Modicon、Siemens、旧款 Rockwell 等
- 存储顺序:高位字节 -> 低位字节 (从左到右)
- 字序:高位字在前 (寄存器 A, 寄存器 B) ->
-
DCBA (小端序 / Little-Endian):
- 字序:低位字在前 (寄存器 B, 寄存器 A) ->
0x56780x1234 - 字节序:每个字内部低字节在前 ->
78 56 34 12 - 常见于:Intel/PC 架构、一些日系PLC
- 存储顺序:低位字节 -> 高位字节 (从左到右)
- 字序:低位字在前 (寄存器 B, 寄存器 A) ->
-
BADC(每个 16 位字内交换字节):
- 字序:高位字在前 (寄存器 A, 寄存器 B) ->
0x12340x5678 - 字节序:每个字内部低字节在前 ->
34 12 78 56 - 存储顺序:低位字节 (字1) -> 高位字节 (字1) -> 低位字节 (字2) -> 高位字节 (字2)
- 字序:高位字在前 (寄存器 A, 寄存器 B) ->
-
CDAB(交换两个 16 位字):
- 字序:低位字在前 (寄存器 B, 寄存器 A) ->
0x56780x1234 - 字节序:每个字内部高字节在前 ->
56 78 12 34 - 存储顺序:高位字节 (字2) -> 低位字节 (字2) -> 高位字节 (字1) -> 低位字节 (字1)
- 字序:低位字在前 (寄存器 B, 寄存器 A) ->
解决方案:
- 查阅设备手册:始终优先查阅从站设备的通信手册,它会明确指出所使用的数据类型和字节序。
- 在线工具测试:当手册信息不明确时,使用 Modbus 调试工具尝试不同的字节序组合,直到数据解析正确。
- 程序逻辑处理:在上位机或主站程序中,根据从站的字节序要求进行相应的字节或字交换操作。
8. 使用在线工具实践Modbus RTU
使用 ModbusKit 工具 生成示例报文,并结合抓取内容和设备寄存器表核对:
-
- 根据从站地址、功能码、起始地址和数量等参数,自动生成符合 Modbus RTU 标准的请求命令帧(包括 CRC 校验)。
- 非常适合验证你手动构建的命令是否正确。
-
- 输入十六进制的 Modbus RTU 响应数据帧,工具将自动解析出从站地址、功能码、数据内容以及 CRC 校验结果。
- 支持多种数据类型(如 UINT16, INT16, UINT32, FLOAT32)和字节序(ABCD, DCBA, BADC, CDAB)的解析,帮助你验证从设备读取到的数据是否正确。
-
- 使用 RTU 页内的 CRC 区域计算报文校验值。CRC 可检测多类传输错误,但不能认证设备,也不能证明寄存器数据的业务含义正确。
-
- 在支持 Web Serial 的桌面浏览器中发送请求、查看串口响应;先选择端口、授权访问并匹配设备通信参数。
9. 常见问题和解决方案
先关闭循环发送,只读取设备手册明确支持的一组寄存器。将请求、响应和串口设置一起保留。没有收到字节、报文无效和设备返回异常,需要分别排查。
| 症状 | 优先检查 | 工具或下一步 |
|---|---|---|
| 没有可选端口或端口打不开 | 浏览器支持、授权、适配器和端口占用 | 串口检查 → 串口调试 |
| 已发送请求,没有收到字节 | 串口参数、从站地址、供电和接线 | 无响应检查 → 请求生成器 |
| CRC 或报文格式错误 | 是否为完整单帧、复制内容和 CRC 顺序 | 报文检查 → RTU 解析器 |
响应含有 83 02 | 请求的寄存器区、偏移和数量 | 异常检查 → 寄存器地址指南 |
| 正常响应有效,但数值不对 | 请求对应关系、类型、字序、倍率和单位 | 数值检查 → 数据转换 |
9.1 没有可选串口或端口打不开
- 在支持 Web Serial 的桌面浏览器(如 Chrome)中,通过 HTTPS 打开 串口调试。页面能打开不代表浏览器支持串口访问,要求见 MDN Web Serial 文档。
- 点击 选择串口,选中自己的适配器并授权。取消选择不会授予访问权限;端口选择需要由用户点击触发,见 Chrome Web Serial 指南。
- 列表中没有适配器时,先确认操作系统能识别其串口、驱动已安装。能看到但打不开时,关闭占用同一端口的串口软件或浏览器标签页,检查连接后重新选择。
- 按设备手册设置串口参数,再连接。端口打开失败发生在 Modbus 收发之前;MDN 单独列出了端口打开错误。
9.2 已发送请求,但没有响应
- 确认端口已连接,发送日志中的字节与预期一致。手动发送完整 RTU 帧时,关闭 Modbus 模式、开启 HEX 发送,不附加文本换行。修改请求后用 生成器 重新生成。
- 按设备设置逐项核对波特率、RTU 的八个数据位、校验位和停止位,再检查从站地址、供电和运行状态。使用设备的单播地址(
1–247);地址0用于广播,广播不会返回响应。 - 按设备和适配器手册检查接口类型、接线、信号公共端和终端电阻。不同厂商的 RS-485 A/B 标注可能不同,应核对端子定义;总线同一时刻只由一个主站发起请求。规范见 串行链路指南第 2.1–2.2 节及第 3 章。
- 单次读取手册中明确支持的寄存器范围,并记录收发内容。有字节但解析失败时,转到下面的报文检查。只有设备响应时间说明或抓取记录表明响应较晚时,才调整等待时间;延长等待不能修复地址或 CRC 错误。
写入后超时不能证明写入失败。先停止发送并确认设备状态,再判断是否需要再次写入;不要通过自动重试写入来测试连通性。
9.3 CRC 或报文格式错误
- 复制完整的一帧接收数据,去掉时间戳、
RX:等日志标签,分开保留请求和响应。浏览器一次读取的数据块不一定是完整 RTU 帧,先检查是否截断或拼接了多帧。 - 将含末尾两个 CRC 字节的整帧放入 RTU 解析器。若提示长度或结构错误,先修复报文内容;即使 CRC 相符,也不能据此认定各字段有效。
- 单独复算时,只把 CRC 之前的字节放入 CRC 计算器,将结果按低字节在前的顺序与帧尾比较。具体步骤复用 CRC 校验指南,不要给完整帧再追加一次校验值。
- 完整抓取仍反复校验失败时,再检查串口参数、接线、干扰和总线时序。每次只调整一个因素,并保证两端参数一致;不要使用损坏帧中的数值。
9.4 异常响应:83 02
地址为 1 的设备拒绝 FC03 请求的寄存器范围时,可能返回下面的完整示例帧:
01 83 02 C0 F1
| 字节 | 含义 |
|---|---|
01 | 返回响应的从站地址 |
83 | FC03 的异常响应:0x03 + 0x80 |
02 | 非法数据地址:请求的偏移与数量组合不在允许范围内 |
C0 F1 | 对 01 83 02 计算的 CRC,低字节先发送 |
将这五个字节放入 RTU 解析器,或在 CRC 计算器 中输入 01 83 02 复算。CRC 数值为 F1C0,追加顺序为 C0 F1。这里的 02 是异常码,不是寄存器数据字节数。异常格式与含义见 Modbus 应用协议第 6.3 节和第 7 章。
先确认地址和原始功能码与本次请求对应,再检查应使用 FC03 还是 FC04、手册采用引用编号还是偏移地址,以及整个读取范围是否越界,包括最后一个寄存器。按 地址指南 修正请求并重新生成 CRC。有效异常响应表示设备报告了请求问题,增加等待时间不能修复这个范围。这是可复算示例,不是你的设备实测记录。
9.5 正常响应有效,但数值不对
- 先确认帧结构、CRC、从站地址、功能码和预期字节数,再与保留的请求匹配:正常 FC03 响应不会重复起始寄存器地址。
- 按手册核对寄存器区、地址编号方式和寄存器数量。FLOAT32 通常占两个 16 位寄存器,只读一个只能得到一半数据。
- 只将寄存器数据字节放入 数据转换工具,确认有符号整数、无符号整数或 FLOAT32,再按手册选择字节序和字序。猜一种顺序得到“看起来合理”的数值,不算验证通过。
- 按手册应用倍率、偏移和单位,并与设备的已知数值对照。需要练习时使用已有的 浮点数与字节序指南 及其中九个示例,无需修改设备设置。
10. 进阶学习建议
掌握 Modbus RTU 仅仅是工业通信的开始。为了成为一名更全面的自动化工程师,建议你继续深入学习以下内容:
- 深入理解所有 Modbus 功能码:了解它们在不同应用场景下的精确用途和数据结构。
- 掌握异常响应的处理逻辑:在程序中编写健壮的异常处理机制,提高系统稳定性。
- 探索 Modbus TCP 协议:理解其与 Modbus RTU 的区别、帧结构以及在以太网环境中的应用。
- 了解 Modbus ASCII 模式:虽然不如 RTU 常用,但了解其文本格式的特点也是有益的。
- 学习工业通信网络拓扑:掌握 RS-485 总线的布线规范、终端电阻、偏置电阻等高级概念。
- 实践多设备通信管理:如何在 Modbus 网络中同时与多个从站进行通信,以及如何优化轮询周期。
- 编程实现 Modbus 主站/从站:尝试使用 Python、C++、Java 或 PLC 编程语言(如 Ladder Logic, Structured Text)自己编写 Modbus 通信程序。
11. 总结
Modbus RTU 作为工业自动化领域的“通用语”,以其简洁、高效和开放的特性,至今仍占据举足轻重的地位。通过本教程的系统学习,你应该已经:
- 清晰地理解了 Modbus RTU 的基本概念、主从通信模型和物理层基础。
- 掌握了 Modbus 的核心数据模型(线圈、离散量输入、保持寄存器、输入寄存器)。
- 深入剖析了 Modbus RTU 数据帧的构成,包括从站地址、功能码、数据域和 CRC 校验。
- 学习了如何识别和处理 Modbus 异常响应。
- 通过具体示例理解了如何发送请求和解析响应。
- 认识到数据类型和字节序的重要性,并学会了如何解决相关问题。
- 了解了利用在线工具进行实践和调试的技巧。
继续保持好奇心,不断实践和学习,你将能够熟练地在各种工业自动化项目中应用 Modbus RTU 协议,构建稳定可靠的通信系统。