RS485 这个标准从 1983 年发布算起,比很多编程语言都“老”,但今天去任何一座工厂、一栋写字楼、一个配电房,依然到处是它的影子。变频器、伺服驱动器、温控表、电表、PLC、门禁控制器,几乎所有工业设备的说明书里,第一个出现的通信接口往往就是 RS485。
不少刚接触 STM32 的读者,会在 RS485 上栽跟头:模块单独测是好的,两台设备一连就乱码;终端电阻加了不行、不加也不行;发送是正常的,接收却全是噪声;好不容易调通,距离拉长几十米又开始丢包。这些问题的根因通常不是“程序算法”,而是硬件电路、总线拓扑、布线和协议时序这些容易被忽略的细节。
这篇文章就以 STM32 系列为主线,把 RS485 的原理、硬件电路、代码实现、多机组网和工业布线一次讲清楚。读完你能达到三个目标:第一,看懂 RS485 收发电路,知道芯片怎么选、保护怎么加;第二,写出稳定可用的 STM32 RS485 收发代码,并理解为什么必须等待发送完成标志;第三,在做多设备组网和现场施工时,知道哪些坑不能踩、哪些规矩必须遵守。
1. 为什么 40 年过去了,RS485 依然在工业现场大量使用
先给一个明确判断:RS485 不是性能最好的总线,但它是“用最低成本解决多点可靠通信”的经典方案。这也是它经过几十年仍然没有被淘汰的根本原因。
在工业现场,大量设备并不需要以太网那种动辄百兆的带宽,也不需要 CAN 那种复杂的仲裁机制。设备之间要传递的数据,往往只有几个寄存器值:温度、压力、转速、运行状态、启停命令。这类数据量很小,但要求稳定、可靠、抗干扰,并且要能一根线串起来很多个设备。RS485 正好满足这些需求,而且芯片极其便宜、协议实现非常简单。
和 RS232 相比,RS485 把单端信号改成了差分信号,抗干扰能力大幅提升,传输距离从十几米扩展到上千米,并且支持一根总线上挂多个节点。和 CAN 相比,RS485 实现成本更低,调试工具更普及,几乎任何一个串口调试助手都能直接收发数据。和以太网相比,RS485 不受交换机、IP 地址、驱动协议这些约束,现场接两根线就能跑。
当然,RS485 也有明显的短板:半双工、无竞争检测、波特率不算高。但在绝大多数工业数据采集和控制的场景里,这些短板并不致命。对 STM32 开发者来说,RS485 是必须要掌握的一项基本功,因为它经常是产品设计里“第一个通信接口”,后续接 Modbus、对接 PLC、接各种传感器模块,都会遇到它。
2. RS485 原理:从单端信号到差分信号
2.1 RS232 的痛点:共地问题让远距离通信变成奢望
RS232 是早期最常用的串行通信标准,很多老式工控设备上都能看到 DB9 接口。RS232 使用单端信号,也就是用一根信号线对地之间的电压来表示逻辑电平。逻辑 1 对应负电压(通常是 -3V 到 -15V),逻辑 0 对应正电压(通常是 +3V 到 +15V)。
这种方式的第一个问题是通信双方必须“共地”。如果两台设备的 GND 电位不一致,比如一个设备外壳接地电位偏高,另一个设备浮空,那么信号线的电压参考点就变了,接收端很容易误判逻辑电平。第二,RS232 发送端的驱动电压虽然看起来有 ±15V,但经过线缆衰减和干扰叠加,实际可靠传输距离一般只有 15 米左右。第三,RS232 只支持点对点通信,一台主机不能直接通过 RS232 带多个从机。
所以 RS232 更适合“设备旁边就是电脑”的场景,例如路由器调试口、老式工控机串口。一旦设备分散在几十米甚至上百米的范围内,它就无能为力了。
2.2 RS485 的差分信号:用两根线把干扰变成共模误差
RS485 的核心思路是把“一根信号线对地”改成“两根信号线之间的电压差”。这两根线通常叫 A 和 B,或者 D+ 和 D-。发送端根据逻辑电平,在 A、B 之间产生一个正负电压差;接收端检测的是 A 与 B 之间的差值,而不是某根线对地的绝对值。
具体的逻辑定义是:当 A 线电压高于 B 线电压,且差值大于一定阈值时,接收端判定为逻辑 1(空闲态);当 B 线电压高于 A 线电压,且差值大于阈值时,判定为逻辑 0(有效数据)。
这种差分结构带来一个巨大优势:外部电磁干扰如果同时叠加在 A、B 两根线上,那么两线之间的差值几乎不变,干扰变成了“共模误差”,接收端不会因此误判。这也是 RS485 能在电机、变频器这些强干扰源旁边稳定工作的根本原因。
2.3 RS485 的关键参数
- 通信距离:理论最大约 1200 米,实际和波特率、线缆质量、节点数量有关。9600bps 下可靠跑几百米问题不大,如果提高到 115200bps,距离就会明显缩短。
- 节点数量:标准 RS485 收发器支持最多 32 个节点(一个单位负载)。使用 1/4 单位负载的芯片,节点数可以扩展到 128 个。
- 半双工:大多数 RS485 应用是半双工,同一时刻只能有一个节点发送,其他节点处于接收状态。
- 逻辑电平阈值:接收器一般需要 A-B 电压差大于 +200mV 才判定为逻辑 1,小于 -200mV 才判定为逻辑 0,在 ±200mV 之间属于不定态。
- 终端电阻:总线两端各接一个 120Ω 电阻,用于匹配双绞线的特性阻抗,减小信号反射。
可以把 RS232、RS485、CAN 做一张简单对比表:
| 对比项 | RS232 | RS485 | CAN |
|---|---|---|---|
| 信号方式 | 单端 | 差分 | 差分 |
| 通信模式 | 全双工 | 半双工(常见) | 半双工 |
| 最大节点数 | 点对点 | 32/64/128 | 110 以上 |
| 典型距离 | 15 米左右 | 1200 米左右 | 几公里(低速) |
| 抗干扰能力 | 弱 | 较强 | 强 |
| 成本 | 低 | 低 | 中 |
| 是否带仲裁 | 否 | 否 | 是 |
3. STM32 + RS485 硬件电路设计
3.1 芯片选型:MAX485、MAX3485 还是 SP3485
RS485 收发芯片的型号很多,最经典的是 MAX485,很多学习板上也用它。但要注意,MAX485 是 5V 供电的芯片,其逻辑输入引脚虽然可以直接接受 3.3V 高电平,但在某些极端情况下电平兼容性并不理想。如果 STM32 是 3.3V 供电,更推荐选用 MAX3485 或者 SP3485,这两款是 3.3V 供电的 RS485 收发器,引脚和 MAX485 基本兼容。
选择时还要注意芯片的“单位负载”参数。标准负载是 12kΩ,这决定了总线上最多能挂多少节点。如果项目需要挂很多设备,建议选 1/4 单位负载的型号,例如 ISL3170E、SN65HVD3082E 等,这类芯片可以让单个总线段支持更多节点。
3.2 电路方案一:GPIO 方向控制,最可靠的方式
单片 MAX3485 的引脚功能如下:
| 引脚 | 名称 | 功能 |
|---|---|---|
| 1 | RO | 接收输出,接 MCU RX |
| 2 | RE# | 接收使能,低电平有效 |
| 3 | DE | 发送使能,高电平有效 |
| 4 | DI | 发送输入,接 MCU TX |
| 5 | GND | 地 |
| 6 | A | 差分信号正端 |
| 7 | B | 差分信号负端 |
| 8 | VCC | 电源 |
最通用的接法是把 RE# 和 DE 并联,接到 STM32 的一个 GPIO 上。当 GPIO 输出高电平时,DE 为高、RE# 为高(接收禁用),芯片进入发送模式;当 GPIO 输出低电平时,DE 为低、RE# 为低(接收使能),芯片进入接收模式。
这个方案最可靠,方向切换完全由软件控制,没有隐性的时序问题,是工业项目中最推荐的方式。缺点是会占用一个 GPIO,并且软件里必须处理好“发送完成后不要立刻切换方向,要等待最后一个字节完全发送出去”。
3.3 电路方案二:自动收发电路,方便但有代价
很多低成本模块上会看到自动收发电路,典型结构是用一只 NPN 三极管对 TXD 信号做反相控制。其工作逻辑是:当 TXD 发送低电平(逻辑 0)时,三极管截止,DE 被上拉电阻拉到高电平,芯片进入发送模式,同时 DI 的低电平被发送到总线;当 TXD 发送高电平(逻辑 1)时,三极管导通,DE 被拉到低电平,芯片进入接收模式,总线依靠外部偏置电阻维持逻辑 1 的空闲状态。
这套电路的好处是少占用一个 GPIO,软件上不用管方向切换。但它有两个隐患:第一,总线真正的数据“0”是主动驱动的,而数据“1”是靠外部上拉电阻维持的,驱动能力较弱;第二,MCU 空闲时 TXD 是高电平,此时芯片处于接收模式,如果外部设备突然发来数据,本机也能收到,但本机发送的长数据帧中间,字节间的停止位会让芯片短暂切回接收模式,如果波特率较高或数据模式特殊,容易导致发出去的波形变形。
因此,自动收发电路更适合单点对单点、波特率不高(9600 或 19200)、帧长度较短的应用。如果要做多机组网、Modbus 协议或者强干扰环境,建议老老实实用 GPIO 方向控制。
3.4 保护电路:总线接口不是裸奔就能跑的
RS485 芯片内部有一定的静电防护能力,但不足以应对工业现场的浪涌和地电位差。实际产品中,A、B 线通常要加保护器件。
一种常见配置是:在 A 和 B 之间并联一对 TVS 管,或者在 A、B 对地之间各接一个 TVS 管,用于吸收快速的浪涌尖峰。如果雷击风险高,还要在 TVS 前级增加气体放电管(GDT)或者半导体放电管(TSS),并使用 PTC 自恢复保险丝做限流。注意 TVS 的钳位电压要低于 RS485 芯片的极限耐压,例如 -7V 到 +12V 范围。
关于 TVS+TSS 还是 TVS+气体放电管,工程上的经验是:TSS 响应速度快、残压低,但通流能力不如气体放电管,也没有明显续流问题;气体放电管通流能力大,但响应慢,而且雷电过压后可能产生续流,需要后端电路配合恢复。室内短距离设备,TVS+PTC 通常足够;室外长距离、有雷击风险的场景,再考虑增加气体放电管或 TSS,并且注意 GDT 要放在 TVS 的前级,靠近接线端子一侧。
4. 实战:STM32 RS485 通信代码实现
4.1 环境准备
本文的代码基于 STM32 HAL 库,工程使用 STM32CubeMX 生成,编译器可以是 Keil MDK 或 STM32CubeIDE。硬件上建议准备两块带 RS485 收发芯片的开发板或最小系统板,例如 STM32F103C8T6 核心板加一块 SP3485 模块。如果只有一块板,也可以用 USB 转 RS485 工具配合调试。
和桌面软件开发不同,RS485 调试时最好准备一个 USB 转 RS485 模块,把它接入总线,用串口助手直接观察总线上收发的字节内容。这样能快速判断问题出在“本机发送”还是“远端响应”。
4.2 STM32CubeMX 工程配置
在 STM32CubeMX 中需要配置以下内容:
- 选择一个 UART 作为 485 通信串口,例如 USART2。
- 配置 UART 参数:波特率 9600、8 位数据、无校验、1 位停止位。
- 给 RE/DE 方向控制分配一个 GPIO,例如 PB3,输出模式。
- 打开 USART2 全局中断,因为接收要用中断方式。
- 配置时钟和调试接口:如果使用 ST-LINK,要保留 SWD 引脚功能,建议把 PA13/PA14 留作 SWDIO/SWCLK。
这里需要注意,很多 RS485 模块上的 RE/DE 是排针引出,默认由跳线帽控制或直接接固定电平。使用 GPIO 控制前,先确认模块上的跳线是否允许外部控制,不能直接用 GPIO 驱动一个已经被硬件拉死的引脚。
4.3 方向控制代码
建立一个 rs485.c 文件,实现方向切换函数:
// 文件路径:Core/Src/rs485.c #include "rs485.h" #include "main.h" #define RS485_DE_PORT GPIOB #define RS485_DE_PIN GPIO_PIN_3 void RS485_SetMode_TX(void) { HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_SET); } void RS485_SetMode_RX(void) { HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_RESET); }这段代码的核心就是拉高 RE/DE 进入发送,拉低进入接收。要注意,GPIO 初始化的默认状态应该是低电平,也就是上电优先处于接收模式,避免设备一上电就占住总线发出噪声。
4.4 串口初始化
HAL 库中,UART 初始化通常由 MX_USART2_Init 完成,关键参数如下:
// 文件路径:Core/Src/usart.c static void MX_USART2_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } }注意,RS485 通信默认推荐 9600bps,这个波特率看起来不高,但在几百米的现场线缆上非常实用。如果项目对速度有要求,可以逐步提高到 19200 或 38400,但要配合更好的线缆和终端匹配。
4.5 发送函数:为什么必须等待 TC 标志
RS485 是半双工,发送之前要切到发送模式,发送完成后要切回接收模式。问题在于,很多人直接调用 HAL_UART_Transmit 后马上就切回接收模式,结果最后一个字节没发完,总线被强行切断了。
正确的做法是在 HAL_UART_Transmit 返回后,再等待 TC(Transmission Complete)标志位,确保移位寄存器里的最后一位也完全移出,然后再切回接收模式。
// 文件路径:Core/Src/rs485.c void RS485_SendData(uint8_t *data, uint16_t len) { // 1. 切换到发送模式 RS485_SetMode_TX(); // 2. 发送数据 HAL_UART_Transmit(&huart2, data, len, 100); // 3. 等待最后一个字节完全移出 while (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC) == RESET); // 4. 切换回接收模式 RS485_SetMode_RX(); }如果不等待 TC,而是依赖 HAL_UART_Transmit 内部的发送完成时间,在多字节帧的最后一个字节上容易出现截断。这个问题在 9600bps 下偶尔出现,到了 115200bps 会非常明显。务必养成等待 TC 的习惯。
4.6 接收函数:中断方式更实用
RS485 接收适合用中断方式,因为从机的响应时间不确定,主机轮询之后要立刻进入接收状态,等待从机返回。如果使用阻塞查询,MCU 会被占住,影响其他任务。
// 文件路径:Core/Src/main.c uint8_t rs485_rx_buf[256]; uint16_t rs485_rx_len = 0; // 在 main 初始化后启动接收 HAL_UART_Receive_IT(&huart2, rs485_rx_buf, 1);单字节接收中断的好处是可以自己控制帧接收逻辑。在中断回调里判断是继续收下一个字节,还是组成一帧后处理。如果一次性调用 HAL_UART_Receive_IT 接收固定长度,在多字节帧长度不固定的场景下反而不好处理。
// 中断回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART2) { // 这里可以根据自己的帧协议处理 rs485_rx_buf[0] // 处理完成后重新开启接收 HAL_UART_Receive_IT(&huart2, rs485_rx_buf, 1); } }4.7 回环自测:先用最短路径验证硬件
把 STM32 的 485 模块的 A、B 接到 USB 转 RS485 工具的 A、B 上,程序里写一个简单的回环发送:每隔 1 秒发送一帧固定的内容,例如 01 03 00 00 00 01 84 0A(这是一个标准的 Modbus 读寄存器请求帧),然后在串口助手里观察是否收到同样的内容。如果串口助手能正常收到完整帧,说明 STM32 发送链路、线缆、USB 转 485 模块都正常。如果收到乱码,先用万用表检查 A/B 是否接反。
A/B 接反是 RS485 调试里最高频的低级错误。很多模块出厂时没有明显标注,有的丝印是 A/B,有的是 D+/D-,还有的是 485+/485-。一旦接反,信号全部反相,接收端就会收到不可识别的噪声。如果总线上有多个节点,某个节点的 A/B 接反,还会拉低整条总线,导致所有设备通信失败。
5. 多机组网实战:从点到总线
5.1 主从架构是 RS485 的常态
RS485 本身没有定义任何通信协议,只规定了电气层。也就是说,它只负责把字节从 A 点搬到 B 点,不关心这些字节代表什么意思。在工业现场,最通用的做法是采用主从架构:一个主机,多个从机,主机依次点名查询每个从机,从机只有在被点名到的时候才允许发送数据。
这种架构适应了 RS485 半双工的特点。如果两个从机同时往总线上发数据,就会发生冲突,导致数据完全不可用。因此,设计协议时一定要保证“总线上同一时刻只有一个节点在发送”。
实际项目中,很多设备支持 Modbus RTU 协议。Modbus RTU 就是基于 RS485 电气接口的最常见应用层协议。它的帧结构非常固定:从机地址 1 字节、功能码 1 字节、数据若干字节、CRC 校验 2 字节(低字节在前)。协议简单,第三方工具软件很多,非常适合学习。
5.2 一个简单的查询协议设计
假设总线上有 3 个从机,地址分别为 0x01、0x02、0x03。主机想要读取从机 0x01 的一个 16 位寄存器,可以发送以下查询帧:
0x01 0x03 0x00 0x00 0x00 0x01 CRC_L CRC_H 地址 功能码 寄存器高 寄存器低 寄存器数量高 寄存器数量低 校验 校验从机收到帧后,先判断帧头是否包含自己的地址,如果匹配,再校验 CRC,最后执行读操作并返回响应。从机没有收到针对自己的帧时,必须保持静默。
5.3 CRC16 计算函数
Modbus RTU 的 CRC16 算法固定,多项式为 0xA001,初始值为 0xFFFF。以下是一个通用的按位计算实现:
// 文件路径:Core/Src/crc16.c #include <stdint.h> uint16_t ModbusCRC16(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc >>= 1; } } } return crc; }使用的时候,发送方把地址、功能码、数据部分交给 CRC 函数算出 16 位 CRC,然后先发低字节再发高字节。接收方收到整帧后,对整个帧(包括 CRC 部分)重新计算,结果如果等于 0x0000,说明帧没有损坏。这种“重算为 0”的校验方法在协议开发中很常见。
5.4 主机轮询与超时处理
主机轮询的伪代码如下:
void Master_Poll(void) { uint8_t cmd[8]; uint16_t crc; cmd[0] = 0x01; // 从机地址 cmd[1] = 0x03; // 功能码:读保持寄存器 cmd[2] = 0x00; // 寄存器地址高字节 cmd[3] = 0x00; // 寄存器地址低字节 cmd[4] = 0x00; // 寄存器数量高字节 cmd[5] = 0x01; // 寄存器数量低字节 crc = ModbusCRC16(cmd, 6); cmd[6] = crc & 0xFF; // CRC 低字节 cmd[7] = crc >> 8; // CRC 高字节 RS485_SendData(cmd, 8); // 等待从机响应,这里应该有超时机制 if (WaitResponse(200) == 0) { // 超时未收到响应,记录错误 LogError("slave timeout"); } }超时处理很关键。如果从机不在线或者故障,主机不能在接收上无限等待。一般建议超时时间设为 100ms 到 500ms,超过后放弃本轮查询,继续下一个从机,否则某一个从机掉线会导致后面所有从机都无法被查询。
从机侧的核心逻辑,则是在中断回调里判断地址是否匹配:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART2) { if (rx_buffer[0] == MY_ADDR) { // 地址匹配,继续收完一帧 // 这里可以用状态机累积数据,直到收到约定长度 } else { // 地址不匹配,丢弃,继续监听 } HAL_UART_Receive_IT(&huart2, rx_buffer, 1); } }这里有一个工程经验:从机接收到一帧后,不要立刻回复。先花几毫秒检查 CRC 是否通过、功能码是否支持、数据是否合法,再决定是否响应。不要贪图那几毫秒的响应速度,正确性永远优先。
6. 工业布线避坑指南
6.1 必须用双绞线,而且 A/B 不能并排走普通线
RS485 的差分特性需要“双绞”来发挥。双绞线的好处是两根线的环路面积小,外部电磁干扰在两根线上感应的电压比较接近,变成共模干扰被接收端抑制。如果图省事,用两根平行的普通导线做长距离传输,一旦旁边有变频器或者动力电缆,干扰会让通信立刻崩溃。
推荐使用特性阻抗 120Ω 的屏蔽双绞线,例如很多 RS485 专用电缆或者低烟无卤的控制电缆。屏蔽层要单端接地,一般接在主机侧的信号地或者保护地,不要两端都接地,否则屏蔽层会形成地环路,在雷击或接地电位差大的场景反而引来干扰。
6.2 终端电阻:加在总线两端,不是每个设备都加
终端电阻的作用是匹配线缆的特性阻抗,防止高速信号在线缆末端反射。RS485 双绞线的典型特性阻抗是 120Ω,所以总线最远的两端各接一个 120Ω 电阻。
这个电阻只能加两个,不能每个节点都加。如果带了 10 个设备,每个设备上都加了 120Ω 电阻,并联后的等效阻值会变得非常小,RS485 芯片的驱动能力根本拉不动,总线信号幅度会异常,通信直接失败。很多初学者搞不清这个概念,每个板子都加终端电阻,最后全线瘫痪。
短距离、低速率、节点少的场景(比如实验室桌面通信),不加终端电阻也能工作,但不建议在工业项目里省这个电阻。加了终端电阻后,如果总线空闲时没有偏置,接收端会更容易收到不定态噪声,所以终端电阻往往和偏置电阻搭配使用。
偏置电阻的做法是:在某一对节点上,把 A 线上拉到 VCC,B 线下拉到 GND,阻值通常取 390Ω 到 560Ω。这样做的目的是让总线在空闲状态下保持 A 高于 B,也就是一个稳定可识别的逻辑 1 状态,避免接收端把空闲噪声当成数据。如果总线上所有节点都处于接收态,又没有任何偏置,A/B 之间电压差接近 0,接收器输出端会随机翻转,产生大量垃圾中断。
6.3 拓扑结构:手拉手,尽量别做星型
RS485 总线的标准拓扑是“手拉手”的直线结构,也就是从一个设备出来,依次串到下一个设备,最终形成一条总线。每个设备上的 A/B 线都是“进来又出去”,不要从总线的中间位置单独拉一根很长的线到一个设备,这种“星型”或“树型”结构会导致信号在支路末端反射,破坏主总线的信号质量。
支路越长、波特率越高,影响越明显。如果现场实在避免不了支路,支路长度要尽量短,一般建议控制在几米以内。对于波特率超过 115200 的高速场景,支路的影响会非常敏感,更需要严格遵循总线型拓扑。
6.4 接地与隔离:地电位差是隐形杀手
前面已经提到,RS485 的本质是检测 A/B 之间的电压差,但这个差值必须落在收发芯片规定的共模范围内,通常是 -7V 到 +12V。如果设备之间地电位差太大,比如一个设备接大地、另一个设备浮空,就可能导致共模电压超出芯片允许范围,轻则通信错误,重则烧毁芯片。
解决办法有两个方向。第一,保证整个 RS485 系统的信号地是等电位的,通常用屏蔽层或额外的接地线实现单点接地。第二,使用带隔离的 RS485 方案:在 UART 侧加数字隔离器,收发芯片单独用隔离电源供电。这样设备之间就没有电气直接连接,地电位差再大也不会形成破坏性电流。常见的隔离方案有 ISO3082、ADM2483 这类集成隔离收发器,也可以用 ISO7721 加外部收发芯片组合。
6.5 保护器件位置:靠近端子,走线短
保护电路不是随便摆的。TVS 管、气体放电管要尽量靠近 RS485 接线端子,并且从端子到保护器件的走线要短,最好小于 5mm。如果保护器件离端子太远,浪涌电流会先经过一段 PCB 走线,这段走线会产生残压,反而干扰后级电路。共模电感一般放在保护器件和 RS485 芯片之间,用来抑制共模噪声。
多层板的 A/B 差分线要尽量等长、等距,避免差分阻抗突变。底层不要有大面积分割,否则回流路径被切断,共模噪声会增加。
7. RS485 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 通信完全不通,接收端全是噪声 | A/B 接反 | 用万用表确认 A/B 丝印,对调两根线测试 | 对调 A/B 接线 |
| 发送正常,接收偶尔丢字节 | 发送后未等待 TC 就切换方向 | 代码里确认是否等待 TC 标志 | 在发送函数中等待 UART_FLAG_TC |
| 总线空闲时 MCU 频繁进入接收中断 | 总线没有加偏置电阻 | 示波器测量 A/B 电压差,看空闲时是否接近 0 | 在主机端加 A 上拉、B 下拉偏置电阻 |
| 挂了很多节点后通信异常 | 每个节点都加了终端电阻 | 检查总线上 120Ω 电阻数量 | 只保留总线两端的终端电阻 |
| 距离拉长后误码率升高 | 线缆类型不对或没有终端匹配 | 检查线缆是否为 120Ω 双绞线,测量 A/B 波形 | 更换双绞线,加终端电阻 |
| 一发送就把单片机复位 | 485 收发芯片的电源或地不稳定,或 DE 引脚电流过大 | 检查电源纹波、DE 驱动能力 | 加去耦电容,GPIO 加限流电阻 |
| 下载程序时报 no STM32 target found | RS485 模块占用了 SWD 引脚,或目标板被总线干扰 | 检查 RS485 相关 GPIO 是否与 SWDIO/SWCLK 冲突 | 换用其他 GPIO,断开总线再下载 |
| 上位机提示传输格式不正确 | 波特率、数据位、停止位、校验位不匹配 | 核对双方串口参数 | 统一串口参数,例如 9600/8/N/1 |
| 多个从机同时回复导致数据错乱 | 协议没有保证单主单从 | 检查从机是否只有在被点名后才发送 | 使用主从查询机制,从机严禁主动上报 |
8. RS485 还是 CAN:怎么选
很多读者在项目选型时会纠结:RS485 和 CAN 都能做多点通信,到底用哪个?这里的核心判断是:如果应用层有现成的 Modbus 设备生态,选 RS485;如果对实时性、多主通信、错误检测要求高,选 CAN。
CAN 的优势在于硬件层面自带仲裁机制,多个节点可以同时尝试发送,总线会根据报文 ID 的优先级自动裁定谁先发。它不需要像 RS485 那样在主从协议里精心设计避免冲突的机制。CAN 的错误检测和重发机制也比 RS485 完善得多,误码率更低,节点数量和距离也更有优势。因此汽车电子、大型自动化设备、分布式传感网络里,CAN 的使用越来越普遍。
但 RS485 也有不可替代的价值:协议简单,单片机实现成本极低,几乎所有串口调试工具都能直接看数据;市场上海量的仪表、传感器、PLC 都是 RS485 接口,Modbus RTU 协议让不同厂商的设备可以互通。在楼宇自控、电力监控、环保设备这些慢速数据采集场景里,RS485 依然是最务实的选择。
更稳妥的判断是:先看你要连接的设备提供什么接口。如果对方的说明书上写的是 RS485/Modbus,那就老老实实用 RS485,不要为了“先进性”在中间加一层 CAN 网关。如果是从零设计一套自己的多点通信系统,且对节点间通信的冲突处理有顾虑,可以考虑 CAN。
9. 总结与实践建议
RS485 是老技术,但不是被淘汰的技术。它在工业现场的生命力,来自于“简单、便宜、可靠”三个词的组合。STM32 开发者如果能把 RS485 的原理、方向切换时序、终端电阻和偏置电阻的用法、主从协议的轮询机制都掌握清楚,那么在面对绝大多数工业串口通信项目时就有了底气。
下一步的动手路径建议是这样的:先把手里的两块开发板用杜邦线或者短双绞线接起来,白天用回环测试确认硬件链路,再写一个最简单的“主机发请求、从机回响应”的程序。跑通之后,再考虑移植 FreeModbus,这是目前工程上最省力的 Modbus RTU 实现方式,没有必要自己再从零写一套应用层协议。
如果你正在准备毕业设计,建议不要止步于“能收发字符串”,比如做一个温湿度采集系统,用一块 STM32 作为主机,轮流读取几个 RS485 从机模块的数据,并在屏幕上显示。这个过程会完整覆盖 RS485 组网、协议、超时处理、错误重试的全部环节,比单纯跑通点对点收获大得多。如果是实际工程项目,布线距离超过 50 米的话,请把第六节关于双绞线、终端电阻、隔离接地的内容当作强制要求来执行。