Files
DLD154Pro/docs/rs485-modbus-protocol.md
T
wangfq b13e8e4030 docs: RS485 Modbus RTU 协议 V1.0 草案
- 物理层: RS485 半双工, 9600-N-8-1 默认
- 寄存器映射: 离散输入(0x02)、输入寄存器(0x04)、保持寄存器(0x03/0x06/0x10)
- RS485 地址/波特率 只读 (BLE 带外修改,防止 Modbus 误改失联)
- 设计决策: 参数持久化、灵敏度ΔL/L对应、输出模式、半双工时序
2026-07-22 14:27:54 +08:00

239 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DLD154Pro RS485 Modbus RTU 协议
> 版本: V1.0 | 日期: 2026-07-22 | 状态: 草案(待评审,未实现)
>
> 适用产品: DLD154Pro (CH591R, 单路线圈车检器)
>
> RS485 支持两种协议:700BS(后续定义)和 Modbus RTU。
> 协议选择通过 `g_function_mode` bit0 控制(BLE 小程序设置 `direction_mode` 高 4 位的 bit0)。
---
## 1. 物理层
| 参数 | 值 |
|------|-----|
| 接口 | RS485 半双工 (UART0) |
| 引脚 | PA14 TX, PA15 RX, PA13 RE (方向控制) |
| 波特率 | 9600 (默认), 19200, 38400, 57600, 115200 |
| 数据位 | 8 |
| 校验 | 无 (N) |
| 停止位 | 1 |
| 从机地址 | 1247 (默认 1) |
| 终端电阻 | 外接 120Ω(板载未集成) |
> **注意**RS485 地址和波特率通过 **BLE 小程序** 修改,不通过 Modbus 寄存器修改(防止改错后设备失联)。Modbus 只读方式可查询当前值。
---
## 2. 帧格式 (Modbus RTU)
```
| 地址 | 功能码 | 数据 | CRC16 |
| 1B | 1B | N B | 2B |
```
- **地址**:1–247,从机地址。0=广播(仅写操作),DLD154Pro 不响应广播。
- **CRC16**:多项式 `0xA001`,低字节在前。超时 3.5 字符作为帧间隔。
- **最大 PDU**:256 字节(含地址 + 功能码)。
### 异常响应
当请求无法处理时,功能码最高位置 1,数据区第一字节为异常码:
| 异常码 | 名称 | 说明 |
|--------|------|------|
| 0x01 | Illegal Function | 不支持的功能码 |
| 0x02 | Illegal Data Address | 寄存器地址超出范围 |
| 0x03 | Illegal Data Value | 写入值超出允许范围 |
| 0x04 | Slave Device Failure | 设备内部错误(如 flash 写入失败) |
---
## 3. 寄存器映射
### 3.1 离散输入 (Discrete Inputs) — 功能码 0x02
| 地址 | 名称 | 说明 | 位值 |
|------|------|------|------|
| 0x0000 | Car Present | 当前有车 | 0=无车, 1=有车 |
| 0x0001 | Loop Fault | 线圈故障(断开/短路) | 0=正常, 1=故障 |
| 0x0002 | Loop Stable | 线圈基线已稳定 | 0=收敛中, 1=稳定 |
| 0x0003 | Opto Input | 光耦输入状态 (PB10) | 0=低, 1=高 |
| 0x0004 | Power Up State | 上电初始化中 | 0=正常, 1=初始化 |
| 0x0005 | Reserved | — | — |
### 3.2 输入寄存器 (Input Registers) — 功能码 0x04
| 地址 | 名称 | 类型 | 说明 |
|------|------|------|------|
| 0x0000 | Loop Freq Hi | uint16 | 线圈振荡频率,高 16 位 (Hz) |
| 0x0001 | Loop Freq Lo | uint16 | 线圈振荡频率,低 16 位 (Hz) |
| 0x0002 | CAPVD | uint16 | 归一化周期值(算法内部) |
| 0x0003 | Origin | uint16 | 基线值(无车时基准) |
| 0x0004 | Variation | int16 | 当前相对于基线的变化量 |
| 0x0005 | FW Version | uint16 | 固件版本,高字节=主版本,低字节=子版本。例 `0x0300` = v3.00 |
| 0x0006 | HW Version | uint16 | 硬件版本。例 `0x0301` = v3.01 |
| 0x0007 | Freq Sens | uint16 | 当前灵敏度等级 (0=低, 1=中, 2=高) |
| 0x0008 | Condition | uint16 | 环境活跃度 (0=安静, 1-100=嘈杂) |
> **频率换算**`freq_hz = (reg[0] << 16) | reg[1]`,单位 Hz。典型空线圈 50–120kHz,有车时下降。
### 3.3 保持寄存器 (Holding Registers) — 功能码 0x03 / 0x06 / 0x10
| 地址 | 名称 | 范围 | 默认 | R/W | 说明 |
|------|------|------|------|-----|------|
| 0x0000 | Sensitivity | 02 | 2 | R/W | 灵敏度: 0=低(0.330%ΔL/L), 1=中(0.165%), 2=高(0.085%) |
| 0x0001 | Output Mode | 02 | 0 | R/W | 0=存在输出, 1=进入脉冲(500ms), 2=离开脉冲(500ms) |
| 0x0002 | Hold Time | 0255 | 0 | R/W | 有限存在时间 (×5s), 0=永久存在。例 12=60s 后强制释放 |
| 0x0003 | Output Delay | 0255 | 10 | R/W | 离开延时 (×50ms), 例 10=500ms。0=无延时 |
| 0x0004 | Freq Level | 01 | 0 | R/W | 线圈频率档: 0=高频, 1=低频 |
| 0x0005 | RS485 Baud | uint32 | 9600 | **R** | RS485 波特率,只读(通过 BLE 修改) |
| 0x0006 | (Baud Lo) | — | — | **R** | 波特率低 16 位 |
| 0x0007 | RS485 Addr | 1247 | 1 | **R** | RS485 从机地址,只读(通过 BLE 修改) |
| 0x0008 | Protocol | 01 | 0 | **R** | 当前协议: 0=700BS, 1=Modbus,只读 |
| 0x0009 | BT Timeout | 0255 | 0 | R/W | 蓝牙空闲超时关闭 (分钟), 0=不关闭 |
| 0x000A | Safe Timeout | 0255 | 0 | R/W | 线圈安全模式超时 (×10s), 0=关闭 |
| 0x000B | Opto Func | 01 | 0 | **R** | 光耦功能: 0=雷达输入, 1=复位,只读(通过 BLE 修改) |
> **只读寄存器 (0x00050x0008, 0x000B)**:对这些地址写 0x06 返回异常 0x02Illegal Data Address)。
> 这些参数只能通过 BLE 小程序修改,避免 RS485 改错后设备失联。
---
## 4. 命令示例
### 4.1 读离散输入 — 功能码 0x02
读取车辆存在状态:
```
>>> 01 02 00 00 00 01 B9 F8 (从机1, 读地址0x0000, 1个)
<<< 01 02 01 01 61 8C (1字节数据=0x01, 有车)
```
### 4.2 读输入寄存器 — 功能码 0x04
读取频率 + CAPVD:
```
>>> 01 04 00 00 00 03 30 1F (读3个: 频率Hi/Lo + CAPVD)
<<< 01 04 06 00 01 86 A0 11 BB 22 xx xx
地址 功能 字节数 FreqHi FreqLo CAPVD CRC
→ freq = 0x000186A0 = 100000 Hz
→ CAPVD = 0x11BB
```
### 4.3 读保持寄存器 — 功能码 0x03
读取全部检测参数:
```
>>> 01 03 00 00 00 05 84 09 (读5个: 灵敏度/输出模式/存在时间/延时/频率档)
<<< 01 03 0A 00 02 00 00 00 00 00 0A 00 00 xx xx
→ 灵敏度=2(高), 存在输出, 永久存在, 延时=500ms, 高频
```
### 4.4 写单个寄存器 — 功能码 0x06
修改灵敏度为"中"
```
>>> 01 06 00 00 00 01 49 F3 (写地址0x0000=1, 中灵敏度)
<<< 01 06 00 00 00 01 49 F3 (回显)
```
修改输出模式为"进入脉冲":
```
>>> 01 06 00 01 00 01 18 33 (写地址0x0001=1, 进入脉冲)
<<< 01 06 00 01 00 01 18 33
```
### 4.5 批量写寄存器 — 功能码 0x10
一次写入灵敏度 + 输出模式 + 存在时间(60s) + 延时(200ms):
```
>>> 01 10 00 00 00 03 06 00 02 00 01 00 0C 00 04 xx xx
地址 功能 起始 数量 字节 灵敏度=2 模式=1 时间=12 延时=4 CRC
<<< 01 10 00 00 00 03 41 F3 (写入成功)
```
---
## 5. 功能码支持矩阵
| 功能码 | 名称 | 支持 | 说明 |
|--------|------|------|------|
| 0x01 | Read Coils | ✗ | 不使用 |
| 0x02 | Read Discrete Inputs | ✓ | 车辆状态、故障、光耦 |
| 0x03 | Read Holding Registers | ✓ | 读所有配置参数 |
| 0x04 | Read Input Registers | ✓ | 读传感器数据 |
| 0x05 | Write Single Coil | ✗ | 不使用 |
| 0x06 | Write Single Register | ✓ | 写单个参数(安全:只写 R/W 寄存器) |
| 0x0F | Write Multiple Coils | ✗ | 不使用 |
| 0x10 | Write Multiple Registers | ✓ | 批量写参数 |
| 0x11 | Report Slave ID | ✓ | 返回设备型号 + 运行状态 |
---
## 6. 设计决策与注意事项
### 6.1 为什么不通过 Modbus 修改 RS485 地址/波特率?
如果在 Modbus 通信中写入错误的地址或波特率,设备将**立即失联**且无法通过同一条总线恢复。
正确的做法:
- 通过 **BLE 小程序** 修改 RS485 地址和波特率(带外管理通道)
- Modbus 侧只读查询当前值,防止误操作
### 6.2 参数持久化
- `0x06``0x10` 写入 Holding Register → 立即更新 RAM → 写入 DataFlash → 返回响应
- Flash 写入失败 → 返回异常码 `0x04` (Slave Device Failure)
- 灵敏度写入 0–2 范围之外的值 → 返回异常码 `0x03` (Illegal Data Value)
### 6.3 灵敏度与 ΔL/L 对应关系
| 灵敏度值 | SensTable 进入 | ΔL/L 进入 | SensTable1 离开 | ΔL/L 离开 |
|---------|---------------|-----------|----------------|-----------|
| 2 (高) | 28 | **0.085%** | 16 | 0.049% |
| 1 (中) | 54 | **0.165%** | 32 | 0.098% |
| 0 (低) | 108 | **0.330%** | 81 | 0.247% |
ΔL/L 越大越不灵敏。高灵敏度 (2) 适合安静环境,低灵敏度 (0) 适合强干扰环境。
### 6.4 输出模式详解
| 模式 | 值 | 行为 |
|------|-----|------|
| 存在输出 | 0 | 有车时继电器吸合,车走释放(+离开延时) |
| 进入脉冲 | 1 | 车进入时继电器吸合 500ms 后释放 |
| 离开脉冲 | 2 | 车离开时继电器吸合 500ms 后释放 |
### 6.5 实现要点(待编码时参考)
```c
// RS485 方向控制 (PA13)
#define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13) // 发送
#define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13) // 接收
// UART0 中断接收,3.5 字符超时判定帧结束
// CRC16 查表法:poly=0xA001, init=0xFFFF
// 帧间隔: t3.5 = 3.5 × 11 / baud (秒)
// @9600 = 4.0ms
// @19200 = 2.0ms
// @115200= 0.33ms
```
RS485 半双工时序关键:发送最后一字节后等待 **1 字节时间** 再切换为接收,
避免 RE 切换过早导致最后一个字节被截断。
---
## 7. 版本历史
| 版本 | 日期 | 作者 | 变更 |
|------|------|------|------|
| V1.0 | 2026-07-22 | wangfq | 初版草案:寄存器映射 + 命令示例 + 设计决策 |