首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >[Modbus]通信协议完整指南

[Modbus]通信协议完整指南

作者头像
科控物联
发布2026-05-06 12:32:45
发布2026-05-06 12:32:45
2320
举报

1.1 Modbus 协议简介

Modbus 是一种串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年发布,用于可编程逻辑控制器(PLC)之间的通信。经过四十多年的发展,Modbus 已成为工业领域最广泛使用的通信协议之一。

主要特点:

特性

说明

开放性

协议规范完全公开,无版权限制

简单性

协议结构简洁,易于实现和调试

兼容性

支持多种物理层和传输模式

可靠性

成熟稳定,工业现场广泛验证

通用性

几乎所有工业设备都支持

1.2 协议版本

版本

说明

应用场景

Modbus RTU

二进制编码,CRC 校验

串口通信,工业现场总线

Modbus ASCII

ASCII 字符编码,LRC 校验

调试环境,低速通信

Modbus TCP

基于 TCP/IP,以太网传输

工业以太网,远程监控

1.3 通信架构

Modbus 采用**主从(Master-Slave)**架构:

代码语言:javascript
复制
┌─────────────┐                    ┌─────────────┐
│   主站      │ ──────────────────▶│   从站 1    │
│  (Master)   │                    │  (Slave)    │
│             │ ◀──────────────────│             │
└─────────────┘                    └─────────────┘
       │
       │          ┌─────────────┐
       ├─────────▶│   从站 2    │
       │          │  (Slave)    │
       │          └─────────────┘
       │
       │          ┌─────────────┐
       └─────────▶│   从站 N    │
                  │  (Slave)    │
                  └─────────────┘

通信规则:

主站主动发起请求,从站被动响应

从站只能响应主站的请求,不能主动发送数据

每个从站有唯一的地址(1-247)

地址 0 为广播地址,所有从站接收但不响应


2. 基础概念

2.1 主站与从站

角色

说明

典型设备

主站 (Master/Client)

发起通信请求的一方

HMI、SCADA、PLC、工控机

从站 (Slave/Server)

响应通信请求的一方

PLC、传感器、变频器、仪表

主站职责:

发起查询请求

管理通信时序

处理从站响应

异常检测与重试

从站职责:

监听并接收请求

解析请求内容

执行相应操作

返回响应数据

2.2 事务处理流程

代码语言:javascript
复制
主站                                    从站
 │                                       │
 │─────── 请求帧 (Request) ─────────────▶│
 │                                       │ 解析请求
 │                                       │ 执行操作
 │                                       │ 组装响应
 │◀─────── 响应帧 (Response) ────────────│
 │                                       │
 │  处理响应数据                          │
 │                                       │

2.3 通信超时

参数

建议值

说明

请求超时

1000-5000ms

等待从站响应的最长时间

帧间隔

3.5 字符时间

RTU 模式下帧与帧之间的静默间隔

字符间隔

1.5 字符时间

RTU 模式下帧内字符的最大间隔

RTU 模式字符时间计算:

字符时间 = 11 位 / 波特率 (秒)

示例 (9600 bps):

字符时间 = 11 / 9600 = 1.146 ms

帧间隔 = 3.5 × 1.146 ≈ 4 ms


3. 数据模型与地址

3.1 四种数据类型

Modbus 定义了四种基本数据类型:

数据类型

访问方式

地址范围

数据格式

典型应用

线圈 (Coil)

读写

00001-09999

单个位 (0/1)

继电器、开关量输出

离散输入 (Discrete Input)

只读

10001-19999

单个位 (0/1)

按钮状态、开关量输入

输入寄存器 (Input Register)

只读

30001-39999

16位字

模拟量输入、传感器数据

保持寄存器 (Holding Register)

读写

40001-49999

16位字

参数设置、模拟量输出

3.2 地址表示方法

五位数表示法(常用):

地址格式: XNNNN

X = 数据类型标识

0 = 线圈 (0x)

1 = 离散输入 (1x)

3 = 输入寄存器 (3x)

4 = 保持寄存器 (4x)

NNNN = 地址编号 (0001-9999)

示例:

00001 = 线圈地址 0

10001 = 离散输入地址 0

30001 = 输入寄存器地址 0

40001 = 保持寄存器地址 0

协议地址与逻辑地址对照:

逻辑地址

协议地址 (PDU)

数据类型

00001

0x0000

线圈

00002

0x0001

线圈

10001

0x0000

离散输入

30001

0x0000

输入寄存器

40001

0x0000

保持寄存器

40002

0x0001

保持寄存器

注意: 协议地址从 0 开始,逻辑地址从 1 开始,两者相差 1。

3.3 数据存储格式

位数据(线圈/离散输入):

字节排列: [Byte0][Byte1][Byte2]...

位排列: [76543210]... (LSB 在前)

示例: 读取 12 个线圈,值为 1,0,1,1,0,0,1,0,1,1,0,0

存储: Byte0 = 0b01001101 = 0x4D (线圈 0-7)

Byte1 = 0b00000110 = 0x06 (线圈 8-11,高位填充 0)

