2. Modbus RTU协议详解:帧结构、字节格式与传输规则

好,咱们进入正题。Modbus RTU 协议,说白了就是设备之间对话的规矩。你想想看,两个设备要通信,总得有个约定吧?谁先说话、说多长、怎么知道对方听懂了——这些都在协议里规定好了。我个人习惯把 RTU 协议拆成三块来理解:帧结构、字节格式、传输规则。咱们一块一块说。

2.1 RTU帧结构

一个完整的 RTU 报文,就像一封信。有收件人地址、有指令内容、有数据、最后还得有个校验,确保信没被篡改。帧结构就这四个部分:

字段 长度 说明
地址码 1 字节 从机地址,范围 1~247
功能码 1 字节 告诉从机要做什么
数据区 N 字节 具体参数或数据内容
CRC校验 2 字节 循环冗余校验,低字节在前

地址码:0 是广播地址,所有从机都得听。1~247 是普通从机地址。248~255 保留。我在项目中遇到过有人把地址设成 0,结果整个总线都乱了——嗯,广播地址不是随便用的。

功能码:常用的就那么几个。比如 0x03 读保持寄存器,0x06 写单个寄存器,0x10 写多个寄存器。你想想看,主机发一个 0x03,从机就知道「哦,是要读我的数据」。功能码如果从机不认识,它会返回异常码。

数据区:这个最灵活。读操作时,数据区里放的是起始地址和读取数量。写操作时,放的是要写入的数据。数据区长度不固定,但整个报文长度是有限制的——RTU 最大 256 字节。

CRC校验:这个我得多说两句。CRC 校验是 RTU 协议的命根子。我曾经调试一个设备,通信老是偶尔出错,查了三天,最后发现是 CRC 计算算法里多项式写错了。CRC 校验计算的是地址码 + 功能码 + 数据区所有字节,生成 16 位校验值。注意:低字节在前,高字节在后。这个顺序搞反了,从机直接不理你。

核心要点:帧结构里最容易出错的就是 CRC 的字节顺序。低字节在前,高字节在后。很多初学者在这里栽跟头。

2.2 字节格式

RTU 用的是串口通信,每个字节在物理线上怎么传输?这就涉及到字节格式了。说白了就是:1 个起始位 + 8 个数据位 + 1 个停止位,无校验。

为什么这么配?我个人的理解是:工业现场干扰大,校验位反而容易引入误判。RTU 的校验靠的是帧尾的 CRC,字节级别的奇偶校验反而多余。所以标准 RTU 就是 8N1——8 数据位、无校验、1 停止位。

你可能会问:那起始位呢?起始位是硬件自动加的,你写代码时不用管。串口发送一个字节时,先拉低电平表示「我要开始发了」,然后依次送出 8 个数据位,最后拉高电平表示「发完了」。停止位就是那个高电平。

小提示:波特率设置也很关键。9600、19200、115200 是常见值。主从机的波特率必须一致,否则收到的全是乱码。我建议项目初期先固定用 9600,调试通了再提速。

2.3 报文传输规则

这部分是协议的精髓。RTU 是主从模式,只有主机能主动发起通信。从机只能被动响应。主机发一个请求,从机回一个响应。就这么简单。

但这里有个关键点:帧间隔时间。RTU 协议规定,两个字节之间的间隔不能超过 1.5 个字符时间。如果超过,接收方就认为这一帧结束了。同样,帧与帧之间的间隔至少要有 3.5 个字符时间。

为什么要这样?因为 RTU 没有帧头帧尾标记,它靠时间间隔来区分帧。你想想看,如果主机连续发数据,从机怎么知道哪里是一帧的结束?就是靠这个时间间隔。

我举个例子:波特率 9600,1 个字符时间大约是 1ms(1 起始位 + 8 数据位 + 1 停止位 = 10 位,9600 波特率下每位约 0.104ms,10 位约 1.04ms)。那么 1.5 个字符时间就是 1.56ms,3.5 个字符时间就是 3.64ms。实际编程时,我一般取整:1.5 字符时间按 1.75ms 算,3.5 字符时间按 4ms 算。留点余量,避免误判。

避坑指南:我曾经在一个项目中,从机老是丢帧。排查后发现是定时器精度不够,导致帧间隔判断出错。后来改用硬件定时器,问题就解决了。记住:帧间隔时间必须精确,不能靠软件延时凑合。

还有一个规则:响应超时。主机发出请求后,从机必须在规定时间内响应。这个时间没有硬性规定,一般设 100ms~1000ms。如果超时没收到响应,主机可以重发请求。我习惯设 500ms,重试 3 次。如果 3 次都超时,就报通信故障。

嗯,这里要注意:从机收到请求后,如果发现功能码不认识、地址不对、数据格式错误,它要返回异常响应。异常响应的功能码是请求功能码 + 0x80,数据区放异常码。比如请求功能码 0x03,异常响应功能码就是 0x83。

常见的异常码有:

  • 01:功能码不支持
  • 02:寄存器地址越界
  • 03:数据值非法
  • 04:从机故障

我建议你在开发从机时,先把异常处理写好。因为调试阶段,主机发过来的请求很可能不合法。如果从机不返回异常,主机就不知道哪里错了,排查起来很痛苦。

最后说一句:RTU 协议虽然简单,但坑不少。帧结构、字节格式、传输规则,这三块必须吃透。尤其是帧间隔时间,很多通信不稳定都是这个引起的。你写代码时,一定要用定时器精确控制,别用软件延时。