RS485通信超时全面排查:终端电阻、A/B线、DE方向控制、串口参数和Modbus一次讲透

RS485通信超时全面排查:终端电阻、A/B线、DE方向控制、串口参数和Modbus一次讲透

前言

在 STM32、GD32、ESP32、PLC 或工业控制设备开发中,RS485 是非常常见的一种通信方式。

实际调试时,经常会遇到这样的现象:

text 复制代码
主机发送请求
    ↓
串口发送函数执行成功
    ↓
等待从机回复
    ↓
一直没有收到数据
    ↓
Timeout

很多人看到"通信超时",第一反应就是:

是不是 Modbus 协议代码写错了?

实际上,RS485通信超时只是最终表现,并不能直接说明是哪一层出了问题。

问题可能发生在:

text 复制代码
MCU UART
   ↓
DE/RE方向控制
   ↓
RS485收发器
   ↓
A/B差分线
   ↓
终端电阻
   ↓
线缆和拓扑
   ↓
对端RS485收发器
   ↓
对端UART
   ↓
Modbus/自定义协议

任何一个环节异常,都可能最终表现为:

text 复制代码
Timeout

因此调试 RS485 最重要的不是反复修改超时时间,而是先判断:

到底是没有发送出去,还是已经发送出去但对端没有收到,还是对端收到了却没有回复,或者回复了但主机没有接收到。

本文将从物理层到协议层,详细讲解 RS485 通信超时的完整排查方法,包括:

  • RS485到底是什么;
  • UART和RS485有什么关系;
  • A/B差分信号如何工作;
  • 为什么需要120Ω终端电阻;
  • A/B接反有什么现象;
  • DE/RE方向控制为什么特别容易出错;
  • 为什么TXE不能代表最后一个字节真正发送完成;
  • 波特率、数据位、校验位、停止位如何影响通信;
  • RS485和Modbus RTU是什么关系;
  • 如何用示波器和逻辑分析仪定位故障;
  • STM32中如何正确控制RS485收发方向;
  • 通信超时应该按照什么顺序排查。

一、RS485到底是什么?

首先要明确:

RS485不是通信协议,它是一种电气接口标准。

RS485主要规定的是:

text 复制代码
电压特性
差分信号
驱动能力
接收能力
总线连接形式
节点数量
共模范围等

RS485本身并不知道:

text 复制代码
设备地址
功能码
寄存器
CRC
帧头
帧尾

这些属于更上层的通信协议。

例如:

text 复制代码
RS485 + Modbus RTU
RS485 + 自定义协议
RS485 + Profibus某些物理实现

所以一定要区分:

text 复制代码
RS485 = 物理层

Modbus RTU = 应用层/通信协议

这也是为什么:

RS485线路完全正常,并不代表Modbus一定能通信。


二、UART和RS485是什么关系?

很多初学者会把 UART 和 RS485 混在一起。

实际上可以这样理解:

text 复制代码
MCU
 ↓
UART
 ↓
TX / RX
 ↓
RS485收发器
 ↓
A / B
 ↓
双绞线

UART产生的是:

text 复制代码
单端数字信号

例如:

text 复制代码
TX = 0V / 3.3V

RX = 0V / 3.3V

而RS485收发器负责将UART信号转换为:

text 复制代码
差分信号

通过:

text 复制代码
A
B

两根线传输。

另一端再通过RS485收发器,把差分信号恢复为UART RX数字信号。

所以完整链路是:

text 复制代码
发送MCU UART_TX
        ↓
RS485 Transceiver
        ↓
      A / B
        ↓
RS485 Transceiver
        ↓
接收MCU UART_RX

三、为什么RS485要使用差分信号?

普通UART直接使用单端信号:

text 复制代码
TX
GND

当通信距离变长,或者现场存在:

text 复制代码
电机
变频器
继电器
开关电源
大电流设备

时,很容易受到干扰。

RS485使用两根差分线:

text 复制代码
A
B

接收器主要判断:

text 复制代码
VA - VB

而不是单独判断某一根线相对于地的绝对电压。

如果外界噪声同时耦合到A、B:

text 复制代码
A + 噪声
B + 噪声

那么差分计算:

text 复制代码
(A + Noise) - (B + Noise)

= A - B

公共噪声在理想情况下会被抵消。

这就是差分通信抗干扰能力强的重要原因。


四、RS485为什么经常是半双工?

工程中最常见的是:

text 复制代码
2线RS485

也就是:

text 复制代码
A
B

一对差分线同时负责:

text 复制代码
发送
+
接收

因此:

同一时刻通常只能由一个节点驱动总线。

这就是:

text 复制代码
Half-Duplex
半双工

例如:

text 复制代码
主机发送请求
    ↓
主机释放总线
    ↓
从机接管总线
    ↓
从机发送回复

因此RS485通信中有一个非常重要的东西:

text 复制代码
方向控制

这通常就是:

text 复制代码
DE
/RE

五、典型RS485收发器有哪些引脚?

常见RS485芯片可能包含:

text 复制代码
RO
/RE
DE
DI
A
B
VCC
GND

可以简单理解:

text 复制代码
DI
Driver Input
发送数据输入

RO
Receiver Output
接收数据输出

DE
Driver Enable
发送驱动使能

/RE
Receiver Enable
接收使能