字数据(寄存器):

大端模式 (Big-Endian):

高字节在前,低字节在后

示例: 寄存器值 0x1234

传输顺序: [0x12][0x34]


4. 传输模式

4.1 Modbus RTU

帧结构:

代码语言:javascript
复制
┌────────┬────────┬────────┬────────┬────────┐
│ 地址   │ 功能码 │   数据  │ CRC低  │ CRC高  │
│ 1字节  │ 1字节  │ N字节   │ 1字节  │ 1字节  │
└────────┴────────┴────────┴────────┴────────┘

特点:

二进制编码,传输效率高

CRC-16 校验,可靠性高

帧间隔 3.5 字符时间

串口参数:8N1(8数据位,无校验,1停止位)

CRC-16 计算:

代码语言:javascript
复制
多项式: 0xA001
初始值: 0xFFFF
示例: 地址=0x01, 功能码=0x03, 数据=0x00 0x00 0x00 0x0A
CRC = 0xC5CD (低字节在前: 0xCD 0xC5)

4.2 Modbus ASCII

帧结构:

代码语言:javascript
复制
┌────┬────────┬────────┬────────┬─────┬─────┐
│起始│ 地址   │ 功能码 │   数据  │ LRC │结束 │
│ ':'│ 2字符  │ 2字符  │ 2N字符 │2字符│CRLF │
└────┴────────┴────────┴────────┴─────┴─────┘

特点:

ASCII 字符编码,可读性好

LRC 校验,计算简单

起始符 ':',结束符 CR LF

适合调试和低速通信

LRC 计算:

方法: 所有字节求和取反加 1

示例: 地址=0x01, 功能码=0x03, 数据=0x00 0x00 0x00 0x0A

和 = 0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x0A = 0x0E

LRC = (0x100 - 0x0E) = 0xF2

4.3 Modbus TCP

帧结构(MBAP 头 + PDU):

代码语言:javascript
复制
┌────────────────────────── MBAP 头 ──────────────────────────┬──────── PDU ────────┐
│ 事务标识 │ 协议标识 │ 长度   │ 单元标识 │ 功能码 │    数据    │
│  2字节   │  2字节   │ 2字节  │  1字节   │ 1字节  │   N字节    │
└──────────┴──────────┴────────┴──────────┴────────┴────────────┘

MBAP 头字段说明:

字段

长度

说明

事务标识

2字节

请求/响应匹配标识,递增计数

协议标识

2字节

Modbus 协议 = 0x0000

长度

2字节

后续字节数(单元标识 + PDU)

单元标识

1字节

从站地址(RTU 兼容)

特点:

基于 TCP/IP,端口 502

无需校验(TCP 保证可靠性)

支持多连接并发

适合工业以太网

TCP 与 RTU 帧对照:

代码语言:javascript
复制
RTU 帧: [01][03][00 00 00 0A][CD C5]
TCP 帧: [00 01][00 00][00 06][01][03][00 00][00 0A]
         ─────  ─────  ─────  ──  ────────────────
         事务ID 协议ID 长度  地址    PDU 部分

5. 标准功能码详解

FC01 读取线圈状态

功能码: 0x01 (1)

功能描述: 读取从站中连续的线圈(输出位)状态,返回 ON/OFF 状态。

适用范围:

读取继电器输出状态

读取开关量输出

读取可写位状态

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x01

固定值

2-3

起始地址

读取起始地址

0x0000-0xFFFF

4-5

数量

读取线圈数量

0x0001-0x07D0 (1-2000)

6-7

CRC

校验码 (RTU)

-

请求帧示例: 读取从站 1,地址 0 开始的 10 个线圈

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 01 │ 00 00   │ 00 0A  │ ED CE   │
└────┴────┴─────────┴────────┴─────────┘
TCP 格式:
┌────────┬────────┬────────┬────┬────┬─────────┬────────┐
│事务标识│协议标识│ 长度   │地址│功能码│起始地址 │ 数量   │
├────────┼────────┼────────┼────┼────┼─────────┼────────┤
│ 00 01  │ 00 00  │ 00 06  │ 01 │ 01 │ 00 00   │ 00 0A  │
└────────┴────────┴────────┴────┴────┴─────────┴────────┘

响应帧格式:

字节

字段

说明

取值范围

0

从站地址

从站地址

0x01-0xF7

1

功能码

0x01

固定值

2

字节数

返回数据字节数

N = ceil(数量/8)

3-N+2

数据

线圈状态数据

每字节8个线圈

N+3-N+4

CRC

校验码 (RTU)

-

响应帧示例: 10 个线圈状态为 1,0,1,1,0,0,1,0,1,1

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────┬─────────┬─────────┐
│地址│功能码│字节数│ 数据    │  CRC    │
├────┼────┼──────┼─────────┼─────────┤
│ 01 │ 01 │ 02   │ 4D 06   │ 89 87   │
└────┴────┴──────┴─────────┴─────────┘
数据解析:
Byte0 = 0x4D = 0b01001101
        位:  7 6 5 4 3 2 1 0
        值:  0 1 0 0 1 1 0 1
        线圈: - - - - 4 3 2 1 (实际: 1,0,1,1,0,0,1,0)
