在工业通信、单片机外设扩展、PLC 采集系统中,常见说法是 RS485扩展 或 485通信扩展,不是“985扩展”。
更准确的理解是:
RS485 是通信硬件接口,Modbus 是通信协议。
485扩展 = 给主控板增加 RS485 总线通信能力。
Modbus RTU over RS485 = 用 RS485 这条线,按照 Modbus RTU 的格式收发数据。
很多传感器、电表、变送器、继电器模块会写:
通信接口:RS485
通信协议:Modbus RTU
波特率:9600
数据格式:8N1 / 8E1
设备地址:1
这时候就表示:硬件上用 RS485,软件上用 Modbus RTU 协议。
1. 一句话理解
1.1 Modbus 是什么?
Modbus 是一种工业通信协议。
它规定了主机和从机之间如何传递数据,例如:
- 主机怎么读取从机的数据;
- 主机怎么写入从机的寄存器;
- 从机返回的数据格式是什么;
- 数据中如何包含设备地址、功能码、寄存器地址、数据长度和校验。
可以简单理解为:
Modbus 规定“数据包怎么组织、怎么读写、每个字节是什么意思”。
1.2 RS485 是什么?
RS485 是一种物理通信接口标准。
它主要规定的是电气层面的东西,例如:
- 使用 A/B 两根差分线传输;
- 适合较远距离传输;
- 抗干扰能力比普通 TTL 串口更强;
- 可以在一条总线上挂多个设备;
- 常用于工业现场。
可以简单理解为:
RS485 规定“信号怎么在线上传输”。
1.3 485扩展是什么?
485扩展就是给 STM32、单片机、开发板或上位机增加 RS485 通信接口。
STM32 自带的是 UART 串口,一般是:
TX
RX
GND
而 RS485 设备使用的是:
A
B
GND / 屏蔽地
所以中间需要 RS485 收发器,例如:
- MAX485
- SP3485
- SN65HVD 系列
- 隔离型 RS485 模块
基本连接关系如下:
STM32_TX ---> DI RS485收发器 A ---> RS485总线A
STM32_RX <--- RO B ---> RS485总线B
GPIO ---> DE / RE方向控制
GND ---- GND
2. Modbus 和 RS485 的关系
很多人会把 Modbus 和 RS485 混在一起说,但它们不是同一层的东西。
| 名称 | 所在层次 | 主要作用 | 类比 |
|---|---|---|---|
| RS485 | 物理层 / 电气接口 | 负责信号传输 | 道路、电话线 |
| UART | 串口通信方式 | 负责字节发送和接收 | 车辆行驶方式 |
| Modbus RTU | 应用层协议 | 负责数据格式和读写规则 | 说话规则、报文格式 |
| Modbus TCP | 应用层协议 | 通过以太网传输 Modbus 数据 | 网络版 Modbus |
所以实际工程里常见的结构是:
应用层:Modbus RTU 协议
↓
串口层:UART,9600 / 8N1 等参数
↓
物理层:RS485差分总线
↓
设备层:传感器、电表、变送器、PLC、继电器模块
最常见说法:
RS485 上跑 Modbus RTU。
3. Modbus 的具体内容
3.1 主从结构
传统 Modbus RTU 通信一般采用 一主多从 结构。
主机:STM32 / PLC / 上位机 / 工控机
|
| RS485总线
|
从机1:温湿度传感器
从机2:电表
从机3:继电器模块
从机4:压力变送器
特点:
- 通常由主机主动发起查询。
- 从机一般不主动发数据。
- 每个从机都有唯一设备地址。
- 同一时刻通常只能有一个设备发送数据。
- 主机轮询不同从机,依次读取数据。
3.2 常见 Modbus 类型
3.2.1 Modbus RTU
这是单片机、PLC、工业传感器中最常见的形式,通常跑在 RS485 或 RS232 上。
特点:
- 数据是二进制格式;
- 通信效率较高;
- 每一帧末尾有 CRC 校验;
- 适合 STM32、PLC、变送器、电表等设备;
- 调试时一般用串口工具查看十六进制数据。
3.2.2 Modbus ASCII
这是较早的一种串口格式,数据用 ASCII 字符表示。
特点:
- 人眼更容易看懂;
- 效率比 RTU 低;
- 实际新项目中没有 Modbus RTU 常见。
3.2.3 Modbus TCP
这是跑在以太网 TCP/IP 上的 Modbus。
特点:
- 常用端口为 502;
- 不使用 RTU 里的 CRC,而是依靠 TCP/IP 传输机制;
- 常见于工控机、PLC、网关、服务器、SCADA 系统。
3.3 Modbus 数据区
Modbus 中经常会看到这些地址类型:
| 地址类型 | 常见地址表示 | 数据类型 | 常见用途 |
|---|---|---|---|
| Coil | 00001 起 | 1 bit | 继电器开关、线圈输出 |
| Discrete Input | 10001 起 | 1 bit | 开关量输入 |
| Input Register | 30001 起 | 16 bit | 只读模拟量输入 |
| Holding Register | 40001 起 | 16 bit | 可读写参数、采集数据 |
需要特别注意:
说明书里的
40001不一定代表报文里的地址就是40001。
很多设备把40001作为保持寄存器的第一个地址,但在实际 Modbus 报文中起始地址要写成0x0000。
例如:
说明书写:温度寄存器地址 40001
实际报文起始地址:00 00
功能码:03
这就是很多初学者最容易踩的坑之一。
3.4 常见功能码
| 功能码 | 十六进制 | 作用 |
|---|---|---|
| 01 | 0x01 | 读取线圈状态 |
| 02 | 0x02 | 读取离散输入 |
| 03 | 0x03 | 读取保持寄存器 |
| 04 | 0x04 | 读取输入寄存器 |
| 05 | 0x05 | 写单个线圈 |
| 06 | 0x06 | 写单个保持寄存器 |
| 15 | 0x0F | 写多个线圈 |
| 16 | 0x10 | 写多个保持寄存器 |
在传感器采集项目中,最常用的是:
03:读保持寄存器
04:读输入寄存器
06:写单个参数
10:写多个参数
3.5 Modbus RTU 报文格式
一个典型的 Modbus RTU 请求帧如下:
[设备地址] [功能码] [起始地址高字节] [起始地址低字节] [寄存器数量高字节] [寄存器数量低字节] [CRC低字节] [CRC高字节]
例如读取 1 号设备,从地址 0 开始的 2 个保持寄存器:
01 03 00 00 00 02 C4 0B
含义:
| 字节 | 含义 |
|---|---|
| 01 | 从机地址为 1 |
| 03 | 功能码,读取保持寄存器 |
| 00 00 | 起始寄存器地址 |
| 00 02 | 读取 2 个寄存器 |
| C4 0B | CRC16 校验,低字节在前 |
从机可能返回:
01 03 04 00 FA 02 58 XX XX
含义:
| 字节 | 含义 |
|---|---|
| 01 | 从机地址 |
| 03 | 功能码 |
| 04 | 后面数据区有 4 个字节 |
| 00 FA | 第 1 个寄存器数据,例如 250 |
| 02 58 | 第 2 个寄存器数据,例如 600 |
| XX XX | CRC 校验 |
如果说明书规定:
温度 = 原始值 / 10
湿度 = 原始值 / 10
那么:
温度 = 250 / 10 = 25.0℃
湿度 = 600 / 10 = 60.0%
4. RS485 扩展的具体内容
4.1 为什么不用普通串口直接接工业设备?
STM32 的 UART 通常是 TTL 电平:
3.3V / 0V
TX / RX
点对点通信
传输距离较短
抗干扰能力一般
工业现场常用 RS485:
差分信号 A/B
可以远距离传输
抗干扰能力更强
支持多节点挂接
适合现场布线
所以当 STM32 要接工业传感器、变送器、电表等设备时,通常需要增加 RS485 收发器。
4.2 RS485 为什么抗干扰更强?
RS485 使用差分信号。
它不是单独看某一根线相对 GND 的电压,而是看 A 和 B 两根线之间的电压差。
逻辑状态 ≈ A 与 B 的电压差
外界干扰通常会同时影响 A、B 两根线,但两根线的电压差变化较小,所以接收端还能识别出正确数据。
这就是 RS485 适合工业环境的原因之一。
4.3 半双工 RS485
最常见的是二线制半双工 RS485:
A ----------- A ----------- A
B ----------- B ----------- B
特点:
- A/B 两根线既用于发送,也用于接收;
- 同一时刻只能一个设备发送;
- 多个设备共用一条总线;
- 需要方向控制。
STM32 接 MAX485 一类芯片时,需要控制 DE 和 RE:
| 模式 | DE | /RE | 状态 |
|---|---|---|---|
| 接收 | 0 | 0 | 接收总线数据 |
| 发送 | 1 | 1 或 0 | 驱动总线发送数据 |
| 禁用 | 0 | 1 | 收发都关闭 |
很多项目中会把 DE 和 /RE 接在一起,用一个 GPIO 控制:
GPIO = 1:发送
GPIO = 0:接收
4.4 STM32 接 RS485 的典型接线
以 MAX485 / SP3485 类芯片为例:
STM32_TX ----> DI
STM32_RX <---- RO
GPIO ----> DE
GPIO ----> /RE
GND ---- GND
MAX485_A ----> RS485_A
MAX485_B ----> RS485_B
如果 DE 和 /RE 合并控制:
STM32_GPIO ----> DE
STM32_GPIO ----> /RE
程序中:
发送前:GPIO = 1,切换到发送模式
发送中:UART发送 Modbus 请求帧
发送后:等待 UART TC 发送完成标志
完成后:GPIO = 0,切换回接收模式
接收中:等待从机响应
特别注意:
发送完不能立刻切回接收,必须等待 UART 的 TC 标志。
如果只等 TXE 标志,最后一个字节可能还没真正发完,导致 CRC 或尾字节丢失。
4.5 RS485 总线布线
推荐方式是总线型,也叫手拉手、菊花链:
主机 ---- 从机1 ---- 从机2 ---- 从机3 ---- 从机4
不推荐星型:
从机1
|
从机2 --- 主机 --- 从机3
|
从机4
原因是星型分支容易造成反射,通信不稳定。
4.6 终端电阻
RS485 长线通信时,通常需要在总线两端加终端电阻。
常见值:
120Ω
布置原则:
总线最前端加一个
总线最末端加一个
中间节点不要随便加
示意:
[120Ω] 主机 ---- 从机1 ---- 从机2 ---- 从机3 [120Ω]
注意:
- 终端电阻不是每个设备都加。
- 只加在总线两端。
- 短距离、低速率时,有时不加也能跑,但工程上要根据距离、速率和现场干扰判断。
- 加太多终端电阻会加重驱动负载,可能导致通信失败。
4.7 偏置电阻
当总线上没有设备发送时,A/B 线可能处于悬空或不确定状态。
为了让空闲状态稳定,常加入偏置电阻。
常见做法:
A 上拉到 VCC
B 下拉到 GND
A 与 B 之间接终端电阻
作用:
- 保证总线空闲时有确定状态;
- 避免接收器误判成随机数据;
- 提高通信稳定性。
实际是否需要、阻值多大,要看收发器芯片、终端电阻、节点数量和线缆情况。
5. STM32 中实现 Modbus RTU + RS485 的基本流程
5.1 硬件层
需要准备:
- STM32 一个 USART;
- RS485 收发器;
- 一个 GPIO 控制 DE/RE;
- A/B 总线接外部设备;
- 必要时加终端电阻、偏置电阻、隔离电源和 TVS 保护。
5.2 串口参数
主机和从机必须完全一致:
波特率:9600 / 19200 / 115200
数据位:8
校验位:None / Even / Odd
停止位:1 / 2
常见配置:
9600, 8N1
9600, 8E1
其中:
8N1 = 8位数据位,无校验,1位停止位
8E1 = 8位数据位,偶校验,1位停止位
工业设备一定要以说明书为准。
5.3 软件流程
主机读取一个从机数据的大致流程:
开始
↓
配置串口和RS485方向控制GPIO
↓
组织Modbus请求帧
↓
计算CRC16
↓
切换RS485为发送模式
↓
通过UART发送请求帧
↓
等待UART发送完成
↓
切换RS485为接收模式
↓
等待从机返回
↓
判断是否超时
↓
校验CRC
↓
解析功能码和寄存器数据
↓
转换成实际物理量
↓
更新系统数据 / UI显示 / 语音提示
5.4 伪代码示例
void RS485_SetTx(void)
{
GPIO_SetBits(RS485_DE_PORT, RS485_DE_PIN);
}
void RS485_SetRx(void)
{
GPIO_ResetBits(RS485_DE_PORT, RS485_DE_PIN);
}
bool Modbus_ReadHoldingRegisters(uint8_t slave_addr,
uint16_t start_addr,
uint16_t reg_num,
uint16_t *out_regs)
{
uint8_t tx_buf[8];
uint8_t rx_buf[64];
tx_buf[0] = slave_addr;
tx_buf[1] = 0x03;
tx_buf[2] = start_addr >> 8;
tx_buf[3] = start_addr & 0xFF;
tx_buf[4] = reg_num >> 8;
tx_buf[5] = reg_num & 0xFF;
uint16_t crc = Modbus_CRC16(tx_buf, 6);
tx_buf[6] = crc & 0xFF;
tx_buf[7] = crc >> 8;
RS485_SetTx();
UART_SendBytes(tx_buf, 8);
UART_WaitTC();
RS485_SetRx();
int len = UART_ReceiveWithTimeout(rx_buf, sizeof(rx_buf), 200);
if (len <= 0)
{
return false;
}
if (!Modbus_CheckCRC(rx_buf, len))
{
return false;
}
if (rx_buf[0] != slave_addr || rx_buf[1] != 0x03)
{
return false;
}
for (int i = 0; i < reg_num; i++)
{
out_regs[i] = (rx_buf[3 + i * 2] << 8) | rx_buf[4 + i * 2];
}
return true;
}
6. 在 FreeRTOS 项目中的设计建议
如果在 STM32 + FreeRTOS 项目中加入 Modbus RTU + RS485,不建议在 LVGL 回调或主界面刷新函数中直接读 Modbus。
更合适的设计是单独建立一个任务:
Modbus_Task
任务职责:
- 定时轮询 RS485 从机;
- 发送 Modbus 请求;
- 等待响应;
- 校验 CRC;
- 解析寄存器;
- 更新全局数据结构;
- 通知 UI 或传感器数据处理模块。
推荐结构:
Modbus_Task
↓
读取RS485传感器
↓
解析温湿度/PM2.5/光照/电表等数据
↓
写入 g_sensor_data
↓
LVGL_Task 定时刷新界面
↓
Voice_Task 根据状态组合播报
这样做的好处:
- UI 不会被串口等待卡住;
- 传感器采集逻辑更清晰;
- 语音播报和界面刷新不会直接抢串口资源;
- 出错时可以单独定位 Modbus 通信问题。
7. 和项目的结合方式
如果你的项目后续要扩展 RS485 传感器,可以这样描述:
系统可通过 RS485 扩展接口接入支持 Modbus RTU 协议的外部传感器或变送器。主控通过 USART 与 RS485 收发器连接,并利用方向控制引脚完成半双工收发切换。软件上按照 Modbus RTU 帧格式组织查询命令,读取从机寄存器数据,经过 CRC 校验和数据换算后,将结果送入传感器数据结构,再由界面刷新任务显示在页面上。该方式便于系统后续扩展工业传感器、电表或其他采集模块。
STM32F407
|
| USART + RS485收发器
|
RS485总线
|
Modbus xxx变送器
Modbus xxx
Modbus xxx变送器
Modbus 继电器模块
这样本地传感器可以分为两类:
| 类型 | 示例 | 接入方式 |
|---|---|---|
| 普通单点传感器 | DHT22、GP2Y1014AU、光敏电阻 | GPIO / ADC |
| 工业扩展传感器 | RS485温湿度、RS485空气质量、电表 | RS485 + Modbus RTU |
8. 常见问题和排查方法
8.1 A/B 接反
现象:
完全无响应
偶尔收到乱码
CRC一直错误
处理:
交换 A/B 两根线再试
注意:不同厂家对 A/B、D+/D- 的标注可能不完全统一。
8.2 DE/RE 方向控制错误
现象:
主机发得出去,但收不到
从机有响应但主机没收到
只收到自己发出去的数据
处理:
- 发送前切换 TX;
- 等待 UART TC;
- 发送完成后切回 RX;
- 不要只等待 TXE。
8.3 CRC 字节顺序写错
Modbus RTU 的 CRC 是:
低字节在前,高字节在后
例如 CRC = 0x0BC4,帧里应写:
C4 0B
而不是:
0B C4
8.4 寄存器地址偏移错误
说明书写:
40001
你报文里可能要写:
00 00
说明书写:
0001
你报文里可能要写:
00 01
也可能要写:
00 00
具体要看厂家说明。有些文档是从 1 开始,有些是从 0 开始。
8.5 终端电阻乱加
错误做法:
每个节点都加120Ω
正确做法:
只在总线两端加
如果每个设备都加,会导致总线负载过重,信号幅度下降,通信反而失败。
8.6 多个从机地址重复
现象:
一发查询,两个设备同时回复
数据冲突
CRC错误
通信不稳定
处理:
给每个从机设置不同地址
8.7 波特率、校验位不一致
必须保证主机和从机一致:
波特率
数据位
校验位
停止位
只要其中一个不一致,通信就可能失败。
8.8 轮询太快
有些低成本传感器响应慢,如果主机连续快速查询,可能出现:
超时
丢帧
返回上一帧数据
设备卡死
建议:
每个设备查询之间留 50 ms ~ 200 ms 间隔
超时时间根据设备说明书设置
9. 表述
9.1 简短版
Modbus 是工业控制领域常用的通信协议,主要用于主机与从机设备之间的数据读写。RS485 是一种差分式串行通信接口,具有抗干扰能力较强、传输距离较远、可多节点挂接等特点。实际应用中,常将 Modbus RTU 协议运行在 RS485 总线上,用于读取传感器、仪表或扩展模块中的寄存器数据。
9.2 项目结合版
为提高系统的扩展能力,后续可预留 RS485 通信接口,使主控能够接入支持 Modbus RTU 协议的外部环境传感器或工业变送器。硬件上,STM32 通过 USART 与 RS485 收发器连接,并通过方向控制引脚完成半双工收发切换;软件上,主控按照 Modbus RTU 帧格式发送查询命令,接收从机返回的数据后进行 CRC 校验和寄存器解析,再将换算后的环境数据用于界面显示和状态判断。该方式能够增强系统对不同类型传感器的兼容性,也便于后续扩展和维护。
10. 最小实现清单
如果要真正做一个 STM32 的 Modbus RTU + RS485 扩展,至少需要:
硬件
- STM32 USART 一路;
- RS485 收发芯片或模块;
- DE/RE 方向控制 GPIO;
- A/B 双绞线;
- 必要时增加 120Ω 终端电阻;
- 必要时增加偏置电阻;
- 工业现场建议加隔离、TVS、防反接保护。
软件
- 串口初始化;
- RS485 方向控制;
- Modbus CRC16;
- 请求帧组包;
- 接收超时机制;
- 响应帧 CRC 校验;
- 功能码判断;
- 寄存器数据解析;
- 异常码处理;
- 多从机轮询机制。
调试工具
- USB 转 RS485 调试器;
- 串口助手;
- Modbus Poll / Modbus Slave 等调试软件;
- 示波器或逻辑分析仪;
- 设备寄存器说明书。
11. 结论
可以把两者的关系总结为:
Modbus:通信协议,规定数据怎么读写。
RS485:物理接口,规定信号怎么传输。
485扩展:给主控增加 RS485 通信能力。
Modbus RTU + RS485:工业现场最常见的串口总线通信组合。
对于 STM32 项目来说,真正落地时要注意三件事:
- 硬件要接对:TX/RX、DE/RE、A/B、GND、终端电阻。
- 协议要写对:地址、功能码、寄存器、CRC、字节序。
- 时序要控制好:发送前切 TX,发送完成后切 RX,接收要有超时和校验。

285

被折叠的 条评论
为什么被折叠?