A/B
差分总线

连接到MCU:

text 复制代码
MCU TX ───── DI

MCU RX ←──── RO

GPIO ─────── DE

GPIO ─────── /RE

很多工程会把:

text 复制代码
DE
/RE

连接到一起,由一个GPIO控制。


六、DE和/RE通常怎么控制?

典型半双工RS485:

发送状态

通常:

text 复制代码
DE = 1
/RE = 1

也就是:

text 复制代码
发送器开启
接收器关闭

如果DE和/RE绑在一起:

text 复制代码
DIR = 1
→ 发送

接收状态

通常:

text 复制代码
DE = 0
/RE = 0

即:

text 复制代码
发送器关闭
接收器开启

于是:

text 复制代码
DIR = 0
→ 接收

不过:

不同RS485收发器引脚定义可能不同,必须查看具体芯片数据手册。

不能机械认为所有芯片逻辑都完全一样。


七、RS485通信超时到底是什么意思?

假设主机发送:

text 复制代码
01 03 00 00 00 02 CRC_L CRC_H

然后进入:

c 复制代码
Wait_Response();

如果在规定时间内没有接收到符合要求的数据,就会:

text 复制代码
Timeout

但是这个Timeout可能对应四种完全不同的情况。


情况1:主机压根没发出去

例如:

text 复制代码
UART没启动
DE没有打开
RS485收发器没供电
TX引脚配置错误

情况2:主机发了,但是从机没收到

例如:

text 复制代码
A/B接反
线路断开
终端异常
波特率不一致
信号畸变

情况3:从机收到,但没有回复

例如:

text 复制代码
设备地址错误
CRC错误
功能码不支持
寄存器地址错误
从机程序异常

情况4:从机回复了,但主机没收到

例如:

text 复制代码
主机DE没有及时关闭
主机一直占着总线
接收器没有打开
UART RX异常
回复时间窗口不合理

所以:

Timeout只是现象,第一步必须判断故障发生在哪一个方向。


八、第一步:先确认主机到底有没有发送数据

不要一看到:

c 复制代码
HAL_UART_Transmit();

返回:

text 复制代码
HAL_OK

就认为RS485总线一定已经有数据。

因为:

text 复制代码
UART发送成功

和:

text 复制代码
RS485差分总线真正发送成功

不是完全一回事。

建议检查:

text 复制代码
MCU TX
↓
收发器DI
↓
CAN? ×
↓
RS485 A/B

正确链路应该是:

text 复制代码
UART_TX
 ↓
DI
 ↓
RS485 Driver
 ↓
A/B差分波形

九、最简单的方法:先测UART TX

使用:

text 复制代码
示波器
逻辑分析仪

直接观察:

text 复制代码
MCU UART_TX

如果TX完全没有波形:

说明问题还没有到RS485层。

优先检查:

text 复制代码
UART初始化
GPIO复用
波特率
发送函数
UART时钟
程序流程

十、如果TX有波形,A/B没有波形怎么办?

这时重点检查:

text 复制代码
RS485收发器

尤其是:

text 复制代码
供电
DE
DI
芯片损坏
A/B线路

例如:

text 复制代码
TX有数据
↓
DI也有数据
↓
DE一直为0

那么发送驱动器没有开启。

结果:

text 复制代码
A/B没有有效差分波形

从机自然收不到。


十一、第二个重点:120Ω终端电阻

RS485通信超时中非常常见的硬件问题就是:

text 复制代码
终端电阻配置错误

典型长距离、高速RS485总线结构:

text 复制代码
       120Ω                              120Ω
A ─────┬──────────────────────────────────┬────
       │                                  │
B ─────┴──────────────────────────────────┴────

     Node1     Node2      Node3       NodeN

重点是:

终端电阻通常加在总线物理两端,而不是每个节点都加。


十二、为什么终端电阻通常是120Ω?

双绞线具有一定:

text 复制代码
特性阻抗

工程中常见RS485线缆特性阻抗约:

text 复制代码
120Ω

如果线路末端没有匹配:

text 复制代码
信号到达线缆末端
       ↓
发生反射
       ↓
反射波返回
       ↓
与原始信号叠加
       ↓
出现振铃和过冲

可能导致接收器错误判断。

尤其在:

text 复制代码
高波特率
长线
复杂拓扑

情况下更加明显。


十三、为什么短距离低速时没终端也可能通信?

这是很常见的现象。

例如:

text 复制代码
9600 baud
线长30cm
实验桌测试

即使没有120Ω,也可能完全正常。

于是开发者容易产生错觉:

终端电阻根本没必要。

但是当变成:

text 复制代码
115200 baud
几十米电缆
复杂工业现场
多个节点

以后,反射问题就可能非常明显。

所以:

"实验桌上能通信"不能证明终端设计一定合理。


十四、断电测A/B阻值为什么经常约60Ω?

如果总线两端分别有:

text 复制代码
120Ω
120Ω

两颗电阻在A/B之间形成并联:

text 复制代码
R = 120 || 120

计算:

text 复制代码
R = 60Ω

因此:

断电以后,从总线上测A与B之间,如果两端终端都存在,常见值约为60Ω。

这是非常好用的排查方法。


十五、测到不同电阻说明什么?

约60Ω

通常说明:

text 复制代码
两个120Ω终端都存在

