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 写个程序,真正把变频器控制起来。到时候你会发现,这些理论知识全都能用上。