2、通信协议入门:Modbus RTU 协议详解、寄存器地址映射、功能码(03、06、16)的使用
好,咱们进入正题。上一章我们把变频器的硬件接口搞定了,线也接上了。但线接上了,设备之间怎么“说话”?这就得靠协议了。
在工业现场,Modbus RTU 可以说是最通用的“普通话”。我做了这么多年项目,十台设备里至少有七八台都支持它。说白了,它简单、稳定、够用。你想想看,一个协议能从上世纪70年代活到现在,肯定有它的道理。
2.1 Modbus RTU 到底是个啥?
Modbus RTU 是一种主从通信协议。什么意思呢?就是一台设备当“老大”(主站),比如你的PLC或者电脑;其他设备当“小弟”(从站),比如变频器。老大问,小弟答。小弟之间不能直接聊天。
它的物理层通常是 RS-485,就是我们上一章讲的那个。数据格式呢,是8位数据位、1位停止位、无校验(或者偶校验)。波特率常见的有9600、19200、38400。我个人习惯先用9600,稳定第一,等调通了再往上提速度。
核心要点:Modbus RTU 报文 = 地址 + 功能码 + 数据 + CRC校验。一个报文就是一问一答,干净利落。
2.2 寄存器地址映射——你得知道“门牌号”
变频器内部有很多参数,比如频率、电流、电压。Modbus 怎么访问它们?靠寄存器地址。每个参数都有一个对应的地址,就像每家每户的门牌号。
Modbus 定义了四种数据对象,但咱们变频器最常用的是保持寄存器(Holding Register)。它可读可写,16位宽。变频器的设定频率、运行命令,一般都映射到这里。
这里有个坑,我当年刚入行时踩过。不同厂家对地址的表示方法不一样。有的用协议地址(比如 0x0000),有的用PLC地址(比如 40001)。
举个例子:
| 参数名称 | 协议地址(十六进制) | PLC地址(十进制) | 说明 |
|---|---|---|---|
| 运行频率设定 | 0x2000 | 48193 | 写入目标频率,单位0.01Hz |
| 当前运行频率 | 0x3000 | 49153 | 读取当前实际频率 |
| 运行命令 | 0x2001 | 48194 | 写入1启动,写入0停止 |
| 故障代码 | 0x3100 | 49281 | 读取故障状态 |
我的经验:拿到变频器手册后,第一件事就是找到“Modbus通信地址表”。别信自己的记忆力,一定要做笔记。我曾经因为记错一个地址,排查了整整一个下午。
2.3 功能码 03——读取寄存器
功能码 03 是用来读取保持寄存器的。你想知道变频器当前的频率、电流、电压?用03就对了。
报文格式很简单:
主站发送:
从站地址 + 0x03 + 起始地址高8位 + 起始地址低8位 + 寄存器数量高8位 + 寄存器数量低8位 + CRC低8位 + CRC高8位
从站响应:
从站地址 + 0x03 + 字节数 + 数据1高8位 + 数据1低8位 + ... + CRC低8位 + CRC高8位
举个例子,读取地址 0x3000 开始的2个寄存器(当前频率和当前电流):
发送:01 03 30 00 00 02 CRC_LO CRC_HI
响应:01 03 04 13 88 00 32 CRC_LO CRC_HI
这里 0x1388 换算成十进制是 5000,如果单位是0.01Hz,那就是50.00Hz。0x0032 是50,如果单位是0.1A,那就是5.0A。
注意:数据是高位在前(Big-Endian),别搞反了。我见过有人把高低字节弄反,读出来的频率忽大忽小,还以为变频器坏了。
2.4 功能码 06——写单个寄存器
功能码 06 用来写一个寄存器。比如你要设定频率、启停变频器,就用它。
主站发送:
从站地址 + 0x06 + 寄存器地址高8位 + 寄存器地址低8位 + 数据高8位 + 数据低8位 + CRC低8位 + CRC高8位
从站响应: 就是把发送的报文原样返回,表示写成功了。
举个例子,把频率设定为30.00Hz(0x0BB8),写入地址 0x2000:
发送:01 06 20 00 0B B8 CRC_LO CRC_HI
响应:01 06 20 00 0B B8 CRC_LO CRC_HI
小技巧:写命令时,建议先读一下当前值,确认后再写。特别是写运行命令这种关键操作,别手抖写错了。
2.5 功能码 16——写多个寄存器
功能码 16(0x10)用来连续写多个寄存器。比如你要同时设定频率、加减速时间、运行方向,用16一次搞定,效率高很多。
主站发送:
从站地址 + 0x10 + 起始地址高8位 + 起始地址低8位 + 寄存器数量高8位 + 寄存器数量低8位 + 字节数 + 数据1高8位 + 数据1低8位 + ... + CRC低8位 + CRC高8位
从站响应:
从站地址 + 0x10 + 起始地址高8位 + 起始地址低8位 + 寄存器数量高8位 + 寄存器数量低8位 + CRC低8位 + CRC高8位
举个例子,从地址 0x2000 开始写3个寄存器(频率、命令、加速时间):
发送:01 10 20 00 00 03 06 0B B8 00 01 00 64 CRC_LO CRC_HI
响应:01 10 20 00 00 03 CRC_LO CRC_HI
这里 0x0BB8 是频率(30.00Hz),0x0001 是启动命令,0x0064 是加速时间(10.0秒)。
为什么用16而不是多次06? 因为多次06需要多次握手,通信效率低。而且如果中间某次写失败了,参数就不一致了。16功能码保证了原子性——要么全写成功,要么全不写。
2.6 避坑指南——我踩过的那些坑
嗯,说到这,我得分享几个实战中遇到的坑:
- CRC校验算不对: 我曾经用了一个网上找的CRC算法,结果死活通信不上。后来发现是多项式用错了。Modbus RTU 用的是 0xA001,别搞混。
- 地址偏移问题: 有些变频器手册上写的地址是 40001,但实际协议地址是 0x0000。你直接发 0x0000 可能不对,得减去 40001 的偏移。这个一定要看手册的说明。
- 写命令后没响应: 有时候写成功了,但变频器没反应。检查一下是不是写到了只读寄存器。比如有些变频器的当前频率是只读的,你写它当然不理你。
- 波特率不匹配: 这个最基础,但也最容易忽略。变频器默认波特率可能是 19200,你的上位机设成了 9600,那肯定对不上话。
重要提醒: 第一次调试时,先用一个简单的功能码03读一个已知的寄存器。比如读设备型号、版本号。如果这个能通,说明物理层和协议层都没问题。然后再去写。这叫“由简入繁”,能省很多排查时间。
2.7 小结
Modbus RTU 不难,但细节决定成败。记住三点:地址要对、CRC要准、数据格式要一致。下一章,我们就用 Python 写个程序,真正把变频器控制起来。到时候你会发现,这些理论知识全都能用上。