约120Ω

通常可能是:

text 复制代码
只有一个终端

或者:

text 复制代码
另一端线路断开

明显小于60Ω

可能:

text 复制代码
终端电阻太多

例如4个节点全部加120Ω:

text 复制代码
120 || 120 || 120 || 120

≈ 30Ω

总线负载过重。


接近无穷大

可能:

text 复制代码
没有终端
线路断路
连接器未接

很小,例如几Ω

重点检查:

text 复制代码
A/B短路
收发器损坏
PCB焊接问题

十六、为什么每个节点都加120Ω是错误的?

例如4个节点:

text 复制代码
Node1
Node2
Node3
Node4

每个节点都接:

text 复制代码
120Ω

等效:

text 复制代码
120 / 4
=
30Ω

收发器驱动负载变得非常重。

结果可能出现:

text 复制代码
差分幅度下降
波形变差
功耗增加
通信不稳定

所以通常应该:

只在总线物理两端终接。


十七、第三个重点:A/B线有没有接反?

这是RS485现场调试最经典的问题之一。

正常情况下:

text 复制代码
主机 A ───────── 从机 A

主机 B ───────── 从机 B

如果接成:

text 复制代码
主机 A ───────── 从机 B

主机 B ───────── 从机 A

差分极性就反了。

于是从机可能看到:

text 复制代码
所有bit逻辑反向

自然无法正确解析UART数据。

最终表现:

text 复制代码
主机发送正常
↓
从机无有效报文
↓
从机不回复
↓
主机Timeout

十八、但A/B命名并不完全统一

这是RS485非常坑的一点。

不同芯片、模块和设备厂家的:

text 复制代码
A
B

命名习惯可能存在差异。

有的设备可能标:

text 复制代码
A+
B-

有的可能:

text 复制代码
A-
B+

还有:

text 复制代码
D+
D-

因此:

不能永远只凭"A接A、B接B"判断。

最好查看:

text 复制代码
收发器Datasheet
设备说明书
原理图
接口电气定义

确认:

text 复制代码
哪个端子对应正差分
哪个端子对应负差分

必要时直接交换A/B进行验证。


十九、A/B接反最典型的现象

可能表现为:

text 复制代码
TX波形正常
DE控制正常
A/B上有明显波形
主机发送函数正常
但是永远收不到从机回复

这种情况下:

交换A/B是非常值得快速尝试的一步。

但正式产品必须根据文档确认,而不是长期依靠"试出来"。


二十、第四个重点:DE方向控制

RS485半双工最容易产生的软件Bug,几乎都和:

text 复制代码
DE

有关。

正常发送过程应该是:

text 复制代码
默认接收
DE = 0

    ↓

准备发送
DE = 1

    ↓

UART发送数据

    ↓

等待最后一个bit真正发送完成

    ↓

DE = 0

    ↓

切回接收

    ↓

等待从机回复

关键就在:

什么时候把DE拉低。


二十一、DE拉高太晚会发生什么?

如果已经开始UART发送:

text 复制代码
TX起始位已经出现

但DE还没有拉高:

text 复制代码
RS485发送器还没有打开

那么最前面的:

text 复制代码
起始位
第一个字节部分bit

可能丢失。

从机收到的是一个残缺帧。

自然不会回复。

所以:

DE必须在UART真正开始发送以前提前进入发送状态。


二十二、DE拉低太早会发生什么?

这是最经典的问题。

例如程序:

c 复制代码
RS485_TX_ENABLE();

HAL_UART_Transmit(
    &huart1,
    tx_buf,
    tx_len,
    100
);

RS485_RX_ENABLE();

乍看没有问题。

但如果所使用的:

text 复制代码
发送函数
中断方式
DMA方式
底层驱动

在"发送完成通知"时最后一个字节还没有真正完全离开TX引脚,就可能:

text 复制代码
DE提前拉低

导致:

text 复制代码
最后一个停止位被截断

甚至:

text 复制代码
最后一个字节部分丢失

从机CRC校验失败以后不会回复。

于是:

text 复制代码
主机Timeout

二十三、UART的TXE和TC有什么区别?

这是RS485必须理解的知识点。

UART经常存在:

text 复制代码
TXE

和:

text 复制代码
TC

两个概念。


TXE

可以理解为:

text 复制代码
Transmit Data Register Empty

发送数据寄存器空。

它只表示:

可以向发送数据寄存器继续放下一个字节。

但此时:

text 复制代码
发送移位寄存器

可能仍然正在把数据一位一位送到TX引脚。

所以:

text 复制代码
TXE = 1

不能简单等价于:

text 复制代码
整个串口帧已经发送完成

TC

TC:

text 复制代码
Transmission Complete

表示:

数据寄存器和移位发送过程均已完成,最后一个停止位已经发送完成。

所以半双工RS485切换DE时:

应该确认真正的发送完成状态,而不能只看到发送寄存器空。


二十四、典型的DE控制流程

可以概念上写成:

c 复制代码
void RS485_Send(
    uint8_t *data,
    uint16_t len)
{
    RS485_DE_HIGH();

    HAL_UART_Transmit(
        &huart1,
        data,
        len,
        100);

    while (__HAL_UART_GET_FLAG(
               &huart1,
               UART_FLAG_TC) == RESET)
    {
    }

    RS485_DE_LOW();
}