Byte1 = 0x06 = 0b00000110
        位:  7 6 5 4 3 2 1 0
        值:  0 0 0 0 0 1 1 0
        线圈: - - - - - 10 9 8 (实际: 1,1,0,-,-,-,-,-)

典型应用案例:

场景: 读取 PLC 的 8 路继电器输出状态

请求: 读取从站 1,地址 0 开始的 8 个线圈

帧: 01 01 00 00 00 08 FD CC

响应: 8 路继电器状态为 ON,OFF,ON,ON,OFF,OFF,ON,OFF

帧: 01 01 01 4D 60 49

解析: 0x4D = 0b01001101

继电器: 8 7 6 5 4 3 2 1

状态: 0 1 0 0 1 1 0 1

即: R1=ON, R2=OFF, R3=ON, R4=ON, R5=OFF, R6=OFF, R7=ON, R8=OFF


FC02 读取离散输入

功能码: 0x02 (2)

功能描述: 读取从站中连续的离散输入(输入位)状态,返回 ON/OFF 状态。

适用范围:

读取按钮状态

读取开关量输入

读取传感器数字信号

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x02

固定值

2-3

起始地址

读取起始地址

0x0000-0xFFFF

4-5

数量

读取输入数量

0x0001-0x07D0 (1-2000)

6-7

CRC

校验码 (RTU)

-

请求帧示例: 读取从站 1,地址 0 开始的 8 个离散输入

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 02 │ 00 00   │ 00 08  │ 79 C9   │
└────┴────┴─────────┴────────┴─────────┘

响应帧格式:

字节

字段

说明

取值范围

0

从站地址

从站地址

0x01-0xF7

1

功能码

0x02

固定值

2

字节数

返回数据字节数

N = ceil(数量/8)

3-N+2

数据

离散输入状态

每字节8个输入

N+3-N+4

CRC

校验码 (RTU)

-

响应帧示例:

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────┬─────────┬─────────┐
│地址│功能码│字节数│ 数据    │  CRC    │
├────┼────┼──────┼─────────┼─────────┤
│ 01 │ 02 │ 01   │ AC      │ 61 81   │
└────┴────┴──────┴─────────┴─────────┘

数据解析:

0xAC = 0b10101100

输入: 8 7 6 5 4 3 2 1

状态: 1 0 1 0 1 1 0 0

典型应用案例:

场景: 读取 8 个限位开关状态

请求: 读取从站 1,地址 0 开始的 8 个离散输入

帧: 01 02 00 00 00 08 79 C9

响应: 限位开关状态

帧: 01 02 01 AC 61 81

解析: 0xAC = 10101100

开关: S1=OFF, S2=OFF, S3=ON, S4=ON, S5=OFF, S6=ON, S7=OFF, S8=ON


FC03 读取保持寄存器

功能码: 0x03 (3)

功能描述: 读取从站中连续的保持寄存器值,每个寄存器为 16 位无符号整数。

适用范围:

读取参数设置

读取配置数据

读取模拟量输出

读取内部寄存器

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x03

固定值

2-3

起始地址

读取起始地址

0x0000-0xFFFF

4-5

数量

读取寄存器数量

0x0001-0x007D (1-125)

6-7

CRC

校验码 (RTU)

-

请求帧示例: 读取从站 1,地址 0 开始的 3 个保持寄存器

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 03 │ 00 00   │ 00 03  │ 84 0A   │
└────┴────┴─────────┴────────┴─────────┘
TCP 格式:
┌────────┬────────┬────────┬────┬────┬─────────┬────────┐
│事务标识│协议标识│ 长度   │地址│功能码│起始地址 │ 数量   │
├────────┼────────┼────────┼────┼────┼─────────┼────────┤
│ 00 01  │ 00 00  │ 00 06  │ 01 │ 03 │ 00 00   │ 00 03  │
└────────┴────────┴────────┴────┴────┴─────────┴────────┘

响应帧格式:

字节

字段

说明

取值范围

0

从站地址

从站地址

0x01-0xF7

1

功能码

0x03

固定值

2

字节数

返回数据字节数

数量 × 2

3-N+2

数据

寄存器值

每寄存器2字节

N+3-N+4

CRC

校验码 (RTU)

-

响应帧示例: 3 个寄存器值为 100, 200, 300

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────┬───────────────────┬─────────┐
│地址│功能码│字节数│    数据           │  CRC    │
├────┼────┼──────┼───────────────────┼─────────┤
│ 01 │ 03 │ 06   │ 00 64 00 C8 01 2C │ 85 D8   │
└────┴────┴──────┴───────────────────┴─────────┘

数据解析:

寄存器 0: 0x0064 = 100

寄存器 1: 0x00C8 = 200

寄存器 2: 0x012C = 300

典型应用案例:

场景: 读取变频器频率设定值、输出频率、输出电流

