4. Modbus协议入门:Modbus RTU与ASCII模式、功能码详解(01-06)、报文实例解析

各位同学,咱们今天来聊聊Modbus协议。说实话,这玩意儿在电力系统里太常见了。我入行那会儿,第一个接触的通信协议就是Modbus。当时觉得挺复杂,后来摸透了才发现,它其实很朴实。

Modbus协议,说白了就是主站问、从站答。主站发一条指令,从站收到后按要求回复。就这么简单。但这里头有几个关键点,咱们得掰开揉碎了讲。

4.1 Modbus的两种传输模式:RTU vs ASCII

Modbus协议有两种传输模式:RTU模式和ASCII模式。嗯,这里要注意,这两种模式不能混用。你设备设成RTU,主站也得是RTU,否则鸡同鸭讲。

对比项 RTU模式 ASCII模式
数据表示 二进制(16进制) ASCII字符(0-9, A-F)
字节格式 8位数据位 7位数据位
帧间隔 3.5字符时间 1秒
校验方式 CRC-16 LRC
传输效率 高(约是ASCII的2倍)
调试难度 需要工具解析 可直接阅读

我个人习惯用RTU模式。为什么?效率高啊!同样一条指令,RTU可能就8个字节,ASCII得写16个字符。我在变电站项目里,几百个设备轮询,用RTU能省一半时间。

但ASCII模式也有它的好处。我记得有一次现场调试,手头没有协议分析仪,我就用串口助手直接看ASCII报文,一眼就能看出问题在哪。RTU的话,你得拿计算器算CRC,麻烦得很。

核心区别一句话:RTU是机器读的,高效但难懂;ASCII是人读的,直观但慢。

4.2 功能码详解:01到06

功能码是Modbus协议的灵魂。它告诉从站:你要干什么。咱们先看最常用的6个功能码。

功能码01:读取线圈状态

这个功能码用来读取开关量输出。说白了,就是读继电器的状态。0表示断开,1表示闭合。

  • 请求格式:从站地址 + 0x01 + 起始地址(2字节) + 数量(2字节) + CRC
  • 响应格式:从站地址 + 0x01 + 字节数 + 数据 + CRC

避坑指南:我曾经遇到过一个问题,读32个线圈,结果返回的数据只有4个字节。后来才发现,数量是按位算的,不是按字节。32个线圈就是4个字节,没错。

功能码02:读取离散输入

这个和01很像,但读的是开关量输入。区别在哪?线圈状态可以写,离散输入只能读。你想想看,一个按钮按下去,你只能知道它按没按,不能去改它的状态。

功能码03:读取保持寄存器

这是最常用的功能码。保持寄存器是16位的,可以存电压、电流、功率这些模拟量。我做过一个项目,一个装置里有200多个保持寄存器,全是遥测数据。

小技巧:Modbus的地址是从0开始的,但很多设备手册写的是从1开始。比如手册说"地址40001",实际报文里地址是0x0000。这个坑我踩过,大家注意。

功能码04:读取输入寄存器

和03的区别?输入寄存器只能读,不能写。一般用来存传感器采集的原始数据。比如温度传感器的AD值,你读回来自己换算。

功能码05:写单个线圈

控制单个继电器。数据是0xFF00(闭合)或0x0000(断开)。注意,不是0x0001!我刚开始做的时候,写了个0x0001过去,设备没反应。查了半天手册才发现,必须是0xFF00。

功能码06:写单个寄存器

写一个16位的值。比如设定过流保护值、修改通信参数。这个比较简单,数据直接填你要写的值就行。

4.3 报文实例解析

光说不练假把式。咱们来看几个真实报文。

实例1:读取3个保持寄存器(功能码03)

假设从站地址是0x01,起始地址0x0000,读取3个寄存器。

请求报文:01 03 00 00 00 03 05 CB
  - 01:从站地址
  - 03:功能码
  - 00 00:起始地址(高字节在前)
  - 00 03:读取数量(3个)
  - 05 CB:CRC校验

响应报文:01 03 06 02 1B 00 0A 01 2C 8A 7D
  - 01:从站地址
  - 03:功能码
  - 06:数据字节数(3个寄存器×2字节=6)
  - 02 1B:寄存器1的值(0x021B = 539)
  - 00 0A:寄存器2的值(0x000A = 10)
  - 01 2C:寄存器3的值(0x012C = 300)
  - 8A 7D:CRC校验

你看,响应报文里每个寄存器占2个字节。高字节在前,低字节在后。这是大端模式,Modbus默认就是大端。

实例2:写单个线圈(功能码05)

把地址0x000A的线圈置为闭合。

请求报文:01 05 00 0A FF 00 6C 3A
  - 01:从站地址
  - 05:功能码
  - 00 0A:线圈地址
  - FF 00:闭合指令
  - 6C 3A:CRC校验

响应报文:01 05 00 0A FF 00 6C 3A
  - 注意:响应报文和请求报文完全一样!

嗯,这里要注意,功能码05的响应就是原样返回请求。如果返回不一样,说明写失败了。

实例3:写单个寄存器(功能码06)

把地址0x0001的寄存器写入0x03E8(十进制1000)。

请求报文:01 06 00 01 03 E8 D8 1A
  - 01:从站地址
  - 06:功能码
  - 00 01:寄存器地址
  - 03 E8:要写入的值(1000)
  - D8 1A:CRC校验

响应报文:01 06 00 01 03 E8 D8 1A
  - 同样,响应和请求一致

重要提醒:写操作一定要谨慎!我曾经在调试时,不小心把保护装置的定值写错了,导致装置误动作。虽然没出大事,但被领导批了一顿。所以,写操作前一定要确认地址和数值。

4.4 知识体系图

下面这张图,把Modbus协议的核心逻辑串起来了。大家看看,心里有个谱。

Modbus协议知识体系 Modbus协议 传输模式 RTU模式 ASCII模式 常用功能码 (01-06) 01: 读线圈 02: 读离散输入 03: 读保持寄存器 04: 读输入寄存器 05/06: 写操作 核心:主站问 → 从站答,功能码决定操作类型

这张图把Modbus协议拆成了两大部分:传输模式和功能码。传输模式决定了数据怎么包装,功能码决定了数据怎么操作。两者结合,就是完整的Modbus通信。

4.5 实战经验总结

最后,我分享几个实战中的小经验:

  1. CRC校验一定要算对。我见过太多人栽在CRC上。网上有现成的计算工具,但建议自己写个函数,调试时方便。
  2. 报文超时时间要设合理。一般设500ms到1s。太短容易误判,太长影响轮询效率。
  3. 从站地址不要设0。0是广播地址,所有从站都会响应。但广播只适用于写操作,读操作不能用。
  4. 调试时先用ASCII模式。等通信通了,再切回RTU。这样能快速定位是协议问题还是硬件问题。

好了,Modbus协议入门就讲到这里。内容不少,但都是干货。大家回去找个Modbus模拟器练练手,发几条报文看看响应,比光看书强多了。

专注资料整理