2. Modbus协议基础(上):物理层与串口通信、RTU与ASCII帧格式、功能码分类与地址映射

各位同学,咱们今天聊聊Modbus协议的基础。说实话,Modbus这玩意儿在工业现场太常见了,几乎每个做嵌入式通信的工程师都绕不开它。我最早接触Modbus是在一个温湿度监控项目里,当时被各种帧格式搞得晕头转向,后来踩了不少坑才慢慢摸清楚门道。

这一节我们重点讲三块内容:物理层怎么搭、报文怎么封装、功能码怎么用。嗯,都是硬骨头,但啃下来之后你会发现,Modbus其实没那么神秘。

2.1 物理层与串口通信

Modbus最常用的物理层就是RS-485和RS-232。我个人习惯在工业现场首选RS-485,为什么?因为它支持多点通信,抗干扰能力强,传输距离能到1200米。你想想看,一个车间里几十个传感器,用RS-485一条总线就能搞定,多省事。

RS-232呢?适合短距离、一对一通信。我在调试一个老款PLC时遇到过,它的串口就是RS-232,距离一超过15米数据就开始乱码。后来我换成了RS-485转接器,问题才解决。

串口参数这块,Modbus默认是:

  • 波特率:9600 bps(常用,也有19200、38400)
  • 数据位:8位
  • 校验位:无校验(或偶校验)
  • 停止位:1位(或2位)
注意: 我曾经因为主站和从站的校验位设置不一致,整整排查了两天。后来发现主站设的是偶校验,从站设的是无校验。所以,通信前一定要先核对串口参数,一个bit都不能错。

物理层接线也有讲究。RS-485是差分信号,A线和B线不能接反。我见过有人把A和B焊反了,结果通信时好时坏,还以为是协议栈有问题。其实说白了,就是线序搞错了。

2.2 RTU与ASCII帧格式

Modbus有两种帧格式:RTU和ASCII。RTU是二进制传输,效率高;ASCII是文本传输,可读性强但效率低。我建议在大多数嵌入式项目里用RTU,因为它的数据密度大,适合带宽有限的串口链路。

RTU帧格式:

| 地址码 | 功能码 | 数据区 | CRC校验 |
| 1字节  | 1字节  | N字节  | 2字节   |

举个例子,读取从站地址0x01的保持寄存器,起始地址0x0000,读取2个寄存器:

请求帧:01 03 00 00 00 02 C4 0B
响应帧:01 03 04 00 0A 00 14 3A 9B

这里CRC校验是Modbus RTU的关键。我刚开始写协议栈时,CRC算法总是算不对,后来发现是多项式顺序搞反了。Modbus RTU用的是CRC-16,多项式0x8005,低位在前高位在后。嗯,这个细节很容易被忽略。

ASCII帧格式:

| 起始符 | 地址码 | 功能码 | 数据区 | LRC校验 | 结束符 |
| 1字节  | 2字符  | 2字符  | 可变   | 2字符   | 2字符  |

ASCII帧以冒号':'开头,以回车换行结束。每个字节拆成两个ASCII字符发送。比如地址0x01就变成'0'和'1'。LRC校验比CRC简单,就是所有字节累加后取补码。我在一个老旧设备上用过ASCII模式,因为那个设备的CPU太慢,算CRC会卡死。所以,如果你的MCU资源紧张,ASCII模式也是个备选方案。

核心区别: RTU用CRC校验,ASCII用LRC校验。RTU效率高,ASCII可读性强。实际项目中,90%以上都用RTU。

2.3 功能码分类与地址映射

Modbus功能码分为三类:位操作、字操作和诊断操作。我整理了一个常用功能码表,你直接拿去用:

功能码 名称 操作对象 读写类型
0x01 读取线圈状态 只读
0x02 读取离散输入 只读
0x03 读取保持寄存器 读写
0x04 读取输入寄存器 只读
0x05 写单个线圈 只写
0x06 写单个寄存器 只写
0x0F 写多个线圈 只写
0x10 写多个寄存器 只写

地址映射是协议栈移植中最容易出问题的地方。Modbus地址空间分为四个区:

  • 线圈(Coil):地址范围00001-09999,对应功能码01、05、0F
  • 离散输入(Discrete Input):地址范围10001-19999,对应功能码02
  • 输入寄存器(Input Register):地址范围30001-39999,对应功能码04
  • 保持寄存器(Holding Register):地址范围40001-49999,对应功能码03、06、10

注意,这里的地址是PLC世界的5位地址。但在嵌入式协议栈里,我们通常用0-based偏移地址。比如保持寄存器40001对应偏移地址0x0000。我见过有人直接把40001当成偏移地址传给协议栈,结果读出来的数据全是错的。所以,移植时一定要做地址转换。

我的经验: 在协议栈里统一用0-based偏移地址,只在应用层做5位地址到偏移地址的映射。这样协议栈逻辑更清晰,也方便调试。

2.4 知识体系结构图

下面这张图是我画的Modbus协议基础结构图,帮你理清本章的知识脉络:

Modbus协议基础(上)知识体系 物理层与串口通信 RTU与ASCII帧格式 功能码分类与地址映射 RS-485 / RS-232 选型与接线 波特率、数据位、校验位、停止位 RTU:二进制传输,CRC校验 ASCII:文本传输,LRC校验 位操作:01、02、05、0F 字操作:03、04、06、10 4个地址区:线圈/离散输入/输入寄存器/保持寄存器 核心要点:物理层决定能不能通,帧格式决定怎么传,功能码决定做什么 移植协议栈时,这三层必须逐一验证,缺一不可

这张图把本章的三个核心模块串起来了。物理层是基础,帧格式是规范,功能码是操作接口。你移植协议栈时,这三层必须逐一验证。我一般先测物理层通不通,再测帧格式对不对,最后才测功能码能不能用。顺序搞反了,排查问题会非常痛苦。

避坑指南: 我曾经在一个项目中,功能码03读回来的数据总是少两个字节。排查了半天,发现是RTU帧的CRC校验字节被当成了数据。原因是从站发送时,CRC放在了数据区后面,但主站解析时把CRC也算进了数据长度。所以,帧格式的边界一定要搞清楚,尤其是CRC的位置。

好了,这一节的内容就到这里。Modbus协议基础(上)主要讲了物理层、帧格式和功能码。下一节我们会深入协议栈的移植细节,包括状态机设计、超时处理和多从站管理。嗯,到时候再聊。


公众号:蓝海资料掘金营,微信deep3321