请求: 读取从站 1,地址 0 开始的 3 个寄存器

帧: 01 03 00 00 00 03 84 0A

响应: 频率设定=50.0Hz, 输出频率=49.8Hz, 电流=5.2A

帧: 01 03 06 01 F4 01 F2 00 34 XX XX

解析:

寄存器 0: 0x01F4 = 500 → 50.0Hz (精度0.1)

寄存器 1: 0x01F2 = 498 → 49.8Hz (精度0.1)

寄存器 2: 0x0034 = 52 → 5.2A (精度0.1)


FC04 读取输入寄存器

功能码: 0x04 (4)

功能描述: 读取从站中连续的输入寄存器值,每个寄存器为 16 位无符号整数。与保持寄存器不同,输入寄存器为只读。

适用范围:

读取模拟量输入

读取传感器数据

读取测量值

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x04

固定值

2-3

起始地址

读取起始地址

0x0000-0xFFFF

4-5

数量

读取寄存器数量

0x0001-0x007D (1-125)

6-7

CRC

校验码 (RTU)

-

请求帧示例: 读取从站 1,地址 0 开始的 2 个输入寄存器

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 04 │ 00 00   │ 00 02  │ 71 0B   │
└────┴────┴─────────┴────────┴─────────┘

响应帧格式:

字节

字段

说明

取值范围

0

从站地址

从站地址

0x01-0xF7

1

功能码

0x04

固定值

2

字节数

返回数据字节数

数量 × 2

3-N+2

数据

寄存器值

每寄存器2字节

N+3-N+4

CRC

校验码 (RTU)

-

响应帧示例:

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────┬─────────────┬─────────┐
│地址│功能码│字节数│    数据     │  CRC    │
├────┼────┼──────┼─────────────┼─────────┤
│ 01 │ 04 │ 04   │ 03 E8 07 D0 │ FB 88   │
└────┴────┴──────┴─────────────┴─────────┘

数据解析:

寄存器 0: 0x03E8 = 1000 (温度: 100.0°C)

寄存器 1: 0x07D0 = 2000 (压力: 200.0kPa)

典型应用案例:

场景: 读取温度传感器和压力传感器数据

请求: 读取从站 1,地址 0 开始的 2 个输入寄存器

帧: 01 04 00 00 00 02 71 0B

响应: 温度=25.6°C, 压力=101.3kPa

帧: 01 04 04 01 00 03 F5 XX XX

解析:

寄存器 0: 0x0100 = 256 → 25.6°C (精度0.1)

寄存器 1: 0x03F5 = 1013 → 101.3kPa (精度0.1)


FC05 写单个线圈

功能码: 0x05 (5)

功能描述: 将单个线圈设置为 ON 或 OFF 状态。

适用范围:

控制继电器输出

控制开关量输出

远程控制设备启停

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x05

固定值

2-3

输出地址

线圈地址

0x0000-0xFFFF

4-5

输出值

线圈状态

0xFF00=ON, 0x0000=OFF

6-7

CRC

校验码 (RTU)

-

请求帧示例: 设置从站 1,地址 0 的线圈为 ON

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬─────────┬─────────┐
│地址│功能码│输出地址 │ 输出值  │  CRC    │
├────┼────┼─────────┼─────────┼─────────┤
│ 01 │ 05 │ 00 00   │ FF 00   │ 8C 3A   │
└────┴────┴─────────┴─────────┴─────────┘
设置 OFF:
│ 01 │ 05 │ 00 00   │ 00 00   │ CD CA   │

响应帧格式:

正常响应为原样返回请求帧:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x05

2-3

输出地址

线圈地址

4-5

输出值

线圈状态

6-7

CRC

校验码

响应帧示例:

代码语言:javascript
复制
RTU 格式 (成功):
┌────┬────┬─────────┬─────────┬─────────┐
│地址│功能码│输出地址 │ 输出值  │  CRC    │
├────┼────┼─────────┼─────────┼─────────┤
│ 01 │ 05 │ 00 00   │ FF 00   │ 8C 3A   │
└────┴────┴─────────┴─────────┴─────────┘

典型应用案例:

场景: 远程启动电机

请求: 设置从站 1,地址 0 的线圈为 ON (启动电机)

帧: 01 05 00 00 FF 00 8C 3A

响应: 成功

帧: 01 05 00 00 FF 00 8C 3A

场景: 远程停止电机

请求: 设置从站 1,地址 0 的线圈为 OFF (停止电机)

帧: 01 05 00 00 00 00 CD CA

响应: 成功

帧: 01 05 00 00 00 00 CD CA


FC06 写单个寄存器

功能码: 0x06 (6)

功能描述: 将单个保持寄存器设置为指定值。

适用范围:

设置参数值

设置频率/速度

设置控制字

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x06

固定值

2-3

寄存器地址

目标寄存器地址

0x0000-0xFFFF

4-5

寄存器值

写入值

0x0000-0xFFFF

6-7

CRC

校验码 (RTU)

-

