第2章:Modbus协议基础
各位同学好,我是老张。今天咱们聊聊Modbus协议的基础知识。说实话,我在工业自动化这行摸爬滚打十几年,Modbus是我见过最「长寿」的通信协议之一。你想想看,一个上世纪70年代末诞生的协议,到现在还在大量使用,这本身就说明了很多问题。
2.1 Modbus协议发展历史
Modbus是1979年由Modicon公司(就是现在的施耐德电气)发明的。当时的目的很简单——让PLC和智能设备之间能互相说话。我记得我第一次接触Modbus是在2008年,那时候刚入行,师傅扔给我一本Modbus协议手册,说:「把这个啃下来,你就能搞定80%的现场通信问题。」
嗯,这话还真不夸张。
Modbus的发展大致经历了这么几个阶段:
- 1979年:Modicon发布Modbus协议,最初用于PLC通信
- 1990年代:Modbus RTU/ASCII成为工业现场的事实标准
- 2000年:Modbus TCP/IP发布,开始拥抱以太网
- 2004年:Modbus组织成立,协议正式成为开放标准
- 至今:Modbus依然是工业物联网的首选协议之一
核心观点:Modbus能活这么久,靠的就是「简单」二字。协议栈只有7层OSI模型中的3层,实现成本极低。我在项目中见过用8位单片机跑Modbus RTU的,照样稳得很。
2.2 Modbus RTU与Modbus TCP的区别
这个问题我几乎每次培训都会被问到。说白了,RTU和TCP就是Modbus的两种「马甲」——核心数据格式一样,但传输方式不同。
我给大家画个对比表,一目了然:
| 对比项 | Modbus RTU | Modbus TCP |
|---|---|---|
| 物理层 | RS-232/RS-485 | 以太网 |
| 传输速率 | 9.6kbps ~ 115.2kbps | 10/100Mbps |
| 最大节点数 | 32(RS-485) | 理论上无限制 |
| 传输距离 | 1200米(RS-485) | 100米(交换机可扩展) |
| 地址范围 | 1~247 | IP地址+单元标识符 |
| 校验方式 | CRC16 | TCP/IP协议栈自带 |
| 报文格式 | 地址+功能码+数据+CRC | MBAP头+功能码+数据 |
实战经验:我个人习惯是:现场总线用RTU,跨系统通信用TCP。为什么?RTU在强电磁干扰环境下更稳定,而TCP方便远程调试。有一次我在一个钢厂项目里,RTU线缆从控制室拉到轧机旁边,1200米距离,一点问题没有。
这里有个关键区别大家要注意:RTU是主从架构,TCP是对等架构。什么意思?RTU模式下,只有主机能发起通信,从机只能被动响应。而TCP模式下,任何设备都可以主动发起请求。这个差异在实际项目中影响很大。
2.3 Modbus协议栈结构
Modbus协议栈其实很简单,就三层:
- 应用层:定义功能码和数据格式
- 数据链路层:处理报文封装和校验
- 物理层:负责电气信号传输
我画了一张结构图,大家看看就明白了:
咱们重点说说应用层的功能码。这是Modbus最核心的部分,我给大家列几个常用的:
| 功能码 | 名称 | 操作对象 | 读写类型 |
|---|---|---|---|
| 01 (0x01) | 读取线圈状态 | DO(数字量输出) | 只读 |
| 02 (0x02) | 读取离散输入 | DI(数字量输入) | 只读 |
| 03 (0x03) | 读取保持寄存器 | AO(模拟量输出) | 只读 |
| 04 (0x04) | 读取输入寄存器 | AI(模拟量输入) | 只读 |
| 05 (0x05) | 写单个线圈 | DO | 只写 |
| 06 (0x06) | 写单个寄存器 | AO | 只写 |
| 15 (0x0F) | 写多个线圈 | DO | 只写 |
| 16 (0x10) | 写多个寄存器 | AO | 只写 |
避坑指南:我曾经在一个项目中,把功能码03和04搞混了。03读的是保持寄存器(可读可写),04读的是输入寄存器(只读)。当时调试了半天,数据死活不对。后来才发现是功能码用错了。大家记住:保持寄存器是「可写的」,输入寄存器是「只读的」。
再来说说数据格式。Modbus的数据模型分四种:
- 线圈(Coil):1位,可读可写,对应DO
- 离散输入(Discrete Input):1位,只读,对应DI
- 输入寄存器(Input Register):16位,只读,对应AI
- 保持寄存器(Holding Register):16位,可读可写,对应AO
这里有个小技巧:保持寄存器是Modbus通信中最常用的对象。为什么?因为它既能读又能写,而且16位的数据宽度足够表示大多数工业参数。我在做机器人通信接口时,90%的数据都放在保持寄存器里。
最后说说报文格式。RTU和TCP的报文结构略有不同:
// Modbus RTU报文格式
[从站地址(1字节)] [功能码(1字节)] [数据(N字节)] [CRC低字节] [CRC高字节]
// Modbus TCP报文格式
[事务标识符(2字节)] [协议标识符(2字节)] [长度(2字节)] [单元标识符(1字节)] [功能码(1字节)] [数据(N字节)]
你看,TCP报文比RTU多了个MBAP头(7字节)。这个头的作用是让TCP能识别不同的Modbus会话。嗯,这里要注意:TCP报文没有CRC校验,因为TCP/IP协议栈自己会处理数据完整性。
个人建议:刚开始学Modbus的同学,我建议先从RTU入手。为什么?RTU的报文结构更直观,而且调试工具多。等你把RTU搞明白了,TCP就是加了个头的事。我当年就是这么过来的。
好了,这一章的内容就到这里。Modbus协议看似简单,但里面的细节不少。下一章我们会动手写代码,用Python实现一个Modbus RTU通信接口。到时候大家就能体会到,理论知识和实战经验结合起来,才是真正的「会了」。