GKMLT通讯工具箱(WPF MVVM) - 02-Modbus RTU 与 TCP 报文格式、原理与CRC校验
目录
- 通讯方式对比
- Modbus协议概述
- Modbus RTU详解
- Modbus 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