请求帧示例: 设置从站 1,地址 0 的寄存器为 100

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────────┬─────────┬─────────┐
│地址│功能码│寄存器地址│ 寄存器值│  CRC    │
├────┼────┼──────────┼─────────┼─────────┤
│ 01 │ 06 │ 00 00    │ 00 64   │ 88 1E   │
└────┴────┴──────────┴─────────┴─────────┘

响应帧格式:

正常响应为原样返回请求帧:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x06

2-3

寄存器地址

目标寄存器地址

4-5

寄存器值

写入值

6-7

CRC

校验码

响应帧示例:

代码语言:javascript
复制
RTU 格式 (成功):
┌────┬────┬──────────┬─────────┬─────────┐
│地址│功能码│寄存器地址│ 寄存器值│  CRC    │
├────┼────┼──────────┼─────────┼─────────┤
│ 01 │ 06 │ 00 00    │ 00 64   │ 88 1E   │
└────┴────┴──────────┴─────────┴─────────┘

典型应用案例:

场景: 设置变频器频率为 50.0Hz

请求: 设置从站 1,地址 0 的寄存器为 500 (50.0Hz)

帧: 01 06 00 00 01 F4 88 30

响应: 成功

帧: 01 06 00 00 01 F4 88 30

解析:

寄存器值: 0x01F4 = 500

实际频率: 500 × 0.1 = 50.0Hz


FC15 写多个线圈

功能码: 0x0F (15)

功能描述: 将多个连续的线圈设置为 ON 或 OFF 状态。

适用范围:

批量控制继电器

批量设置输出状态

模式切换

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x0F

固定值

2-3

起始地址

写入起始地址

0x0000-0xFFFF

4-5

数量

写入线圈数量

0x0001-0x07B0 (1-1968)

6

字节数

数据字节数

N = ceil(数量/8)

7-N+6

数据

线圈状态数据

每字节8个线圈

N+7-N+8

CRC

校验码 (RTU)

-

请求帧示例: 设置从站 1,地址 0 开始的 10 个线圈

设置值: 1,0,1,1,0,0,1,0,1,1

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬──────┬─────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │字节数│  数据   │  CRC    │
├────┼────┼─────────┼────────┼──────┼─────────┼─────────┤
│ 01 │ 0F │ 00 00   │ 00 0A  │ 02   │ 4D 06   │ 26 99   │
└────┴────┴─────────┴────────┴──────┴─────────┴─────────┘

数据解析:

Byte0 = 0x4D = 0b01001101 (线圈 0-7)

Byte1 = 0x06 = 0b00000110 (线圈 8-9)

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x0F

2-3

起始地址

写入起始地址

4-5

数量

写入线圈数量

6-7

CRC

校验码

响应帧示例:

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 0F │ 00 00   │ 00 0A  │ 26 99   │
└────┴────┴─────────┴────────┴─────────┘

典型应用案例:

场景: 控制 8 路继电器,设置 1,3,5,7 为 ON,2,4,6,8 为 OFF

请求: 设置从站 1,地址 0 开始的 8 个线圈

数据: 0b01010101 = 0x55

帧: 01 0F 00 00 00 08 01 55 DF 8B

响应: 成功

帧: 01 0F 00 00 00 08 A6 8B

解析:

继电器: 8 7 6 5 4 3 2 1

状态: 0 1 0 1 0 1 0 1

OFF ON OFF ON OFF ON OFF ON


FC16 写多个寄存器

功能码: 0x10 (16)

功能描述: 将多个连续的保持寄存器设置为指定值。

适用范围:

批量设置参数

写入配置数据

多参数联动设置

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x10

固定值

2-3

起始地址

写入起始地址

0x0000-0xFFFF

4-5

数量

写入寄存器数量

0x0001-0x007B (1-123)

6

字节数

数据字节数

数量 × 2

7-N+6

数据

寄存器值

每寄存器2字节

N+7-N+8

CRC

校验码 (RTU)

-

请求帧示例: 设置从站 1,地址 0 开始的 3 个寄存器

设置值: 100, 200, 300

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬──────┬───────────────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │字节数│      数据         │  CRC    │
├────┼────┼─────────┼────────┼──────┼───────────────────┼─────────┤
│ 01 │ 10 │ 00 00   │ 00 03  │ 06   │ 00 64 00 C8 01 2C │ 89 8F   │
└────┴────┴─────────┴────────┴──────┴───────────────────┴─────────┘

数据解析:

寄存器 0: 0x0064 = 100

寄存器 1: 0x00C8 = 200

寄存器 2: 0x012C = 300

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x10

2-3

起始地址

写入起始地址

4-5

数量

写入寄存器数量

6-7

CRC

校验码

响应帧示例:

代码语言:javascript
复制
RTU 格式:
┌────┬────┬─────────┬────────┬─────────┐
│地址│功能码│起始地址 │ 数量   │  CRC    │
├────┼────┼─────────┼────────┼─────────┤
│ 01 │ 10 │ 00 00   │ 00 03  │ 40 08   │
└────┴────┴─────────┴────────┴─────────┘

典型应用案例:

场景: 设置变频器参数 (频率=50Hz, 加速时间=10s, 减速时间=15s)

请求: 设置从站 1,地址 0 开始的 3 个寄存器

值: 500, 100, 150

帧: 01 10 00 00 00 03 06 01 F4 00 64 00 96 XX XX

响应: 成功

帧: 01 10 00 00 00 03 40 08

解析:

寄存器 0: 0x01F4 = 500 → 50.0Hz

寄存器 1: 0x0064 = 100 → 10.0s (精度0.1)

寄存器 2: 0x0096 = 150 → 15.0s (精度0.1)


FC22 屏蔽写寄存器

功能码: 0x16 (22)

功能描述: 使用屏蔽算法修改单个保持寄存器的特定位,不影响其他位。

适用范围:

修改控制字的特定位

状态标志位操作

位级参数设置

算法原理:

结果 = (当前值 AND andMask) OR (orMask AND (NOT andMask))

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x16

固定值

2-3

寄存器地址

目标寄存器地址

0x0000-0xFFFF

4-5

AND 屏蔽

AND 掩码

0x0000-0xFFFF

6-7

OR 屏蔽

OR 掩码

0x0000-0xFFFF

8-9

CRC

校验码 (RTU)

-

请求帧示例:

场景: 将寄存器地址 0 的第 3 位设置为 1,其他位不变

AND 屏蔽 = 0xFFF7 (第3位为0,其他位为1)

OR 屏蔽 = 0x0008 (第3位为1,其他位为0)

代码语言:javascript
复制
RTU 格式:
┌────┬────┬──────────┬─────────┬─────────┬─────────┐
│地址│功能码│寄存器地址│AND屏蔽  │ OR屏蔽  │  CRC    │
├────┼────┼──────────┼─────────┼─────────┼─────────┤
│ 01 │ 16 │ 00 00    │ FF F7   │ 00 08   │ 98 16   │
└────┴────┴──────────┴─────────┴─────────┴─────────┘

响应帧格式:

正常响应为原样返回请求帧。

典型应用案例:

场景: 修改控制字的第 0 位(启动位)

当前值: 0x0000 (停止状态)

目标: 设置第 0 位为 1 (启动)

AND 屏蔽 = 0xFFFE (保持其他位不变)

OR 屏蔽 = 0x0001 (设置第 0 位)

请求: 01 16 00 00 FF FE 00 01 98 16

执行过程:

结果 = (0x0000 AND 0xFFFE) OR (0x0001 AND 0x0001)

= 0x0000 OR 0x0001

= 0x0001

响应: 01 16 00 00 FF FE 00 01 98 16


FC23 读写多个寄存器

功能码: 0x17 (23)

功能描述: 在一个请求中完成读取和写入操作,原子性操作。

适用范围:

读取状态同时设置参数

减少通信次数

需要原子操作的场景

请求帧格式:

字节

字段

说明

取值范围

0

从站地址

目标从站地址

0x01-0xF7

1

功能码

0x17

固定值

2-3

读取起始地址

读取起始地址

0x0000-0xFFFF

4-5

读取数量

读取寄存器数量

0x0001-0x007D

6-7

写入起始地址

写入起始地址

0x0000-0xFFFF

8-9

写入数量

写入寄存器数量

0x0001-0x0079

10

写入字节数

写入数据字节数

写入数量 × 2

11-N+10

写入数据

写入寄存器值

每寄存器2字节

N+11-N+12

CRC

校验码 (RTU)

-

请求帧示例:

场景: 读取地址 0 开始的 3 个寄存器,同时写入地址 10 开始的 2 个寄存器

写入值: 100, 200

代码语言:javascript
复制
RTU 格式:
┌────┬────┬───────────┬──────────┬───────────┬──────────┬──────┬───────────┬─────────┐
│地址│功能码│读起始地址 │ 读数量   │写起始地址 │ 写数量   │字节数│ 写入数据  │  CRC    │
├────┼────┼───────────┼──────────┼───────────┼──────────┼──────┼───────────┼─────────┤
│ 01 │ 17 │ 00 00     │ 00 03    │ 00 0A     │ 00 02    │ 04   │ 00 64 00 C8│ XX XX   │
└────┴────┴───────────┴──────────┴───────────┴──────────┴──────┴───────────┴─────────┘

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x17

2

字节数

读取数据字节数

3-N+2

读取数据

读取的寄存器值

N+3-N+4

CRC

校验码

典型应用案例:

场景: 设置变频器频率并读取当前状态

请求:

- 写入: 地址 0 (频率设定) = 500 (50.0Hz)

- 读取: 地址 10 开始的 3 个寄存器 (状态、电流、电压)

帧: 01 17 00 0A 00 03 00 00 00 01 02 01 F4 XX XX

响应:

寄存器 10: 0x0001 = 运行中

寄存器 11: 0x0034 = 5.2A

寄存器 12: 0x0258 = 380V

帧: 01 17 06 00 01 00 34 02 58 XX XX


6. 扩展功能码详解

FC07 读取异常状态

功能码: 0x07 (7)

