1. 课程导入
Modbus 是工业自动化领域应用最广泛的通信协议之一。它诞生于 1979 年,最初用于施耐德(Modicon)PLC 之间的串行通信,如今已扩展到串口、以太网、光纤甚至无线链路。对嵌入式与工控开发者而言,掌握 Modbus 驱动开发,意味着能够把传感器、仪表、变频器、PLC、网关等设备接入统一的数据模型,实现采集、控制与远程运维。本课程以“能写、能调、能排障”为目标,从协议原理出发,逐步构建一套可复用的 Modbus RTU/TCP 驱动。
一、Modbus 协议概述:主从模型与两种承载
Modbus 的核心是请求—响应的主从(Client/Server,传统称 Master/Slave)模型。主站发起请求,从站按地址识别自己并返回响应;从站之间不直接通信。协议定义了一个与物理层无关的协议数据单元 PDU,再由不同承载方式加上头部,形成应用数据单元 ADU。
- Modbus RTU:运行在 RS-232/RS-485 串行链路上,以二进制编码,靠 3.5 个字符时间的静默间隔划分帧,常用波特率 9600、19200、115200,配合 CRC16 校验。
- Modbus TCP:运行在以太网 TCP/IP 之上,默认端口 502,在 PDU 前加 7 字节 MBAP 报文头,用事务标识符匹配请求与响应,不再需要 CRC(由 TCP 保证可靠性)。
- Modbus ASCII:以可读字符传输,效率低,现代工程中较少使用,但理解其 LRC 校验有助于建立完整概念。
无论哪种承载,PDU 都由功能码 + 数据组成。常用功能码包括:01 读线圈、02 读离散输入、03 读保持寄存器、04 读输入寄存器、05 写单个线圈、06 写单个寄存器、15 写多个线圈、16 写多个寄存器。理解“四类数据区 + 功能码”的映射,是驱动开发的第一块基石。
二、学习目标:从协议理解到可交付驱动
完成本课程后,你应当能够独立完成以下任务:
- 准确描述 Modbus RTU/TCP 的帧结构、地址模型与异常响应机制。
- 在 MCU 或 Linux 平台上实现串口/以太网收发、帧同步、CRC 校验与超时重试。
- 设计分层驱动:物理层、链路层、协议层、应用接口层,并给出可测试的 API。
- 使用调试工具定位常见故障:无响应、CRC 错误、地址越界、粘包拆包、并发冲突。
- 把驱动接入实际场景,如采集电表、控制变频器、对接 SCADA 或网关。
三、驱动开发整体框架
一个可维护的 Modbus 驱动不应把所有逻辑塞进一个函数。推荐按四层组织:
| 层次 | 职责 | 典型实现 |
|---|---|---|
| 物理层 | 串口/网口打开、参数配置、字节收发 | UART + RS485 方向控制、socket |
| 链路层 | 帧定界、CRC 校验、MBAP 解析、超时重传 | RTU 状态机、TCP 事务表 |
| 协议层 | 功能码编解码、地址映射、异常码处理 | PDU 构造与解析 |
| 应用接口层 | 面向业务的读写 API、数据缓存、回调 | read_holding_regs()、write_coil() |
核心设计原则:协议层不关心串口还是网口;链路层不关心寄存器含义;应用层不关心字节序与 CRC。层与层之间用统一的数据结构传递 PDU,才能让 RTU 与 TCP 共用同一套上层代码。
四、典型场景与最小可用示例
假设要读取一台电表的保持寄存器,从站地址 1,起始地址 0x0000,读取 2 个寄存器。RTU 请求帧如下:
请求: 01 03 00 00 00 02 C4 0B
| | | | └── CRC16
| | | └──────── 寄存器数量 = 2
| | └────────────── 起始地址 = 0x0000
| └───────────────── 功能码 03(读保持寄存器)
└──────────────────── 从站地址 1
响应: 01 03 04 00 64 00 C8 3A 5E
| | | └── 数据:0x0064=100,0x00C8=200
| | └───── 字节数 = 4
| └──────── 功能码 03
└─────────── 从站地址 1
若从站返回异常,功能码最高位置 1,例如 01 83 02 ... 表示功能码 03 出错,异常码 02 代表“非法数据地址”。驱动必须把异常码翻译成可读错误,而不是简单丢弃。
五、常见误区与复盘
误区一:认为 TCP 不需要处理粘包。TCP 是字节流,一次 recv 可能收到半个 MBAP 或两个完整帧,必须按长度字段循环解析。
误区二:RTU 用固定延时替代 3.5 字符间隔。高波特率下固定延时会导致帧粘连,应基于波特率计算字符时间。
误区三:忽略 RS485 收发方向切换。发送完成后未及时切回接收,会丢失从站响应。
误区四:把寄存器地址与协议地址混为一谈。设备手册常以 40001 表示保持寄存器,而报文中使用偏移 0x0000,需要做映射。