Modbus-RTU 数据帧格式 、报文解析

举报
yd_247939230 发表于 2026/10/14 14:25:56 2026/10/14
【摘要】 Modbus 协议详解:报文结构、功能码与通信示例 一、Modbus 协议简介 1.1 什么是 Modbus?Modbus 可以从名称上拆分为 Mod 和 Bus 两部分。其中,Bus 表示总线,意味着多个设备可以通过同一种通信协议进行数据交换。Modbus 最初由 Modicon 公司开发,主要用于 PLC(可编程逻辑控制器)之间以及 PLC 与外围设备之间的通信。后来,Modicon ...

Modbus 协议详解:报文结构、功能码与通信示例

一、Modbus 协议简介

1.1 什么是 Modbus?

Modbus 可以从名称上拆分为 Mod 和 Bus 两部分。其中,Bus 表示总线,意味着多个设备可以通过同一种通信协议进行数据交换。

Modbus 最初由 Modicon 公司开发,主要用于 PLC(可编程逻辑控制器)之间以及 PLC 与外围设备之间的通信。后来,Modicon 被施耐德电气(Schneider Electric)收购,Modbus 协议也逐渐得到广泛应用,成为工业自动化领域常见的通信协议之一。

目前,Modbus 广泛应用于 PLC、HMI、传感器、变频器、电力设备以及各类嵌入式控制器中。

1.2 Modbus 的通信特点

Modbus 支持多种传输方式,常见的有:

  • Modbus RTU:通常通过 RS-485 或 RS-232 等串行接口传输,使用二进制编码。
  • Modbus ASCII:通过串行接口传输,使用 ASCII 字符表示报文。
  • Modbus TCP:通过 TCP/IP 网络传输,通常使用以太网接口。

在传统的 Modbus 串行通信中,通常采用主从通信模式:

  • 主站负责发起请求。
  • 从站接收请求,并根据功能码执行相应操作。
  • 从站通常不会主动向主站发送数据,而是在收到请求后返回响应。
  • 一条串行总线上通常只有一个主站,可以连接多个从站。

Modbus 串行规范使用 1~247 作为常规从站地址,因此最多可以寻址 247 个常规从站。不过,实际可连接设备数量还受到收发器负载能力、总线布线、通信距离等因素的限制。

需要注意的是,Modbus TCP 的通信模型与传统串行主从通信有所不同,通常使用客户端和服务器的概念。

二、Modbus 报文模型

2.1 基本报文结构

一个 Modbus 报文可以理解为一次完整的通信请求或响应。不同传输方式的报文结构有所区别。

对于 Modbus RTU,一帧报文通常由以下部分组成:

字段 长度 说明
从站地址 1 字节 指定目标从站
功能码 1 字节 指定需要执行的操作
数据 不定长 包含起始地址、数量、寄存器值等信息
CRC 校验 2 字节 用于检测报文传输错误

对于 Modbus TCP,报文前部使用 MBAP(Modbus Application Protocol)报文头,后面跟随功能码和数据,不使用 RTU 格式的 CRC 校验字段。

2.2 字节序与位序

Modbus 寄存器数据采用 大端字节序(Big Endian):对于一个 16 位寄存器,先传输高字节,再传输低字节。

例如,寄存器值为 0x1234,其数据部分为:

12 34

对于线圈和离散量输入,数据则按照位进行打包。每个数据字节的最低有效位(LSB)对应较小的线圈地址,后续位依次对应递增的地址。

例如,数据字节 0xCD 的二进制表示为:

1100 1101

对应的线圈状态如下:

位 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
状态 1 1 0 0 1 1 0 1

如果该字节表示从线圈地址 20 开始的状态,则:

线圈地址 27 26 25 24 23 22 21 20
状态 1 1 0 0 1 1 0 1

也就是说,线圈地址 20 对应 Bit 0,线圈地址 27 对应 Bit 7。

三、常用功能码

Modbus 通过功能码区分不同的读写操作。

以下是常见的标准功能码:

功能码 名称 操作对象 说明
0x01 读线圈 Coil 读取一个或多个线圈状态
0x02 读离散量输入 Discrete Input 读取一个或多个离散输入状态
0x03 读保持寄存器 Holding Register 读取一个或多个保持寄存器
0x04 读输入寄存器 Input Register 读取一个或多个输入寄存器
0x05 写单个线圈 Coil 写入一个线圈状态
0x06 写单个寄存器 Holding Register 写入一个保持寄存器
0x0F 写多个线圈 Coil 一次写入多个线圈状态
0x10 写多个寄存器 Holding Register 一次写入多个保持寄存器