功能描述: 读取从站的异常状态字节,用于快速诊断设备状态。

请求帧格式:

字节

字段

说明

0

从站地址

目标从站地址

1

功能码

0x07

请求帧示例:

RTU: 01 07 41 0E

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x07

2

异常状态

1字节状态值

响应帧示例:

RTU: 01 07 55 41 0E

状态: 0x55 = 0b01010101


FC08 诊断功能

功能码: 0x08 (8)

功能描述: 提供一系列诊断测试功能,用于通信链路测试和故障诊断。

子功能码:

子功能码

名称

说明

0x0000

返回查询数据

原样返回请求数据

0x0001

重启通信选项

重置通信参数

0x0002

返回诊断寄存器

返回诊断寄存器值

0x0003

更改 ASCII 输入分隔符

设置分隔符

0x0004

强制监听模式

进入监听模式

0x000A

清除计数器

清除所有计数器

0x000B

返回总线消息计数

返回消息计数

0x000C

返回总线通信错误计数

返回错误计数

0x000D

返回总线异常错误计数

返回异常计数

0x000E

返回从站消息计数

返回从站消息计数

0x000F

返回从站无响应计数

返回无响应计数

0x0010

返回从站 NAK 计数

返回 NAK 计数

0x0011

返回从站忙计数

返回忙计数

0x0012

返回总线字符溢出计数

返回溢出计数

请求帧示例(返回查询数据):

代码语言:javascript
复制
RTU: 01 08 00 00 A5 37 XX XX
         └──┴─ 子功能码
             └──┴─ 数据字段

FC11 报告服务器 ID

功能码: 0x11 (17)

功能描述: 读取从站的服务器标识信息,包括设备类型、版本等。

请求帧格式:

字节

字段

说明

0

从站地址

目标从站地址

1

功能码

0x11

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x11

2

字节数

数据长度

3-N+2

数据

服务器 ID 数据

响应数据格式(典型):

字节 0: 设备类型

字节 1: 运行状态 (0x00=OFF, 0xFF=ON)

字节 2-N: 附加数据 (厂商自定义)


FC20 读取文件记录

功能码: 0x14 (20)

功能描述: 读取设备文件中的记录数据,支持多个子请求。

请求帧格式:

字节

字段

说明

0

从站地址

目标从站地址

1

功能码

0x14

2

字节数

请求数据长度

3

参考类型

固定值 0x06

4-5

文件号

文件编号

6-7

记录号

记录编号

8-9

记录长度

记录长度 (寄存器数)


FC21 写入文件记录

功能码: 0x15 (21)

功能描述: 将数据写入设备文件中的记录。

请求帧格式:

字节

字段

说明

0

从站地址

目标从站地址

1

功能码

0x15

2

字节数

请求数据长度

3

参考类型

固定值 0x06

4-5

文件号

文件编号

6-7

记录号

记录编号

8-9

记录长度

记录长度 (寄存器数)

10-N

数据

写入数据


FC24 读取 FIFO 队列

功能码: 0x18 (24)

功能描述: 读取先进先出 (FIFO) 队列中的数据。

请求帧格式:

字节

字段

说明

0

从站地址

目标从站地址

1

功能码

0x18

2-3

FIFO 地址

FIFO 队列地址

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x18

2-3

字节数

数据字节数 + 2

4-5

FIFO 计数

FIFO 数据个数

6-N

数据

FIFO 数据


FC43 读取设备标识

功能码: 0x2B (43)

功能描述: 读取设备的标识信息,支持基本、常规、扩展和特定对象读取。

MEI 类型: 0x0E (14)

读取模式:

模式

代码

说明

Basic

0x01

基本标识 (厂商、产品、版本)

Regular

0x02

常规标识

Extended

0x03

扩展标识

Specific

0x04

特定对象标识

对象类型:

代码

对象

说明

0x00

VendorName

厂商名称

0x01

ProductCode

产品代码

0x02

MajorMinorRevision

版本号

0x03

VendorUrl

厂商 URL

0x04

ProductName

产品名称

0x05

ModelName

型号名称

0x06

UserApplicationName

用户应用名称

请求帧示例(读取基本标识):

代码语言:javascript
复制
RTU: 01 2B 0E 01 00 XX XX
         └─ MEI类型
            └─ 读取模式 (Basic)
               └─ 起始对象ID

响应帧格式:

字节

字段

说明

0

从站地址

从站地址

1

功能码

0x2B

2

MEI 类型

0x0E

3

一致性等级

设备支持级别

4

更多数据

0x00=无更多, 0xFF=有更多

5

下一对象ID

下一个对象标识

6

对象数量

返回对象个数

7-N

对象数据

对象ID + 长度 + 值


7. 异常响应与错误处理

7.1 异常响应格式

当从站无法正常处理请求时,返回异常响应:

代码语言:javascript
复制
┌────┬────────────┬──────────┐
│地址│ 功能码     │ 异常码   │
│1字节│ 1字节     │ 1字节    │
└────┴────────────┴──────────┘

功能码 = 请求功能码 | 0x80