这里重点不是代码本身,而是思想:

text 复制代码
拉高DE
↓
发送
↓
确认最后一位真正发完
↓
拉低DE
↓
接收

具体HAL版本是否已经在阻塞API内部等待TC,应结合对应STM32 HAL实现确认。


二十五、DE一直保持高会怎么样?

另外一种常见错误:

text 复制代码
发送完成以后忘了关闭DE

于是:

text 复制代码
主机一直处于发送状态

主机虽然没有继续发送数据,但是RS485发送器仍然占据总线驱动状态。

当从机准备回复:

text 复制代码
Slave准备发送

但主机发送器没有释放总线。

就可能出现:

text 复制代码
总线冲突
从机无法正常驱动
数据畸变
主机收不到回复

最终依然是:

text 复制代码
Timeout

所以:

半双工RS485的关键不是只会"打开发送",而是必须"及时释放总线"。


二十六、示波器应该同时看TX和DE

排查DE问题,非常推荐同时观察:

text 复制代码
CH1 = UART_TX

CH2 = DE

正确波形应该类似:

text 复制代码
DE
       ┌──────────────────┐
_______│                  │________
       ↑                  ↑
     发送前             真正发送完

TX
      |_|‾|__|‾‾|_|...

需要满足:

text 复制代码
DE先有效
↓
TX开始
↓
TX最后停止位结束
↓
DE再释放

如果看到:

text 复制代码
TX还没结束
DE就已经掉下去了

问题基本就定位了。


二十七、第五个重点:波特率必须一致

RS485只是物理层。

UART的数据格式仍然必须匹配。

例如主机:

text 复制代码
115200

从机:

text 复制代码
9600

显然无法通信。

常见波特率:

text 复制代码
9600
19200
38400
57600
115200

双方必须一致。


二十八、不只是波特率,整个UART格式都必须一致

例如:

主机:

text 复制代码
9600
8 data bits
No parity
1 stop bit

即:

text 复制代码
9600 8N1

从机:

text 复制代码
9600
8 data bits
Even parity
1 stop bit

即:

text 复制代码
9600 8E1

虽然:

text 复制代码
Baud Rate一样

但是:

text 复制代码
Parity不同

从机仍然可能不断出现:

text 复制代码
Parity Error
Frame Error

无法正确识别报文。

所以必须一起检查:

text 复制代码
Baud Rate
Data Bits
Parity
Stop Bits

二十九、为什么有时候低波特率正常,高波特率就超时?

例如:

text 复制代码
9600正常
115200失败

这往往说明问题可能已经偏向物理层。

需要重点检查:

text 复制代码
线路长度
终端电阻
布线拓扑
支线长度
线缆类型
屏蔽
接地
收发器速度
波形反射

因为波特率越高:

text 复制代码
Bit时间越短

系统对:

text 复制代码
上升沿
下降沿
传播延迟
反射

越敏感。


三十、RS485适合什么拓扑?

典型RS485推荐:

text 复制代码
总线型

结构:

text 复制代码
120Ω ─ Node1 ─ Node2 ─ Node3 ─ Node4 ─ 120Ω

中间节点使用:

text 复制代码
较短支线

连接到主干。

而不是:

text 复制代码
             Node2
               │
               │
Node1 ────── 中心 ───── Node3
               │
               │
             Node4

复杂星形结构。


三十一、为什么星形布线容易出问题?

因为每一个分支都可能形成:

text 复制代码
阻抗不连续点

信号到达分叉处:

text 复制代码
一部分继续向前
一部分进入支线
一部分发生反射

最终可能产生:

text 复制代码
振铃
过冲
多次反射
边沿畸变

在低速短距离场景可能没问题。

但在:

text 复制代码
高速
长距离
节点多

情况下问题会越来越明显。


三十二、第六个重点:偏置电阻和总线空闲状态

RS485发送器都释放总线以后:

text 复制代码
没有节点主动驱动A/B

如果系统没有:

text 复制代码
Failsafe Bias

总线差分状态可能不确定。

噪声可能导致接收器输出:

text 复制代码
随机跳变

形成:

text 复制代码
伪字符
UART错误
假数据

因此一些系统会使用:

text 复制代码
上拉
下拉

形成总线偏置。

例如:

text 复制代码
A → 上拉
B → 下拉

或者根据所使用收发器和极性定义设计。


三十三、是不是一定需要外部偏置电阻?

不一定。

很多现代RS485收发器内部已经提供:

text 复制代码
Fail-Safe Receiver

可以在:

text 复制代码
开路
短路
总线空闲

情况下保持稳定输出。

所以:

是否需要额外偏置电阻,应查看具体收发器数据手册和系统拓扑。

不能看到RS485就机械增加一堆上拉下拉。


三十四、偏置和终端电阻不是一回事

一定要区分:

终端电阻

例如:

text 复制代码
120Ω

主要用于:

text 复制代码
阻抗匹配
减少反射

偏置电阻

主要用于:

text 复制代码
总线空闲时建立确定差分状态

两者解决的是完全不同的问题。

不要把:

text 复制代码
120Ω终端

误认为:

text 复制代码
上下拉偏置

三十五、第七个重点:共地和共模范围

有人会说:

RS485是差分通信,所以不需要GND。

这句话不够准确。

