3. Modbus RTU帧结构:起始位、地址码、功能码、数据区、CRC校验

好,咱们今天聊聊Modbus RTU的帧结构。说白了,就是数据在串口线上到底长什么样。我刚开始接触电力系统采集时,总觉得这东西玄乎,后来亲手抓了几次报文,发现其实没那么复杂。

3.1 帧结构概览

一个完整的Modbus RTU帧,就像一封信。你得知道信写给谁、要干什么、内容是什么、最后还得有个防伪标记。具体结构如下:

字段 长度 说明
起始位 ≥3.5字符时间 静默时间,表示一帧的开始
地址码 1字节 从站设备地址(1-247)
功能码 1字节 操作类型(读/写等)
数据区 N字节 具体数据内容
CRC校验 2字节 循环冗余校验,低字节在前
结束位 ≥3.5字符时间 静默时间,表示帧结束

嗯,这里要注意:起始位和结束位不是真正的数据字节,而是总线上的空闲时间。我见过不少新手在这上面栽跟头。

3.2 起始位——帧的"敲门砖"

起始位其实就是一个≥3.5字符时间的总线空闲。为什么是3.5?这是Modbus协议规定的,目的是让接收方知道:嘿,新的一帧要来了!

我个人习惯在调试时,用示波器抓一下总线波形。如果看到一段安静的时间,后面跟着密集的数据,那基本就是帧的开始。

小技巧: 3.5字符时间怎么算?很简单:1个字符 = 起始位(1) + 数据位(8) + 校验位(1) + 停止位(1) = 11位。波特率9600时,1位时间≈104μs,3.5字符≈4ms。记住这个数,调试时很有用。

3.3 地址码——谁是收信人?

地址码占1个字节,范围1-247。0是广播地址,248-255保留。我在项目中遇到过一个问题:有人把地址设成了0,结果所有从站都响应,总线乱成一锅粥。

避坑指南: 我曾经在变电站调试时,发现某个保护装置总是不响应。查了半天,原来是地址码设成了250,超出了Modbus标准范围。从那以后,我每次配置设备都会先确认地址范围。

3.4 功能码——要干什么?

功能码告诉从站:你要读数据还是写数据?常见的功能码有:

  • 0x03:读保持寄存器(最常用,读取遥测数据)
  • 0x04:读输入寄存器(读取遥信状态)
  • 0x06:写单个寄存器(遥控操作)
  • 0x10:写多个寄存器(批量设置参数)

你想想看,在电力系统中,我们最常用的是0x03和0x06。读电压电流用0x03,下发遥控命令用0x06。功能码选错了,设备要么不理你,要么报异常。

3.5 数据区——干货都在这里

数据区是帧的核心,长度可变。比如读寄存器时,数据区包含起始地址和寄存器数量:

// 读取从站1,从地址0开始,读2个寄存器
// 请求帧:01 03 00 00 00 02 C4 0B
// 01 - 地址码
// 03 - 功能码(读保持寄存器)
// 00 00 - 起始地址(高字节在前)
// 00 02 - 寄存器数量(读2个)
// C4 0B - CRC校验

响应帧的数据区就包含实际数据:

// 响应帧:01 03 04 00 01 00 02 79 85
// 01 - 地址码
// 03 - 功能码
// 04 - 数据字节数(2个寄存器×2字节=4字节)
// 00 01 - 第一个寄存器值(比如A相电压)
// 00 02 - 第二个寄存器值(比如B相电压)
// 79 85 - CRC校验

我个人经验: 数据区最容易出问题的是字节序。Modbus RTU默认是大端模式(高字节在前),但有些设备厂商会自作聪明改成小端。我建议你在调试时,先用一个已知值测试一下字节顺序。

3.6 CRC校验——数据的"指纹"

CRC校验是Modbus RTU的防伪标记。它用2个字节,低字节在前,高字节在后。计算时,从地址码开始到数据区结束的所有字节都要参与。

为什么需要CRC?说白了,串口通信容易受干扰。电力现场电磁环境复杂,一个CRC错误就能帮你过滤掉99%的噪声数据。

核心要点: CRC校验算法是固定的,主站和从站必须用同一套算法。我见过有人自己写CRC函数,结果和标准算法不一致,导致通信失败。建议直接用现成的库函数,别自己造轮子。

3.7 实际案例分析

我在一个光伏电站项目中,遇到过这样的问题:逆变器偶尔会返回错误数据。抓包发现,CRC校验有时正确有时错误。后来发现是RS485总线过长,信号反射导致数据位翻转。

解决方案:

  1. 在总线两端加120Ω终端电阻
  2. 降低波特率从38400降到9600
  3. 增加CRC重试机制(连续3次错误才报故障)

嗯,从那以后,通信就稳定多了。所以说,CRC校验不仅是协议要求,更是工程实践的保障。

3.8 帧结构总结

最后,我用一个完整的例子把帧结构串起来:

// 读取从站1的遥测数据(电压、电流)
// 请求:01 03 00 00 00 04 44 0A
// 响应:01 03 08 00 01 00 02 00 03 00 04 8A 7B

// 解析:
// 地址码:01(从站1)
// 功能码:03(读保持寄存器)
// 数据区:08(8字节数据)
//         00 01(电压值,换算后=1.0V)
//         00 02(电流值,换算后=2.0A)
//         00 03(功率值,换算后=3.0W)
//         00 04(频率值,换算后=4.0Hz)
// CRC:8A 7B(校验通过)

记住这个结构,你就能看懂任何Modbus RTU报文了。说白了,就是地址+功能+数据+校验,四个部分缺一不可。下次调试时,先看地址对不对,再看功能码,然后解析数据,最后验证CRC。按这个顺序来,问题基本都能定位到。