diff --git a/docs/rs485-modbus-protocol.md b/docs/rs485-modbus-protocol.md new file mode 100644 index 0000000..47c4937 --- /dev/null +++ b/docs/rs485-modbus-protocol.md @@ -0,0 +1,238 @@ +# 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 | +| 从机地址 | 1–247 (默认 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 | 0–2 | 2 | R/W | 灵敏度: 0=低(0.330%ΔL/L), 1=中(0.165%), 2=高(0.085%) | +| 0x0001 | Output Mode | 0–2 | 0 | R/W | 0=存在输出, 1=进入脉冲(500ms), 2=离开脉冲(500ms) | +| 0x0002 | Hold Time | 0–255 | 0 | R/W | 有限存在时间 (×5s), 0=永久存在。例 12=60s 后强制释放 | +| 0x0003 | Output Delay | 0–255 | 10 | R/W | 离开延时 (×50ms), 例 10=500ms。0=无延时 | +| 0x0004 | Freq Level | 0–1 | 0 | R/W | 线圈频率档: 0=高频, 1=低频 | +| 0x0005 | RS485 Baud | uint32 | 9600 | **R** | RS485 波特率,只读(通过 BLE 修改) | +| 0x0006 | (Baud Lo) | — | — | **R** | 波特率低 16 位 | +| 0x0007 | RS485 Addr | 1–247 | 1 | **R** | RS485 从机地址,只读(通过 BLE 修改) | +| 0x0008 | Protocol | 0–1 | 0 | **R** | 当前协议: 0=700BS, 1=Modbus,只读 | +| 0x0009 | BT Timeout | 0–255 | 0 | R/W | 蓝牙空闲超时关闭 (分钟), 0=不关闭 | +| 0x000A | Safe Timeout | 0–255 | 0 | R/W | 线圈安全模式超时 (×10s), 0=关闭 | +| 0x000B | Opto Func | 0–1 | 0 | **R** | 光耦功能: 0=雷达输入, 1=复位,只读(通过 BLE 修改) | + +> **只读寄存器 (0x0005–0x0008, 0x000B)**:对这些地址写 0x06 返回异常 0x02(Illegal 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 | 初版草案:寄存器映射 + 命令示例 + 设计决策 |