RS485确实主要通过:

text 复制代码
A-B差分电压

传递数据。

但RS485收发器仍然具有:

text 复制代码
共模输入范围

如果两个设备之间地电位差太大:

text 复制代码
设备A GND
        ≠
设备B GND

可能导致A/B相对于本地地的共模电压超出收发器允许范围。

于是:

text 复制代码
通信异常
收发器损坏

都有可能。


三十六、实验室调试时建议怎么做?

如果不是隔离RS485:

text 复制代码
短距离实验
同一电源系统

通常应该确认:

text 复制代码
GND参考连接合理

如果:

text 复制代码
距离很长
强干扰
不同供电区域
地电位差大

则更适合考虑:

text 复制代码
隔离RS485

例如:

text 复制代码
数字隔离器
+
隔离DC/DC
+
RS485收发器

或者使用集成隔离RS485器件。


三十七、第八个重点:从机地址错误

如果底层物理通信完全正常,但是:

text 复制代码
主机一直超时

就要进入协议层检查。

例如Modbus RTU请求:

text 复制代码
01 03 00 00 00 02 CRC

第一个:

text 复制代码
01

就是从机地址。

如果实际从机地址是:

text 复制代码
02

那么:

text 复制代码
地址01的请求

对从机02来说:

text 复制代码
不是发给我的

它通常直接忽略。

结果:

text 复制代码
主机等待
↓
没有回复
↓
Timeout

物理层可以完全正常。


三十八、RS485并不知道设备地址

再次强调:

text 复制代码
RS485本身没有地址

设备地址来自:

text 复制代码
Modbus RTU
自定义协议

因此:

text 复制代码
RS485线路通了

并不代表:

text 复制代码
从机一定会回复

必须继续检查协议。


三十九、第九个重点:CRC错误

Modbus RTU中:

text 复制代码
CRC16

非常重要。

典型帧:

text 复制代码
01 03 00 00 00 02 C4 0B

如果CRC计算错误:

text 复制代码
从机收到整帧
↓
计算CRC
↓
发现错误
↓
直接丢弃报文

很多设备不会回复:

text 复制代码
CRC错误

而是直接保持沉默。

于是主机只能看到:

text 复制代码
Timeout

所以:

Modbus超时并不代表从机没收到,很可能是从机收到了但判定报文非法。


四十、Modbus CRC最常见的问题

包括:

text 复制代码
CRC算法错误
CRC初始值错误
高低字节顺序错误
少算一个字节
多算一个字节
发送Buffer长度错误

Modbus RTU CRC通常:

text 复制代码
CRC Low Byte先发送
CRC High Byte后发送

这一点很容易搞反。


四十一、第十个重点:功能码不支持

例如发送:

text 复制代码
功能码 0x03

表示读取保持寄存器。

如果从机:

text 复制代码
根本不支持0x03

按Modbus标准可能返回异常响应。

但一些自定义或者实现不完整的设备也可能:

text 复制代码
直接不回复

所以要确认从机协议手册。


四十二、寄存器地址也可能写错

例如文档写:

text 复制代码
40001

程序就直接发送:

text 复制代码
0x40001

这是典型错误。

Modbus文档中:

text 复制代码
40001
30001

有时只是:

text 复制代码
逻辑编号

实际协议PDU中可能使用:

text 复制代码
零起始偏移

例如:

text 复制代码
40001

对应协议地址可能是:

text 复制代码
0x0000

具体必须按照设备手册判断。

寄存器地址错误也可能导致:

text 复制代码
异常响应
或者
无响应

四十三、第十一个重点:从机回复时间

RS485主从通信通常不是:

text 复制代码
主机最后一个bit发完
↓
从机零延时马上回复

从机可能需要:

text 复制代码
解析报文
校验CRC
读取寄存器
处理业务
准备回复
切换DE

因此存在:

text 复制代码
Response Time

如果主机设置:

text 复制代码
Timeout = 1 ms

但从机需要:

text 复制代码
5 ms

那么从机其实完全正常。

只是主机:

text 复制代码
等得太短

所以:

超时参数必须根据真实协议响应时间设置。


四十四、但不能通过无限增大Timeout解决问题

这是调试中很常见的错误:

text 复制代码
100ms超时
↓
改成500ms

还不行
↓
改成2秒

还不行
↓
改成10秒

如果根本原因是:

text 复制代码
A/B接反
DE没关闭
波特率错误
CRC错误

再大的Timeout也没有意义。

所以应该:

text 复制代码
先定位
↓
再调整合理Timeout

而不是用Timeout掩盖问题。


四十五、Modbus RTU还需要注意帧间隔

Modbus RTU 使用:

text 复制代码
时间间隔

区分帧。

经典规则中会涉及:

text 复制代码
3.5个字符时间

等概念。

所以如果自己实现Modbus RTU接收状态机,还需要处理:

text 复制代码
帧间隔
字节间隔
接收超时

如果时间判断严重错误:

text 复制代码
一帧被拆成两帧

或者:

text 复制代码
两帧粘成一帧

都会导致CRC失败。


四十六、一个字符时间怎么算?

假设:

text 复制代码
9600 baud
8N1

一帧UART字符:

text 复制代码
1起始位
8数据位
1停止位

共:

text 复制代码
10 bit

一个字符时间:

