3、RS-485与Modbus:差分信号、帧结构与功能码
各位同学,今天我们来聊聊工业自动化里最经典的一对搭档——RS-485和Modbus。说实话,这两个东西几乎绑在一起出现,你搞自动化,绕不开它们。我最早接触这俩,是在一个污水处理项目上,当时被现场干扰搞得焦头烂额,后来才真正理解了RS-485差分信号的厉害之处。
3.1 RS-485差分信号原理
先说说RS-485。它本质上是一种物理层标准,定义了电气特性。说白了,就是规定信号怎么在线上传输。
RS-485用的是差分信号传输。什么叫差分?就是一根信号用两条线来传,一条叫A(或D+),一条叫B(或D-)。接收端看的是A和B之间的电压差,而不是对地的绝对电压。
- 逻辑1:A线电压比B线电压高(通常高200mV以上)
- 逻辑0:A线电压比B线电压低(通常低200mV以上)
为什么用差分?抗干扰能力强。你想想看,外部噪声进来,通常是共模噪声,同时叠加在A和B两条线上。接收端看的是差值,噪声就被抵消掉了。我在一个变频器密集的车间里测过,单端信号(比如RS-232)根本没法用,换成RS-485就稳得很。
关键参数:RS-485支持多点通信,一条总线上最多可以挂32个节点(标准负载),实际中我用过挂到50多个的,但需要加中继器。传输距离最远可达1200米,速率最高10Mbps(距离越短速率越高)。
我的经验:布线时A和B线一定要双绞,绞距越密越好。终端电阻也很关键,长线传输时必须在总线两端各加一个120欧姆电阻,否则信号反射会让你抓狂。
3.2 Modbus RTU帧结构
RS-485只负责把比特流从A点搬到B点,但搬的是什么内容,它不管。Modbus就是定义这个内容的协议。Modbus RTU是其中最常用的模式,用二进制编码,效率高。
一个完整的Modbus RTU帧长这样:
| 地址码 | 功能码 | 数据区 | CRC校验 |
| 1字节 | 1字节 | N字节 | 2字节 |
我来拆开讲讲:
- 地址码:从站地址,范围1-247。0是广播地址,所有从站都接收但不回复。
- 功能码:告诉从站要干什么,比如读线圈、写寄存器。
- 数据区:具体参数,比如起始地址、数据个数、要写入的值。
- CRC校验:循环冗余校验,保证数据没被篡改。这个很重要,我曾经遇到过CRC算错导致通信时好时坏的情况,排查了一整天。
帧与帧之间必须有至少3.5个字符时间的静默间隔,用来区分不同的帧。这个时间跟波特率有关,9600波特率下大约是3.5ms。嗯,这里要注意,如果间隔太短,接收端会把两帧当成一帧处理,数据就乱了。
避坑指南:我曾经在一个项目中,因为主站发送间隔没控制好,导致从站频繁报错。后来用示波器一量,发现帧间隔只有2ms,不符合3.5字符时间的要求。调整后问题解决。
3.3 功能码详解
功能码是Modbus的核心。常用的就那么几个,我列个表,大家一目了然:
| 功能码 | 名称 | 作用 | 数据区内容 |
|---|---|---|---|
| 01 (0x01) | 读线圈 | 读取DO(数字量输出)状态 | 起始地址 + 数量 |
| 02 (0x02) | 读离散输入 | 读取DI(数字量输入)状态 | 起始地址 + 数量 |
| 03 (0x03) | 读保持寄存器 | 读取AO(模拟量输出)或参数 | 起始地址 + 数量 |
| 04 (0x04) | 读输入寄存器 | 读取AI(模拟量输入)值 | 起始地址 + 数量 |
| 05 (0x05) | 写单线圈 | 控制单个DO | 线圈地址 + 值(0xFF00/0x0000) |
| 06 (0x06) | 写单寄存器 | 写入单个AO或参数 | 寄存器地址 + 值 |
| 15 (0x0F) | 写多线圈 | 批量控制DO | 起始地址 + 数量 + 字节数 + 数据 |
| 16 (0x10) | 写多寄存器 | 批量写入AO或参数 | 起始地址 + 数量 + 字节数 + 数据 |
我重点说说最常用的03和06功能码。
03功能码(读保持寄存器):这是最常用的读操作。比如你要读一个温度传感器的值,它存在保持寄存器地址100里。主站发:
01 03 00 64 00 01 C5 B5
| | | | |
地址 功能码 起始地址 数量 CRC
地址100=0x0064 读1个寄存器
从站回复:
01 03 02 01 2C B8 14
| | | |
地址 功能码 字节数 数据(0x012C=300)
你看,温度值是300,如果量程是0-100度,那实际温度就是30.0度。具体怎么换算,得看设备手册。
06功能码(写单寄存器):比如你要把PID参数写入寄存器200:
01 06 00 C8 03 E8 89 2D
| | | |
地址 功能码 地址 值(0x03E8=1000)
从站成功后会原样回复这帧数据,表示写入成功。
我的习惯:调试时先用03功能码读一遍所有寄存器,确认通信正常,再用06写。这样能快速定位问题是出在通信上还是设备本身。
最后说一句,Modbus RTU的CRC计算有点麻烦,但几乎所有编程语言都有现成的库。我早期手算过几次,后来再也不干了,直接用现成的函数。你想想看,人工算CRC,算错一次就得排查半天,何必呢?
好了,RS-485和Modbus RTU的核心内容就这些。下一章我们讲Modbus TCP,看看它跟RTU有什么不同,以及怎么在以太网上跑Modbus。