其中,线圈通常用于表示开关量输出,离散量输入用于表示开关量输入;保持寄存器和输入寄存器则用于保存或提供 16 位数据。

需要注意:

  1. 0x03 和 0x04 的区别在于寄存器类型,而不只是名称不同。
  2. 0x06、0x10 是标准功能码;0x41、0x43 等属于设备或厂商可能自行定义的扩展功能码,并非通用标准功能码。
  3. 原始 Modbus 协议并不规定 PLC 中 X、Y、M、D 等设备地址的具体含义。这些通常是特定 PLC 厂商定义的内部元件名称,需要由设备手册说明其与 Modbus 地址的映射关系。

四、读线圈与读离散量输入(0x01、0x02)

功能码 0x01 用于读取线圈状态,功能码 0x02 用于读取离散量输入状态。

两者的报文结构基本相同,主要区别在于访问的对象不同。

4.1 请求报文

请求报文由从站地址、功能码、起始地址、读取数量和 CRC 校验组成。

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 0x01 或 0x02
起始地址 2 字节 要读取的第一个线圈或输入地址
读取数量 2 字节 要读取的位数
CRC 校验 2 字节 RTU 帧校验

其中,起始地址和读取数量均采用高字节在前的顺序。

4.2 响应报文

字段 长度 说明
从站地址 1 字节 响应设备地址
功能码 1 字节 与请求功能码相同
字节计数 1 字节 后续状态数据的字节数
状态数据 N 字节 按位打包的线圈或输入状态
CRC 校验 2 字节 RTU 帧校验

状态数据的字节数为:

[
N=\left\lceil\frac{Q}{8}\right\rceil
]

其中,(Q) 为请求读取的位数。

如果读取数量不是 8 的整数倍,最后一个数据字节中未使用的高位填充为 0。

4.3 示例:读取 19 个线圈

假设从站地址为 0x01,需要从线圈地址 0x0013 开始读取 19 个线圈。

请求报文的数据部分如下:

字段 十六进制
从站地址 01
功能码 01
起始地址高字节 00
起始地址低字节 13
读取数量高字节 00
读取数量低字节 13
CRC 校验 根据前面的报文字节计算

由于需要读取 19 位状态,因此需要:

[
N=\left\lceil\frac{19}{8}\right\rceil=3
]

响应中的三个数据字节可以为:

CD 6B 05

对应关系如下。

第一个字节 0xCD:

位 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
线圈地址 26 25 24 23 22 21 20 19
状态 1 1 0 0 1 1 0 1

第二个字节 0x6B:

位 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
线圈地址 34 33 32 31 30 29 28 27
状态 0 1 1 0 1 0 1 1

第三个字节 0x05:

0x05 = 0000 0101
位 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
线圈地址 未使用 未使用 未使用 未使用 未使用 38 37 36
状态 0 0 0 0 0 1 0 1

这里读取的 19 个线圈地址为 19~37。第三个字节中只有三个有效位,其余五个高位补 0。

因此,完整响应的数据部分为:

03 CD 6B 05

其中,03 表示后续状态数据占 3 字节。

注意:上面的报文仅展示关键字段,未列出实际 CRC 校验值。

五、读寄存器(0x03、0x04)

功能码 0x03 用于读取保持寄存器,功能码 0x04 用于读取输入寄存器。

每个寄存器占用 16 位,即 2 字节。

5.1 请求报文

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 0x03 或 0x04
起始地址 2 字节 第一个寄存器的地址
寄存器数量 2 字节 要读取的寄存器数量
CRC 校验 2 字节 RTU 帧校验

5.2 响应报文

字段 长度 说明
从站地址 1 字节 响应设备地址
功能码 1 字节 与请求功能码相同
字节计数 1 字节 后续寄存器数据的总字节数
寄存器数据 2 × N 字节 每个寄存器占 2 字节
CRC 校验 2 字节 RTU 帧校验

其中,(N) 为读取的寄存器数量。

响应的数据字节数为:

[
B=2N
]

5.3 示例:读取寄存器 108~110

假设从站地址为 0x01,从寄存器地址 0x006B 开始读取 3 个寄存器。

请求报文的关键字段如下:

字段 十六进制
从站地址 01
功能码 03
起始地址高字节 00
起始地址低字节 6B
寄存器数量高字节 00
寄存器数量低字节 03

注意:这里的 0x006B 是十进制 107。如果设备手册使用从 1 开始的寄存器编号,那么它可能对应文档中的第 108 个寄存器。这取决于设备采用的地址编号规则。

假设读取到的数据为:

02 2B 00 00 00 64

响应字段如下:

字段 十六进制
从站地址 01
功能码 03
字节计数 06
寄存器 108 的数据 02 2B
寄存器 109 的数据 00 00
寄存器 110 的数据 00 64

将每两个字节组合为一个 16 位无符号整数,得到:

寄存器 内容值(十六进制) 内容值(十进制)
寄存器 108 0x022B 555
寄存器 109 0x0000 0
寄存器 110 0x0064 100

在实际开发中,解析寄存器数据时应先组合高、低字节,再根据设备协议将结果解释为无符号整数、有符号整数、浮点数或其他数据类型。

六、写单个线圈(0x05)

功能码 0x05 用于写入单个线圈状态。

6.1 请求报文

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 固定为 0x05
线圈地址 2 字节 要写入的线圈地址
写入值 2 字节 0xFF00 表示 ON,0x0000 表示 OFF
CRC 校验 2 字节 RTU 帧校验

写入值的定义如下:

写入值 含义
FF 00 置为 ON,即 1
00 00 置为 OFF,即 0

6.2 响应报文

正常情况下,从站返回与请求报文内容相同的响应,包括从站地址、功能码、线圈地址、写入值以及重新计算的 CRC。

6.3 示例:将线圈地址 172 置为 ON

线圈地址 172 的十六进制表示为 0x00AC。

请求报文的关键字段如下:

字段 十六进制
从站地址 01
功能码 05
线圈地址高字节 00
线圈地址低字节 AC
写入值高字节 FF
写入值低字节 00

响应报文的上述字段与请求相同。

七、写单个寄存器(0x06)

功能码 0x06 用于向一个保持寄存器写入 16 位数据。

7.1 请求报文

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 固定为 0x06
寄存器地址 2 字节 目标寄存器地址
写入值 2 字节 要写入的 16 位数据
CRC 校验 2 字节 RTU 帧校验

7.2 响应报文

正常情况下,从站返回与请求内容相同的响应。

7.3 示例:向寄存器地址 2 写入 0x0003

请求报文的关键字段如下:

字段 十六进制
从站地址 01
功能码 06
寄存器地址高字节 00
寄存器地址低字节 02
写入值高字节 00
写入值低字节 03

从站成功执行写入后,返回对应的响应报文。

需要注意,0x41 并不是标准的单寄存器写入功能码。如果某个设备使用 0x41 完成类似操作,应以该设备的通信协议文档为准。

八、写多个线圈(0x0F)

功能码 0x0F 用于一次写入多个线圈状态。

8.1 请求报文

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 固定为 0x0F
起始地址 2 字节 第一个待写入的线圈地址
线圈数量 2 字节 待写入的线圈总数
字节计数 1 字节 后续线圈数据的字节数
线圈数据 N 字节 按位打包的线圈状态
CRC 校验 2 字节 RTU 帧校验

如果要写入 (Q) 个线圈,需要的数据字节数为:

[
N=\left\lceil\frac{Q}{8}\right\rceil
]

8.2 响应报文

字段 长度 说明
从站地址 1 字节 响应设备地址
功能码 1 字节 固定为 0x0F
起始地址 2 字节 本次写入的起始地址
线圈数量 2 字节 本次写入的线圈数量
CRC 校验 2 字节 RTU 帧校验

与请求报文不同,正常响应不会再次携带全部线圈状态数据。

8.3 示例:从线圈地址 19 开始写入 10 个线圈

假设要写入的状态数据为:

CD 01

二进制表示为:

1100 1101 0000 0001

由于只写入 10 个线圈,实际使用前 10 个有效位,剩余 6 位不参与本次写入。

对应的线圈状态如下:

数据位 Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 0 Bit 1
线圈地址 19 20 21 22 23 24 25 26 27 28
状态 1 0 1 1 0 0 1 1 1 0

请求报文的关键字段如下:

字段 十六进制
从站地址 01
功能码 0F
起始地址高字节 00
起始地址低字节 13
线圈数量高字节 00
线圈数量低字节 0A
字节计数 02
第一个数据字节 CD
第二个数据字节 01

正常响应会返回起始地址 0x0013 和写入数量 0x000A,不包含 CD 01 这两个数据字节。

九、写多个寄存器(0x10)

功能码 0x10 用于一次写入多个保持寄存器。

每个寄存器占用 2 字节,因此写入 (N) 个寄存器时,数据部分需要 (2N) 个字节。

9.1 请求报文