text 复制代码
10 / 9600
≈ 1.0417 ms

3.5字符:

text 复制代码
≈ 3.65 ms

如果还有:

text 复制代码
校验位

字符总bit数还会变化。

因此实际协议时间需要根据UART格式计算。


四十七、UART RX溢出也会导致"看起来像超时"

例如:

text 复制代码
从机已经回复

但是主机:

text 复制代码
CPU长时间关中断
UART数据没及时读取
出现ORE

结果:

text 复制代码
数据丢失
协议帧不完整
CRC失败

上层最后仍然可能报告:

text 复制代码
Timeout

所以还要检查:

text 复制代码
ORE
FE
NE
PE

等UART错误状态。


四十八、使用DMA接收RS485时还要注意什么?

DMA很适合RS485连续接收。

例如:

c 复制代码
HAL_UARTEx_ReceiveToIdle_DMA(
    &huart1,
    rx_buffer,
    sizeof(rx_buffer));

但是要正确处理:

text 复制代码
IDLE
DMA长度
缓冲区
重新启动
帧边界

如果:

text 复制代码
DMA接收到回复

但软件没有正确获取长度:

text 复制代码
上层仍然认为没有数据

最终还是:

text 复制代码
Timeout

所以调试时不能只看:

text 复制代码
RS485物理总线

还要检查:

text 复制代码
UART RX是否真正进入Buffer

四十九、如何判断故障到底在哪一层?

这是整篇文章最重要的方法。

可以把通信分成四个观察点:

text 复制代码
① UART_TX

② RS485 A/B

③ 对端UART_RX

④ 对端回复A/B

如果:

UART_TX没有波形

问题在:

text 复制代码
MCU/UART/软件

UART_TX有,A/B没有

问题在:

text 复制代码
DE
收发器
硬件

A/B有,对端RX没有

问题在:

text 复制代码
A/B极性
收发器
线路
终端
波特率

对端RX有,但没有回复

问题偏向:

text 复制代码
地址
CRC
协议
从机程序

对端已经回复,但主机RX没有

问题偏向:

text 复制代码
主机DE未释放
主机接收器
UART RX
DMA
线路方向

这种分层定位效率非常高。


五十、示波器如何排查RS485?

推荐观察:

text 复制代码
CH1:UART TX
CH2:DE
CH3:A
CH4:B

最理想。

如果通道不足,可以先看:

text 复制代码
TX + DE

再看:

text 复制代码
A + B

五十一、看A/B波形时重点看什么?

不要只看:

text 复制代码
有没有跳变

还应该看:

text 复制代码
差分幅度
上升沿
下降沿
振铃
过冲
噪声
空闲状态

如果出现:

text 复制代码
明显多次振荡
边沿很慢
差分幅度不足

就需要重点排查:

text 复制代码
终端
线缆
拓扑
负载

五十二、逻辑分析仪应该接在哪里?

如果使用普通逻辑分析仪:

不建议直接把普通单端数字逻辑分析仪随意接在RS485 A/B上当UART分析。

更合理的是观察:

text 复制代码
收发器DI
收发器RO

也就是:

text 复制代码
MCU TX
MCU RX

这些已经是标准数字电平。

这样可以直接使用:

text 复制代码
UART Decoder

解码。


五十三、USB-RS485工具有什么用?

USB-RS485非常适合:

text 复制代码
隔离主机问题
隔离从机问题
抓取协议
验证设备

例如:

测从机

使用:

text 复制代码
PC + USB-RS485

直接访问从机。

如果正常:

text 复制代码
从机和线路大概率没问题

问题可能在自己MCU主机。


测自己的主机

可以让:

text 复制代码
MCU主机

发送给PC上的USB-RS485。

检查:

text 复制代码
数据是否正确
CRC是否正确
波特率是否正确

这种"分段替换法"非常高效。


五十四、推荐的RS485通信超时排查流程

实际现场可以按照下面顺序。


第1步:确认供电和共地

检查:

text 复制代码
MCU供电
RS485收发器供电
从机供电
GND参考

第2步:查看UART TX

确认主机到底有没有发送。


第3步:查看DE

确认:

text 复制代码
发送前开启
发送期间保持
发送完成后及时关闭

第4步:查看A/B

确认存在正常差分波形。


第5步:检查A/B极性

不要完全相信字母。

结合:

text 复制代码
Datasheet
设备说明书
实测

判断。


第6步:断电测终端

测:

text 复制代码
A ↔ B

两个120Ω终端时通常约:

text 复制代码
60Ω

第7步:核对UART参数

检查:

text 复制代码
波特率
数据位
停止位
校验位

第8步:检查总线拓扑

确认:

text 复制代码
主干清晰
支线短
两端终接

第9步:确认从机有没有收到

观察:

text 复制代码
从机RO/UART_RX

或者查看从机调试日志。


第10步:检查协议

例如Modbus:

text 复制代码
Slave Address
Function Code
Register
Length
CRC16

第11步:确认从机有没有回复

查看从机:

text 复制代码
TX
DE
A/B

第12步:确认主机有没有及时切回接收

如果从机已经回复:

text 复制代码
主机仍然收不到

重点检查主机:

text 复制代码
DE
/RE
UART_RX
DMA
RX Buffer

五十五、快速排查口诀

可以直接记成:

