RS-485 与 Modbus
RS-485 与 Modbus
一、概述
RS-485 是差分、异步、半双工的物理层标准,支持多点(32 个节点以上)和远距离(1200m)通信,是工业总线的基础。Modbus 是运行在 RS-485(或其他串行)上的应用层协议,定义了主从设备如何读写寄存器的报文格式。
一句话核心:RS-485 解决"电气上怎么传"(差分多机),Modbus 解决"数据怎么组织"(主从轮询 + 寄存器读写 + CRC 校验)。两者结合是工业控制最常用的通信方案。
二者是"物理层 + 应用层"的配套关系(不是并列的两个独立协议,如同"TCP + HTTP"):
┌─────────────────────────────────────────────────┐
│ 应用层:Modbus 协议 │
│ · 主从轮询(Master/Slave) │
│ · 寄存器读写(线圈/输入/保持/输入寄存器) │
│ · 帧格式 + 功能码 + CRC16 │
├─────────────────────────────────────────────────┤
│ 物理层:RS-485 │
│ · 差分双线传输(A/B) │
│ · 半双工,收发切换(DE/RE) │
│ · 多点挂接(32+ 节点)、远距离(1200m) │
├─────────────────────────────────────────────────┤
│ 电气层:差分电压 ±200mV 判定 0/1、终端/偏置电阻 │
└─────────────────────────────────────────────────┘理解要点:RS-485 本身只负责"把一帧字节流在电线上可靠地传来传去",不规定数据格式;而 Modbus 负责"这帧字节流怎么组织、怎么解读"。两者常配套使用——RS-485 提供传输,Modbus 提供语义,脱离 RS-485 的 Modbus 还能跑在串口/TCP 上,脱离 Modbus 的 RS-485 就只是一根"能多机通信的串口线"。
二、RS-485 物理层
2.1 差分传输
RS-485 用两根差分线(A/B 或 D+/D-),通过两线电压差表示逻辑:
| 状态 | 电压差(B−A) | 逻辑 |
|---|---|---|
| 差分高 | V_B − V_A ≥ +200mV | 逻辑 1 |
| 差分低 | V_B − V_A ≤ −200mV | 逻辑 0 |
| 参数 | 典型值 | 说明 |
|---|---|---|
| 差分阈值 | ±200mV | 判定 0/1 的最小电压差 |
| 有效差分电压 | 1.5~5V | 实际驱动时 A/B 线电压差,远大于阈值 |
| 共模电压范围 | −7V ~ +12V | 收发器允许的 A/B 对地共模范围,允许两地电位不同 |
| 最大速率 / 距离 | 10Mbps@15m ~ 100kbps@1200m | 速率越高,可靠距离越短 |
波形示意(一帧字节的某几位):
位: 逻辑 1 逻辑 0 逻辑 1 逻辑 0
V_B−V_A ┌───────────┐ ┌───────────┐
(+V) │ │ │ │
└───────────┘ └───────────┘
(0V) ─────────────────────────────────────────────────────── ← 参考零电平
(−V) ┌───────────┐ ┌───────────┐
│ │ │ │
└───────────┘ └───────────┘上图直接画差分电压
V_B−V_A随时间的高低:逻辑 1 时差分电压抬到+V(高位),逻辑 0 时压到−V(低位),零电平为参考线。
每一位的 A/B 电平(体现反相 + 差分判定):
| 位 | 逻辑 | A 线电平 | B 线电平 | V_B − V_A | 差分判定 |
|---|---|---|---|---|---|
| 第 1 位 | 1 | 低 | 高 | +V | ≥ +200mV → 逻辑 1 |
| 第 2 位 | 0 | 高 | 低 | −V | ≤ −200mV → 逻辑 0 |
| 第 3 位 | 1 | 低 | 高 | +V | ≥ +200mV → 逻辑 1 |
| 第 4 位 | 0 | 高 | 低 | −V | ≤ −200mV → 逻辑 0 |
- A、B 始终反相(互补):一位里 A 高则 B 低、A 低则 B 高;
- 逻辑 1 = B 高、A 低 →
V_B − V_A = +V;逻辑 0 = A 高、B 低 →V_B − V_A = −V; - 差分电压幅度(1.5~5V)远大于 ±200mV 阈值,留足判定余量。
抗干扰原理:两根线绞在一起、受同样噪声干扰(共模干扰),差分接收器只计算 V_B − V_A,共模干扰一减就被抵消 → 抗共模干扰强,可传 1200m,适用于电机、变频器等强干扰工业环境。
2.2 半双工与收发控制
RS-485 是半双工——同一时刻只能发或收。通常用一根控制脚(DE/RE)切换方向:
┌──────┐ A ═══════════════════ A ┌──────┐
│ 主机 │─DE/RE─ B ═══════════════════ B │ 从机 │
└──────┘ └──────┘
发送时 DE=1;接收时 DE=0关键理解:收发切换要留切换时间(发完立即切换接收会丢失回复起始字节)。通常发完延时几十 μs 再切接收。
2.3 终端电阻与偏置
① 终端电阻(防反射)
- 总线两端各接一个 120Ω 终端电阻(匹配传输线特性阻抗),消除信号反射和振铃;
- 中间节点不接;两端电阻并联等效
120Ω/2 = 60Ω差分负载,与收发器匹配; - 距离短、速率低时可省略,但长线/高速必须接(否则波形畸变、误码)。
② 偏置电阻(空闲态保护)
- 空闲时没有任何节点驱动总线(所有收发器都释放),此时 A/B 间电压不确定(可能落入 ±200mV 的模糊区),噪声可能被误判为数据;
- 用一对偏置电阻(上拉到 VCC / 下拉到 GND)在空闲时把 A/B 间电压钳制到确定的隐性电平(如 B 高 A 低 → 判定逻辑 1);
- 偏置电阻典型几百 Ω ~ 几 kΩ,具体按收发器要求选取。
VCC
│
R1(偏置上拉)
│
A ───────┼───────────────────────────── A
B ───────┼───────────────────────────── B
│
R2(偏置下拉)
│
GND
(A、B 各接一个 120Ω 终端在两端 + 中间某处加偏置电阻对)关键理解:终端电阻防反射、偏置电阻防空闲误判,两者作用不同、都要有。工业现场"偶尔收到乱码/首个字节丢失"的怪问题,常与偏置电阻缺失或接错有关。
三、Modbus 协议
Modbus 是主从(Master/Slave)协议:主机发起请求,从机响应。同一时刻只有主机可主动发,从机只能应答。
3.1 三种传输模式
| 模式 | 说明 | 特点 |
|---|---|---|
| RTU | 二进制(最常用) | 紧凑、CRC16 校验、帧靠静默时间分隔 |
| ASCII | 十六进制 ASCII 字符 | 可读性好、效率低、LRC 校验 |
| TCP | 运行在 TCP/IP | 局域网/以太网用(Modbus TCP) |
3.2 Modbus 数据模型(四种数据类型)
Modbus 把从机内的数据分成四种区域,每种用不同的功能码读写。这是理解"功能码 + 地址"的前提:
| 数据类型 | 数据宽度 | 读写性 | 地址范围 | 对应功能码 |
|---|---|---|---|---|
| 线圈(Coil) | 1 位 | 可读可写 | 0x0000~0xFFFF | 读 0x01 / 写 0x05、0x0F |
| 离散输入(Discrete Input) | 1 位 | 只读 | 0x0000~0xFFFF | 读 0x02 |
| 保持寄存器(Holding Register) | 16 位 | 可读可写 | 0x0000~0xFFFF | 读 0x03 / 写 0x06、0x10 |
| 输入寄存器(Input Register) | 16 位 | 只读 | 0x0000~0xFFFF | 读 0x04 |
理解要点:
- "1 位"是开关量(如继电器通断、按钮状态);"16 位"是数值量(如温度、速度、电压);
- 线圈/保持寄存器可写(主机能下发控制),离散输入/输入寄存器只读(主机只能读取从机采集的现场数据);
- 地址是从机内部的偏移量(0x0000 起),与"从机地址"(区分不同设备)是两码事——从机地址选设备,寄存器地址选该设备内部的数据;
- 有些从机还支持"地址区间偏移"(如 PLC 把保持寄存器映射到 4xxxx 区间),但 Modbus 报文里用的仍是 0 起始的偏移地址。
3.3 Modbus RTU 帧格式
┌────────┬────────────┬─────────┬──────────┐
│从机地址 │ 功能码+数据 │ CRC16 低│ CRC16 高 │
│ 1字节 │ N字节 │ 1字节 │ 1字节 │
└────────┴────────────┴─────────┴──────────┘
帧间静默 ≥ 3.5 字符时间| 字段 | 说明 |
|---|---|
| 从机地址 | 1 字节(0~247),0 为广播 |
| 功能码 | 表示操作类型(见下) |
| 数据 | 寄存器地址、数量、值等 |
| CRC16 | 校验整个帧(低字节在前) |
例 1:读请求帧如何按字段组帧(读从机 1、保持寄存器 0x0000 起 3 个)
| 字节序号 | 值 | 字段 | 含义 |
|---|---|---|---|
| 1 | 01 | 从机地址 | 目标是 1 号从机 |
| 2 | 03 | 功能码 | 读保持寄存器 |
| 3~4 | 00 00 | 起始地址 | 从寄存器地址 0x0000 开始读 |
| 5~6 | 00 03 | 数量 | 连续读 3 个寄存器 |
| 7~8 | 05 CB | CRC16 | 对前 6 字节计算,低字节 05 在前 |
完整帧:01 03 00 00 00 03 05 CB(8 字节,RTU 无长度字段,靠静默分隔)。
例 2:请求与响应帧的对应关系(写单个保持寄存器,功能码 0x06)
| 方向 | 帧内容 | 说明 |
|---|---|---|
| 请求 | 01 06 00 01 03 E8 <CRC> | 写从机1、寄存器 0x0001、值 0x03E8 |
| 响应 | 01 06 00 01 03 E8 <CRC> | 从机原样回显请求内容,表示写入成功 |
帧间静默(≥3.5 字符时间):RTU 帧没有"长度"字段,收发双方靠静默间隔判定一帧结束——两帧之间必须留 ≥3.5 个字符时间的静默,否则前一帧的尾部 CRC 可能被误判为下一帧的起始,导致粘包、错帧。
3.4 常用功能码
| 功能码 | 名称 | 操作对象 | 一次操作几个 | 典型场景 |
|---|---|---|---|---|
0x01 | Read Coils | 线圈(1 位,可读写) | 1~2000 个 | 读一组开关量输出状态 |
0x02 | Read Discrete Inputs | 离散输入(1 位,只读) | 1~2000 个 | 读一组现场开关量输入 |
0x03 | Read Holding Registers | 保持寄存器(16 位,可读写) | 1~125 个 | 读一组可写的数值参数 |
0x04 | Read Input Registers | 输入寄存器(16 位,只读) | 1~125 个 | 读一组只读的采集数据 |
0x05 | Write Single Coil | 线圈 | 1 个 | 单独控制一个开关量(如启停一个继电器) |
0x0F | Write Multiple Coils | 线圈 | 1~1968 个 | 批量设置一组开关量 |
0x06 | Write Single Register | 保持寄存器 | 1 个 | 改单个设定值/参数 |
0x10 | Write Multiple Registers | 保持寄存器 | 1~123 个 | 批量下发一组参数(如整段配置) |
为什么保持寄存器有两个写功能码(0x06 和 0x10):它们操作的是同一种数据(保持寄存器,地址范围相同 0x0000~0xFFFF),区别只在一次写几个——
0x06只写 1 个(帧短、适合单独改参数),0x10一次写多个(适合批量下发,避免反复发 0x06)。0x05 / 0x0F 之于线圈同理。读功能码本身就能读多个(0x01~0x04),所以读不需要"单个/多个"之分。
四、CRC16 校验(Modbus RTU)
Modbus RTU 用 CRC16-MODBUS 校验,原理与 CAN 的 CRC 相同(都是"模 2 多项式除法,余数作校验值",见 4.1 节的对比),但它的算法需要自己在软件里实现。先看它的具体参数:
Modbus CRC16 参数表:
| 参数 | 值 | 说明 |
|---|---|---|
| CRC 宽度 | 16 位 | 校验值占 2 字节 |
| 多项式(数学形式) | 0x8005 | 即 x^16 + x^15 + x^2 + 1,标准形式 |
| 算法用多项式 | 0xA001 | 0x8005 的位反转形式,配合右移算法 |
| 初始值 | 0xFFFF | 计算前 CRC 寄存器的初值 |
| 结果处理 | 不取反 | 算完直接发 |
| 发送字节序 | 低字节在前 | 最大易错点 |
算法(右移版,代码里实际用的):
CRC = 0xFFFF ← 初始值
for each 字节:
CRC ^= 字节 ← 与当前字节异或
for i in 0..7: ← 每字节做 8 次移位
if CRC & 1: CRC = (CRC >> 1) ^ 0xA001 ← 最低位为1,右移后异或反向多项式
else: CRC = (CRC >> 1) ← 最低位为0,只右移
发送顺序:CRC 低字节在前,高字节在后为什么算法里是
0xA001而不是0x8005(这是理解 Modbus CRC 实现的关键):CRC 的多项式数学上写成
0x8005,但用软件实现时有"左移"和"右移"两种方式,多项式要跟着移位方向反转:
- 左移算法:从最高位开始处理,用
0x8005;- 右移算法:从最低位开始处理,用
0x8005的位反转0xA001。
0x8005→0xA001的位反转过程(16 位逐位倒序):0x8005 = 1000 0000 0000 0101 位反转(从右往左读)= 1010 0000 0000 0001 = 0xA001Modbus 的实现采用右移(因为串行数据通常从最低位开始处理,右移更贴合"每来一位处理一位"的硬件/串口思路),所以用反转后的
0xA001。两种方向、两个多项式,算出的 CRC 结果完全一致——只是实现路径不同。判断技巧:代码里看到
0xA001= Modbus CRC16;看到0x8005通常是其他 CRC-16 变体(如 CCITT)。Modbus 官方文档写数学多项式0x8005,但实现代码统一用0xA001。
逐步演算(用真实帧 01 03 00 6B 00 03 完整走一遍):
先明确:数据是什么、为什么 8 轮:
- 待校验数据 =
01 03 00 6B 00 03(6 字节:从机地址 01、功能码 03、起始地址 006B、数量 0003); - 为什么每字节做 8 轮:一个字节有 8 位,CRC 要逐位处理,所以每处理一个字节做 8 轮右移;6 字节 = 6×8 = 48 轮。
通用规则(每个字节都执行"异或 + 8 轮右移"):
① CRC ^= 当前字节
② 做 8 轮右移:每轮看 LSB —— LSB=1 则 (CRC>>1) ^ 0xA001;LSB=0 则 CRC>>1为什么"①先异或"? 这一步把当前数据字节混进 CRC 寄存器——相当于把这 1 字节"接入"正在进行的多项式除法运算。如果只移位不先异或,该字节的高位就无法参与计算,会漏掉部分数据位。每个字节都要先异或,就是为了让这个字节的 8 位都进入运算。可类比"搅拌机":异或 = 把这勺数据倒进桶里,8 轮右移 = 搅匀。
为什么初始 CRC 是
0xFFFF? 这是 Modbus 协议规定的初始值(见参数表)。处理第 1 个字节前,CRC 寄存器里预置为0xFFFF,所以第 1 步是0xFFFF ^ 0x01。它和"结果不取反、低字节在前"一样,都是 Modbus 实现约定的一部分,收发双方必须一致才能算出相同结果。
第 1 字节 0x01(详细展开 8 轮):
先异或:0xFFFF ^ 0x01 = 0xFFFE,再对 0xFFFE 做 8 轮右移:
| 轮 | 当前 CRC | LSB | 操作 | 结果 |
|---|---|---|---|---|
| 1 | 0xFFFE | 0 | 只右移 | 0x7FFF |
| 2 | 0x7FFF | 1 | (0x7FFF>>1)^0xA001 | 0x9FFE |
| 3 | 0x9FFE | 0 | 只右移 | 0x4FFF |
| 4 | 0x4FFF | 1 | (0x4FFF>>1)^0xA001 | 0x87FE |
| 5 | 0x87FE | 0 | 只右移 | 0x43FF |
| 6 | 0x43FF | 1 | (0x43FF>>1)^0xA001 | 0x81FE |
| 7 | 0x81FE | 0 | 只右移 | 0x40FF |
| 8 | 0x40FF | 1 | (0x40FF>>1)^0xA001 | 0x807E |
处理完第 1 字节 0x01,CRC = 0x807E。
完整异或举例(第 2 轮):0x7FFF>>1 = 0x3FFF,再 0x3FFF ^ 0xA001:
0x3FFF 0011 1111 1111 1111
0xA001 1010 0000 0000 0001
异或⊕ 1001 1111 1111 1110 = 0x9FFE ← 相同位得0,不同位得1第 2~6 字节(用同样的"异或 + 8 轮",这里给出每字节处理完的 CRC 结果):
| 处理字节 | 前一步 CRC | 异或后 | 8 轮右移后(本字节结果) |
|---|---|---|---|
0x01 | 0xFFFF | 0xFFFE | 0x807E |
0x03 | 0x807E | 0x807D | 0x2140 |
0x00 | 0x2140 | 0x2140 | 0xF020 |
0x6B | 0xF020 | 0xF04B | 0x37B0 |
0x00 | 0x37B0 | 0x37B0 | 0xB436 |
0x03 | 0xB436 | 0xB435 | 0x1774 |
最终 CRC = 0x1774,完整帧 = 01 03 00 6B 00 03 74 17(CRC 低字节 74 在前)。
多字节流程小结:每个字节重复"①异或 → ②8 轮右移",上一步 CRC 作为下一步初始值,逐字节推进,6 字节全处理完即得最终 CRC。第 1 字节 0x01 的完整 8 轮已展开,其余字节过程完全相同、只是初始 CRC 不同。
关键理解:CRC 低字节在前(小端)。这是 Modbus RTU 最容易踩的坑——CRC 高低字节顺序反了会通信失败。
4.1 Modbus CRC16 vs CAN CRC 的区别
两者原理相同(模 2 多项式除法,余数作校验值)、目的相同(检错),但实现细节不同:
| 对比项 | Modbus RTU(CRC16) | CAN(CRC15) |
|---|---|---|
| CRC 宽度 | 16 位 | 15 位 |
| 多项式(数学形式) | 0x8005 | 0x4599 |
| 算法用多项式 | 反向 0xA001(右移算法) | 硬件内部实现 |
| 初始值 | 0xFFFF | 0 |
| 结果处理 | 不取反,低字节在前 | 结果取反后发送 |
| 计算对象 | 整个帧(地址+功能码+数据) | 帧前半部分(仲裁+控制+数据) |
| 谁计算 | 软件(MCU 逐字节算) | 硬件(CAN 控制器自动算) |
| 字节序问题 | 有(低字节在前,易踩坑) | 无(逐位发送,不存在高低字节) |
核心区别一句话:
- Modbus 的 CRC16 要你自己写——MCU 软件逐字节算,代码里会出现循环 + 异或
0xA001,还要注意低字节在前; - CAN 的 CRC15 是控制器硬件自动算——发送时硬件自动填充,接收时硬件自动校验,程序员基本不用碰 CRC。
原理相通但关注点不同:两种 CRC 都在帮我们检测"帧有没有被干扰"。写 Modbus 时你得亲自动手算 CRC 并小心字节序;用 CAN 时 CRC 由芯片包办了,你更应关注的是位时序、采样点这些硬件配置(见 CAN 文档第五节)。
五、数据传输的完整流程
前几节分别讲了物理层、Modbus 帧格式、CRC,本节把它们串起来,完整演示 RS-485 + Modbus 上一次"一问一答"的全过程。
关键模型:Modbus 是主从(Master/Slave)——主机主动发请求,从机被动应答。同一时刻只有主机可主动发,从机只能等收到请求后回应。这不同于 CAN 的"广播、多主可主动发"模型。
5.1 一次完整数据传输的总体流程
主机 从机
│ ①构造请求帧(地址+功能码+数据+CRC) │
├──────────────────────────────────────►│ ②接收整帧
│ │ ③校验地址→功能码→CRC
│ │ ④执行操作(读/写)
│◄──────────────────────────────────────┤ ⑤构造应答帧(数据+CRC)
│ ⑥接收应答帧 │
│ ⑦校验CRC→处理数据 │
│ ⑧(可能)发起下一次请求 │一句话:主机"发请求"、从机"回应答",一来一回是一次完整事务;主机再发下一个请求,从机再回…… 周而复始,就是主从通信。
5.2 主机:发送请求的流程
| 步骤 | 动作 | 说明 |
|---|---|---|
| ① | 构造请求帧 | 组装"从机地址 + 功能码 + 数据 + CRC16"(如 01 03 00 6B 00 03 + CRC) |
| ② | 切换到发送 | RS-485 半双工,置 DE=1 进入发送态 |
| ③ | 发送整帧 | 逐字节发出,发完保留帧间静默(≥3.5 字符时间) |
| ④ | 切换到接收 | 发完延时几十 μs,置 DE=0 进入接收态,准备收应答 |
关键:半双工要先发完再切接收,且切换要留延时(见 2.2 节),否则应答首字节会丢。
5.3 从机:接收请求、处理、应答的流程
| 步骤 | 动作 | 说明 |
|---|---|---|
| ① | 监听总线 | 一直处于接收态,等待帧间静默后的一帧 |
| ② | 接收整帧 | 收到一帧(靠静默判定结束) |
| ③ | 校验 | ①从机地址是否匹配自己;②CRC 是否正确(见第四节) |
| ④ | 执行操作 | 校验通过则按功能码执行(读寄存器/写寄存器/……) |
| ⑤ | 构造应答帧 | 读操作回"地址+功能码+字节数+数据+CRC";写操作回显请求内容+CRC |
| ⑥ | 发送应答 | 半双工切换后发出 |
应答帧里的地址是从机自己的地址,不是主机地址:Modbus 中只有从机有地址(1~247),主机没有地址。从机应答时,地址字段回显请求里的"目标从机地址"(也就是自己),告诉总线"这条应答是谁发的"。主机不需要靠应答地址来识别(主机靠"自己发出的请求 + 超时"判断),所以应答帧没有、也不需要主机地址。
异常路径:若从机地址不匹配或 CRC 错误,从机不回应(保持静默,让主机超时处理);若请求非法(功能码/地址/值不对),从机回异常帧(见 6.2 节)。
5.4 主机:接收应答并校验的流程
| 步骤 | 动作 | 说明 |
|---|---|---|
| ① | 等待应答 | 发请求后进入接收态,等从机回应 |
| ② | 校验 CRC | 收到整帧先算 CRC 比对,不一致则丢弃(可能重试) |
| ③ | 校验功能码 | 确认应答功能码是否带异常标志(最高位 1 = 异常帧) |
| ④ | 解析数据 | 从应答帧数据段提取寄存器值(注意大端字节序) |
| ⑤ | 处理结果 | 使用读到的数据 / 确认写入成功 |
5.5 完整示例:主机读从机 1 的保持寄存器
需求:主机读取 1 号从机、地址 0x0000 起 3 个保持寄存器(功能码 0x03)。
主机 从机1
│ ①组请求: 01 03 00 00 00 03 05 CB (地址01+功能码03+地址0000+数量0003+CRC)
├───────────────────────────────────────────────────────►
│ ②收帧,地址=01匹配,CRC正确
│ ③执行:读出寄存器0,1,2
│ ④组应答:01 03 06 12 34 56 78 9A BC +CRC
│◄────────────────────────────────────────────────────────
│ ⑤收应答,CRC正确
│ ⑥解析: 0x1234=寄存器0, 0x5678=寄存器1, 0x9ABC=寄存器2
│ ⑦发起下一次请求(轮询下一个从机/寄存器)- 请求帧:
01 03 00 00 00 03 05 CB(含 CRC,见 3.3 节例 1); - 应答帧:
01 03 06 12 34 56 78 9A BC <CRC>(06是字节数,6 字节 = 3 寄存器 × 2 字节); - 寄存器值大端:
12 34= 寄存器 0 的值,高字节在前。
小结:RS-485 + Modbus 的数据传输是严格的一问一答主从模型——主机组帧发送、从机校验执行、再回应答、主机校验解析。它没有 CAN 的"广播 + 仲裁 + 自动 ACK",可靠性主要靠主机主动的超时/重试和每帧的 CRC 校验保证。
六、主从轮询流程
Modbus 是主机轮询从机:主机依次给每个从机发请求,从机应答。
主机 从机1 从机2
发请求(读从机1) ───────────────────► 应答数据◄─...
发请求(读从机2) ───────────────► ...
发请求(写从机1) ───────────────────► 应答写成功◄─...
循环轮询异常响应:从机收到错误请求,回异常帧(功能码最高位置 1,如 0x83)+ 异常码:
| 异常码 | 含义 |
|---|---|
0x01 | 非法功能 |
0x02 | 非法数据地址 |
0x03 | 非法数据值 |
0x04 | 从机设备故障 |
超时与重试机制(轮询的可靠性保障):
从机可能因离线、故障、忙等不响应。主机绝不能无限等待,必须设超时:
| 环节 | 做法 | 说明 |
|---|---|---|
| 帧间静默 | ≥ 3.5 字符时间 | 判定一帧结束(RTU 无长度字段,靠静默分隔) |
| 接收超时 | 发请求后等 T 秒(如 500ms) | T 内无响应,判从机超时 |
| 重试次数 | 超时后重发,最多 N 次(如 3 次) | 避免一次偶发失败就判定设备故障 |
| 跳过/报警 | 重试仍失败则跳过该从机,记错误 | 继续轮询下一个,不阻塞整条总线 |
主机轮询状态机:
发起请求 → 等待响应 → 收到并校验CRC → 处理数据 → 轮询下一从机
│(超时 T 内无响应)
↓
重试(最多 N 次)── 仍失败 ──→ 跳过该从机、记录错误 → 轮询下一从机关键理解:超时 + 重试让"单个从机故障"不至于拖垮整条总线——主机超时后跳过继续轮询其他从机,其余设备正常工作。这与 CAN 的"错误计数 + 离线保护"思想类似,都是"隔离故障节点、保障整网可用"。
七、RS-485 vs CAN 对比
| 维度 | RS-485 | CAN |
|---|---|---|
| 物理层 | 差分双线 | 差分双线 |
| 多主/仲裁 | 通常主从(无自动仲裁) | 多主(自动仲裁) |
| 寻址 | 从机地址(Modbus) | 报文 ID 优先级 |
| 协议栈 | Modbus(应用层) | CAN 自身有帧类型+错误机制 |
| 错误处理 | Modbus CRC | CAN 错误帧+离线保护 |
| 典型场景 | 工控仪表、PLC、楼宇 | 汽车、高速控制 |
八、常见坑与排查
| 问题 | 可能原因 | 排查 |
|---|---|---|
| 完全无通信 | 收发控制(DE/RE)没切、A/B 接反 | 检查方向脚,A↔B 对调 |
| 偶发错帧 | 缺终端电阻、干扰、波特率偏差 | 加 120Ω 终端电阻 |
| CRC 错误 | CRC 高低字节反了 | 确认 CRC 低字节在前 |
| 从机无响应 | 从机地址错、超时太短 | 核对地址,加超时 |
| 寄存器值反 | 大小端/字节序 | 确认大端(高字节在前) |
| 从机收不到广播 | 广播地址 0 需特殊处理 | 确认功能码支持广播 |