字段 长度 说明
从站地址 1 字节 目标设备地址
功能码 1 字节 固定为 0x10
起始地址 2 字节 第一个待写入的寄存器地址
寄存器数量 2 字节 待写入的寄存器数量
字节计数 1 字节 后续寄存器数据的字节数
寄存器数据 2 × N 字节 每个寄存器占 2 字节
CRC 校验 2 字节 RTU 帧校验

其中:

[
B=2N
]

9.2 响应报文

字段 长度 说明
从站地址 1 字节 响应设备地址
功能码 1 字节 固定为 0x10
起始地址 2 字节 本次写入的起始地址
寄存器数量 2 字节 本次写入的寄存器数量
CRC 校验 2 字节 RTU 帧校验

正常响应只确认本次写入的起始地址和寄存器数量,不会再次返回所有寄存器数据。

9.3 示例:连续写入两个寄存器

假设从寄存器地址 0x0001 开始,依次写入以下两个值:

  • 第一个寄存器:0x000A
  • 第二个寄存器:0x0102

请求报文的关键字段如下:

字段 十六进制
从站地址 01
功能码 10
起始地址高字节 00
起始地址低字节 01
寄存器数量高字节 00
寄存器数量低字节 02
字节计数 04
第一个寄存器高字节 00
第一个寄存器低字节 0A
第二个寄存器高字节 01
第二个寄存器低字节 02

因此,请求的数据部分为:

00 0A 01 02

正常响应中的关键字段为:

字段 十六进制
从站地址 01
功能码 10
起始地址高字节 00
起始地址低字节 01
寄存器数量高字节 00
寄存器数量低字节 02

十、Modbus 开发中的注意事项

在嵌入式设备、PLC、HMI 或上位机程序中实现 Modbus 通信时,需要特别注意以下问题。

10.1 寄存器地址与文档编号的区别

部分设备手册使用从 1 开始的寄存器编号,而 Modbus 报文中的起始地址字段通常以 0 为基准。

例如,文档中的第 108 个寄存器可能对应报文地址 107,即十六进制 0x006B。

但不同设备的文档约定可能不同,不能简单地对所有地址统一减 1,应优先查阅设备手册。

10.2 寄存器字节序与多寄存器数据顺序

Modbus 标准规定每个 16 位寄存器内部采用高字节在前的顺序,但对于 32 位整数、浮点数等跨多个寄存器的数据,寄存器之间的排列顺序可能由设备厂商定义。

例如,浮点数可能采用:

寄存器 1:高 16 位
寄存器 2:低 16 位

也可能采用其他字序排列。因此,解析跨寄存器的数据时,应根据设备协议确定具体的字节序和字序。

10.3 CRC 校验

Modbus RTU 使用 CRC-16 校验。CRC 计算结果为 16 位,发送时通常先发送低字节,再发送高字节。

这与寄存器数据采用高字节在前的顺序不同。

实现时,应按照 Modbus RTU 规定的 CRC 算法计算校验值,并对接收报文进行校验。

10.4 超时与异常响应

实际通信中,从站可能由于地址错误、寄存器不存在、数量超出限制或设备忙碌等原因,无法正常执行请求。

Modbus 异常响应通常将原功能码的最高位置 1,并在数据字段中返回异常码。例如,功能码 0x03 对应的异常响应功能码为 0x83。

常见异常码包括:

异常码 名称 说明
0x01 Illegal Function 不支持该功能码
0x02 Illegal Data Address 请求的数据地址无效
0x03 Illegal Data Value 请求中的数据值不合法
0x04 Server Device Failure 从站执行操作时发生故障

对于没有响应的情况,主站还应实现接收超时、有限次数重试和通信恢复机制。

10.5 标准功能码与厂商扩展功能码

开发通用 Modbus 主站时,应优先实现标准功能码,例如 0x01、0x03、0x05、0x06、0x0F 和 0x10。

如果设备使用 0x41、0x43 等自定义功能码,则应按照厂商提供的协议文档实现对应的报文格式、数据解释和异常处理逻辑。

十一、总结

Modbus 是工业自动化领域常见的通信协议,具有结构简单、实现方便、设备兼容性较好等特点。

掌握 Modbus 的关键在于理解以下内容:

  1. 报文结构:从站地址、功能码、数据和校验字段的作用。
  2. 数据编码:寄存器的字节序,以及线圈数据的位打包规则。
  3. 功能码:不同功能码对应的读写对象和操作方式。
  4. 地址映射:设备手册中的寄存器编号与报文起始地址之间的关系。
  5. 异常处理:CRC 校验、超时重试和异常响应的处理方式。

在实际开发中,只要明确设备的通信方式、寄存器映射关系以及数据格式,就可以基于这些基础知识实现 Modbus RTU 或 Modbus TCP 的主站通信功能。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。