先看TX,再看DE;
再看A/B和终端;
然后查波特率;
最后查地址、CRC和协议。

再压缩:

text 复制代码
发送
↓
方向
↓
线路
↓
参数
↓
协议

五十六、典型案例1:A/B接反

现象:

text 复制代码
UART TX正常
A/B有波形
DE正常
从机完全不回复

处理:

text 复制代码
确认设备A/B极性定义
交换两根差分线验证

交换后立即恢复。

根因:

text 复制代码
差分极性错误

五十七、典型案例2:DE一直高

现象:

text 复制代码
主机发送正常
从机日志显示收到请求
从机也调用了发送
但主机一直收不到回复

示波器看到:

text 复制代码
主机DE一直=1

说明:

text 复制代码
主机没有释放总线

解决:

text 复制代码
最后一个UART停止位发完
↓
DE切换为接收

五十八、典型案例3:DE拉低太快

现象:

text 复制代码
从机偶尔回复
短帧正常
长帧容易失败
CRC经常错误

示波器发现:

text 复制代码
最后一个字节没发完
DE已经拉低

导致:

text 复制代码
尾部数据被截断

解决:

text 复制代码
等待真正TC

再切换方向。


五十九、典型案例4:终端太多

现场:

text 复制代码
5个节点

每个设备都拨码启用了:

text 复制代码
120Ω

结果总线等效阻抗过低。

表现:

text 复制代码
短距离偶尔能用
节点增加后不稳定
高速时大量超时
差分电压异常

解决:

text 复制代码
只保留总线物理两端终端

六十、典型案例5:9600正常,115200失败

重点检查:

text 复制代码
线长
拓扑
支线
120Ω终端
线缆阻抗
收发器型号
波形边沿

这种情况通常比"协议代码错误"更像:

text 复制代码
物理层信号完整性问题

六十一、典型案例6:从机确实收到,但不回复

此时不要继续查120Ω。

直接进入协议层。

检查:

text 复制代码
地址正确吗?

功能码正确吗?

寄存器正确吗?

长度正确吗?

CRC正确吗?

例如Modbus CRC错误时:

text 复制代码
从机收到
↓
CRC失败
↓
直接丢弃
↓
不回复

主机最终只看到:

text 复制代码
Timeout

六十二、典型案例7:从机回复了,主机还是超时

示波器已经看到:

text 复制代码
从机A/B上确实出现回复波形

那么问题已经基本锁定到主机接收路径:

text 复制代码
A/B
↓
主机收发器
↓
RO
↓
UART_RX
↓
DMA/中断
↓
Buffer
↓
协议解析

重点检查:

text 复制代码
主机DE是不是仍然高
/RE有没有开启
UART RX引脚是否正确
DMA是否启动
RXNE是否触发
ORE是否出现

六十三、RS485和Modbus RTU完整关系

可以把整个系统理解成:

text 复制代码
Modbus RTU
│
│ 地址
│ 功能码
│ 数据
│ CRC
↓
UART
│
│ 波特率
│ 数据位
│ 校验位
│ 停止位
↓
RS485收发器
│
│ DE/RE
↓
A/B差分总线
│
│ 终端
│ 拓扑
│ 线缆
↓
对端

每一层都可能造成:

text 复制代码
Timeout

所以不要把所有问题都称为:

text 复制代码
Modbus通信不通

最好明确是哪一层。


六十四、STM32简单RS485发送框架

例如:

c 复制代码
#define RS485_TX_ENABLE() \
    HAL_GPIO_WritePin(
        RS485_DE_GPIO_Port,
        RS485_DE_Pin,
        GPIO_PIN_SET)

#define RS485_RX_ENABLE() \
    HAL_GPIO_WritePin(
        RS485_DE_GPIO_Port,
        RS485_DE_Pin,
        GPIO_PIN_RESET)

发送:

c 复制代码
HAL_StatusTypeDef RS485_Send(
    uint8_t *data,
    uint16_t length)
{
    HAL_StatusTypeDef ret;

    RS485_TX_ENABLE();

    ret = HAL_UART_Transmit(
        &huart1,
        data,
        length,
        100);

    if (ret != HAL_OK)
    {
        RS485_RX_ENABLE();
        return ret;
    }

    while (__HAL_UART_GET_FLAG(
               &huart1,
               UART_FLAG_TC) == RESET)
    {
    }

    RS485_RX_ENABLE();

    return HAL_OK;
}

核心逻辑:

text 复制代码
DE发送
↓
UART发送
↓
等待TC
↓
DE接收

六十五、如果使用UART中断发送怎么办?

不能:

text 复制代码
启动IT发送
↓
立即DE拉低

因为:

text 复制代码
HAL_UART_Transmit_IT()

通常是非阻塞的。

函数返回以后:

text 复制代码
实际发送还在继续

所以应该在真正的:

text 复制代码
发送完成事件

以后再切回接收。


六十六、如果使用DMA发送怎么办?

同理:

text 复制代码
HAL_UART_Transmit_DMA()

只是:

text 复制代码
启动DMA发送

并不是函数返回时已经把整帧全部从TX物理引脚发完。

需要根据:

text 复制代码
具体HAL实现
UART TC状态
发送完成回调

确认真正的物理发送完成时刻,再切换DE。

设计原则永远是:

DMA搬完数据,不等于最后一个UART停止位一定已经离开引脚。


六十七、为什么RS485故障最好不要一开始就改协议代码?

因为如果:

text 复制代码
A/B接反

你修改CRC没有意义。

如果:

text 复制代码
DE一直高

你修改从机地址没有意义。

如果:

text 复制代码
收发器没供电

你把Timeout从100ms改成10秒仍然没意义。

所以一定按照层级:

text 复制代码
物理层
↓
UART
↓
方向控制
↓
数据内容
↓
协议

逐层确认。


六十八、RS485通信超时快速对照表

现象 优先怀疑
UART TX无数据 UART初始化/程序
TX有,A/B无波形 DE/收发器
A/B有,从机完全收不到 A/B、参数、物理层
从机收到但不回复 地址/CRC/协议
从机已回复但主机收不到 DE/RE/RX
短距离正常,长距离失败 终端/线缆/拓扑
9600正常,115200失败 信号完整性
偶发CRC错误 反射/干扰/方向切换
最后一个字节异常 DE切换过早
主机发送后一直超时 DE未切回接收
增加节点以后失败 终端过多/拓扑
刚上电有乱码 Bias/Failsafe
Modbus无回复 地址/功能码/CRC/寄存器

六十九、面试:RS485通信超时怎么排查?

如果面试官问:

RS485通信一直超时,你会怎么排查?

可以这样回答:

首先要把超时拆成发送和接收两个方向。

我会先使用示波器确认 MCU UART TX 是否有数据,如果TX有数据,再检查RS485收发器的DE控制以及A/B是否存在正常差分波形。

然后检查A/B极性、总线终端电阻和拓扑。典型RS485总线通常在物理两端分别配置120Ω终端,断电测A/B之间通常约为60Ω。

如果物理层正常,再检查主从双方UART参数,包括波特率、数据位、停止位和校验位。

半双工RS485还需要特别确认DE方向切换,发送前开启驱动,最后一个停止位真正发送完成以后才能释放DE并切回接收,否则可能截断最后一个字节或者导致主机持续占用总线。

如果这些都正常,再进入Modbus等协议层检查设备地址、功能码、寄存器地址、数据长度以及CRC。

最后通过示波器、逻辑分析仪或者USB-RS485工具确认从机是否真正收到请求以及是否已经发送回复,这样可以逐层确定故障位置。


七十、最终总结

RS485通信超时不是一个具体故障,而是:

某个通信环节失败以后,上层等待不到预期回复产生的最终结果。

整个链路:

text 复制代码
主机程序
 ↓
UART
 ↓
DE
 ↓
RS485收发器
 ↓
A/B
 ↓
终端和线缆
 ↓
从机收发器
 ↓
从机UART
 ↓
协议处理
 ↓
从机回复
 ↓
主机接收

任何一层异常:

text 复制代码
最终都可能Timeout

因此遇到RS485通信超时,可以按照下面顺序:

text 复制代码
① 看UART TX有没有发送

② 看DE有没有正确切换

③ 看A/B有没有差分波形

④ 查A/B极性

⑤ 断电测终端电阻

⑥ 查波特率、数据位、校验位和停止位

⑦ 查总线拓扑和线缆

⑧ 确认从机是否收到请求

⑨ 查设备地址、功能码和CRC

⑩ 确认从机是否回复

⑪ 确认主机是否及时切回接收

⑫ 查UART RX、DMA和接收缓冲区

其中最容易踩的三个坑是:

text 复制代码
A/B接反

DE切换时间错误

120Ω终端电阻配置错误

最后用一句话总结:

RS485通信超时时,不要第一时间修改Timeout和协议代码。先确认数据有没有真正从UART进入A/B总线,再检查终端、A/B极性和DE方向控制,最后才进入波特率、CRC、设备地址和Modbus协议层排查。


相关推荐
嘉子的秃头日记5 小时前
机械革命“自动修复”无法修复你的电脑
stm32·单片机·电脑
hsjiasb15 小时前
FreeRTOS学习(二十六)——动态内存管理heap_1到heap_5
stm32·单片机·学习·学习笔记·freertos
LCG元16 小时前
STM32+ESP8266+MQTT 物联网气象站:从零搭建温湿度远程监测系统(附完整源码)
stm32·物联网·struts
FakeOccupational18 小时前
【电路笔记 STM32】Cortex-M7 内核上的数据缓存(D-Cache)结构+MPU+DMA&Cache+STM32CubeMX配置
笔记·stm32·缓存
殷忆枫18 小时前
基于K210与STM32的智能垃圾分类与物联网监管系统
stm32·物联网·分类
周洲083019 小时前
STM32 GPIO 外部中断深度解析:边沿触发 / 电平触发、NVIC 优先级配置、中断嵌套实战
stm32·单片机·嵌入式硬件
LCG元19 小时前
STM32F103 CAN 总线通信实战:标准库双机通信与过滤器配置精讲
stm32·单片机·嵌入式硬件
zlinear数据采集卡20 小时前
D223的PWM电机控制:6路独立脉冲+加减速算法深度解析
arm开发·stm32·嵌入式硬件·算法·fpga开发·架构
z203483152021 小时前
STM32单片机控制无源蜂鸣器播放《晴天》
stm32·单片机·嵌入式硬件