异常响应示例:

请求: 01 03 00 00 00 01 84 0A (读取1个寄存器)

响应: 01 83 02 C0 F1 (非法数据地址)

解析:

地址: 0x01

功能码: 0x83 = 0x03 | 0x80 (异常响应)

异常码: 0x02 = 非法数据地址

7.2 标准异常码

异常码

名称

说明

常见原因

0x01

ILLEGAL FUNCTION

非法功能码

设备不支持该功能码

0x02

ILLEGAL DATA ADDRESS

非法数据地址

地址超出范围或不存在

0x03

ILLEGAL DATA VALUE

非法数据值

数据值超出范围或无效

0x04

SLAVE DEVICE FAILURE

从站设备故障

设备内部错误

0x05

ACKNOWLEDGE

确认

请求已接受,处理中

0x06

SLAVE DEVICE BUSY

从站忙

设备正在处理其他请求

0x07

NEGATIVE ACKNOWLEDGE

否定确认

设备拒绝执行

0x08

MEMORY PARITY ERROR

内存奇偶错误

内存校验失败

0x0A

GATEWAY PATH UNAVAILABLE

网关路径不可用

网关配置错误

0x0B

GATEWAY TARGET DEVICE FAILED TO RESPOND

网关目标设备无响应

目标设备离线

7.3 异常处理最佳实践

超时处理:

1. 设置合理的超时时间 (建议 1-5 秒)

2. 超时后重试 (建议最多 3 次)

3. 重试间隔递增 (如 100ms, 200ms, 400ms)

4. 记录超时日志便于排查

异常码处理流程:

收到异常响应

├─ 0x01 (非法功能码) → 检查设备是否支持该功能

├─ 0x02 (非法地址) → 检查地址范围是否正确

├─ 0x03 (非法值) → 检查数据格式和范围

├─ 0x04 (设备故障) → 检查设备状态,稍后重试

├─ 0x05 (确认) → 等待处理完成,轮询结果

├─ 0x06 (从站忙) → 等待后重试

└─ 其他 → 记录日志,人工排查


附录

A. CRC-16 计算代码 (C#)

代码语言:javascript
复制
public static class ModbusCrc
{
    public static ushort Calculate(byte[] data, int length)
    {
        ushort crc = 0xFFFF;
        for (int i = 0; i < length; i++)
        {
            crc ^= data[i];
            for (int j = 0; j < 8; j++)
            {
                if ((crc & 0x0001) != 0)
                {
                    crc >>= 1;
                    crc ^= 0xA001;
                }
                else
                {
                    crc >>= 1;
                }
            }
        }
        return crc;
    }
}

B. LRC 计算代码 (C#)

代码语言:javascript
复制
public static byte CalculateLrc(byte[] data, int length)
{
    byte lrc = 0;
    for (int i = 0; i < length; i++)
    {
        lrc += data[i];
    }
    return (byte)((~lrc + 1) & 0xFF);
}

C. 常用波特率与字符时间对照表

波特率

1字符时间

3.5字符时间

1.5字符时间

1200

9.17 ms

32.08 ms

13.75 ms

2400

4.58 ms

16.04 ms

6.88 ms

4800

2.29 ms

8.02 ms

3.44 ms

9600

1.15 ms

4.01 ms

1.72 ms

19200

0.57 ms

2.01 ms

0.86 ms

38400

0.29 ms

1.00 ms

0.43 ms

57600

0.19 ms

0.67 ms

0.29 ms

115200

0.10 ms

0.33 ms

0.14 ms

D. 功能码快速参考表


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 科控物联 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.2 协议版本
  • 1.3 通信架构
  • 2. 基础概念
    • 2.1 主站与从站
    • 2.2 事务处理流程
    • 2.3 通信超时
  • 3. 数据模型与地址
    • 3.1 四种数据类型
    • 3.2 地址表示方法
    • 3.3 数据存储格式
  • 4. 传输模式
    • 4.1 Modbus RTU
    • 4.2 Modbus ASCII
    • 4.3 Modbus TCP
  • 5. 标准功能码详解
    • FC01 读取线圈状态
    • FC02 读取离散输入
    • FC03 读取保持寄存器
    • FC04 读取输入寄存器
    • FC05 写单个线圈
    • FC06 写单个寄存器
    • FC15 写多个线圈
    • FC16 写多个寄存器
    • FC22 屏蔽写寄存器
    • FC23 读写多个寄存器
  • 6. 扩展功能码详解
    • FC07 读取异常状态
    • FC08 诊断功能
    • FC11 报告服务器 ID
    • FC20 读取文件记录
    • FC21 写入文件记录
    • FC24 读取 FIFO 队列
    • FC43 读取设备标识
  • 7. 异常响应与错误处理
    • 7.1 异常响应格式
    • 7.2 标准异常码
    • 7.3 异常处理最佳实践
  • 附录
    • A. CRC-16 计算代码 (C#)
    • B. LRC 计算代码 (C#)
    • C. 常用波特率与字符时间对照表
    • D. 功能码快速参考表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档