目录
- 通讯方式对比
- Modbus协议概述
- [Modbus RTU详解](#Modbus RTU详解)
- [Modbus TCP详解](#Modbus TCP详解)
- [RTU vs TCP对比](#RTU vs TCP对比)
- CRC16校验原理
- 实战示例
串口通讯 vs 网口通讯
物理层对比
┌─────────────────────┐ ┌─────────────────────┐
│ 串口通讯 (RTU) │ │ 网口通讯 (TCP) │
├─────────────────────┤ ├─────────────────────┤
│ • RS-232 / RS-485 │ │ • 以太网 / 光纤 │
│ • 点对点 / 总线型 │ │ • 交换机 / 路由器 │
│ • 传输距离: 短 │ │ • 传输距离: 长 │
│ • 速率: 低 │ │ • 速率: 高 │
│ • 抗干扰: 强 │ │ • 抗干扰: 需屏蔽 │
└─────────────────────┘ └─────────────────────┘
应用场景对比
- 串口: 工业现场设备 → 短距离、实时控制
- 网口: 远程监控 → 跨网络、数据采集
Modbus 协议基础
主从架构
Master (主站) Slave (从站)
↓ 发送请求
↓ ↕ 返回响应
┌─────────────┐ ┌─────────────┐
│ PLC/上位机 │────────→│ 传感器 │
│ │ │ 执行器 │
│ │←────────│ 变频器 │
└─────────────┘ └─────────────┘
核心特点
- 单向通讯:只能由主站发起
- 事务模型:一次一个事务
- 地址空间:线圈、离散输入、寄存器
Modbus 功能码
8种标准功能码
| 功能码 | 名称 | 数据类型 | 方向 |
|---|---|---|---|
| 0x01 | 读取线圈 | Bool[] | 读 |
| 0x02 | 读取离散输入 | Bool[] | 读 |
| 0x03 | 读取保持寄存器 | Byte[] | 读 |
| 0x04 | 读取输入寄存器 | Byte[] | 读 |
| 0x05 | 写入单个线圈 | - | 写 |
| 0x06 | 写入单个寄存器 | - | 写 |
| 0x0F | 写入多个线圈 | - | 写 |
| 0x10 | 写入多个寄存器 | - | 写 |
Modbus RTU 报文结构
完整帧结构
┌─────────────────────────────────────────────────┐
│ Modbus RTU 数据帧 │
├─────────────────────────────────────────────────┤
│ [从站地址][功能码][数据区][CRC16低][CRC16高] │
│ 1字节 1字节 N字节 1字节 1字节 │
└─────────────────────────────────────────────────┘
关键特性
- ✅ 串行传输(RS-232/485)
- ✅ 二进制编码
- ✅ CRC16循环冗余校验
- ✅ 紧凑的帧结构
Modbus RTU 读取报文示例
读取保持寄存器 (功能码 0x03)
请求报文 (读取10个寄存器)
HEX: 01 03 00 00 00 0A [CRC_L] [CRC_H]
┌─┬─┬────┬────┬───────┐
│ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓
[01][03][00][00][00][0A][xx][xx]
│ │ │ │ │ │
│ │ │ │ │ └─ CRC16校验
│ │ │ │ └──── 数量(10)
│ │ └───┴─────── 起始地址(0)
│ └─────── 功能码(读保持寄存器)
└────────── 从站地址(1)
响应报文 (返回20字节数据)
HEX: 01 03 14 [20字节寄存器数据] [CRC_L] [CRC_H]
┌─┬─┬──┬───────────────────┬───────┐
│ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓
[01][03][14][D0 D1 D2 D3 ... 0F][xx][xx]
│ │ │ └── 实际数据 ──┘ │
│ │ └─── 字节数(20) │
│ └───── 功能码 │
└──────── 从站地址 └─ CRC16校验
Modbus TCP 报文结构
MBAP + Modbus PDU
┌────────────────────────────────────────────────────────────┐
│ Modbus TCP 数据帧 │
├────────────────────────────────────────────────────────────┤
│ MBAP Header (7字节) │ Modbus PDU (N+1字节) │
├──────────────┬───────────┼──────────────────────────────┤
│ 事务ID │ 协议ID │[单元ID][功能码][数据区] │
│ 2字节 │ 2字节 │ 1字节 1字节 N字节 │
├──────────────┴───────────┴──────────────────────────────┤
│ Transaction │ Protocol │ Unit │Function│ Data │
│ ID │ ID │ ID │ Code │ │
└──────────────┴───────────┴────────┴────────┴──────────────┘
MBAP头结构
- Transaction ID (2字节): 事务标识符,匹配请求/响应
- Protocol ID (2字节): 协议标识符,Modbus TCP = 0x0000
- Length (2字节): 后续字节数
- Unit ID (1字节): 从站地址
Modbus TCP 读取报文示例
读取保持寄存器 (功能码 0x03)
请求报文
HEX: 00 01 00 00 00 06 01 03 00 00 00 0A
┌───┬───┬───────┬───┬───┬────┬───┬────┬────┬────┐
│ │ │ │ │ │ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
[00][01][00][00][00][06][01][03][00][00][00][0A]
│ │ │ │ │ │ │ │ │ └── 数量(10)
│ │ │ │ │ │ │ │ └─────── 起始地址(0)
│ │ │ │ │ │ │ └────────── 功能码(0x03)
│ │ │ │ │ │ └─────────────── 单元ID(从站1)
│ │ │ │ │ └──────────────────── 长度(6字节)
│ │ │ │ └────────────────────────── 协议ID(0)
│ │ │ └─────────────────────────────── 事务ID(1)
│ │ └──────────────────────────────────────────── 帧头(7字节)
│ └────────────────────────────────────────────────── MBAP头
└───┴──────────────────────────────────────────────────── 完整TCP报文
响应报文
HEX: 00 01 00 00 00 15 01 03 14 [20字节寄存器数据]
┌───┬───┬───────┬───┬────┬───┬──┬──────────────┐
│ │ │ │ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
[00][01][00][00][00][15][01][03][14][D0 D1 ... 0F]
│ │ │ │ │ │ │ └── 字节计数 │
│ │ │ │ │ │ │ └── 实际数据(20字节) │
│ │ │ │ │ │ └──────────────────── 功能码
│ │ │ │ │ └─────────────────────── 单元ID
│ │ │ │ └────────────────────────────── 长度(21字节)
│ │ │ └─────────────────────────────────── 协议ID
│ │ └──────────────────────────────────────────── 事务ID
│ └───────────────────────────────────────────────── MBAP头
└───┴────────────────────────────────────────────────── 完整报文
RTU vs TCP 核心区别
报文结构对比图
┌─────────────────────────────────────────────────────────────┐
│ Modbus RTU 报文结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┬────┬─────────────┬────────┐ │
│ │从站地址│功能│ 数据区 │CRC16 │ │
│ │ (1B) │码 │ (N Bytes) │(2B) │ │
│ └─────────┴────┴─────────────┴────────┘ │
│ 简单紧凑 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Modbus TCP 报文结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────── MBAP Header ─────────────┬─────────┐ │
│ │TransID│ProtID│Length│UnitID│ 功能码│ 数据区 │ │
│ │ (2B) │(2B) │ (2B) │ (1B) │ (1B) │(N Bytes) │ │
│ ├───────┼──────┼──────┼──────┼──────┼──────────┤ │
│ │事务ID │ 协议 │ 长度 │ 单元 │ 功能 │ 数据 │ │
│ │ │ 标识 │ │ 标识 │ 码 │ │ │
│ └───────┴──────┴──────┴──────┴──────┴──────────┘ │
│ 复杂但标准化 │
└─────────────────────────────────────────────────────────────┘
RTU vs TCP 详细对比
| 特性 | Modbus RTU | Modbus TCP |
|---|---|---|
| 传输层 | 串口(RS-232/485) | TCP/IP |
| 最大长度 | 256字节 | 260字节 |
| 地址标识 | 从站地址(1字节) | 单元标识符(1字节) |
| 寻址范围 | 1-247 | 1-247 |
| 校验方式 | CRC16 | TCP校验和 |
| 帧头开销 | 2字节(从站+功能) | 7字节(MBAP) |
| 默认端口 | 串口参数 | 502 |
| 速度 | 较慢 | 较快 |
| 传输距离 | <1200米 | 无限制(网络) |
CRC16 校验原理 (1/4)
什么是CRC?
CRC (Cyclic Redundancy Check) - 循环冗余校验
定义: 根据数据块产生简短固定位数校验码的散列函数
主要用于检测或验证数据传输/存储后可能出现的错误
┌─────────────────────────────────────────────────┐
│ 原始数据 │
│ ↓ │
│ 生成CRC校验码 │
│ ↓ │
│ 数据 + CRC ──→ 传输/存储 ──→ 接收端验证 │
└─────────────────────────────────────────────────┘
CRC16 特点
- 校验码: 2字节 (16位)
- 多项式: x¹⁶ + x¹⁵ + 1 (Modbus)
- 初始值: 0xFFFF
- 反转输入/输出: 是
CRC16 校验原理 (2/4)
计算流程图
┌─────────────────────────────────────────────────────┐
│ CRC16 计算流程 │
└─────────────────────────────────────────────────────┘
初始化:
CRC高字节 = 0xFF
CRC低字节 = 0xFF
索引指针 = 0
┌──────────────────────────────────────────┐
│ 开始处理每个字节 │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 取下一个字节 │
│ dataByte = buffer[index++] │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 计算索引 │
│ index = CRC_H ⊕ dataByte │
│ └── CRC_H = 高字节 │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 查表更新CRC │
│ CRC_H = CRC_L ⊕ TABLE[index] │
│ CRC_L = TABLE_H[index] │
└──────────────────────────────────────────�
↓
还有数据? ──→ 否 ──→ 返回CRC
↓ 是
CRC16 校验原理 (3/4)
查表法计算
查表原理
预计算的查找表 (256字节)
┌─────────────────────────────────────────┐
│ 索引 0x00: CRC_H = 0x00, CRC_L = 0x00 │
│ 索引 0x01: CRC_H = 0xC1, CRC_L = 0xC0 │
│ 索引 0x02: CRC_H = 0x81, CRC_L = 0xC1 │
│ ... │
│ 索引 0xFF: CRC_H = 0x40, CRC_L = 0x81 │
└─────────────────────────────────────────┘
计算步骤:
1. 取当前CRC高字节与数据字节异或
2. 异或结果作为索引
3. 从查找表取出新的CRC高低字节
4. 重复直到所有数据处理完
示例计算
数据: 0x01, 0x03, 0x00, 0x00, 0x0A
初始: CRC = 0xFFFF
第1字节(0x01):
index = 0xFF ⊕ 0x01 = 0xFE
CRC_H = 0x40 ⊕ aucCRCLo[0xFE] = ...
CRC_L = aucCRCHi[0xFE] = ...
... (重复5次)
最终: CRC = [CRC_L] [CRC_H]
CRC16 校验原理 (4/4)
在Modbus RTU中的应用
报文验证流程
发送端:
┌──────────────┐
│ 数据 │
│ 01 03 00 00 │
│ 00 0A │
└──────┬───────┘
↓
┌──────────────┐
│ 计算CRC16 │
│ CRC = ... │
└──────┬───────┘
↓
┌──────────────┐
│ 附加CRC │
│ 01 03 00 00 │
│ 00 0A CRC_L │
│ CRC_H │
└──────────────┘
接收端:
┌──────────────┐
│ 接收数据 │
│ 含CRC │
└──────┬───────┘
↓
┌──────────────┐
│ 计算CRC16 │
│ CRC_calc │
└──────┬───────┘
↓
┌──────────────┐
│ 比较CRC │
│ CRC_recv == │
│ CRC_calc ? │
└──────┬───────┘
↓
✓ 校验通过 ✗ 数据错误
Modbus RTU 写入报文
写入单个寄存器 (0x06)
请求报文
HEX: 01 06 00 00 00 01 [CRC_L] [CRC_H]
┌─┬─┬────┬────┬────┬──────┐
│ │ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓ ↓
[01][06][00][00][00][01][xx][xx]
│ │ │ │ │ └─ CRC16
│ │ │ │ └───── 写入值 (0x0001)
│ │ │ └───────── 寄存器地址 (0)
│ │ └───────────── 功能码 (写单个寄存器)
│ └────────────────── 从站地址 (1)
响应报文 (Echo - 回显)
HEX: 01 06 00 00 00 01 [CRC_L] [CRC_H]
└─────────────── 完全相同 ───────┘
特点: 响应报文与请求报文完全相同(回显机制)
Modbus TCP 写入报文
写入多个寄存器 (0x10)
请求报文
HEX: 00 05 00 00 00 0F 01 10 00 00 00 02 04 00 10 20 30
┌───┬───┬───────┬───┬────┬───┬────┬────┬────┬────┬────┬────
│ │ │ │ │ │ │ │ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
[00][05][00][00][00][0F][01][10][00][00][00][02][04][00][10][20][30]
│ │ │ │ │ │ │ │ │ │ │ │ └─ 数据
│ │ │ │ │ │ │ │ │ │ │ └───── 字节计数(4)
│ │ │ │ │ │ │ │ │ │ └─────────── 数量(2)
│ │ │ │ │ │ │ │ │ └─────────────── 起始地址(0)
│ │ │ │ │ │ │ │ └────────────────────── 功能码(0x10)
│ │ │ │ │ │ │ └───────────────────────────── 单元ID(1)
│ │ │ │ │ │ └─────────────────────────────────── 长度(15)
│ │ │ │ │ └──────────────────────────────────────── 协议ID(0)
│ │ │ │ └───────────────────────────────────────────── 事务ID(5)
│ │ │ └────────────────────────────────────────────────── MBAP头
│ │ └─────────────────────────────────────────────────────── 数据PDU
│ └─────────────────────────────────────────────────────────────── Modbus TCP帧
Modbus TCP 响应报文
写入多个寄存器响应
响应报文
HEX: 00 05 00 00 00 06 01 10 00 00 00 02
┌───┬───┬───────┬───┬────┬───┬────┬────┬────┐
│ │ │ │ │ │ │ │ │ │
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
[00][05][00][00][00][06][01][10][00][00][00][02]
│ │ │ │ │ │ │ │ └───── 数量(2)
│ │ │ │ │ │ │ └─────────── 起始地址(0)
│ │ │ │ │ │ └─────────────── 功能码
│ │ │ │ │ └─────────────────────── 单元ID
│ │ │ │ └────────────────────────────── 长度(6)
│ │ │ └─────────────────────────────────── 协议ID
│ │ └──────────────────────────────────────────── 事务ID
│ └───────────────────────────────────────────────── MBAP头
注意: 响应只包含MBAP头 + 功能码 + 地址 + 数量,不包含写入的数据
Modbus 完整通讯流程
RTU 通讯流程
主站 (Master) 从站 (Slave)
│ │
│ ①发送请求报文 │
├─────────────────────→│
│ [Slave][FC][Data][CRC] │
│ │
│ ②接收响应报文 │
│←─────────────────────┤
│ [Slave][FC][Data][CRC] │
│ │
│ ③CRC验证 │
│ ✓ 通过 → 处理数据 │
│ ✗ 失败 → 重传/报错 │
TCP 通讯流程
客户端 (Client) 服务器 (Server)
│ │
│ ①建立TCP连接 │
├─────────────────────→│
│ Socket(Connect) │
│ │
│ ②发送Modbus请求 │
│ ├─────────────────────→│
│ [MBAP][PDU] │
│ │
│ ③接收Modbus响应 │
│ ←─────────────────────┤
│ [MBAP][PDU] │
│ │
│ ④TCP校验和 │
│ ✓ 通过 → 处理数据 │
│ ✗ 失败 → 重传/报错 │
实际应用场景对比
工业现场设备
┌─────────────────────────────────────────────────────────────┐
│ 工业自动化系统 │
└─────────────────────────────────────────────────────────────┘
┌──────┐ RS-485总线 ┌──────┐ ┌──────┐ ┌──────┐
│ PLC │◄──────►│ 传感器│◄────►│ 执行器│◄──►│ HMI │
└──────┘ └──────┘ └──────┘ └──────┘
│ ▲ │ ▲
│ │ │ │
Modbus RTU Modbus RTU Modbus RTU Modbus RTU
┌──────┐ 以太网交换机 ┌──────┐ ┌──────────┐
│ 上位机│◄───────────►│ PLC │◄─►│ 远程I/O │
└──────┘ └──────┘ └──────────┘
│ ▲ ▲
│ Modbus TCP Modbus TCP
跨地域监控
性能与可靠性对比
传输性能
| 指标 | Modbus RTU | Modbus TCP |
|---|---|---|
| 帧开销 | 2字节 | 7字节 |
| 吞吐量 | 中等 | 高 |
| 延迟 | 低(<10ms) | 中(10-50ms) |
| 并发性 | 单路 | 多路 |
可靠性
| 特性 | Modbus RTU | Modbus TCP |
|---|---|---|
| 纠错 | CRC16 | TCP校验和+重传 |
| 干扰抑制 | 强(RS-485) | 中(需屏蔽) |
| 距离限制 | <1200m | 无限制 |
| 故障隔离 | 总线故障影响全部 | 网络节点隔离 |
如何选择 Modbus RTU 或 TCP?
选择流程图
开始
│
├─ 设备有串口?
│ │
│ ├─ 是 ──→ 距离 < 100米?
│ │ │
│ │ ├─ 是 ─→ ✅ Modbus RTU
│ │ │
│ │ └─ 否 ─→ ❌ 考虑其他方案
│ │
│ └─ 否 ──→ 设备有网口?
│ │
│ ├─ 是 ──→ ✅ Modbus TCP
│ │
│ └─ 否 ─→ ❌ 需要转换器
│
└─ 环境要求高可靠性?
│
├─ 是 ──→ 有RS-485? ─→ ✅ Modbus RTU
│
└─ 否 ──→ 需远程访问? ─→ ✅ Modbus TCP
常见问题与解决
Q1: CRC校验失败
问题: 返回报文错误
原因:
• 波特率/数据位/校验位/停止位配置不匹配
• 从站地址错误
• 通讯线路干扰
解决:
1. 检查串口参数配置
2. 验证从站地址是否正确
3. 检查线路连接和屏蔽
4. 增加超时时间
Q2: TCP连接超时
问题: 无法连接到设备
原因:
• IP地址或端口号错误
• 网络不通
• 防火墙阻止
解决:
1. ping设备IP地址
2. 检查端口号(默认502)
3. 关闭防火墙或添加白名单
4. 确认设备支持Modbus TCP
总结
核心要点回顾
Modbus RTU
- ✅ 串口通讯,紧凑高效
- ✅ CRC16校验,可靠性强
- ✅ 适合工业现场短距离
- ✅ RS-485总线支持多设备
Modbus TCP
- ✅ 网络通讯,跨地域
- ✅ MBAP头标准化
- ✅ 支持并发通讯
- ✅ 适合远程监控集成
CRC16校验
- ✅ 16位校验码
- ✅ 查表法快速计算
- ✅ 检测数据完整性
- ✅ 工业标准算法
END