Files
DLD154Pro/docs/rs485-modbus-protocol.md
T
wangfq 8a01a5e769 refactor: 输出融合模式枚举重排,出厂默认模式0(地感或光耦)
枚举值重新分配:
  旧: 0=复位 1=仅地感 2=仅光耦 3=或 4=且 5=混行 6=ETC
  新: 0=或   1=且     2=混行   3=ETC 4=仅地感 5=仅光耦 6=复位

涉及:
- storage.h: 枚举顺序重排
- peripheral_main.c: 注释号同步; LED区间判断改用精确比较; 工厂默认direction_mode=0x00
- 三份文档: 融合模式表/示例/默认值全部同步
2026-07-29 18:46:35 +08:00

336 lines
16 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.3 | 日期: 2026-07-29 | 状态: 定稿(待编码实现)
>
> 适用产品: 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 | Relay Output | 继电器当前输出状态 | 0=释放, 1=吸合 |
| 0x0006 | Freq Low Fault | 频率下限异常 (Fre_Err bit0) | 0=正常, 1=频率过低 |
| 0x0007 | Freq High Fault | 频率上限异常 (Fre_Err bit4) | 0=正常, 1=频率过高 |
| 0x0008 | Init Flag | 复位后首次指示 (Init_B) | 0=正常, 1=复位后首次—上位机读后清零* |
> **Init Flag 清除机制**:上位机读到 1 后,应发送 **写 HR 0x00FE=0x0001** 清零。参见 §4.7。
### 3.2 输入寄存器 (Input Registers) — 功能码 0x04
| 地址 | 名称 | 类型 | 宽度 | 说明 |
|------|------|------|------|------|
| 0x0000 | Loop Freq Hi | uint16 | 2 寄存器 | 线圈振荡频率,高 16 位 (Hz) |
| 0x0001 | Loop Freq Lo | uint16 | | 线圈振荡频率,低 16 位 (Hz) |
| 0x0002 | CAPVD Hi | uint16 | **2 寄存器** | 归一化周期值 (32-bit),高 16 位 |
| 0x0003 | CAPVD Lo | uint16 | | 归一化周期值,低 16 位 |
| 0x0004 | Origin Hi | uint16 | **2 寄存器** | 基线值 (32-bit),高 16 位 |
| 0x0005 | Origin Lo | uint16 | | 基线值,低 16 位 |
| 0x0006 | Variation Hi | int16 | **2 寄存器** | 变化量 = CAPVD Origin (32-bit 有符号),高 16 位 |
| 0x0007 | Variation Lo | int16 | | 变化量,低 16 位 |
| 0x0008 | FW Version | uint16 | 1 寄存器 | 固件版本,高字节=主版本,低字节=子版本。例 `0x0300` = v3.00 |
| 0x0009 | HW Version | uint16 | 1 寄存器 | 硬件版本。例 `0x0301` = v3.01 |
| 0x000A | Freq Sens | uint16 | 1 寄存器 | 当前灵敏度等级 (0=低, 1=中低, 2=中高, 3=高) |
| 0x000B | Condition | uint16 | 1 寄存器 | 环境活跃度 (0=安静, 1–100=嘈杂) |
| 0x000C | DIP Virtual | uint16 | 1 寄存器 | 虚拟拨码(700BS 兼容): bit[7:4]=function_mode, bit[3:0]=fusion_mode |
> **32-bit 值换算**
> - 频率:`freq_hz = (reg[0x0000] << 16) | reg[0x0001]`,单位 Hz。典型空线圈 50120kHz。
> - CAPVD`capvd = (reg[0x0002] << 16) | reg[0x0003]`,归一化到 `MEASUREMENT_BASE = 524288`。
> - Origin`origin = (reg[0x0004] << 16) | reg[0x0005]`500 窗滑动平均累加值。
> - Variation`var = (int32_t)((reg[0x0006] << 16) | reg[0x0007])`,正值=频率降低(可能有车),负值=频率升高。
### 3.3 保持寄存器 (Holding Registers) — 功能码 0x03 / 0x06 / 0x10
| 地址 | 名称 | 范围 | 默认 | R/W | 说明 |
|------|------|------|------|-----|------|
| 0x0000 | Sensitivity | 03 | 1 | R/W | 灵敏度等级 (0=低, 1=中低, 2=中高, 3=高) |
| 0x0001 | Fusion Mode | 06 | 0 | R/W | 输出组合模式(见 §6.5) |
| 0x0002 | Hold Time | 0255 | 0 | R/W | 有限存在时间 (×5s), 0=永久存在 |
| 0x0003 | Output Delay | 0255 | 10 | R/W | 离开延时 (×50ms), 例 10=500ms |
| 0x0004 | Freq Level | 01 | 0 | R/W | 线圈频率档: 0=高频, 1=低频 |
| 0x0005 | RS485 Baud Hi | — | — | **R** | RS485 波特率高 16 位(只读) |
| 0x0006 | RS485 Baud Lo | — | — | **R** | RS485 波特率低 16 位(只读) |
| 0x0007 | RS485 Addr | 1247 | 1 | **R** | RS485 从机地址(只读) |
| 0x0008 | Protocol | 01 | 0 | **R** | 当前 RS485 协议: 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=复位 |
| 0x00FE | Init Flag Clear | 01 | — | **W** | 写 1 清零 Init Flag (DI 0x0008) |
| 0x00FF | Device Reset | — | — | **W** | 写入 `0x5A5A` 触发复位初始化线圈(等效 700BS 0x2C |
> **只读寄存器 (0x00050x0008, 0x000B)**:写操作返回异常 0x02Illegal Data Address)。
> **写入寄存器 (0x00FE, 0x00FF)**:只写不读。读返回 0x0000。写入非魔数值返回异常 0x03。
---
## 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 + Origin + Variation(共 8 个寄存器,16 字节):
```
>>> 01 04 00 00 00 08 70 1F (读 0x0000 起 8 个寄存器)
<<< 01 04 10
00 01 38 80 ← Freq = 0x00013880 = 80000 Hz
00 07 FF C8 ← CAPVD = 0x0007FFC8 = 524200
00 08 00 00 ← Origin = 0x00080000 = 524288
FF FF FF A8 ← Var = 0xFFFFFFA8 = 88 (频率升高,有车)
XX XX
```
### 4.3 读保持寄存器 — 功能码 0x03
读取全部可读写参数:
```
>>> 01 03 00 00 00 05 84 09 (读 5 个: 灵敏度/融合模式/存在时间/延时/频率档)
<<< 01 03 0A 00 01 00 00 00 00 00 0A 00 00 XX XX
→ 灵敏度=1(中), 融合模式=0(地感或光耦), 永久存在, 延时=500ms, 高频
```
### 4.4 写单个寄存器 — 功能码 0x06
修改灵敏度为"高"
```
>>> 01 06 00 00 00 02 88 32 (写 0x0000=2)
<<< 01 06 00 00 00 02 88 32
```
修改融合模式为"地感或光耦":
```
>>> 01 06 00 01 00 00 D9 F6 (写 0x0001=0, 地感或光耦)
<<< 01 06 00 01 00 00 D9 F6
```
### 4.5 批量写寄存器 — 功能码 0x10
一次写入灵敏度和存在时间(60s)
```
>>> 01 10 00 00 00 02 04 00 02 00 0C XX XX
地址 功能 起始 数量 字节 灵敏度=2 时间=12 CRC
<<< 01 10 00 00 00 02 40 31 (写入成功)
```
### 4.6 异常响应示例
写入超出范围的灵敏度值:
```
>>> 01 06 00 00 00 05 88 32 (写 0x0000=5, 超出 03)
<<< 01 86 03 02 63 (异常: 功能码 0x86, 异常码 0x03=Illegal Data Value)
```
### 4.7 触发设备复位 — 写 HR 0x00FF
```
>>> 01 06 00 FF 5A 5A xx xx (写 0x00FF=0x5A5A, 触发复位)
<<< 01 06 00 FF 5A 5A xx xx (回显, 随后设备复位)
```
> 复位后设备重新初始化线圈基线,Init Flag (DI 0x0008) 置 1。
### 4.8 清除 Init Flag — 写 HR 0x00FE
```
>>> 01 06 00 FE 00 01 xx xx (写 0x00FE=1)
<<< 01 06 00 FE 00 01 xx xx (Init Flag → 0)
```
---
## 5. 功能码支持矩阵
| 功能码 | 名称 | 支持 | 说明 |
|--------|------|------|------|
| 0x01 | Read Coils | ✗ | 不使用 |
| 0x02 | Read Discrete Inputs | ✓ | 车辆状态、故障、继电器、光耦 |
| 0x03 | Read Holding Registers | ✓ | 读所有配置参数 |
| 0x04 | Read Input Registers | ✓ | 读传感器数据 (32-bit 跨双寄存器) |
| 0x05 | Write Single Coil | ✗ | 不使用 |
| 0x06 | Write Single Register | ✓ | 写单个参数 |
| 0x0F | Write Multiple Coils | ✗ | 不使用 |
| 0x10 | Write Multiple Registers | ✓ | 批量写参数 |
| 0x11 | Report Slave ID | ✓ | 返回设备型号 + 运行状态 |
---
## 6. 设计说明
### 6.1 为什么 CAPVD/Origin/Variation 用 2 个寄存器?
DLD154Pro 算法移植后,`MEASUREMENT_BASE = 524288 (2^19)`CH591R@60MHz 无内部分频,需要比 DLD154V4B 更大的 BASE)。IIR 滤波后的 CAPVD 和 Origin 是 `uint32_t` 类型,典型值在 50 万级,远超 16 位(65535)。Variation 是差值且可能为负,需要完整 32-bit 有符号表示。因此各占 2 个 Modbus 寄存器(寄存器对,大端序)。
### 6.2 为什么不通过 Modbus 修改 RS485 地址/波特率?
如果在 Modbus 通信中写入错误的地址或波特率,设备将**立即失联**且无法通过同一条总线恢复。
- 通过 **BLE 小程序** 修改 RS485 地址和波特率(带外管理通道)
- Modbus 侧只读查询当前值,防止误操作
### 6.3 参数持久化
- `0x06``0x10` 写入 Holding Register → 立即更新 RAM → 写入 DataFlash → 返回响应
- Flash 写入失败 → 返回异常码 `0x04` (Slave Device Failure)
- 值超出范围(如灵敏度写 5)→ 返回异常码 `0x03` (Illegal Data Value)
### 6.4 灵敏度与 ΔL/L 对应关系
| 灵敏度值 | SensTable 进入 | ΔL/L 进入 | SensTable1 离开 | ΔL/L 离开 |
|---------|---------------|-----------|----------------|-----------|
| 3 (高) | 14 | **0.043%** | 12 | 0.037% |
| 2 (中高) | 28 | **0.085%** | 16 | 0.049% |
| 1 (中低) | 54 | **0.165%** | 32 | 0.098% |
| 0 (低) | 108 | **0.330%** | 81 | 0.247% |
ΔL/L 越大越不灵敏。高灵敏度 (3) 适合摩托车/安静环境,低灵敏度 (0) 适合强干扰环境。
> ⚠ 第 3 档 (sens=14) 信噪比余量仅约 2:120°C 温漂 ≈0.1% 感量偏移),仅推荐摩托车/小型车辆检测场景,需确保线圈安装质量。
### 6.5 输出组合模式 (Fusion Mode)
`direction_mode` 字段重构:
| 位段 | 名称 | 说明 |
|------|------|------|
| bit [7:4] | `function_mode` | 功能代码。bit0=RS485 协议选择(0=700BS, 1=Modbus), bit1=光耦功能(0=雷达输入, 1=复位) |
| bit [3:0] | `fusion_mode` | 输出组合模式 — **仅当光耦配置为"雷达输入"时有效** |
> DLD154Pro 为单路线圈、单刀双掷继电器,**仅支持存在输出**。继电器输出信号由线圈检测结果与雷达输入(光耦 PB10)按 `fusion_mode` 组合后驱动。
| 值 | 模式 | 继电器吸合条件 | 适用场景 |
|----|------|--------------|---------|
| 0 | 地感 **或** 光耦 | 线圈 OR 雷达 | 互补冗余,任一触发即吸合 |
| 1 | 地感 **且** 光耦 | 线圈 AND 雷达 | 双确认,防地感误触(如行人/拖车) |
| 2 | 人车混行防砸 | 有车后 coil OR opto,离场 3s 窗口内光耦有效 | 混合交通流防砸人 |
| 3 | ETC 模式 | 有车后 coil OR opto,光耦断开 500ms 防抖才释放 | ETC 专用逻辑 |
| 4 | 仅地感 | 线圈有车 | 纯线圈场景(默认,未接光耦时也用此模式) |
| 5 | 仅光耦 | 光耦输入有效(雷达有车) | 纯雷达,地感只做背景监控 |
| 6 | 光耦复位 | 线圈有车(光耦用于远程复位) | 需要远程复位功能 |
**模式 2(人车混行防砸)状态机**:IDLE(仅线圈检测)→ 线圈有车 → ARMED(线圈||光耦)→ 两者均释放 → WINDOW(3s 防砸窗口,光耦触发即续吸,连续进入持续防砸)→ 3s 超时 → IDLE。
**模式 3ETC)状态机**:IDLE(仅线圈检测)→ 线圈有车 → ARMED(线圈||光耦)→ 两者均释放 → DEBOUNCE(500ms 防抖,期间触发即回 ARMED)→ 超时 → IDLE。
> **注意**:当光耦配置为复位功能(`function_mode` bit1=1)时,`fusion_mode` 无效,继电器仅由线圈检测结果驱动。模式 6 下光耦输入低电平 ≥400ms 后上升沿触发 `SYS_ResetExecute()` 芯片全复位。
### 6.6 实现要点(待编码时参考)
```c
// RS485 方向控制 (PA13)
#define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13) // 发送
#define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13) // 接收
// 32-bit 值拆分为 2 个 Modbus 寄存器(大端序)
// CAPVD = 131196 → reg[0]=0x0002, reg[1]=0x007C
reg[0] = (uint16_t)(capvd >> 16);
reg[1] = (uint16_t)(capvd & 0xFFFF);
// CRC16 查表法:poly=0xA001, init=0xFFFF
// 帧间隔: t3.5 = 3.5 × 11 / baud (秒)
// @9600 = 4.0ms
// @19200 = 2.0ms
```
RS485 半双工时序:发送最后一字节后等待 **1 字节时间** 再切换 RE 为接收,
避免 RE 过早翻转导致最后一个字节被截断。
---
## 7. 700BS 协议兼容对照
DLD154Pro RS485 同时支持 700BS 协议(`g_fm_700bs_disable=0`)和 Modbus RTU`g_fm_700bs_disable=1`)。
协议选择通过 BLE 小程序切换。参见 `docs/rs485-700bs-protocol.md`
| 700BS 命令 | 700BS 字段 | Modbus 等价 | 说明 |
|-----------|-----------|------------|------|
| 0xFA 查询状态 | bit7=有车 | DI 0x0000 Car Present | ✅ |
| | bit5=线圈故障 | DI 0x0001 Loop Fault | ✅ |
| 0x1B 查询频率 | 3 字节 Freq | IR 0x000001 Freq (32bit) | ✅ |
| 0x2C 复位 | — | **HR 0x00FF** 写 0x5A5A | ✅ V1.2 新增 |
| 0x3D 查拨码 | DIP1-5 | **IR 0x000C DIP Virtual** | ✅ V1.2 新增 (DLD154Pro 无物理拨码,虚拟映射) |
| 0xAA 心跳 Init_B | A1=首次 | **DI 0x0008 Init Flag** | ✅ V1.2 新增 |
| 0xAA 心跳 Fre_Err | bit0=下限异常 | **DI 0x0006 Freq Low Fault** | ✅ V1.2 新增 |
| | bit4=上限异常 | **DI 0x0007 Freq High Fault** | ✅ V1.2 新增 |
| 0xAA 心跳 主动上报 | 5s/1s/事件 | — | ⚠️ Modbus 主从架构不支持 push,上位机周期性轮询 DI+IR 等价 |
> **主动上报限制**:Modbus RTU 为严格主从协议,从机不能主动发送数据。
> 700BS 的心跳/事件上报在 Modbus 模式下不可用。需上位机以 >=1s 周期轮询 DI 0x000001
> 并通过边沿检测模拟事件触发。
---
## 8. 版本历史
| 版本 | 日期 | 作者 | 变更 |
|------|------|------|------|
| V1.4 | 2026-07-29 | wangfq | 枚举重排: 融合模式值重新分配 (0=或,1=且,2=混行,3=ETC,4=仅地感,5=仅光耦,6=复位); 默认融合模式 0→4(仅地感); 修正所有示例和说明文本 |
| V1.3 | 2026-07-29 | wangfq | 修正: 灵敏度档位 3→4 (范围 03, 默认 1); MEASUREMENT_BASE 131072→524288; Origin 窗口 100→500; 融合模式 5/6 待定义→已实现+状态机; 命令示例数值对齐实际硬件; 700BS 标注为已完成; 版本号 FW 3.00→3.01 |
| V1.2 | 2026-07-22 | wangfq | 700BS 兼容: +DI Freq Low/High Fault, +DI Init Flag, +IR DIP Virtual, +HR Device Reset(0x00FF), +HR Init Flag Clear(0x00FE); +§7 700BS 兼容对照表 |
| V1.1 | 2026-07-22 | wangfq | 修正: CAPVD/Origin/Variation 扩展为 32-bit (×2 寄存器); Output Mode 删除脉冲模式(仅存在输出); 新增 Fusion Mode(输出组合模式 0-6); direction_mode 字段重构说明 |
| V1.0 | 2026-07-22 | wangfq | 初版草案 |