Files
STM32F4-Base/docs/产品需求/产品需求设计文档.md
T
wandering1 4ceb5b41e6 docs(任务规划): 故障录波卡片登记簿销项——判据并入设计说明后删除
用户指令(2026-10-30):确认录波任务完成度,把除实际操作之外的所有
步骤做完,然后删除规划文件,避免已完成的工作被误读为未做。

审计结论:代码面确已全部收口——wave_read.py --selftest 92/92、
三套主机单测 113/170/110 复跑全 rc=0、bench 自抓与 PC 读段脚本在仓、
数据可信/复刻明细/短板评测/UI 两处回写均已做过。**唯一未跑的是
板级台架那一轮(实际操作)**,判据不得丢,故先迁后删:

- 设计说明 §9 承接落盘道失败留痕名全清单(13+1 条,打印点现值
  default_task.c:816/:831 实测核对)
- 设计说明 §10 承接原卡 2/卡 3 的板级段(触发来路一行、回放页
  一行)与本波执行顺序图(mermaid,原规划 §3 逐字迁)
- §4/§11 头部定位、§11 尾注、§14 关系行改属主口径
- 全库 32 处引用改挂设计说明 §10/§9(18 文件):任务规划 README
  独立行(顺带订正单测数 100→113 为实测)、登记簿、数据可信 ×2、
  复刻明细、产品需求 ×2、ADC 注入 ×3、短板评测 ×2、页面补齐规划、
  App/algo README、配图生成器注释、录波三套单测注释 ×6
- git rm docs 该规划文件;人类索引任务规划 6→5 个文件(沿革注照
  屏板链路先例走裸句,不留反引号死引用)

验证:残留两形态(任务规划/故障录波、裸文件名)全树扫描 0;
mermaid 预检新发 0;doc-links 11 个被改文件逐文件 PASS;生成器
注释改动重跑产物逐字节不变(svg 60a0dd39e4);三套单测与
wave_read selftest 复跑全绿;18 文件 BOM/CR/U+FFFD 全零。

本笔不含固件编译单元(test/host 仅注释行与文档),构建留待合并
轮统一跑;板级轮启动判据与顺序见设计说明 §10。
2026-10-30 18:20:37 +08:00

1820 lines
135 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# STM32F4-Base 产品需求设计文档
> **文档定位**:本项目**面向产品的需求基线**——回答三个问题:产品是什么(§2 背景·用户·场景)、
> 要做到什么程度(§4~§8 需求与验收标准)、当前做到哪一步(§4 总览表 + §9 路线图)。
> 散落在架构、子系统、测试文档中的"产品做什么 / 做到什么程度 / 依据是什么"以本文为唯一收敛点。
> **版本**:v2.0 **日期**:2026-10-30 **状态**:现状基线(含已实现项 + 待实现项)
> **范围口径:1:1 复刻**——本产品是参考产品 **CLS100**(`Scatter_P_HV03.01_SV01.01`,OMAP-L138
> ARM+DSP 双核 + VxWorks 配电保护测控装置)的**逐模块复刻**;差异只允许来自物理不可移植项
> (§3.2)。能力基线与逐项明细已独立成档:
> [`参考产品复刻明细.md`](参考产品复刻明细.md)(能力清单、点表/定值/元件/规约逐项差额)。
> **配套权威档**:运行时架构(就绪位表、启动时序、线程通信)→
> [`../架构设计/软件架构与运行时规格.md`](../架构设计/软件架构与运行时规格.md);硬件规格与
> 能力边界(引脚表、H1~H5)→ [`../架构设计/硬件规格与能力边界.md`](../架构设计/硬件规格与能力边界.md)。
> **事实来源**:本文所有数值与结论均取自仓库内**受管事实源**(代码、`.ioc`、`.map`、网表分析
> 报告与各模块设计文档),凡与其它文档冲突处,以本文标注的权威来源为准。
> **阅读前提**:建议先读 [`docs/README.md`](../../docs/README.md)(文档索引)。
> 产品定义的技术细化(保护功能复现)见
> [`docs/算法/继电保护功能复现规划.md`](../算法/继电保护功能复现规划.md)。
## 1. 文档说明
### 1.1 目的与范围
本文回答三个问题:
1. **产品是什么**——一块工业级嵌入式 DTU 平台固件,做什么、面向谁、部署在哪(§2);
2. **产品要做到什么程度**——每一项功能/接口/性能/可靠性的**可验收要求**(§4~§8);
3. **产品当前做到哪一步**——已实现、已验收、待实现的**明确边界**(§4 总览 + §9)。
**不含**(各有权威档,本文不重复):
- 代码实现细节 → 各模块设计文档(`docs/子系统设计/`、`docs/算法/`、`docs/网络/`);
- 运行时架构(分层/任务/就绪位/启动时序)→ [`../架构设计/软件架构与运行时规格.md`](../架构设计/软件架构与运行时规格.md);
- 硬件规格与能力边界(引脚/硬件边界)→ [`../架构设计/硬件规格与能力边界.md`](../架构设计/硬件规格与能力边界.md);
- 参考产品能力基线与复刻逐项明细 → [`参考产品复刻明细.md`](参考产品复刻明细.md);
- 测试用例步骤 → `docs/测试说明/`;未闭环事项逐条跟踪 → [`docs/任务规划/README.md`](../任务规划/README.md)。
### 1.2 术语与缩写
| 术语 | 全称 / 含义 |
|---|---|
| **DTU** | Data Transfer Unit,数据传输单元;本产品的设备角色 |
| **MCU** | STM32F407ZGTx(Cortex-M4,LQFP144) |
| **FTL** | Flash Translation Layer,闪存转换层(本项目用 dhara 实现) |
| **BBT** | Bad Block Table,NAND 坏块表(持久化在保留块池) |
| **FatFs** | 通用 FAT 文件系统模块(`Lib/FatFs`) |
| **BSD Socket API** | 本项目在 CH395F 之上封装的 `net_socket` 接口(`socket/bind/listen/accept/recv/send/close`) |
| **RemoDispBus** | 远程显示总线协议(MCU 无屏,UI 以 1bpp 帧经 TCP 推流到远端客户端) |
| **cfg_file** | 通用配置引擎(INI 段键表,MCU/模拟器双端共享核心) |
| **sys_clock** | 系统墙钟时间服务(RTC 基准 + tick 推进) |
| **sys_log** | 定长 64B 记录引擎(运行/自检日志) |
| **IWDG** | Independent Watchdog,独立看门狗(LSI 时钟源) |
| **就绪屏障** | 用 CMSIS-RTOS2 Event Flags 表达"子系统就绪"的启动顺序机制 |
| **双端** | MCU(Keil/ARMCC)与 PC 模拟器(CMake/MSVC)共用同一份 UI/配置源码 |
| **/keil-build / /serial-boot-test** | 仓库约定的构建 / 自动化测试命令别名 |
| **语义取数与有效性 / 标定折算 / 全周傅氏相量 / 测频与遥测快照 / 保护判据** | 采集链路分层:语义取数与有效性(语义取数与有效性)→ 标定折算(标定折算)→ 全周傅氏相量(全周傅氏相量)→ 测频与遥测快照(测频与遥测快照)→ 保护判据(保护判据) |
| **全周傅氏** | 单频 DFT 取基波相量,窗长 = 整周波(20 点);幅值即基波有效值 |
| **相量** | `{fRe, fIm, fAm, fAmSQ}`,实部 / 虚部 / 幅值 / 有效值 |
| **双区交换** | 相量(及遥测快照)计算区 / 使用区索引单字节翻转,读者无锁取整份快照 |
| **等周波采样** | 软件测频 + 调采样周期,使窗长恒等于实际周波(严格 20 点/周波) |
| **标定 k / b** | 通道折算系数(µV/码,Q16 定点)与零点(码值);`工程量 = (code + b) × k` |
| **QDS** | IEC 60870-5-104 的品质描述符字节(OV / NT / IV 等位) |
| **SOE** | Sequence of Event,带时标的事件顺序记录(本产品落 `/log/soe.log`) |
| **CP56Time2a** | 104 的 7 字节时标格式(毫秒 / 分 / 时 / 日 / 月 / 年 + 时标标志位) |
| **点表 / IOA** | 104 上送对象表与信息体地址;本产品点表口径见 §6.8 |
| **固有 / 运行 / 保护定值** | 三类参数:只读身份信息、可整定的运行参数、可整定的保护定值(§5.13) |
| **软压板** | 软件投退开关(全局软压板 = 保护总投入,可经 104 遥控投退) |
| **遥测死区** | 遥测变化上送门槛;本产品按"连续 2 个新测量点越限才确认" |
| **软件架构档** | [`docs/架构设计/软件架构与运行时规格.md`](../架构设计/软件架构与运行时规格.md) 的简称(本文与复刻明细档共用此简称) |
| **硬件规格档** | [`docs/架构设计/硬件规格与能力边界.md`](../架构设计/硬件规格与能力边界.md) 的简称(本文与复刻明细档共用此简称) |
| **复刻明细档** | [`docs/产品需求/参考产品复刻明细.md`](参考产品复刻明细.md) 的简称 |
| **CLS100** | 参考产品(配电保护测控装置,ARM + DSP 双核 / VxWorks),仅作能力对照,非本产品代次 |
### 1.3 读者与相关文档
| 角色 | 建议读法 |
|---|---|
| 甲方 / 产品 | §2 背景·用户·场景 → §4 需求总览 → §9 路线图 → §10 验收判据 |
| 开发 | 全文;深入时按 §11 指向各模块设计文档 |
| 测试 / 验证 | §5、§6、§7、§10 + `docs/测试说明/` |
| 评审 | §2、§3、§7、§9 |
---
## 2. 背景、目标用户与产品目标
### 2.1 背景与产品定位
**STM32F4-Base 是一块工业级嵌入式 DTU 平台固件**,基于 STM32F407ZGTx,集成
**以太网通信、SPI NAND 存储、ADC 采集、RS-485 通信与 FTP 文件服务**,并自带
**远程显示(UI 推流)、墙钟对时、看门狗自恢复、运行/自检记录**等工业现场能力。
它的**产品目标是 1:1 复刻参考产品 CLS100**(配电保护测控装置)——按参考工程源码的
常量表与模块文件逐项对齐点表、定值、保护元件与规约栈(§3.1、复刻明细 §1)。在此过程中沉淀出的
分层底座(采集链 语义取数与有效性~出口与记录、配置引擎、就绪屏障、双端测试)同时是可复用的平台能力。
> **产品现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)
> **已实现并台架验收**;采集主线(语义取数与有效性采集 → 标定折算 → 全周傅氏相量 → 测频与遥测快照 → 保护判据
> → 出口与记录 SOE → IEC 60870-5-104 上送)**软件链路已全线贯通并台架闭环**;与参考产品的差距
> 集中在三处:① 保护判据处于**未标定闭锁**态(等标准源两点标定)、② **点表规模**远小于参考
> (定值 40 vs 249 项、遥测 9 vs 31 点、开入开出 0 vs 138/31 点)、③ **独立子系统未立项**
> (故障录波、电度、多规约、SNTP)。即:**"数"与"判据"已可信到引脚电压口径,
> 缺的是标定、点表铺开与一次设备接口**(明细见 复刻明细 §1.2、§9)。
> 交付节奏与验收锚点见 §9、§10;本文对"为什么是这四点能力组合"不再另立论证——
> 它是 DTU 数据接入类产品的工业现场最小完备集(通信、存储、采集、对外规约),
> 并以 1:1 复刻 CLS100 为纵深(§3)。
### 2.2 产品形态与部署
```mermaid
flowchart LR
subgraph DEV["开发机(Windows)"]
K[Keil MDK-ARM v5<br/>编译/烧录]
SIM[LVGL PC 模拟器<br/>CMake + SDL2]
PY[Python .venv<br/>脚本/测试]
end
subgraph TGT["目标板(DTU 硬件)"]
M[STM32F407ZGTx @168MHz]
P[CH395F / GD5F2GQ5UE / TPAFE5160 / SD2506 / RS-485 / LED×6]
end
subgraph RMT["远端客户端(PC/浏览器)"]
C[DTU-RemoteLCD 或任意 TCP 客户端]
end
DEV -- "USB:ST-Link 下载器" --> TGT
DEV -- "USB→RS-232 适配器:RS232#1 @115200 调试日志" --> TGT
TGT -- "以太网 CH395F<br/>FTP:21 / 对时:8888 / 远程显示:7003" --> RMT
```
| 环境 | 角色 | 必需性 |
|---|---|---|
| ① 开发机 | 编译/烧录、UI 模拟器、脚本工具链 | 开发/测试必需 |
| ② 目标板 | 运行固件的 DTU 硬件 | 上机必需 |
| ③ 远端客户端 | 远程看图/按键、对时推送、FTP 文件操作 | 现场使用必需 |
**关键产品特征**:**MCU 端无物理屏幕**——UI 画面以 160×160 单色位图(1bpp)经 TCP 推送到远端
客户端渲染,按键亦由远端下发(见 §5.6、§8)。
### 2.3 目标用户
| 角色 | 与产品的接触面 | 关心的事 |
|---|---|---|
| **主站操作员 / 调度** | IEC 60870-5-104(工厂默认 :2404):总召、变位、遥控、定值、对时 | 数据可信(品质位如实)、动作可追溯(SOE 时标)、异常可自恢复 |
| **现场调试与运维** | 串口屏板(本地带屏运行)、UART5 日志口(:115200)、FTP(:21)、ST-LINK 烧录 | 上电即用的接线口径、看得见的状态灯与日志、改配置不重启、掉电不丢参数 |
| **工厂生产 / 出厂** | `/config.ini` 工厂默认、UI 工厂菜单、自检记录 | 产线可批量预置网络与口令、自检留痕可查 |
| **远程看图 / 注键人员** | TCP :7003(RemoDispBus)+ DTU-RemoteLCD | 与本地屏板同源的画面与按键(两出口并发互不干扰) |
| **送检 / 验收方** | §10 验收判据、第三方 104 主站往返 | 每项要求都有可跑通的判据,"没做"与"接线错"可区分 |
### 2.4 典型使用场景
1. **开关柜现场部署**:设备上电 → 按 `/config.ini` 出厂 IP 组网 → 主站连工厂默认 :2404 总召 →
现场用屏板看测量、按按键翻菜单;死机由看门狗自恢复(无需到场)。
2. **远程运维**:PC 经 :7003 看同一份 UI、注键操作;FTP 取 `/log`(运行/自检/SOE 记录)
与 `/config.ini`;改 IP 后 2~5 s 生效不重启;PC 推 :8888 对时。
3. **脱离 PC 独立带屏运行**:串口屏板直连 USART1(700 kbaud),主机 275 ms 召唤、
≤200 ms 回 4208 B 帧(3200 B 显存 + 状态灯掩码 + info)——无 PC 也能看与按(§5.16)。
4. **保护运行(等标定解锁)**:1 kHz 采样 → 全周傅氏相量 → 20 ms 判据节拍 → 动作只落
遥信 + SOE + 日志(V0.1 无出口硬件);**未标定态判据整体停走且 `YX31` 如实置位**——
这是刻意的安全设计,不是缺陷。
5. **复刻验收**:按 [`参考产品复刻明细.md`](参考产品复刻明细.md) 逐项对照点表/定值/元件/规约,
差额即排期输入(§9)。
### 2.5 目标与成功指标
| # | 目标 | 落地手段 |
|---|---|---|
| G1 | **可靠**:现场死机可自恢复、掉电不丢配置、错误可追溯 | IWDG + 任务心跳位图、原子写 + 迁移、错误码通道四要素 |
| G2 | **可维护**:分层解耦、单一事实源、双端复用、门禁强制 | 六层架构、就绪屏障、生成器(菜单/字体/任务表)、pre-commit 钩子族 |
| G3 | **可移植**:UI/配置源码 MCU 与 PC 模拟器共用 | 平台后端抽象(`*_mcu.c` / `*_sim.c`),核心零平台依赖 |
| G4 | **可扩展**:新子系统/新配置段/新页面按模板接入 | 事件位扩展、段注册三步、页面插件描述符、`App/protocol/` 预留 |
| G5 | **可验收**:每项能力有明确判据与自动化门禁 | `/keil-build` / `/serial-boot-test` / 静态门禁 / 主机侧纯逻辑单测 / 台架验证 |
**设计原则**(贯穿全项目):
1. **上层只依赖相邻下层(单向)**;禁止反向依赖(例外经登记)。
2. **状态位常驻、命令位消费**——Event Flags 就绪位必须 `osFlagsNoClear`。
3. **能推导的就不存**——持久化"不可推导"的状态(导航历史、配置、日志),其余现算。
4. **单写者**——配置文件属主 = `defaultTask`;syslog 唯一写者 = `defaultTask`;控制台唯一发送者 = `defaultTask`。
5. **一切写入 = 临时文件 + rename**(原子替换),半截窗口结构消除。
6. **测量不可信就不判据**——未标定(VALID 未置位)或采样故障时保护停走,品质位如实上送,
绝不用"看起来对"的数去动作(§5.12、§5.15)。
7. **点表优先于实现**——本期做不了的 104 点照常入表并置 IV;缺行比置 IV 更糟,
主站将无法区分"没做"与"接线错"(§6.8)。
**成功指标(可判定)**:
| 指标 | 目标 | 当前状态 |
|---|---|---|
| 平台能力台架验收 | 网络/存储/时间/UI/日志/看门狗/FTP/配置 全部闭环 | ✅ 已达成(§10.4) |
| 采集主线到业务出口 | 采样→相量→测频→遥测快照→判据→104/UI 全链路数据同源 | ✅ 软件链路闭环;判据等标定(§9.1.1) |
| 屏板独立带屏 | 串口通道交付并台架验收 | ✅ 主机侧交付(§5.16;尾巴见屏板链路设计说明 §9.2) |
| 送检就绪 | 两点标定 → 保护解卡 → 第三方主站验收 | ⬜ 等标准源与端子变比(§9.2 第 4/5 步) |
| 复刻完成 | 复刻明细各项达"做完了"口径 | 🔶 见 [`参考产品复刻明细.md`](参考产品复刻明细.md) §1.2 |
---
## 3. 范围与产品级非目标
### 3.1 产品范围(1:1 复刻口径)
**产品范围 = 参考产品 CLS100 的可移植能力全集。** 本产品不是"在参考产品里挑一部分做",
而是**按参考工程源码逐模块复刻**:凡参考源码中存在的点表、定值单元、保护元件、记录类型、
规约栈,都属本产品目标范围;差异只允许来自**物理不可移植项**(§3.1.1)。
范围按**复刻完成度**分三档,判定口径见 `参考产品复刻明细.md` §1.1:
| 档 | 含义 | 处置 |
|---|---|---|
| ✅ **已对齐** | 参考源码能力在本产品上已实现,且有台架/单测证据 | 进 §10 验收判据 |
| 🔶 **部分对齐** | 骨架、口径、地址编排已对齐,覆盖范围小于参考(通道数 / 元件数 / 点数) | 差额逐条进 §9 |
| ⬜ **未开始** | 参考源码存在、本产品无对应实现 | 逐条进 §9,**不等于"不做"** |
**当前分布**(20 项能力,明细见 `参考产品复刻明细.md` §1.2):✅ **2 项**(采样节拍、自检类型)、🔶 **10 项**、
⬜ **7 项**、— **1 项**(双核分工,不可移植)。
#### 3.1.1 不可移植项(产品级非目标)(唯一允许落在范围外的部分)
| # | 不可移植项 | 参考实现 | 本产品 | 归因 |
|---|---|---|---|---|
| X1 | 双核 ARM + DSP 分工 | OMAP-L138(ARM 跑通信/HMI,DSP 跑保护) | 单核 Cortex-M4F 按任务划分承担 | 芯片形态 |
| X2 | 实时操作系统与工具链 | VxWorks 5.x + CCS | FreeRTOS V10.3.1 + Keil ARMCC | 平台形态 |
| X3 | 一次设备接口 | KO / KI 板卡(出口、开入、硬压板) | V0.1 无该硬件(硬件规格档 §5 H3) | 硬件缺失 |
| X4 | 独立计量通道 | 专用计量芯片(电度) | 无(跨转换器无同刻,§4.6 H1) | 硬件缺失 |
> **X1~X4 是范围外项的全部。** 除此以外**不存在"参考有、我们决定不做"的能力**—
### 3.2 产品级非目标(本期不做,与"范围外"不同义)
| 非目标 | 边界 | 理由 |
|---|---|---|
| 屏板硬件本体固件 | `DTU-LCD` 独立工程 | 不在本仓;主机侧以需求档 §5.16 契约为黑盒验收线 |
| 多定值区 / 定值文件下装 | 参考有 4 区 | 本产品只定义单定值区(§5.15 未实现项表) |
| 主站 IP 白名单 | — | 单 socket 语义下"拒绝"= 断链重听,可被扫描打成 SPI 风暴(§5.15) |
| LOGO 下发 | 帧 info[4..5] 保留位恒 0 | 参考该功能无消费方,协议保留位存在但主机不提供操作(§5.16) |
| 归一化 / 标度化遥测 | 点表要求短浮点(系数 1) | 协议语义决定,非进度缺口 |
> "非目标"是**产品决策**;复刻差额不是(那是进度,见 §9 与复刻明细档)。两者不混。
---
## 4. 需求总览(优先级与状态)
> 优先级是**排期语义**:P0 = 交付/送检主线;P1 = 复刻铺开项(可并行、依赖标定或硬件);
> P2 = 冻结或等立项。状态口径同 §5 章头(✅ / 🔶 / ⬜)。细节与验收判据在 §5 各节。
| 优先级 | 需求域 | 状态 | 一句话边界 |
|---|---|---|---|
| P0 | FR-NET 网络通信(§5.1) | ✅ | CH395F + BSD 抽象 + 单连接自愈 |
| P0 | FR-STO 存储(§5.2) | ✅ | SPI NAND→dhara→FatFs,卷自愈 |
| P0 | FR-TIME 时间(§5.3) | ✅ | SD2506 墙钟 + :8888 对时(2038 已知限) |
| P0 | FR-CFG 配置引擎(§5.4) | ✅ | INI 段键表 + 外科保存 + 免重启生效 |
| P0 | FR-LOG 记录与日志(§5.5) | ✅ | 定长记录 + 控制台单写者 |
| P0 | FR-WD 看门狗(§5.7) | ✅ | 心跳位图 + 复位原因 + Fault 现场 |
| P0 | FR-ADC 采集与标定(§5.9) | ✅ | 1 kHz 同源节拍;两点标定后解卡 |
| P0 | FR-MEAS 遥测与相量(§5.11) | ✅ | 全周傅氏 + 测频 + 200 ms 快照单源 |
| P0 | FR-IEC104 上行(§5.15) | ✅ | 被控站;无效点入表置 IV |
| P0 | FR-PROT 保护(§5.12) | 🔶 | 软件就绪;**未标定闭锁**(用户裁定走标准源) |
| P0 | FR-SET 定值(§5.13) | ✅ | 40 项;原子封存 + 掉电保持 |
| P0 | FR-SOE 事件记录(§5.14) | ✅ | 判据路径零 NAND I/O |
| P0 | FR-HMI 屏板链路(§5.16) | 🔶 | 串口主通道已交付;7003 报文口径 6 项未做 |
| P0 | FR-UI 远程显示(§5.6) | ✅ | 两出口同源推流 |
| P1 | FR-LED 状态指示(§5.8) | ✅ | 主板 6 路;屏板 20 路属 FR-HMI |
| P1 | 复刻铺开(定值/遥测/元件/规约/软压板) | 🔶 | 明细见复刻明细档;顺序不可早于标定(§9.2) |
| P1 | 故障录波 | 🔶 | 最小闭环代码面已交付;4 kHz/多段/COMTRADE 二期 |
| P2 | FR-RS485 对下采集(§5.10) | ⬜ | 整簇冻结(用户决定),重启门禁见该节 |
| P2 | FR-PROT-5 出口(§5.12) | ⬜ | 等 V0.2 硬件 |
---
## 5. 功能需求
> 每项功能给出:**需求 → 实现要点 → 验收判据**。状态标注:✅ 已实现并验收 / 🔶 部分对齐(含已实现待专项、软件就绪待条件)/ ⬜ 待实现。
### 5.1 网络通信子系统(FR-NET)✅
**FR-NET-1 以太网组网**
- 需求:上电后按配置的 IP/掩码/网关/MAC 完成以太网初始化,PHY 强制 100M 全双工,等链路稳定后置就绪位。
- 实现:`netTask` 等 `STORAGE_READY` → 读 RAM 配置副本 → `net_init(ip,mask,gw,mac)` → 等链路 ≤3 s → 置 `NET_READY`。
- 判据:串口出现 `Network Init` / `net init: OK` / `PHY: 100M FULL`;PC 能 ping 通设备 IP。
**FR-NET-2 网络抽象层(BSD Socket API)**
- 需求:为上层提供 `socket/bind/listen/accept/recv/send/close` 与 `select/poll` 多路复用;所有 CH395F 操作串行化。
- 实现:`net_socket.c`(BSD API)→ `netMsgQueue`(深度 8)→ `netTask` 串行执行;`net_select.c` 提供 I/O 多路复用。
- 判据:三个 TCP 服务(FTP / 对时 / 远程显示)均经同一 API 工作;单连接模型下连接断开自动回到 LISTEN。
**FR-NET-3 单连接模型与自愈**
- 需求:CH395F 配置为 8 Socket(索引寻址)+ TCP Server 单连接(`set_fun_para(0x08)`,bit3 SOCKET_CLOSE;bit0 为保留位必须为 0),Socket 0 复用监听与数据通道;网络重建后服务自动重连。
- 实现:三个 TCP 服务**串行**占用连接,断开自动重听;代次号(`net_get_generation()`)驱动服务自愈。
- 判据:FTP 旧会话异常断开后约 30 s 恢复监听;网络重建(改 IP)后服务自动重建监听。
> 网络层是全项目**最成熟子系统**(pending 机制 + 四份 Trap 记录 + 量化基线 + 探针族)。
### 5.2 存储子系统(FR-STO)✅
![存储栈分层](../images/存储栈分层.svg)
**FR-STO-1 三层存储栈**
- 需求:在 SPI NAND 上提供 FAT32 文件系统;具备磨损均衡、坏块管理、垃圾回收;上电无卷自动格式化。
- 实现:FatFS(FAT32)→ dhara FTL → gd5f2gq5ue 驱动;`defaultTask` 挂载(无卷 `f_mkfs`)+ 卷健康探针/自愈。
- 判据:FTP 可列目录/读写卷根文件;掉电后数据仍在。
**FR-STO-2 坏块管理与持久化 BBT**
- 需求:坏块表持久化,重启后不丢失;不交给 dhara 管理。
- 判据:全片擦除后容量恢复流程可用(见 `docs/测试说明/专项验证/存储维护与容量恢复.md`)。
**FR-STO-3 卷并发一致性**
- 需求:FTP 传输(ftpTask)与配置读写(defaultTask)并发访问卷时不得损坏。
- 实现:`FF_FS_REENTRANT=1` + 逐次加锁/放锁(超时 1 s);三条一致性协议(写 tmp+rename / 沉降去抖 / 单写者)。
- 判据:并发 FTP 下载 + UI 保存无卷损坏、无死锁。
### 5.3 时间子系统(FR-TIME)✅
**FR-TIME-1 墙钟维护**
- 需求:以 SD2506 RTC 建立 Unix epoch 墙钟基准,运行期以 tick 推进(零 I2C 流量),提供字符串格式时间。
- 实现:`sys_clock_init()` 读一次 RTC 建基准(无效时按默认值自动校时);base+delta 法;30 天惰性再基准化(防 49.7 天 tick 回绕);`sys_clock_get_str()` → `"YYYY-MM-DD HH:MM:SS"`。
- 判据:日志时间戳真实正确;主机侧单测 100 断言点通过。
**FR-TIME-2 网络对时**
- 需求:PC 主动通过 TCP :8888 推送时间到设备,设备写回 RTC。
- 实现:`timeSyncTask` 等 `NET_READY` → TCP listen :8888 → 收 PC 推送 → 写 RTC;30 s 无包则掉线重建监听。
- 判据:对时后设备墙钟更新、日志时间戳正确。
> **已知限制**:uint32 epoch → **2038 年**上限。
### 5.4 配置引擎(FR-CFG)✅
![配置文件分层架构](../images/配置文件分层架构.svg)
**FR-CFG-1 通用配置文件**
- 需求:网络参数(IP/掩码/网关/MAC)持久化到 `/config.ini`(INI `[network]` 段);文件可经 FTP 下载/上传;格式纯文本、带注释、可编辑器编辑。
- 实现:`cfg_file` 段键表引擎(段 RAM 副本复用模块静态结构体,键表 `const` 映射键→偏移);工厂模板首启自动生成。
- 判据:文件缺失/损坏回退工厂默认并重建模板;FTP 上传后 2~5 s 自动生效。
**FR-CFG-2 外科手术式保存 + 原子写**
- 需求:保存时保留用户注释/未知段/未知键(降级安全);写入原子(并发读者只见旧完整或新完整文件)。
- 实现:受管键行替换,其余原样保留;临时文件 + `unlink` 目标 + `f_rename`;输出 >4096 B 拒绝保存。
- 判据:设备保存后下载文件,用户注释与未来版本段落原样保留。
**FR-CFG-3 变更检测与立即应用**
- 需求:FTP 上传配置后无需重启即生效;网络段变化触发网络重建。
- 实现:`defaultTask` 每 2 s `f_stat` 比对 (size, mtime),连续 2 次稳定才重载(沉降去抖);`[network]` 段变化 → `SYS_EVT_NET_RECONFIG` → 网络重建(代次 +1,服务自愈)。
- 判据:FTP 改 IP 后 2~5 s 生效(网络重建含等链路 ≤3 s)。
**FR-CFG-4 旧档迁移**(已取消 · 2026-10-10)
- 原需求:旧固件的 `/cfg/netcfg.bin`(NCFG 二进制)首启自动迁移为 INI 并删除旧文件——**已取消,不再实现**。
- 现口径:旧档若存在则被**忽略**(不读、不删、不迁移),配置直接回落工厂默认(`/config.ini` `[network]` 段模板)。
- 取消理由:该一次性路径把整条配置写链(`net_cfg_save → cfg_file_save → cfg_store_write_atomic → f_rename → FatFS → dhara FTL`)挂进启动期调用图,defaultTask 静态最坏栈链 +600 B;本仓库不存在携带旧档的设备。
- 判据:卷上残留 `/cfg/netcfg.bin` 时开机不产生任何迁移日志、旧档不被改动,`/config.ini` 按工厂默认生成。
**FR-CFG-5 扩展性**
- 需求:未来所有模块配置(ADC/RS-485/端口等)进同一文件、按段组织。
- 实现:未来模块接入三步——① 定义段结构体(静态工厂默认)② 写键表 ③ `cfg_file_register()`。
### 5.5 运行 / 自检记录与日志(FR-LOG)✅
**FR-LOG-1 定长记录引擎**
- 需求:运行记录与自检记录持久化到 NAND,FTP 可直下。
- 实现:`sys_log` 定长 64 B 文本记录(一行一条);`/log/run.log`(1024 条 / 64 KB 上限)、`/log/selftest.log`(512 条 / 32 KB);`defaultTask` 唯一写者;ISO8601 时间戳 + epoch。
- 判据:UI"运行记录/自检记录"页可显示;FTP 可下载。
**FR-LOG-2 控制台单写者日志**
- 需求:多任务并发日志不得字符级交错;异常场景(HardFault)日志可控。
- 实现:4 KB 环形缓冲 + `defaultTask` 唯一发送(行级原子);内核前/ISR/异常钩子有界直发回退。
- 判据:日志无交错;崩溃时能输出归因日志。
**FR-LOG-3 上电/重启分类与待机显示**
- 需求:区分上电与重启;空闲切待机屏。
- 实现:`run.log` 末条 epoch 时间差推断;硬件复位原因(`RCC_CSR`)优先;`ui_standby` 空闲 60 s 切入。
### 5.6 UI 与远程显示(FR-UI)✅
**FR-UI-1 离屏渲染 + 双出口推流**
- 需求:MCU 无物理屏幕,UI 以 160×160 单色位图(1bpp,3200 B)渲染到离屏显存,
经**两个出口**送出:**串口屏板通道**(主,§5.16.2)与 **TCP :7003**(镜像);两路的按键都回注 UI。
- 实现:LVGL 160×160 1bpp 离屏渲染到 `g_ui_fbuf`(3200 B)→ `ui_net_sink` 经 RemoDispBus 推流;
远端按键经协议注入。**串口屏板通道尚未实现**(§5.16.9 第 11 条)。
- 判据:DTU-RemoteLCD 连 7003 出图(160×160 1bpp)+ 远端菜单导航可用;
屏板实物接入后 280 ms 节拍出图 + 状态灯正确(§5.16.10 第 8~15 条)。
**FR-UI-2 菜单与内容页**
- 需求:42 节点层级菜单(最大 4 级)+ 内容页(装置信息/交流量/直流量等)。
- 实现:三层拆分(数据层 `ui_menu_tab` 生成物 / 逻辑层 `ui_menu` 17 B 导航状态 / 显示层 `ui_menu_view` 级联框);内容页框架 + 页面插件描述符。2026-10-29 实到 43 节点(新增「录波回放」叶,需求原文的 42 是 V0.1 立项数)。
- 判据:`UI menu init: OK`;菜单像素回归全 PASS。
**FR-UI-3 待机屏**
- 需求:空闲超时(默认 60 s)自动切待机屏,仅确认键回菜单。
- 判据:空闲后进入待机屏,按键返回菜单。
> 详见 §8 UI 规格。
### 5.7 看门狗与故障自恢复(FR-WD)✅
![看门狗心跳模型](../images/看门狗心跳模型.svg)
**FR-WD-1 IWDG + 任务心跳位图**
- 需求:任务级死锁/饿死/断言死循环可自恢复(硬件复位),无需人工到场;不误杀正常长耗时窗口。
- 实现:IWDG(LSI 独立时钟,PR=4 / RLR=1249 → 标称 **2498 ms**、最坏 **≈1701 ms**);任务心跳位图(各自置位,无锁单字)+ `defaultTask` 单一喂狗者(`task_wd_poll` 每 100 ms 抽样);连续 8 次(800 ms)未达标 → 停喂 → 约 2.5 s 后复位;必需位掩码 `0x37`。
- 判据:V2 任务饿死 ≤2.5 s 复位;V4 关中断死循环每 ~4.9 s 稳定复位;V5 30 min 真实负载零复位。
**FR-WD-2 复位原因诊断**
- 需求:复位后可区分上电 / 看门狗 / 软件 / 引脚 / 欠压。
- 实现:`HAL_Init()` 后一次性读 `RCC_CSR` 锁存并清标志;优先级 POR > PIN > IWDG > WWDG > SFT > LPWR > BOR。
- 判据:串口 `[APP] reset cause: <类型>`;`[WD] armed:` 自检标记。
**FR-WD-3 故障现场采集**
- 需求:HardFault / MemManage / BusFault / UsageFault 触发时输出完整现场。
- 实现:4 个 Fault Handler 采集(PC/LR/xPSR/R0-R3/CFSR/HFSR/BFAR/MMFAR)+ `fault_handler_enable()` 使能 SHCSR 后三者。
- 判据:日志形如 `[ERR] MEMFAULT PC=... CFSR=... MMFAR=...`。
> **已知边界**:启动段(`app_main_init` + bring-up)无看门狗保护;`ftpTask` 首期不纳入监视(掩码剔除)。
### 5.8 LED 状态指示(FR-LED)✅
- 需求:6 路 LED(低电平点亮)随系统状态变化。
- 实现:`defaultTask` 循环驱动(闪烁指示系统活跃)。
- 判据:目检 6 路 LED 随系统状态变化。
> ⚠ **本节只讲主板 LED。** 屏板上另有 **20 路状态指示灯**(ID 0 电源常亮 + ID 1~19 位掩码),
> 由主机每帧下发 4 B 控制,属**另一套输出**,规格见 §5.16.3 ①。
> 两套 LED 的"位 → 业务状态"语义**各自独立**,不要混用同一个状态表。
### 5.9 ADC 采集与标定(FR-ADC)✅
**FR-ADC-1 采样节拍与触发**
- 需求:8 路电气量(`UA/UB/UC/UN`、`IA/IB/IC/IN`)**持续采集**,1000 Hz(50 Hz 下 20 点/周波),
采集节拍与任务唤醒**同源**;不得用任务周期软定时冒充硬件节拍。
- 实现:TIM3 更新事件(1 kHz)在 ISR 内触发 TPAFE `CONVST`,同一事件经 TRGO 触发 ADC1 一轮
9 通道并由 DMA2 循环搬入乒乓双缓冲;ISR 只置"消费者就绪"门并唤醒 `adcTask`(不做重运算)。
- 判据:`frame_seq` 每 10 s 精确 +10000(1000.0 Hz)、`frst_err=0`、`adc1_err=0`、`adc1_hw=0`;
单拍消费耗时 ≤10 µs(DMA 搬运后实测 8 µs,软件轮询时为 122 µs)。
**FR-ADC-2 语义通道映射**
- 需求:按**语义**取数(`UA`/`UN`/`IA` 等),不写死缓冲区下标——V0.2 换映射表即可切换硬件归属。
- 实现:`App/algo/adc_input.c` 的语义表(TPAFE `buf[6]/[5]/[2]`;ADC1 `CH14/3/6/1/2/5`)+ 开机
**映射自检**(STM32 映射通道必须落在扫描序列内,否则拒绝并自检留痕)。
- 判据:`ADC input layer init: OK`;8 路 + 温度首帧全部读出、互异、非饱和。
**FR-ADC-3 标定与量纲折算(标定折算)**
- 需求:把码值折算为工程量 `(code + b) × k`,系数与零点可落盘、掉电保持;未标定不得冒充二次值。
- 实现:`App/algo/adc_cal.c`——`k` 以 Q16(µV/码 ×65536)、`b` 以码值存 `[adc_cal]` / `[adc_cal_ofs]`
段;ROM 默认 = 引脚侧 LSB(TPAFE 152.59 µV/码、STM32 805.66 µV/码);
三态可用性 `ADC_CAL_F_VALID` / `ADC_CAL_F_CLIPPED` 由 `adc_cal_flags()` 单点查询,104 / UI / 保护闭锁共用。
- 判据:已知电压注入斜率与理论 LSB 偏差 ≤2%(2026-10-27 台架实测端子侧 **6544.2 码/V**,与引脚侧
理论 6553.6 差 **−0.14%**,零点 0~5 码、R²=0.9999997、极性对称 ≤0.34%);8 通道系数/零点往返一致。
- 证据与现场步骤:[`docs/硬件平台/ADC通道映射说明.md`](../硬件平台/ADC通道映射说明.md) §8.1、
[`docs/测试说明/专项验证/ADC前端注入验证流程.md`](../测试说明/专项验证/ADC前端注入验证流程.md)。
**FR-ADC-4 采样有效性校验**
- 需求:芯片静默、丢帧、序号倒退、转换错误必须被检出并可闭锁保护,且**不误报**。
- 实现:`App/algo/adc_health.c` 纯逻辑判定层——静默 100 ms 门限(电平位、可自愈)、丢帧边沿计数、
序号倒退锁存、FRSTDATA/HAL 错误/STM32 停摆计数增量;严重事件与上升沿写自检记录(每类每次开机一次)。
- 判据:主机侧 31 用例全绿;台架零 `[ERR]`(健康不误报),人为制造丢帧时 `tgap` 与 `adc1_gap` 同步可见。
**FR-ADC-5 前端物理口径(已实测盖章)**
- `1k 串联 + 1k 到地` 是**源阻抗匹配、不是分压**:`ADCn` 节点只有 {串阻、滤波电容、转换器引脚},
「1k 到地」接在差分负端(`ADC7_GND` → R32 → AGND),故 AINn↔引脚变比 **1:1**。
- 芯片手册 ±10 V 档的 LSB(305.18 / 1611.3 µV/码)是**另一回事**,不可与本板变比混用。
- 剩余未知量只有**子板/端子板变比**,因此系数只能推到"µV/码 @ 引脚"(硬件规格档 §5 量纲口径)。
- 拓扑图 [`docs/images/ADC前端电路.svg`](../images/ADC前端电路.svg)。
### 5.10 RS-485 采集(FR-RS485)⬜ 冻结
- **需求(已确认)**:半双工 RS-485 **对下 ModBus RTU 采集**(下行采集口;上行走以太网 104)。
- **现状**:`rs485_init()` 已调用;`rs485_task.c` 为空桩、未在 `freertos.c` 创建;`App/protocol/` 为该
协议族预留(当前仅 104 在用)。**整簇冻结**(用户决定,2026-10-12)。
- **重启门禁**:先修「单缓冲包撕裂」「TX 超时拉 DE 未等 TC」两条,再谈接消费者;实现须满足
双缓冲 + 序号、DE 收尾超时路径、协议解析与硬件 I/O 分离。
- 详见 [`docs/任务规划/暂缓-RS485.md`](../任务规划/暂缓-RS485.md)。
### 5.11 遥测与相量(FR-MEAS)✅
**FR-MEAS-1 全周傅氏相量**
- 需求:每 1 ms 产出基波/谐波相量,幅值直接是**基波有效值**,读者无锁取整份快照。
- 实现:`App/algo/phasor.c`——N=20 全周 DFT,系数表离线预乘 `√2/N`;5 个实例
(`IA`/`UA`/`UN` 基波 + `IA` 二次/五次);每来新点整窗重算,算完单字节翻转计算区/使用区。
- 判据:主机侧 57 用例全绿(系数表逐项复核、合成正弦幅值误差 ≤1%、双区交换);台架开机即产出 5 行
`phasor(first)`,心跳持续刷新。
- **双轨有效值**:保护用**基波有效值**(抗衰减直流与谐波误动),计量/遥测要**真有效值**(谐波方和根)
——两者不可混用;本产品当前只实现基波轨,谐波方和根轨未做(§9)。
**FR-MEAS-2 频率测量(测频与遥测快照)**
- 需求:45~55 Hz 内测频,精度优先于响应速度。
- 实现:`App/algo/freq.c`——共轭乘积相位增量 + 解缠绕 + **自适应窗长**(对齐纹波周期整数倍);
假值钳制带与系统合理带分离;幅值门 <50 码判无效;`UA` → `IA` 优先级。
- 判据:45~55 Hz 扫频最坏误差 **0.0101 Hz**、1% 噪声 0.0160 Hz、启动收敛 ≤40 ms、阶跃 50→51 Hz ≤120 ms;
浮空(无信号)时 `valid=0` 不发散。
**FR-MEAS-3 遥测快照(200 ms)**
- 需求:104 与 UI **共用同一份上游**——"什么时候算新值、品质怎么判"只此一处决定;快照必须可无锁整体读。
- 实现:`App/algo/meas.c`——每 200 ms(**经过时间**分频,非拍计数)取基波有效值 + 逐点品质,双区 + `seq`;
**唯一发布者 = adcTask**(`meas_tick` 全仓仅一处调用),其余任务只读。
- 判据:主机侧布局用例钉死字段偏移(改字段 = 破协议);台架 104 与 UI 读数同源一致;快照未发布时
消费者得到 `IV|NT` 而不是 0。
**FR-MEAS-4 品质与死区**
- 需求:品质必须如实反映"这一路能不能信";遥测变化上送不得被单点噪声触发。
- 实现:品质位低字节与 104 `QDS` 同值直传;`CLIPPED`(削顶)由 104 侧映射为 `IV`;
死区 = **连续 2 个新测量点越限才确认**(基准恒为上次上送值,防慢漂移吃掉基准),品质翻转当拍直报。
- 判据:`UB/UC/IB/IC/IN` 五路恒 IV(品质驱动,V0.2 消除 H1 后零改动自动启用);死区两拍确认、
回摆撤销;`TEMP` 通道恒 NT。
### 5.12 保护功能(FR-PROT)🔶 软件就绪,判据现闭锁
**FR-PROT-1 元件框架**
- 需求:统一"越限 + 返回系数滞回 + 定时限"元件核,判据为**纯逻辑**(吃测量值 + 定值),可离线单测;
新增元件只加实例表一行,不改核。
- 实现:`App/algo/prot_elm.c`——数据驱动实例表 9 行;`prot_elm_eval()` 纯判据、`prot_elm_run()` 为
相量/频率到测量值的薄适配;延时用 `now_ms` 差值法(与节拍无关,精度不受 20 ms 节拍影响)。
- 判据:主机侧 47 断言全绿(1.01× 启动 / 0.9× 返回 / 滞回保持 / 欠量方向 / 投退 / 闭锁联动 / 时基回绕)。
**FR-PROT-2 元件清单(V0.1 有效子集)**
| 元件 | 测量量 | 判据要点 | 104 遥信 |
|---|---|---|---|
| 过流 Ⅰ/Ⅱ/Ⅲ 段 | `IA` | 越定值经延时动作,返回系数滞回 | `YX16/17/18`(并汇入 `YX15` 故障总) |
| 过负荷 | `IA` | 越定值告警(默认不跳闸) | `YX23` 过载 |
| 过压 / 欠压 | `UA` | 过量 / 欠量方向,各自延时 | **无遥信点**(仅 SOE + 日志) |
| 零序过压 | `UN`(外部零序专用通道) | 越定值经延时 | 汇入 `YX15` 故障总 |
| 过频 / 低频 | 测频结果 | 过量 / 欠量方向,默认 51.0 / 49.0 Hz | **无遥信点**(仅 SOE + 日志) |
> ⚠ **`YX15` 故障总的构成是 OC1∨OC2∨OC3∨3U0**——电压类与频率类元件**不在其中**,其动作目前只能从
> SOE、`[PROT]` 日志与 UI 记录看到。这是**点表层面的已知缺口**(送检点表要求独立分点时再补),
> 不要把它误读成"元件没动作"。
- **未复刻的元件**(建接口不使能,等条件):三相完整过流(B/C 相)、零序过流、方向/功率类、
PT/CT 断线、涌流制动、重合闸、故障隔离——原因见硬件规格档 §5 的 H1/H2,属需求档 §9.1.1「三相与同期」,
解除条件为 V0.2 硬件。
- 判据:`[PROT] elm` 边沿日志与 104 变位一致;未使能元件不误动。
**FR-PROT-3 判据节拍**
- 需求:判据独立于采集任务,周期稳定,且不得阻塞在存储 I/O 上。
- 实现:`protectTask` 20 ms 节拍(元件吃 50 Hz 基波有效值,相量每工频周更新,20 ms 足够);
纳入 IWDG 必需位;动作只投递 SOE(SPSC 环),**不做 NAND I/O**。
- 判据:静态最坏链 872 B / 配置 2048 B(余 57%);动作到 104 上送端到端 ≤120 ms。
**FR-PROT-4 闭锁(四源,任一命中即停走判据)**
| 闭锁源 | 判据 | 行为 |
|---|---|---|
| **未标定** | `UA`/`UN`/`IA` 的 `ADC_CAL_F_VALID` 未全部置位 | 整条判据链停走(产品构建默认态,等两点标定) |
| **采样故障** | `adc_health_flags()` 电平位(TPAFE/STM32 静默、序号倒退) | 元件全部复位 + 本拍不判据,自愈后自动恢复 |
| **定值非法** | 定值校验和(sum32 正/反码)复核失败 | 闭锁相关保护并告警,需重写定值解除 |
| **全局软压板退出** | `PS_GLOBAL_EN` = OFF(104 遥控 `YK12`) | 元件复位 + 停判据,压板恢复即重返联 |
- 判据:`YX31` 保护闭锁遥信如实反映;锁存类事件位(FRSTDATA/HAL 错误)**不构成**"当前测量不可信",
只登记不闭锁(口径见 [`docs/算法/继电保护功能复现规划.md`](../算法/继电保护功能复现规划.md) §3.5)。
**FR-PROT-5 出口(⬜ 未实现)**
- 需求:判据满足驱动跳闸/告警出口继电器,带保持/脉冲与回读自检。
- 现状:**V0.1 无出口硬件** → 保护"动作"= 遥信 + SOE + 日志;104 遥控 `YK1~4` 为否定确认。
- 判据(立项后):动作驱动对应出口;出口回路自检可检出断线;遥控执行有确认与回读。
### 5.13 定值与运行参数(FR-SET)✅
**FR-SET-1 三类参数**
| 类 | 地址范围 | 内容 | 属性 | 本产品状态 |
|---|---|---|---|---|
| 固有参数 | `8001H~800AH` | 终端类型 / 操作系统 / 制造商 / 硬件版本 / 软件版本 / 校验码 / 规约类型 / 型号 / ID / MAC | 只读 | ⬜ 未实现(字符串类无法短浮点上送,未知 IOA 应答) |
| 运行参数 | `8020H~8058H` | 遥测死区、PT/CT 变比、负荷越限/重载/过载报警、录波投退与定值、功率置负 | 可整定 | 🔶 已落死区 9 点 + `k/b` 覆盖 16 点(`0x8030~0x804F`);其余类别无消费者,留白不预埋 |
| 保护定值 | `8820H~8882H` | 过流 Ⅰ/Ⅱ/Ⅲ 段、零序过流、重合闸、低压/低频、励磁涌流闭锁、小电流接地等 | 可整定 | 🔶 **40 项**(Q10 定点):过流三段、过负荷、过压/欠压、零序过压、过频/低频 + 本地项(投退/返回系数/全局软压板) |
**FR-SET-2 定值模型与封存**
- 需求:定值改动必须**原子可见**——保护巡检任何时刻都看到自洽快照;范围校验失败整笔拒绝。
- 实现:`App/algo/prot_set.c`——影子区写好后重封 sum32 正/反码,再**单字交换**活跃区;
`prot_set_selfcheck()` 用 seqlock 式复核(提交撞见则本轮跳过);范围校验 + 整表 all-or-nothing 装载。
- 判据:主机侧 90 断言(含伪造自洽越界对抗样本);104 写定值与巡检并发无 µs 级误锁。
**FR-SET-3 持久化**
- 需求:定值掉电保持;档案损坏与版本换代必须能区分,损坏不得静默使用。
- 实现:`/prot_set.bin`(magic + 版本 + 计数 + 值区 + sum32 正/反码对),走临时文件 + rename 原子写;
写路径 = 脏标志 → 事件位 → defaultTask 落盘(失败保留脏位下轮重试);
装载在 `STORAGE_READY` 之前整表原子完成。
- 判据:非默认定值跨复位保持(台架 7.5 A 往返读回一致);**版本换代**(旧档)记 INFO,
**真损坏**记 ERROR + 自检留痕并回落出厂默认。
**FR-SET-4 运行参数段**
- 需求:死区与通道系数可由主站整定并持久化,且**系数只有一份真源**。
- 实现:`App/algo/run_par.c`——`[run_par]` 段 9 键(死区,毫单位 int32)+ `k/b` 覆盖 16 点;
`k/b` 写为 `adc_cal_set` **代理**(合法带 `ROM×[0.5,2.0]` 由执行段裁决),run_par 不存系数。
- 判据:`0x8027~0x802A`(过压/欠压告警段)永不被覆盖(路由 run_par 先、未命中落 prot_set);
越带写回否定确认;整档保存一趟覆盖 `[run_par]` 与被改写的 `[adc_cal]`。
**FR-SET-5 主站读写**
- 需求:主站可读定值、两段式写定值,失配/越界必须明确否定。
- 实现:`C_RD` 读 + `C_SE_NC` 两段写;地址 = prot_set `comm_addr`(**点表不另立地址表**);
`comm_addr = 0` 的本地项显式不可寻址(防 IOA=0 误命中)。
- 判据:读回值 = 写入值(COT 正确);本地项读回 COT=14 + EVI;执行段写失败不影响旧值。
### 5.14 SOE 与动作记录(FR-SOE)✅
**FR-SOE-1 记录格式**
- 需求:动作与状态变化必须带时标留痕,且**保护路径不得因写盘被拖慢**。
- 实现:`App/log/soe_rec.c`——64 B 定长记录 = 类型 + 元件号 + IV 位 + `seq` + `CP56Time2a` + epoch + 值 + 校验和;
六类边沿:元件动作 / 复归、软压板投 / 退、采样数据块 / 恢复。
- 判据:`/log/soe.log` 经 FTP 下载后逐条解码通过(校验和、`seq` 严格 +1、CP56 与 epoch 交叉一致)。
**FR-SOE-2 落盘与上送**
- 需求:写盘单写者;104 变位与落盘记录同源同时基。
- 实现:protectTask 投递 SPSC 环(深度 8,**禁 DBG**)→ `defaultTask` 排空落盘;
104 自发单点换 `M_SP_TB_1`(带 CP56 时标),总召/周期保持 `M_SP_NA_1`。
- 判据:动作路径零 NAND I/O(protectTask 静态链 872 B 改前后不变);104 时标 = 事件上送时刻,
与落盘记录(事件发生时刻)差 ≤ 一拍 + 排空延迟(≤50 ms)。
**FR-SOE-3 记录体系(✅ 软件面已落地;真机判据待台架)**
- 现状:七类文本记录(运行 / 自检 / 事故 / 告警 / 启动 / 操作 / 遥控)+ 二进制 SOE 已落地,
「记录类型 = 日志 ID 本身」(一类记录一个文件,记录体内无类型字段位);UI 六页已绑
(记录 5 页 + 运行报告,2026-10-28)。运行报告的四个数取自 boot_stats(SD2506 用户 SRAM
上电次数 + 本次复位原因快照)与 iec104_serv 的四个只读观测计数,经 info 帧下发
(口径 docs/UI设计/UI_数据供给设计说明.md §4.4)。
- 判据:五类记录各有写入点、可翻最近 20 条、空记录明确显示"无记录"、共用 reader——**软件面已满足**
(空态与四个范围标签由 66 张像素黄金钉住;模拟器侧这些文件没有写入方,只拍到空态)。
真记录翻页、掉电后保持、清除记录后归零、boot_count 自增与四态判定**待台架复核**
(docs/任务规划/登记簿.md §7)。
### 5.15 IEC 60870-5-104 上行(FR-IEC104)✅
本产品为**被控站(受控端)**:104 **TCP 服务器**,端口 `[comm]` 段 `port`(工厂默认 **2404**),主站主动连入。
点表口径见 §6.8,设计细节见 [`docs/子系统设计/IEC104上行设计说明.md`](../子系统设计/IEC104上行设计说明.md)。
**FR-IEC104-1 链路层**
- 需求:APCI U/S/I 帧、STARTDT/STOPDT/TESTFR、k/w 窗、t1/t2/t3;TCP 流重组与坏帧自恢复。
- 实现:`App/protocol/iec104/iec104_link.c`(纯逻辑:收=喂流、发=回调、时基注入);参数 k=12 / w=8 /
t1=15 s / t2=10 s / t3=20 s;序号 15 bit 回绕、超时一律差值法。
- 判据:主机侧 174 断言全绿;台架主站往返 STARTDT → 总召四组 → actterm 全通过。
**FR-IEC104-2 遥测上送**
- 需求:遥测短浮点、系数 1、点号落在 `0x4001` 起的遥测段(段基址见 复刻明细 §1);支持背景/总召、
周期(60 s)、死区与品质上送。
- 实现:数据源 = 测频与遥测快照(§5.11);品质直传,`CLIPPED` 映射为 `IV`;死区运行时副本(运行参数段可读写)。
- 判据:总召读回值与 UI 同源一致;死区两拍确认、品质翻转直报;削顶五路恒 IV。
**FR-IEC104-3 遥信上送**
- 需求:双位置遥信上双点、其余单点;变位突发 + 背景扫描;**无效点也入表并置 IV**。
- 实现:`iec104_pts.c` 点表(33 点)+ COV 判定;突发单点带 CP56 时标。
- 判据:`YX15~18/23/31` 为活数据;`YX1~14/19~22/24~30/32~33` 恒 IV(缺行比置 IV 更糟)。
**FR-IEC104-4 遥控**
- 需求:双点命令两段式(选择 `82H` / 执行 `02H`),失配或越界否定确认。
- 实现:`YK11` 信号复归 → 跨上下文复归请求;`YK12` 全局软压板 → 定值原子写;
`YK1~4` 开关、`YK13` 电池活化、`YK14` 解除闭锁 = 否定确认(无硬件)。
- 判据:`YK11`/`YK12` 两段正答且行为可观测(压板退出态跨复位保持);否定确认带正确 COT + PN。
**FR-IEC104-5 定值与运行参数读写**
- 需求:`C_RD` 读、`C_SE_NC` 两段写;地址即定值表 `comm_addr`;写成功即持久化。
- 实现:见 §5.13;路由顺序 = run_par 表先、未命中落 prot_set。
- 判据:读回一致、越界否定、断电重启保持。
**FR-IEC104-6 对时**
- 需求:主站 `C_CS` 对时写入设备墙钟;时标无效如实置 IV。
- 实现:CP56 → epoch ms → `sys_clock`(含 RTC 写回);epoch >2^32 显式拒绝回 PN。
- 判据:对时后日志时间戳跟随主站;`C_CS` 后 SOE 时标同步。
**FR-IEC104-7 响应时间(点表推荐值)**
| 参数 | 要求 | 本产品实测 / 设计 |
|---|---|---|
| 终端启动时间 | ≤100 s | 开机到 `net init: OK` ≈0.6 s,到 104 监听 <2 s |
| 104 连接 / 总召 | ≤3 s / ≤8 s | 连接 <1 s;总召分片 + 单轮 4 ASDU 预算,实测远小于 8 s |
| SOE 上送 | ≤5 s | 变位到上送 ≤120 ms |
| 遥控选择 / 撤销 / 执行 | ≤5 s | 同轮应答(ms 级) |
| 读定值 / 改定值预置 / 执行 | ≤10 s / ≤5 s | 同轮应答(ms 级) |
| 读文件目录 / 文件 | ≤30 s | ⬜ 未实现(文件传输未立项) |
| 录波文件存储 | ≤15 s | ⬜ 未验收(代码面 2026-10-29 已交付:/wave 段文件 + FTP 取回;本波按 1 kHz / 8 路 / 单段做;≤15 s 时限未测,80 点/周波属二期;判据见 docs/子系统设计/故障录波设计说明.md §10) |
**FR-IEC104-8 单连接与自愈**
- 需求:单连接语义(CH395F 单 socket);断链自动重听;网络重建(改 IP)后服务自愈;协议故障主动拆链。
- 实现:监听与数据通道复用同一 socket(EOF 时 net 层 auto_relisten,应用层不关 fd);
`net_get_generation()` 变化即重建;坏帧 / N(R) 倒退 / t1 超时 / 发送 3 连败 → 本端拆链促主站重连。
- 判据:断链后自动恢复监听;改 IP 后重建;**V0.1 为首到优先**(后连优先需 net 层支持,未实现)。
**FR-IEC104-9 本期未实现项(如实登记,均为 §9.1 复刻进度条目)**
| 项 | 原因 | 归属 |
|---|---|---|
| 多主站 / 双链路冗余 | 单连接 socket 语义;后连优先需 net 层扩展 | 规模型 |
| 归一化值 / 标度化值上送 | 点表要求短浮点(系数 1),无需另做 | 不适用 |
| 双点遥信带时标 | V0.1 双点恒 IV,无意义 | 随开入硬件(X3) |
| 定值区切换 / 定值文件下装 | 产品未定义多定值区(参考有 4 区) | 规模型 |
| 文件传输(目录 / 读文件 / 录波) | 录波已立项,段文件取回走现成 FTP(21 端口);**规约侧文件服务仍未做** | 规模型 |
| 固有参数 `8001H~800AH` | 字符串类无法短浮点上送 | 规模型 |
| 主站 IP 白名单 | 单 socket 上"拒绝"= 断链重听,可被扫描打成 SPI 风暴且易锁死真主站 | 不适用 |
### 5.16 屏板链路:串口主通道 + 网络镜像(FR-HMI)🔶
> **口径(2026-10-28 定版)**:参考产品的屏与键**不在主机板上**。主机侧源码里 5 个"碰硬件"的函数
> `KeyGet()` / `WriteScreenByByte()` / `LCD_LED_Update()` / `getRs485Key()` / `anyNewKey()`
> **全树只有声明、没有实现**(由屏板 BSP 库提供);按键轮询函数名即 `Key_Rs485_Process()`
> (注释"轮询从485传来的按键值");屏板插槽号 `CN_SLOT_DISP = 0`(注释"显示板插槽号固定为0")。
> 结论:**屏和键都在一块独立的屏板板件上,主机只负责"送帧 + 收键"**。本产品与参考同构,
> 不在本仓实现的只剩那块**屏板板件硬件本体**(独立工程 DTU-LCD);主机侧"送帧 + 收键"链路是**范围内待做**(§5.16)。
>
> **两条通道,串口为主、网络为镜像**:参考把**同一份显存**同时喂给两个出口——**串口屏板通道**
> (`LCD_LED_Update` 变化驱动 + `WriteScreenByByte` 500 ms 兜底)和**网络显示总线**(TCP 7003)。
> 也就是说"显示信息不只走网络,还走串口"。**本产品目前只有网络这一路**,串口屏板通道整条缺失
> (§5.16.9 第 11 条),这是本项复刻的最大缺口——没有它,产品无法脱离 PC 独立带屏运行。
>
> **屏板固件已到手(2026-10-28)**:屏板侧源码在 `D:\Code\DTU Code\DTU-LCD`(STM32F103ZE +
> UC1698U 160×160 单色 + **20 路状态指示灯** + 7 个物理按键),**物理层与帧格式全部可从代码确定**,见 §5.16.2、§5.16.3。
> 该链路**不是可选项**:屏板是**主动召唤方**,主机不应答则屏板**无画面、无灯、无键**。
#### 5.16.1 一份显存、两个出口(串口为主,网络为镜像)
参考的 HMI 只有**一份显存**(`g_tVsr[0].pwbyLCDMemory`,物理基址 `CN_VIDEO_ADDRESS_BASE = 0xC1F00000`,
非 cache 区,3200 B),但有**两个互不影响的出口**——**串口屏板通道是主通道,网络总线是镜像**:
| 出口 | 归属任务 | 调用 | 下行载荷 | 节拍 |
|---|---|---|---|---|
| **串口屏板通道**(主) | `tDisp`(`Dsp_Main`) | `LCD_LED_Update(ptr, 3200, ledbuf, 4)` | 显存 3200 B + 面板 LED **4 B** | **变化驱动**(`anyNewKey() \|\| LCD_UPDATE_FLAG`) |
| **串口屏板通道**(周期兜底) | `tRemoDisp`(`RDisp_Main`) | `WriteScreenByByte(byLcdMemory)` | 显存 3200 B | **500 ms**(`tTimerLCD`) |
| 网络显示总线(镜像) | `tRemoDisp`(`RDisp_Main`) | `RDisp_Proc` / `RDisp_Send` | `0xAA`/`0xBB` 帧(INIT/KEY/LCDMEM) | 客户端召唤驱动 |
上行(按键)同样是两条,最后在 `Key_Read()` 汇合:
| 上行通道 | 采集方式 | 载荷 | 与另一路的关系 |
|---|---|---|---|
| **串口屏板通道**(主) | `getRs485Key()` 轮询 + `anyNewKey()` / `ClearNewKeyEvent()` 事件 | 物理键号 `1~9`(实测 7 个物理键,§5.16.3 ②) | 映射为逻辑键值写入 `g_tKey` |
| 网络显示总线(镜像) | `RDisp_Proc_SaveKey` → `g_tRemoteKey` | 键值位掩码 1 B | 写入 `g_tRemoteKey` |
| 汇合点 | `Key_Read()`(`Key.c:199`) | — | **先取远程、再被本地覆盖**——两键同时挂起时**本地(串口)优先** |
**串口通道的物理层与协议以屏板源码为准**(§5.16.2 / §5.16.3)。参考主机侧源码给不出物理层,
只能给出**调用契约**——能确定的只有这几项:
| 契约项 | 已确定的内容 | 依据 |
|---|---|---|
| 下行粒度 | 3200 B 整帧 + 4 B LED,**一次调用一帧**(无分片参数) | `LCD_LED_Update(ptr,len,ledbuf,ledbuflen)` 签名 |
| 上行粒度 | 单字节物理键号,`0` 表示无键 | `getRs485Key()` 返回 `uint8_t`,`Key.c:176` 判 `key == 0` 即返回 |
| 事件语义 | 有"新键事件"与"清除事件"两个动作 | `anyNewKey()` / `ClearNewKeyEvent()` |
| 屏板地址 | 板件槽号 **0**(`CN_SLOT_DISP`) | `Key.c:8` 注释"显示板插槽号固定为0" |
| 屏板初始化 | `Lcd_init_display(0xC1F00000)` 由 `LocalDisp_StartTask()` 调用,**不经过任何 UART 打开动作** | `Disp_Main.c:56` |
#### 5.16.2 串口通道:物理层与帧格式(屏板源码实测)
屏板固件工程 `DTU-LCD` 已在手(STM32F103ZE / 512 KB Flash / 64 KB RAM / 72 MHz,UC1698U 160×160 单色),
**链路协议逐字节可确定**,本节即**主机侧必须实现的契约**。
> ⚠ **角色与直觉相反:屏板是 Modbus 主站,主机是从站。** 屏板主动发请求,主机在**请求后**应答;
> 主机**从不主动推送**,也不允许主动推送(半双工总线,谁先说谁占用)。
> 这与 §5.16.4 的 TCP 7003 通道方向相反——网络通道是**客户端召唤**,串口通道是**屏板召唤**。
**物理层**
| 项 | 屏板侧实测值 | 依据 |
|---|---|---|
| 接口 | RS-485 **半双工** | `rs485.c` |
| 引脚 | TX = PA2、RX = PA3、**DE = PA1** | `rs485.h` |
| DE 极性 | **低 = 发送,高 = 接收** | `RS485_SEND_ENABLE()` 拉低、`RS485_RECEIVE_ENABLE()` 拉高 |
| 波特率 | **700000** bps | `#define BAUDRATE 700000` |
| 数据格式 | 8 数据位 / 无校验 / 1 停止位,无流控,**16 倍过采样** | `rs485.c` 初始化 |
| 接收 | `HAL_UARTEx_ReceiveToIdle_IT`(空闲断帧),**DMA 版本被注释掉** | `rs485.c:192-195` |
| 发送 | `HAL_UART_Transmit(..., 1000)` 阻塞发完,随即切回接收 | `RS485_SendBuff()` |
| 缓冲 | RX **4208 B**、TX **8 B** | `UART_RX_LEN` / `UART_TX_LEN` |
> **700 kbaud 在两种候选时钟下都零误差**:16 倍过采样(`OVER8 = 0`)时
> `USARTDIV = fCK / (16 × 波特率)`,写入值 `BRR = (整数部分 << 4) | (小数部分 × 16)`。
> - 屏板链路的**默认端口 USART1 挂 APB2 = 84 MHz**:`84 000 000 / (16 × 700 000) = 7.5` →
> 整数 7、小数 8 → **`BRR = 0x0078`**,误差 **0 %**。
> - 若改用 APB1 上的实例(UART4 / USART2 / USART3,42 MHz):`42 000 000 / (16 × 700 000) = 3.75` →
> 整数 3、小数 12 → **`BRR = 0x003C`**,误差 **0 %**。
>
> 若改时钟树或改用 HSI,**必须重算**——700 kbaud 下 2 % 误差已足以丢帧。
>
> **屏板自己那侧不是零误差**:它的串口挂 APB1 = 36 MHz,`UART_BRR_SAMPLING16(36e6, 700000)` →
> `BRR = 0x33`(USARTDIV = 3.1875)→ 实跑 **705 882 bps,比标称高 0.84 %**;它源码里「手动写精确 BRR」
> 那段注释后面**没有对应代码**。与主机侧的 0 % 合起来 0.84 %,8 位数据 / 1 停止位(10 bit/帧)下在容差内;
> 但**位宽与整帧耗时的实测判据要按 705 882 算,不是按 700 000**(1 字符 ≈14.17 µs、整帧 ≈59.6 ms)。
>
> **端口裁定(用户决定,2026-10-28):屏板走 USART1,日志口让位。**
> 屏板链路改用 **USART1(PA9/PA10)**——这条通道是 **MCU 引脚直出 TTL,不经过板上任何收发器**,
> 因此**收发器速率上限问题不复存在**:本板 485 通道上的 `U15 = TP8485E-SR` 只有 250 kbps
> (`docs/芯片手册/TP8485E-SR.md`)这件事,对屏板链路**不再构成约束**。
> 作为交换,**调试日志迁到 RS-232 通道**(V0.1 板上 RS232#3 = **USART2 PD5/PD6**,
> 备选 RS232#1 = UART5 PC12/PD2,两者都经隔离器 → `U13` TPT3232E → CN1),
> 迁移已实施——控制台现走 UART5/RS232#1,见下方"主机侧端口重排"。
>
> **PA10 一直是接线的**(2026-10-28 用户确认)→ 屏板链路**不需要额外飞线**。
>
> ORE 锁存的充分条件与接线无关——**RX 使能了却从不读 `DR`**:线上只要出现
> 任意一个字节或一个噪声沿,`RXNE` 置位后没人读,下一个事件就把 **ORE 锁存**;而 ORE 只能用
> "先读 SR 再读 DR"清除,于是**永不自愈**。这条对本实现有一条硬要求:
> 屏板链路要用 RX,就必须真的消费 `DR`,并在 `HAL_UART_ErrorCallback` 里**清 ORE 后重挂接收**
> (否则一次噪声就永久锁死,症状与"屏板不回帧"一模一样)。
**帧 A:召唤 / 按键(屏板 → 主机,固定 6 B)**
| 偏移 | 长度 | 字段 | 取值 |
|---|---|---|---|
| 0 | 1 | 从站地址 | 固定 `0x01`(**主机的从站地址**,不是屏板地址) |
| 1 | 1 | 功能码 | 固定 `0x03`(读保持寄存器) |
| 2 | 1 | 命令类型 | `0x00` = `KEY_CMD`(按键事件,**立即**召唤)<br>`0x01` = `RT_CMD`(周期刷新,**280 ms** 一次:`RT_count > 55`,第 56 拍 × 5 ms) |
| 3 | 1 | 按键值 | `KEY_CMD` 时有效(见 §5.16.3 键值表);`RT_CMD` 时**恒为 `0`**(按键发完即清 `key_bk`,见 `RS485_Process()`) |
| 4 | 1 | CRC-16 **低**字节 | CRC-16/MODBUS |
| 5 | 1 | CRC-16 **高**字节 | 同上 |
CRC-16/MODBUS(多项式 `0xA001` 反转式、初值 `0xFFFF`、查表法),
**只覆盖前 4 字节**,**低字节在前**——这是标准 Modbus RTU 的线上顺序(教科书校验值:数据 `123456789`
→ `0x4B37`,线上写 `37 4B`)。
> ⚠ **屏板源码里的字节序注释是反的,别照抄**:`Calculate_CRC()` 返回的**不是**标准 CRC 值,而是高低
> 互换后的「线上字」(`123456789` 它返回 `0x374B`)。于是 `RS485_RefreshScreenCMD()` 里那句
> `cmdbuf[4] = crc>>8`(注释写"CRC校验高字节")实际写出去的是**低字节**——两处命名都反,**线上字节是对的**。
> 主机侧按「标准值 + 低字节在前」实现,两侧字节逐一相同;若照字面写成"高字节在前",屏板会把每一帧
> 都判成 CRC 错,而它的超时判定只看"收没收到一整包"、不看 CRC,现场只表现为画面不刷新,
> 极易误判成波特率或时序问题。实测向量与实现见 `Drivers/BSP/ScreenLink/sb_proto.c` 文件头。
**帧 B:图像应答(主机 → 屏板,固定 4208 B)**
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 3200 | `Picture[]` | 显存位图:160 行 × **20 B/行**,**1 bpp,MSB 在前**,bit=1 → 白 |
| 3200 | 4 | `info[0..3]` | 普通图:**LED 状态,32 位位掩码,小端**(`info[0]` = bit0~7)<br>LOGO 图:`info[0..1]` = 宽、`info[2..3]` = 高(均大端) |
| 3204 | 1 | `info[4]` | LOGO 魔数高字节 `0x89` |
| 3205 | 1 | `info[5]` | LOGO 魔数低字节 `0x45` |
| 3206 | 2 | `info[6..7]` | 保留,屏板不读 |
| 3208 | 998 | 保留填充 | 屏板不读,**但参与 CRC 计算**,须固定填充(建议 `0x00`) |
| 4206 | 2 | CRC-16 | 覆盖 `[0, 4205]`,**低字节在前**(与帧 A 同一口径,见上方的字节序说明) |
> ⚠ **帧长以代码为准:4208 B**(屏板 readme 与 `BMP_LEN` 写的"3208 字节"不作准):接收侧实参是
> `Check_CRC(DR, UART_RX_LEN)`,`UART_RX_LEN = 4208` → CRC 取 `DR[4206]/DR[4207]`、覆盖 `DR[0..4205]`。
> 有效载荷只有前 3208 B,其后 998 B 为凑齐 CRC 窗口的填充;**主机只发 3210 B 时屏板会在
> `DR[4206]` 读到陈旧字节,CRC 必失败并静默丢弃**。**已台架与真屏板实测固化**(屏板出图 = 端到端证据,`docs/子系统设计/屏板链路设计说明.md` §9.1)。
> ⚠ **应答帧不是合法 Modbus RTU 响应**:没有地址、没有功能码、没有字节数域,是一段裸二进制块。
> 因此**不能套用现成 Modbus 从站栈**,只能按上表手工组帧。
> ⚠ **同一 4 字节双解释**:`info[0..3]` 在普通图里是 LED 位掩码、在 LOGO 图里是宽高。
> 区分只靠 `info[4..5] == 0x89 0x45`。**先判魔数再解释这 4 字节**,顺序不能反。
**时序与预算**
| 参数 | 屏板侧常量 | 实际时间 | 含义 |
|---|---|---|---|
| 基准节拍 | 5 ms 定时器 | — | `Process_Count()` 每 5 ms 递增 |
| 周期召唤 | `RS485_RT_COUNT_MAX = 55`,判据 `RT_count > 55` | **280 ms**(第 56 拍 × 5 ms) | 无按键时每 280 ms 召唤一次;屏板 readme 与常见口径写的 275 ms 是按 55×5 算的 |
| 应答超时 | `RS485_ACK_COUNT_MAX = 40`,判据 `ACK_count > 40` | **205 ms**(第 41 拍 × 5 ms) | 到点只置「可以重发」标志 + 累计一次超时——**不丢已收字节、不清缓冲、不重启串口**;计时起点是召唤帧**最后一位的 TC** |
| 迟到的应答 | 收帧处理与超时判定**相互独立**,帧里没有序号 | — | 超过 205 ms 才到的完整帧**照样显示**(当成下一轮的应答,无从区分);代价 = 多一轮画面滞后 + 一次超时计数 |
| 有按键待报时 | 收到帧或超时即打开发送闸门 | 最早 **约 205 ms** 就可能再来一帧 | 上一答还在总线上 → **双向对撞**;且键**只发一次不重试**(发完立刻清备份)→ 错过那一轮 = **丢键** |
| 收到任意一包 | 无论 CRC 是否通过 | — | 超时阶梯全部清零(`ACK_count` / `ACK_OverTimeCnt`)→ **主机把帧发断,屏板也不报超时**,只表现为画面不更新 |
| 连续超时 | `RS485_ACK_OVERTIME_MAX = 5` | 5 次 | 连续 5 次超时 → 强制允许重发(**不重启串口**,重初始化被注释) |
| 屏板自复位 | `sysRunTime > 1 000 000` | **≈5000 s(83 min)** | 屏板**自己**无条件 `HAL_NVIC_SystemReset()` |
**真正的硬约束不是 200 ms,是下面三条**(按风险排序,均出自屏板源码):
1. **整帧零间隙**:屏板按「总线空闲 1 个字符时间」断帧,而它的串口 IP **没有接收超时寄存器**可调
(该系列 USART 只有 SR / DR / BRR / CR1 / CR2 / CR3 / GTPR)。705 882 bps 下 1 字符 ≈14.17 µs,
**间隙 ≥2 字符(约 28 µs)必把一帧切成两帧**,两半都过不了 CRC——**这条才是卡主机实现方式的约束**。
2. **必须正好 4208 B**:CRC 固定取 `DR[4206]/DR[4207]`、覆盖 `DR[0..4205]`,实收长度被丢弃 →
**短帧永远通不过**(998 B 填充是承重的);超过 4208 B 会让它把正在处理的那一帧撕裂(收满即回调、缓冲从头再填)。
3. **绝不让它溢出**:一旦 ORE 就**永久收不到**(它错误回调里的重启被注释掉),而屏幕上没有任何通信状态指示
(运行灯翻转被注释、故障显示函数无人调用)→ 唯一症状是「画面与状态灯一起冻死」,只能断电重上或等
83 min 自复位(自复位后是**白屏**,没有开机 LOGO)。此外它下发 LOGO 时要写 Flash + 4×500 ms 延时,
那段时间既不能处理也不能应答,是最容易撞上本条的窗口。
**所以主机侧整帧必须一次性交给 DMA + 发送完成中断,不要逐块阻塞发**:任务态轮询随时可能被几十 µs 的
ISR 抢占,一抢就撞第 1 条,而后果在屏板侧完全不可见。时间预算 4208 × 10 bit ÷ 705 882 ≈ **59.6 ms**
(按标称 700 000 算是 60.1 ms),占 280 ms 周期约 21 %;「≤200 ms 内发完」仍是我们对自己的要求——
理由是**不与下一轮召唤撞总线、不多一轮画面滞后**,不是因为屏板会把迟到的帧判废。宿主与起发方式见
`docs/子系统设计/屏板链路设计说明.md` §1(`uiTask` 组帧 + USART1 空闲中断点 DMA 起发)。
**主机侧端口重排(2026-10-28 用户裁定)**
| 串口 | 引脚 | V0.1 板上通道(`docs/硬件平台/V0.1_网表详细分析报告.md` §4.2) | 重排后用途 |
|---|---|---|---|
| **USART1** | PA9 / PA10 | 调试口 + **U5 直连 TTL**(ST_TX0/RX0),**MCU 直出、不经任何收发器** | ✅ **屏板链路**(700 kbaud) |
| **USART2** | PD5 / PD6 | **RS232#3**(U10 隔离 → U13 TPT3232E → CN1) | 已初始化、固件未占用(日志最终选了 UART5) |
| UART5 | PC12 / PD2 | **RS232#1**(U9 隔离 → U13 → CN1) | ✅ **调试日志**(115200,已迁移、台架在用;端子见 `docs/子系统设计/屏板链路设计说明.md` §3.2) |
| UART4 | PC10 / PC11,DE = **PA15** | **RS485#4**(U11 隔离 → U15 → A/B → CN1) | 冻结簇,本项不碰 |
| USART3 | PB10 / PB11 | U5 直连 TTL(ST_TX2/RX2) | 空闲 |
**为什么是 USART1**:它是唯一一条**不经收发器、MCU 引脚直出**的通道,而 700 kbaud 恰好卡在收发器上
(`U15 = TP8485E-SR` 只有 250 kbps,`docs/芯片手册/TP8485E-SR.md`)。换到这条线上,速率问题从根上消失,
也不用改任何硬件。UART4 保持 RS-485 冻结簇不动——`rs485_init(&g_rs485, &huart4, …)` 与 V0.1 一致,**无需摘除**。
**这次重排的落地形态(2026-10-29 已实施)**:控制台端口已收口成 `App/log/console_log.c` 的
**`CON_UART*` 单点宏**(句柄 / IRQn / 时钟 / 自愈 `con_uart_reinit()` 一体,现指向 UART5/RS232#1),
换口只改那一段——当初"日志口迁移不是一行改动、建议收口成一处可配置端口宏"的顾虑由此闭环。
> ⚠ **TTL 直连没有方向控制**:主机侧不再有半双工方向切换,建立时间、"等 TC 收尾"一类约束全部不适用。
> ⚠ 主机 RX 只需吃下 **6 B 召唤帧**(不像屏板要吃 4208 B):16 B 缓冲 + 空闲中断断帧就够——TX / RX 是两根独立线,无自发自收。
#### 5.16.3 屏板侧行为:状态灯 / 按键 / 背光 / LOGO
本节描述**屏板固件实际做什么**——决定主机侧要下发什么、以及哪些语义必须由产品自己定义。
**① 状态指示灯(4 B 位掩码下行)**
屏板有 **20 路 LED**,由主机每帧的 `info[0..3]` 控制。解码规则(`LED_Printf`):
```c
leddata = (ledbuf[3]<<24) | (ledbuf[2]<<16) | (ledbuf[1]<<8) | ledbuf[0]; /* 32 位小端 */
LED_POW(LED_ON); /* ID 0 无条件常亮 */
for (i = 1; i < LED_COUNT; i++) /* LED_COUNT = 20 */
LED_Set(i, (leddata & (1UL << i)) ? LED_ON : LED_OFF);
```
- **ID 0 = 电源灯,硬件常亮,不受位掩码控制**(`leddata` 的 bit0 被忽略)。
- **ID 1~19 一一对应 bit1~bit19**;bit20~bit31 未使用,填 0。
- 全部**低电平点亮**(`LED_ON` = 低)、推挽输出、内部上拉、高速模式。
- **只在普通图像帧到达时刷新**;LOGO 帧走另一分支,**不动 LED**。
屏板侧 LED 引脚表(`led_config[20]`,仅备查——主机不直接驱动):
| ID | 引脚 | 备注 | ID | 引脚 | ID | 引脚 | ID | 引脚 |
|---|---|---|---|---|---|---|---|---|
| 0 | PC8 | 电源灯,常亮 | 5 | PC11 | 10 | PC7 | 15 | PC12 |
| 1 | PC9 | 运行灯 | 6 | PD0 | 11 | PA8 | 16 | PD1 |
| 2 | PA9 | | 7 | PD2 | 12 | PA10 | 17 | PD3 |
| 3 | PA11 | | 8 | PD4 | 13 | PA12 | 18 | PD5 |
| 4 | PA15 | | 9 | PD6 | 14 | PC10 | 19 | PD7 |
**对本产品的含义**:§5.8 的主板 6 路 LED(`LED1~6`)与屏板 20 路指示灯是**两套独立输出**,
语义不同——主板 LED 表"设备自身状态",屏板指示灯是"操作员面前的状态/告警灯组"。
> ⚠ **位掩码 → 业务状态的映射表,参考源码里没有**。参考的 `LCD_LED_Update(ptr, 3200, ledbuf, 4)`
> 只证明"主机侧填这 4 B",填哪几位对应哪个业务状态在**未实现的屏板 BSP 里**。
> 因此"哪几号灯表哪个状态"**必须由本产品定义**(建议按 §5.8 的 6 路语义扩展到 19 路,
> 并保留 1~2 路作屏板自检/通信状态)。**这是本项唯一需要"新设计"而非"照抄"的地方。**
**② 按键上行**
| 项 | 屏板侧实测 |
|---|---|
| 库 | MultiButton,只绑 `BTN_SINGLE_CLICK`(单击事件) |
| 扫描 | 5 ms 定时器内 `Button_Ticks()` |
| 电气 | 输入模式、**无内部上拉/下拉**(依赖外部)、低电平有效 |
| 引脚 | `KEY_ENTER`=PD12、`KEY_UP`=PD11、`KEY_DOWN`=PD10、`KEY_LEFT`=PD13、`KEY_RIGHT`=PD8、`KEY_ESC`=PB15、`KEY_RESET`=PB3 |
| JTAG | PB3 需 `__HAL_AFIO_REMAP_SWJ_NOJTAG()` 才能当普通 GPIO |
| 回调 | 任意键按下 → `BackLight_ON()` + 更新 `keyStatus`,主循环转交 `RS485_Process()` |
键值枚举(`KEY_TYPE`,**即帧 A 偏移 3 的 `key` 字节**):
| 值 | 名称 | 值 | 名称 |
|---|---|---|---|
| 0 | `KEY_NONE`(无键) | 5 | `KEY_ADD` |
| 1 | `KEY_ENTER` | 6 | `KEY_DEC` |
| 2 | `KEY_UP` | 7 | `KEY_LEFT` |
| 3 | `KEY_DOWN` | 8 | `KEY_RIGHT` |
| 4 | `KEY_ESC` | 9 | `KEY_RESET` |
| | | 10 | `KEY_FACTORY` |
> ⚠ **两套键号只在 1~8 对齐,9/10 语义不同**,必须显式建映射:
>
> | 语义 | 屏板 `KEY_TYPE`(帧 A 的 `key`) | 参考主机 `getRs485Key()` 物理键号 |
> |---|---|---|
> | 确认 ENT | 1 | 1 |
> | 上 UP | 2 | 2 |
> | 下 DOWN | 3 | 3 |
> | 取消 ESC | 4 | 4 |
> | 加 ADD | 5 | 5 |
> | 减 SUB | 6 | 6 |
> | 左 LEFT | 7 | 7 |
> | 右 RIGHT | 8 | 8 |
> | **9** | **`KEY_RESET`(复位键)** | **F1** |
> | **10** | **`KEY_FACTORY`(工厂模式键)** | **`F1\|ENT` 组合** |
>
> 帧 A 的 `key` 字节**只能按屏板枚举解释**。主机收到 9/10 时**不得**按 `F1` / `F1|ENT` 处理。
> 若产品要求保留 F1/F2 语义,只有两条路:**改屏板固件** 或 **在主机侧加语义映射表**——二者选一,**须先定**。
> ⚠ **`key_configs[]` 只有 7 项,且与枚举不自洽**:表内第 7 项为 `{PB3, KEY_RESET}`(注释却写"加键");
> 枚举有 `KEY_ADD`(5) / `KEY_DEC`(6) / `KEY_FACTORY`(10),但 **`key_configs[]` 没有映射任何 ADD/DEC 物理键**。
> 即**实物只有 7 个物理键**(上/下/左/右/确认/取消/复位),ADD/DEC/FACTORY 有枚举无入口。
> 屏板 readme 的按键表列了 8 个键,与代码不符——**以 `key_configs[]` 的 7 项为准**。
> 这直接影响 §5.16.9 第 8 条"补 F1/F2"是否成立:**屏板上根本没有 F1/F2 物理键**,
> 网络通道的 8 键表是给**远程工具**用的;串口通道**只可能收到 `1/2/3/4/7/8/9`** 这 7 个值
> (`5`/`6`/`10` 有枚举无入口)。
**③ 背光**
- 任意键按下 → `BackLight_ON()`(按键回调里,不在 5 ms 定时器内)。
- 5 ms 定时器每拍调 `BackLight_Close()`,超时熄灭(时长在 `160160D.C`,**屏板自管**)。
- LOGO 帧到达时也会 `BackLight_ON()`。
- **主机侧不参与背光控制**——参考主机的 `Lcd_BackLight()` / 空闲 120 s 熄灭在**主机 BSP**里,
而屏板固件自己就管背光,**两条路径不能同时启用**。本产品按"屏板自管"实现,主机不做(§5.16.9 第 14 条)。
**④ LOGO 与 Flash**
- 魔数命中(`info[4]==0x89 && info[5]==0x45`)→ 按 LOGO 处理:
`Picture` 存入屏板 Flash **`0x0803F000`(扇区 126)**,宽高取 `info[0..1]` / `info[2..3]`(大端),
然后点背光、`ClearScreen()`、`LOGO_Printf()`、延时 **2 s**(4 × `delay_ms(500)`)。
- 上电时 `BMP_Get()` 从同一地址读回并显示。
- **LOGO 帧不刷新 LED、不进入常规画面**;一帧 LOGO 会**永久覆盖**屏板上一次的 LOGO。
- **LOGO 下发已出局、不实现**:主机侧不提供该操作,总线上不会出现 `info[4..5] = 0x89 0x45` 的帧。
**⑤ 屏板自身的健壮性机制**
| 机制 | 实现 | 对本产品的影响 |
|---|---|---|
| CRC 错计数 | 连续 ≥3 次错 → 计数清零(**重初始化串口被注释掉**) | 错帧静默丢弃,链路**不会自愈**,只能靠下一次召唤重试 |
| 应答超时 | 第 41 拍 = **205 ms**;连续 5 次 → 只是打开重发闸门(**重初始化被注释掉**) | 主机长时间不应答**不会锁死**屏板,恢复后自动续上。但两个坑:① **收到任意一包就清零计数、不看 CRC** → 主机把帧发断时屏板也不报超时,只表现为画面不更新;② 真会永久锁死的是**接收溢出**(§5.16.2 硬约束 3),不是超时 |
| 系统自复位 | `sysRunTime > 1 000 000`(≈5000 s)→ `HAL_NVIC_SystemReset()` | **每 83 分钟屏板重启一次**,约 1 s 空窗 |
| 看门狗 | 5 ms 定时器内 `Clear_Watchdog()` | 主循环卡死 → 屏板复位 |
| DMA 接收 | 屏板提交记录明写"DMA 接收的问题没有解决" | 长帧接收可靠性**未经充分验证**,台架必须压测 |
> ⚠ **5000 s 周期性自复位必须被主机容忍**:复位期间屏板约 1 s 不响应召唤。
> 主机的链路监视**不能**把这种周期性空窗判成"屏板掉线",更不能联动告警/保护。
> 判据建议:连续 **≥ 10 s** 无任何应答才置"屏板离线"。
**待与屏板固件方确认的 4 项**(不影响本产品先开工,但验收前必须闭环):
1. `Check_CRC` 用缓冲区容量而非实际接收长度,**短帧会被误判**;是否计划改为传 `Size`。
2. 键值 9 / 10 是否要改成 `F1` / `F1|ENT`(对齐参考),还是保持"复位 / 工厂"语义。
3. `key_configs[]` 是否要补 ADD / DEC 物理键(当前 7 键,枚举 11 项)。
4. 屏板 4208 B 接收缓冲与 700 kbaud 在长线(> 50 m)下的误码率实测数据。
#### 5.16.4 链路 B 帧格式(逐字节)
| 项 | 规格 |
|---|---|
| 传输 | TCP **7003**;设备为 **Server**(`TcpAddServer` + `TcpAddConnection(..., EN_NET_MODE_SERVER, RDisp_MsgRecv)`);目标 IP 填 `255.255.255.255` = 接受任意客户端 |
| 连接参数 | `TcpSetConnPara(iTcpNo, 10240, 10240, 0, 30, 5)`;收发缓冲各 **10240 B** |
| 建链条件 | `getnetport() != 1` 才建 Server(该函数源码无注释,疑为单网口板件跳过本链路;**未验证,勿照抄此条件**) |
| 帧结构 | `[TAG 1B][CMD 1B][LEN_H 1B][LEN_LO 1B][DATA nB][CRC 1B]` |
| TAG | `0xAA` = 客户端→设备(`CN_RDISP_C_TAG`);`0xBB` = 设备→客户端(`CN_RDISP_R_TAG`) |
| LEN | **数据段**长度(不含 4 B 头与 CRC),大端 |
| CRC | 数据段逐字节**异或**(`RDisp_CalCrc`),**不覆盖头与 CRC 自身** |
| 最长帧 | `CN_RDISP_FRAME_LEN_MAX = 9800`(数据段上限) |
| 接收定界 | 逐字节找 `0xAA`(最多 10240 次防死循环)→ 再收 3 B(CMD+LEN)→ `LEN > 10237` 视为噪声重新同步 → 再收 `LEN+1` B(数据+CRC)→ 返回 `LEN+4` |
> **接收侧不校验 CRC**:`RDisp_MsgRecv` 只做定界;CRC 只在 `RDisp_Proc_LcdMem` /
> `RDisp_Proc_SaveKey` 内比对,且 **INIT / KEEPLIVE 帧根本不校验**。这是参考的既有行为,
> 本产品在全帧校验上更严(见 §5.16.9 第 10 行),不作退让。
#### 5.16.5 命令集与数据段布局
| CMD | 名称 | 客户端请求 | 设备应答 | 备注 |
|---|---|---|---|---|
| 0 | `KEEPLIVE` | 空 | **不应答** | 源码 `return`,注释"无 keep alive 报文";仅重置 1 分钟通信计时 |
| 1 | `INIT` | 空 | **16 B**(见下表) | 工具召唤屏幕参数 |
| 2 | `KEY` | `[键值 1B]` | 空(LEN=0) | 键值写入 `g_tRemoteKey`,`byKeyValid = EN_KEY_FLAG_NEW` |
| 3 | `LCDMEM` | `[起始偏移 4B 大端]` | `[起始偏移 4B][显存 nB]` | 支持断点续传;拒绝语义见 §5.16.6 |
| 4 | `ACK` | — | — | 参考 `RDisp_Send` 中为空分支,未使用 |
| 5 | `NCK` | — | — | 同上,未使用 |
| `0xFF` | `IDLE` | — | — | 内部空闲码,不上线 |
**INIT 应答数据段(16 B,即 `RDisp_Form_InitInf` 的单色子集)**:
| 偏移 | 长度 | 字段 | 取值 |
|---|---|---|---|
| 0 | 2 | LCD 像素宽(大端) | `UI_SCREEN_W` = 160 |
| 2 | 2 | LCD 像素高(大端) | `UI_SCREEN_H` = 160 |
| 4 | 4 | 显存字节数(大端) | `UI_FB_SIZE` = `(160*160)/8` = **3200** |
| 8 | 8 | 键值表 `F1/F2/U/D/L/R/ENT/ESC` | `0x04/0x80/0x02/0x40/0x10/0x08/0x20/0x01` |
> **为什么是 16 B 而不是参考的 29 B**:参考的 29 B 里有 **13 B 颜色信息**(1 B 颜色位
> `CN_RDISP_LCD_TYPE` + 12 B RGB `CN_RGB_RED/GREEN/BLUE`),而这 13 B 在**参考自己的构建里就是死字段**:
> `RemoDispBus.h:46` 的 `CN_RDISP_TYPE_MONO = TRUE` → 链路显存本身就是 1 bpp;三个 RGB 值是编译期
> 常量(每次召唤发同样的 12 B);`RDisp_LcdColor_To_Mono()` 对 `CN_MODE_GREY_MONO` 直接 `return TRUE`,
> RGB565 分支永不进入(§3.1.1)。本产品屏板同为单色 1 bpp(`DTU-LCD` 的 `ScreenPrintf()` 只吃 1 bpp
> 点阵),这 13 B **既无接收方也无语义** → **整段删除,不对齐多色协议**:29 − 13 = **16 B**。
**键值是位掩码,不是枚举**——可组合下发:`CN_KEY_F1|CN_KEY_ENT = 0x24`(`Key.c` 物理键号 `10` 即此值)。
`CN_KEY_ADD=0x82`、`CN_KEY_SUB=0x84` 只在**串口屏板通道**使用,不进 7003 键值表。
#### 5.16.6 显存上送的续传与拒绝语义
`RDisp_Form_LcdMem` / `RDisp_Proc_LcdMem` 的完整规则:
1. **分片**:`dwSendBase + 9800 > 3200` 成立 → 本帧发 `3200 - dwSendBase` 字节;否则发 9800 B。
因 `MEMSIZE = 3200 < 9800`,**实际恒为单帧 3204 B**(4 B 偏移 + 3200 B 位图);
分片逻辑为"显存变大"预留,当前不触发。
2. **推进**:发完后 `dwSendBase += dwLen`;若 `>= MEMSIZE` → **`bNeedFlash = TRUE`**、`dwSendBase = 0`。
3. **拒绝**:召唤时若 `bNeedFlash` 为真(上一帧刚发完、新图还没拷贝)→ 返回 **`-1`**,且把
`dwSendBase` 清 0,**不应答**。含义是"**一帧图像只允许被完整上送一次**",客户端要下一帧
必须等主机完成下一次刷新。
4. **越界**:`dwSendBase >= MEMSIZE` → 返回 **`-2`**,不应答。
5. **成功**:返回 `1`,置 `wSendCode = LCDMEM`,由 `RDisp_Send` 组帧发出。
6. **刷新来源**:`RDisp_Main` 每轮检查 `bNeedFlash`,为真则 `memcpy(byLcdMemory, __Lcd_Pub_GetScreenAddr(0), 3200)`
→ `RDisp_LcdColor_To_Mono()` 转单色 → 清 `bNeedFlash`、`dwSendBase = 0`。
> **`RDisp_LcdColor_To_Mono()` 这一步本产品不需要(参考自己也不走)**:该函数按 `CN_LCD_TYPE` 分派,
> 而本构建 `CN_LCD_TYPE = CN_MODE_GREY_MONO` → 命中第一分支**直接 `return TRUE`**,不做任何转换;
> 只有 `CN_MODE_COLOR_16B` 才调 `RDisp_Lcd16B_To_Mono()` 做 RGB565 → 单色,那条路是**死代码**。
> 本产品的 `g_ui_fbuf` **本来就是 1 bpp 单色**(LVGL 单色渲染,`UI_FB_SIZE = 3200`),拷贝即可送,
> **没有"颜色 → 单色"的转换环节**。这是"不对齐多色协议"带来的简化之一。
> **为什么必须有 `bNeedFlash` 握手**:显存由显示任务异步改写,若允许客户端随时按偏移拉取,
> 跨两次刷新的位图会在客户端拼成**撕裂帧**。参考用"整帧一次性 + 拒绝重拉"消除这个窗口,
> 本产品当前无此保护(§5.16.9 第 4 行)。
#### 5.16.7 按键注入与状态机
| 项 | 规格 |
|---|---|
| 存储 | `tagRKeyCtrl g_tRemoteKey { byUsrFlg, byKeyValid, byKeyValue, bUseRkey }` |
| 有效性 | `EN_KEY_FLAG_NULL=0`(无新键)/ `EN_KEY_FLAG_NEW=1`(有新键)/ `EN_KEY_FLAG_SAM=2`(采样中) |
| 注入 | 收 KEY 帧 → `byKeyValue = DATA[0]`、`byKeyValid = EN_KEY_FLAG_NEW` |
| 消费 | `Key_Read()` 读到 NEW 后**立即清 NULL**(边沿消费,不重复触发) |
| 合并顺序 | 有本地键板时:**先取远程 `g_tRemoteKey`,再被本地 `g_tKey` 覆盖**——两键同时挂起时**本地优先** |
| 连接联动 | TCP 连上 → `bUseRkey = TRUE`;断开 → `FALSE` + `wSendCode = IDLE` + `dwSendBase = 0` |
| 背光 | `Key_HaveNewKey()` 每次调用都执行 `MenuProc_Have_Key()`(无新键也点背光),再返回是否有新键 |
#### 5.16.8 时序、超时与重试
| 定时器 | 周期 | 作用 |
|---|---|---|
| `tTimerLCD` | **500 ms** | 参考主机侧**串口屏板通道**的周期兜底节拍(`WriteScreenByByte`)。<br>⚠ 本产品**不照抄**:屏板固件自己按 **275 ms** 主动召唤(§5.16.2),主机只需"**来一帧答一帧**",无需自带节拍 |
| `tTimerComBrk` | **1 分钟** | 通信中断判定;**每收到任一帧、每次发送成功**都重置 |
| `tTimerAbandon` | **200 ms** | 发送未完成时的放弃窗口 |
**发送状态机**(`RDisp_Send`):
1. `TcpGetSendState(iTcpNo) <= 0` → 直接返回(链路未建或芯片发送忙),不组帧。
2. `wSendFlag == CN_RDISP_SEND_BUSY` → 重发上次缓冲;成功 → `IDLE` + 重置 `tTimerComBrk`;
失败且 `tTimerAbandon` 未到 → 返回;已到 → `IDLE` 后**重新组帧**(宁可丢帧不阻塞)。
3. 组帧:`byCRC = XOR(DATA)`;填 `TAG=0xBB`、`CMD`、`LEN`;`CRC` 写在 `DATA` 之后。
4. `TcpSend` 返回值 **必须等于** `wMsgLen` 才算成功(部分发送视为失败,进 BUSY 重试)。
**显存刷新互斥**:`g_tShareSetBuf.dwReserSet[3]` 三态——`0xaa` = 被其他模块占用(本轮跳过)、
`0x55` = 显示已刷新(供远程工具判断)、`0` = 空闲。取用前查 `0xaa`、用后清 `0`。
**任务模型**:`taskSpawn("tRemoDisp%d", 111, 0x0008, 0x35000, RDisp_Main, ...)`
——prio **111**、栈 **0x35000**(212 KB,VxWorks 字节语义)、主循环 `taskDelay(1)`,
每轮 `DogTask_InfoUpdate(EN_TASK_DSP, ...)` 喂狗,退出时 `DogTask_InfoError` 报异常。
#### 5.16.9 与参考的差异(本产品当前实现)
| # | 项 | 参考 | 本产品 | 处置 |
|---|---|---|---|---|
| 1 | INIT 应答长度 | **29 B**(= 宽高 4 + **颜色位 1** + 显存 4 + **RGB 三色 12** + 键值表 8) | **8 B**(宽 2 + 高 2 + 帧字节 4) | **补 8 B 键值表 → 16 B**;**颜色 13 B 不对齐**(单色屏无接收方,§5.16.5) |
| 2 | KEEPLIVE 应答 | 不应答 | 回显空 KEEPLIVE 帧 | 改为不应答(或留兼容开关) |
| 3 | LCDMEM 非 0 偏移 | 支持续传(从 `dwSendBase` 起) | **仅支持偏移 0**,非 0 静默忽略 | 补续传 |
| 4 | 召唤拒绝语义 | `bNeedFlash` 为真 → `-1` 不发 | 无 | 补(防撕裂帧) |
| 5 | 越界语义 | `>= 3200` → `-2` 不发 | 无 | 补 |
| 6 | 链路监视 | `tTimerComBrk` 1 分钟 | 无 | 补(掉线判定 / 重连驱动) |
| 7 | 发送重试 | BUSY + 200 ms 放弃 | 依赖 `net_send` 阻塞模型 | 评估是否需独立实现 |
| 8 | 键值集(网络通道) | 8 键(含 F1 / F2) | 6 键(无 F1 / F2) | 补 F1 / F2;**仅网络通道**——屏板无 F1/F2 物理键(§5.16.3 ②) |
| 9 | 任务模型 | 独立任务 prio 111 / 栈 212 KB | `uiTask` 10 ms 轮询(prio BelowNormal / 16 KB) | **形态差异,不追平**(X1 / X2) |
| 10 | 接收侧 CRC | 仅 LCDMEM / KEY 校验 | 全帧校验 | **本产品更严,保留** |
| 11 | **串口屏板通道** | **有**:显存 3200 B + 状态灯 4 B 下行、键号上行,变化驱动 + 500 ms 兜底 | **已交付**:700000 8N1、一召一答 4208 B、键号上行(docs/子系统设计/屏板链路设计说明.md) | 验收尾巴见该设计说明 §9.2 |
| 12 | 双出口并发 | 同一份显存同时喂串口与网络,互不阻塞 | 两出口并存(7003 + 串口通道),60 s 并发台架无互扰 | —(屏板链路设计说明 §9.1) |
| 13 | 屏板状态指示灯 | 20 路(ID 0 电源常亮 + ID 1~19 位掩码),**4 B 小端**随每帧下发 | **已交付**:5 位映射 `App/algo/sb_led_map.h`(屏板链路设计说明 §6);主板另有 6 路 GPIO LED(`LED1~6`) | 逐位目视验收未闭环(设计说明 §9.2) |
| 14 | 背光 / 对比度 | 主机 BSP 有 `Lcd_BackLight` / `Lcd_SetContrast` + 空闲 120 s 熄灭 | 无 | **不做**——屏板固件自管背光,两条路径不能同时启用 |
**来自屏板固件的新增约束(参考主机侧源码里看不出来,但对实现是硬约束)**:
- **角色反转**:屏板是 **Modbus 主站(主动召唤)**,主机是**从站 `0x01`**;与网络通道"客户端召唤"方向相反。
- **帧长 4208 B**(3208 B 有效载荷 + 998 B 填充 + 2 B CRC,§5.16.2)。
- **700000 8N1**,主机侧走 **USART1 TTL 直连**(无方向控制)。
- **屏板每 ≈5000 s 自复位一次**,约 1 s 空窗;主机链路监视须容忍(建议连续 ≥10 s 无应答才判离线)。
- **屏板只有 7 个物理键**,键值 `9`/`10` 是"复位 / 工厂",与参考主机的 `F1` / `F1|ENT` **语义不同**。
- **单色**:屏板是 1 bpp 单色面板,参考的 13 B 颜色信息(颜色位 + RGB 三色)**无接收方**,不对齐。
> 第 1 / 3 / 4 / 5 / 8 条是**客户端可见的报文口径差异**——现有 `DTU-RemoteLCD` 按 8 B INIT 实现,
> 补到 **16 B** 后**客户端必须同步改**,否则会错位解析。这是本项复刻唯一的跨端风险点。
> 注意:**16 B 与参考的 29 B 不是同一口径**——客户端**不得**按 29 B 预留缓冲再截取前 16 B 使用
> (**从偏移 4 起含义即不同**:参考偏移 4 是颜色位,本产品偏移 4 是显存大小的高字节)。
>
> 第 11 / 12 / 13 条(串口通道三件套)**主机侧已交付**(docs/子系统设计/屏板链路设计说明.md)——本产品已可脱离 PC 独立带屏运行;仍开放的是第 2~9 条(7003 网络通道报文口径)。
>
> 第 14 条**已决定不做**:屏板固件自管背光与超时熄灭,主机再插一手会打架。
#### 5.16.10 复刻完成判据
1. 任意 TCP 客户端连 7003 → 发 `INIT` → 收到 **16 B**(宽 2 + 高 2 + 显存 4 + 8 键值表),
字段值与 §5.16.5 逐字节一致;**不得**出现颜色位或 RGB 字段。
2. 发 `LCDMEM(0)` → 收到 `3204 B`;**再次**发 `LCDMEM(0)`(新图未就绪)→ **无应答**(对应参考 `-1`)。
3. 发 `LCDMEM(3200)` → **无应答**(对应参考 `-2`)。注:该分支在 `bNeedFlash` 为真时先被 `-1` 截住,
只能在"新图已拷贝、尚未上送"的窗口内触发,属**边角路径**,判据以"无应答且不越界读"为准。
4. 发 `KEEPLIVE` → **无应答**,且链路 1 分钟内不被本端主动断开。
5. 发 `KEY(0x24)` → 收到空 KEY 应答,界面表现为 `F1+ENT` 组合键。
6. 断开 TCP 60 s 以上 → 设备侧可观测到"通信中断"(日志 / 状态位),重连后自动恢复推流。
7. 8 键逐个下发 → 界面动作与按键表一致;组合键 `F1|ENT` 生效。
**串口屏板通道(与上面 7 条并列,待建后验收)**:
8. 主机上电后,屏板按 **280 ms** 节拍发帧 A(6 B,形如 `01 03 01 xx CRC_H CRC_L`);主机每帧在
**≤200 ms** 内**发完**帧 B(**4208 B**,整帧无 2 个字符时间以上的间隙,CRC 覆盖 `[0, 4205]`),
**主机侧**连续 10 分钟零超时、零 CRC 错帧(屏板不指示通信状态,计数只能在主机侧取)。
9. 帧 B 前 3200 B 与主机 `g_ui_fbuf` **逐字节一致**(160 行 × 20 B,1 bpp,MSB 在前,bit = 1 → 白)。
10. 屏板 7 个物理键逐个按下 → 主机收到的 `key` 依次为 `1/2/3/4/7/8/9`
(ENTER / UP / DOWN / ESC / LEFT / RIGHT / RESET),界面动作与 §8.2 菜单树一致;
`0` 表示无键、不产生事件。
11. **状态灯下行**:主机把 19 路业务状态映射为位掩码 → 屏板 LED 1~19 的亮灭与掩码逐位一致;
LED 0 恒亮、不受掩码控制(bit0 置位不改变任何灯)。
12. **双通道并发**:同一时刻 7003 客户端在拉帧、屏板在召唤,两侧画面一致,**互不丢帧、不串帧**;
串口侧 4208 B 发送阻塞不得导致 7003 侧超时(反之亦然)。
13. **屏板自复位容忍**:屏板跑满 ≈5000 s 触发自复位,主机**不误判"屏板离线"**(无告警、不触发保护),
约 1 s 后自动续上。
14. 拔掉屏板 → 7003 侧不受影响;插回后自动恢复,无需复位主机。
15. **LOGO 下发(出局,不实现)**:主机不提供 LOGO 帧,本条不参与验收;本表串口侧判据按其余十四条计。
---
## 6. 接口规范
### 6.1 网络服务端口
| 服务 | 协议 / 端口 | 用途 | 核对方法 |
|---|---|---|---|
| FTP | TCP **21** | 文件上传/下载(根目录 `/`,NAND 存储) | 登录后任意 FTP 客户端列目录/读写(鉴权见 §7.6) |
| 墙钟对时 | TCP **8888** | PC 主动推送时间到设备 | 对时后核对设备时间 / 日志时间戳 |
| 远程显示(RemoDispBus) | TCP **7003** | 远端看图(160×160 1bpp)+ 远端导航按键 | DTU-RemoteLCD 或任意 TCP 客户端连 7003 收帧 |
| IEC 60870-5-104 上行 | TCP **工厂默认 2404** | 被控站接入:遥测/遥信上送、总召、遥控/定值下发(点表见 §6.8) | 任意 104 主站连 2404,STARTDT+总召往返(→ `docs/子系统设计/IEC104上行设计说明.md`) |
> **现场组网要求**:设备与客户端**同网段**(默认 .100/24、网关 .1);跨网段先改 IP 再上线。
### 6.2 RemoDispBus 协议
![RemoDispBus 帧格式](../images/RemoDispBus帧格式.svg)
> **口径**:本节按**参考源码**(`HMI/RemoeDisp/RemoDispBus.c`)给线上格式;本产品当前实现与该口径的
> 逐项差异、以及为什么会有差异,见 §5.16.9。**以参考为准**,差异项按 `参考产品复刻明细.md` §11 排期收敛。
>
> 本节只讲**网络**通道(TCP 7003)。**串口屏板通道不走本协议**——它是 RS-485 上的
> **Modbus RTU 风格裸二进制帧**(6 B 召唤 + **4208 B** 图像应答,CRC-16/MODBUS **低字节在前**),
> 帧格式已按屏板源码逐字节定版,见 §5.16.2。
> 两条通道**共用同一份显存**,但帧格式、校验、时序、主从方向全都不同,**不能共用组帧代码**。
| 项目 | 内容 |
|---|---|
| 传输 | TCP,端口 **7003**;设备为 **Server**,目标 IP `255.255.255.255` = 接受任意客户端 |
| 帧格式 | `[TAG(1)][CMD(1)][LEN_H(1)][LEN_LO(1)][DATA(n)][CRC(1)]` |
| 方向标识 | TAG `0xAA` = 客户端→服务端,`0xBB` = 服务端→客户端 |
| 校验 | CRC = **数据段**逐字节异或(低 8 位,**不含** TAG/CMD/LEN,也不含 CRC 自身) |
| 字节序 | 多字节字段**大端**(网络字节序) |
| 最短帧 | 5 字节(LEN=0) |
| 最长帧 | 数据段 ≤ **9800 B**(`CN_RDISP_FRAME_LEN_MAX`) |
| 缓冲 | 收发各 **10240 B**(`CN_RDISP_BUF_RECV/SEND`) |
| 接收方式 | 逐字节有限状态机(TAG 定界 + 长度定长 + CRC 校验,自动重同步) |
**命令集**:
| CMD | 名称 | 请求数据段 | 应答数据段 |
|---|---|---|---|
| 0 | KEEPLIVE | 无 | **无应答**(仅重置 1 分钟通信计时) |
| 1 | INIT | 无 | `[宽u16][高u16][显存u32][8 键值]`(**16 B**;参考的 29 B 含 13 B 颜色信息,**本产品不对齐**) |
| 2 | KEY | `[按键码u8]` | 无(空帧确认,LEN=0) |
| 3 | LCDMEM | `[起始偏移u32]` | `[偏移u32][1bpp位图 3200B]`(3204 B);偏移非 0 时续传,`bNeedFlash` 期间不应答 |
**INIT 字段明细与按键码表**:见 §5.16.5(含 16 B 逐字段偏移)。
> **单色口径**:本产品**只实现单色分支**。参考 `INIT` 里的颜色位 `CN_RDISP_LCD_TYPE` 与
> `CN_RGB_RED/GREEN/BLUE`(共 13 B)**整段删除**,不做多色对齐——屏板是 1 bpp 单色面板,
> 没有彩色/灰度的接收方;参考侧 `RDisp_LcdColor_To_Mono()` 这道"彩色转单色"也因此**不存在**
> (本产品显存本来就是单色)。
**按键码**:`F1=0x04` / `F2=0x80` / `UP=0x02` / `DOWN=0x40` / `LEFT=0x10` / `RIGHT=0x08` /
`ENTER=0x20` / `ESC=0x01`;**位掩码可组合**(如 `F1|ENTER = 0x24`)。
**位图格式**:1bpp 单色,**MSB 优先**(bit7 = 该字节第一个像素);像素 (x,y) → `byte_i=(y*W+x)/8`、`bit_i=7-(x%8)`;位值 `1`=白、`0`=黑;160×160 → 3200 B。
**关键常量**:`UI_NET_SINK_PORT=7003`、`RM_MAX_RX_PAYLOAD=256`、`RM_RX_BUF=64`、帧重组缓冲 261 B
(本产品侧常量;参考侧对应 `CN_RDISP_TCP_PORT` / `CN_RDISP_FRAME_LEN_MAX` / `CN_RDISP_MEMSIZE`)。
> 详见 `docs/UI设计/RemoDispBus协议与接收设计说明.md`。MCU 与模拟器**共用同一份** `ui_net_sink.c`。
### 6.3 对时协议
- **载体**:TCP :8888,PC 主动推送,设备接收并写 RTC。
- **行为**:`timeSyncTask` 监听 → 收包 → 写回 RTC;30 s 无包则掉线重建监听(约 4 s)。
- **待补充**:推送报文的具体帧格式(当前实现细节见 `App/sync/time_sync.c` 与 `test/host/time_sync_server.py`)。
### 6.4 FTP 服务
- **实现**:lftpd(轻量 FTP 服务器),root = `/`(FatFS 卷根),端口 21,**单客户端**。
- **支持命令**:登录 / TYPE I / PASV / STOR / RETR / LIST / DELE / QUIT。
- **鉴权**(已鉴权 · 2026-10-11):口令登录(`[ftp]` 段 `user` / `pwd_hash` = SHA-256(user‖password) / `write`);未登录仅 USER/PASS/QUIT/FEAT/SYST/NOOP 放行,其余一律 530;`write=0` 可禁 STOR/DELE;连续 5 次失败锁 60 s;出厂默认 `admin`/`admin`,默认口令在用有启动告警。口径详见 `docs/子系统设计/网络配置说明.md` §3.4。
- **启动门**:`ftpTask` 必须等 `NET_READY` **且** `STORAGE_READY`(两位 `NoClear`)才 `lftpd_start`。
- **注意**:同一时刻只服务 1 个会话,旧会话异常断开后约 30 s 恢复监听;测试脚本须内置连接重试,**不要用裸 TCP 工具探测 21 端口**。
- **实测**:128 KB 上传下载逐字节一致(约 49/60 KB/s)。
### 6.5 配置文件格式
**位置**:MCU `/config.ini`(卷根);模拟器 `sim_config.ini`(工作目录)。
**语法**:`key=value`(key `[a-z0-9_]+` 大小写敏感);`[section]`;`;`/`#` 注释行;UTF-8(容忍 BOM);CRLF(设备写出)/ LF 均接受;行长 >127 字符该行无效;文件 >4096 B 视为损坏。
**出厂模板**(首启自动生成):
```ini
; DTU 通用配置文件
[network]
; IP 地址
ip=192.168.1.100
; 子网掩码
mask=255.255.255.0
; 默认网关
gateway=192.168.1.1
; MAC 地址 (全零 = 使用芯片内置 MAC)
mac=00:00:00:00:00:00
```
**值类型**:`IP4`(`a.b.c.d`)、`MAC`(`aa:bb:cc:dd:ee:ff`)、`UINT`(十进制,兼容 `0x`)、`STR`(≤31 字符)。
**工厂默认**:IP `192.168.1.100` / 掩码 `255.255.255.0` / 网关 `192.168.1.1` / MAC 全零(= 芯片内置)。
> MAC 全零是"使用芯片内置 MAC"的**哨兵**,不是"MAC 为 0"。恢复出厂:FTP `DELE /config.ini` → 下次上电重新生成模板。
### 6.6 调试串口
| 项 | 值 |
|---|---|
| 端口 | UART5(RS232#1:PC12 TX / PD2 RX,经 U9 隔离 → U13 → CN1 端子),115200 8N1;台架端子接法见 `docs/子系统设计/屏板链路设计说明.md` §3.2 |
| 用途 | printf 日志 + `/serial-boot-test` 采集通道 |
| 配置源 | `.serialtest/config.json`(单一配置源) |
| 工具 | 仓库内置 Python 串口助手(`.dsh/skills/serial-assistant/`,常驻 daemon + CLI) |
**启动标记(`/serial-boot-test` 判据)**:
- Required:`App init OK` / `Network Init` / `net init: OK` / `PHY: 100M FULL`
- Forbidden:`FAIL` / `ERROR` / `HardFault` / `[ERR]`
### 6.7 对外 API 一览
| 模块 | 关键 API | 说明 |
|---|---|---|
| `net_socket` | `socket/bind/listen/accept/recv/send/close` + `select/poll` | BSD Socket 抽象 |
| `net_cfg` | `net_cfg_load/save/get/set/get_mac/set_active_mac/fmt_ipv4/set_apply_cb/request_apply` | 网络配置(签名与语义稳定) |
| `cfg_file` | `cfg_file_register/load/save/poll/section_data` | 配置引擎核心(双端共享) |
| `sys_clock` | `sys_clock_init/get/get_str/get_time` | 墙钟时间服务 |
| `sys_log` | `syslog_post`(RUN / SELFTEST) | 定长记录引擎 |
| `ui_net_sink` | `ui_net_sink_start(port)` / `ui_net_sink_poll()` | 远程显示服务端 |
| `ui_content_page` | `ui_content_page_open/close/invalidate/is_open` | 内容页框架 |
| `task_watchdog` | `task_wd_beat/poll/start` + 观测接口 | 看门狗判定状态机 |
| `sys_events` | `sys_evtHandle` + 9 个事件位 | 启动屏障 + 落盘请求 |
| `adc_input` | `adc_input_init/poll/raw/desc` | 语义取数与有效性语义取数(8 电气量 + 温度) |
| `adc_health` | `adc_health_init/report/flags` | 语义取数与有效性采样有效性判定(纯逻辑) |
| `adc_cal` | `adc_cal_init/apply/flags/set/b_codes/prot_blocked` | 标定折算 + 三态可用性 |
| `phasor` | `phasor_init/run/read` | 全周傅氏相量(双区) |
| `freq` | `freq_run/get/valid` | 测频与遥测快照测频(自适应窗) |
| `meas` | `meas_tick/snap` | 测频与遥测快照(200 ms,双区) |
| `prot_set` | `prot_set_init/load/selfcheck/set_sealed/get/locked` | 保护判据定值模型(原子写 + 封存) |
| `prot_elm` | `prot_elm_init/run/eval/state/request_reset` | 保护判据元件框架(9 实例) |
| `run_par` | `run_par_init/get/set_104_write/dirty_peek` | 运行参数段(死区 + 系数代理) |
| `soe_rec` / `soe_store` | `soe_rec_encode/decode` · `soe_store_post/drain` | SOE 编解码 + 落盘投递 |
| `iec104` | `iec104_link_*` / `iec104_pts_*` / `iec104_serv_*` | 104 链路 / 点表 / 板端胶水 |
### 6.8 IEC 60870-5-104 点表摘要
> **权威口径**:验收以《检测平台配电终端送检要求(点表)》为准;本表是 V0.1 实现摘要,
> 逐点细节与未实现项见 [`docs/子系统设计/IEC104上行设计说明.md`](../子系统设计/IEC104上行设计说明.md) §3。
> **段基址与参考产品逐字对齐**(8 段全表见 复刻明细 §1):遥信 `0x0001`、保护 `0x1001`、遥测 `0x4001`、
> 参数 `0x5001`、遥控 `0x6001`、设定 `0x6201`、电能 `0x6401`、步位置 `0x6601`。
> **点表规模是当前最大差距**:本产品 9 遥测 / 33 遥信 / 2 遥控,参考产品 31 遥测 / 138 开入 / 31 开出。
**遥测(`0x4001` 段起,短浮点,系数 1)**
| IOA | 点 | 来源 | V0.1 |
|---|---|---|---|
| `4011` / `4014` / `4015` | `1Ua` / `1U0` / `1Ia` | 遥测快照(TPAFE 侧,有效) | ✅ |
| `4012` / `4013` / `4016~4018` | `1Ub`/`1Uc`/`1Ib`/`1Ic`/`1I0` | 快照(STM32 侧,削顶) | ❌ 恒 IV |
| `4019~401C` | `1P`/`1Q`/`1S`/`1Pf` | 需跨转换器同刻 | ❌ 恒 IV(V0.2) |
| `401D` | `1f` 频率 | 测频结果 | ✅(未收敛则 IV) |
| `4010` / `401E` / `401F` | `UBA` 后备电源 / `V1`/`V2` 直流 | 无硬件通道 | ❌ 未入表(X3) |
> **与参考的差额**:参考 `g_tYcIndexTab` 共 **31 点**——双回线各三相电流/电压(12)、双回线各三组
> 线电压(6)、双回零序电流/电压(3)、频率(1)、直流 2 路(2)、有功/无功/视在/功率因数
> 单相 3 + 三相 1(4)、电度 8 种另列。本产品 9 点,缺的是**第二回线、线电压、零序双回与
> 功率四量**——前者等通道(X4),后两者等标定后的功率运算。
**遥信(`0x0001` 段起,V0.1 定义 33 点)**:`1~4` 开关位置(双点,恒 IV)、`5~14` 预留(IV)、
`15` 故障总、`16~18` 过流 Ⅰ/Ⅱ/Ⅲ 段、`19~20` 零序过流 Ⅰ/Ⅱ(IV)、`21~22` 接地故障/重载(IV)、
`23` 过载、`24~30` 外部遥信(RS-485 冻结 → IV)、`31` 保护闭锁、`32~33` 预留(IV)。
有效位图 `YX_VALID_MASK` = YX15~18 / 23 / 31(6 点)。
> **与参考的差额**:参考 `g_tInputModelTab` 共 **138 点**(开关合/分位、未储能、就地远方、接地位置、
> 隔离位置、停用重合闸、保护投入、检修压板、远方信号、手合手分、电源输入、活化状态、电池欠压…),
> 另 `g_tOutputModelTab` **31 点**开出(保跳/保合/遥分/遥合/遥控备用/电池活化/电机保护/闭锁…)。
> 两者都需要一次设备接口硬件(X4),属 §9.1 阻塞型。
**遥控(`0x6001` 段起,双点两段式)**:`YK1~4` 开关(否定确认)、`YK11` 信号复归、`YK12` 遥控软压板、
`YK13` 电池活化(否定)、`YK14` 解除闭锁(否定)。
**定值与运行参数**:保护定值地址 = `prot_set.comm_addr`(基址 `0x8820`,过压/欠压沿用告警段
`0x8027~0x802A`);运行参数段 `0x8030~0x8038`(死区 9 点)/ `0x8040~0x8047`(`k` 覆盖 8 路)/
`0x8048~0x804F`(`b` 覆盖 8 路),共 25 项。
**逐项地址为本仓自定,送检点表到位后以送检为准替换常量段。**
> **与参考的差额**:参考 104 定值映射 **107 项**占 `0x8820~0x8891`(8 个空洞),
> 底层定值表 **249 项 / 23 单元**。本产品 40 项,缺的是重合闸(16)、电压时间型自愈(17)、
> 同期(13)、看门狗(63)、遥测越限(10)等整单元(复刻明细 §1)。
**链路参数**:k=12 / w=8 / t1=15 s / t2=10 s / t3=20 s;IOA 3 字节、公共地址 2 字节、传送原因 2 字节;
公共地址与端口可配(`[comm]` 段,工厂默认 1 / 2404)。**生效语义**(用户裁定 2026-10-30):两者都按「保存即生效」要求;现实现 = 公共地址改后重启、端口下次重 bind 才生效,差距已登记 `docs/任务规划/登记簿.md` §1「104 端口改后立刻重 bind」。
---
---
## 7. 非功能需求
### 7.1 可靠性
| 需求 | 要求 | 落地 |
|---|---|---|
| **自恢复** | 任务死锁/饿死/断言死循环 → 硬件复位 | IWDG + 心跳位图(§5.7) |
| **掉电不丢配置** | 配置原子写(旧档迁移已取消,2026-10-10) | tmp+rename;旧 `/cfg/netcfg.bin` 不读、不删、不迁移 |
| **错误可追溯** | 错误码分级/透传/留痕,不吞不猜 | 错误码通道四要素(分类/透传/分级/留痕) |
| **存储自愈** | 卷损坏可探针/自愈;丢弃可观测 | 卷健康探针 + 丢弃计数 + TX 僵尸通道自愈 |
| **不误杀** | 正常长耗时窗口不触发复位 | 启动段不喂狗 + 心跳切片 + 白名单 |
> **错误码通道四要素**(CH395F 范式,后续任何驱动/子系统建立错误通道时应沿用):
> ① 分类(超时/真故障/参数错)② 透传(逐层上报,禁 `(void)ret`)③ 分级(可恢复重试/不可恢复上报)
> ④ 留痕(不可恢复写 syslog selftest,可恢复计数并 UI/日志可见)。
> **纪律**:自愈逻辑严禁把"名字/参数类错误码"归入"卷损坏触发集"。
### 7.2 性能指标
| 指标 | 基线值 | 来源 |
|---|---|---|
| M3 压力档帧周期 | med 60.1 / p95 66.4 / max 69.3 ms(零尖峰) | `docs/测试说明/专项验证/链路延迟测试说明.md` |
| 队列响应性(挂起客户端) | 92.5 ms(pending 一期) | `test/host/e02_probe.py` |
| FTP 传输速率 | 约 49 / 60 KB/s(上传/下载) | 128 KB 实测 |
| 网络重建生效 | 2~5 s(含等链路 ≤3 s) | 配置设计 |
| UI 刷新 | 10 ms 循环 | `ui_task_func` |
| IWDG 停喂到复位 | ≤2.5 s | IWDG 设计 |
| **采样节拍** | **1000 Hz**(每 10 s 帧号 +10000,零丢帧) | 台架 `/serial-boot-test` |
| **单拍采集消费** | ≤10 µs(DMA 搬运后实测 8 µs) | `adc_task` 实测 |
| **相量刷新** | 1 ms(每拍整窗重算 5 实例) | `phasor_run` |
| **遥测快照周期** | 200 ms(经过时间分频) | `meas_tick` |
| **测频精度** | 45~55 Hz 最坏 0.0101 Hz | 主机侧扫频用例 |
| **保护判据节拍** | 20 ms(延时精度由 `now_ms` 差值保证) | `protectTask` |
| **动作到 104 上送** | ≤120 ms(判据拍 + COV + 服务周期) | 台架主站往返 |
### 7.3 内存与资源预算
> **权威来源**:`.map` 受管快照(`docs/内存管理/RAM使用分析与优化方案.md` §2.1,由
> `scripts/check_mem_numbers.py --write` 生成)+ 2026-10-22 栈预算快照
> (`docs/内存管理/data/stack_report_20261022.md`)。**本节数值随代码演进会过期,提交前以受管快照为准。**
**静态区(产品构建,2026-10-24 `.map`)**:
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| Flash ER_IROM1 | 261,928 B | 1024 KB | 25.0% |
| ROM 合计 | 263,500 B | 1024 KB | 25.1% |
| RW_IRAM1 | 81,072 B | 131,072 B | 61.9% |
| RW_CCM | 57,376 B | 65,280 B | 87.9% |
**运行时栈预算(9 任务,静态最坏链 × 真机水位取更坏者)**:
| 任务 | 配置 | 静态最坏链 | 真机峰值 | 余量 | 判定 |
|---|---|---|---|---|---|
| `defaultTask` | 5120 B | 3024 B | 2152 B | 40% | ✅ |
| `netTask` | 3840 B | 1912 B | 1116 B | 50% | ✅ |
| `ftpTask` | 4096 B | 1456 B | 740 B | 64% | ✅ |
| `uiTask` | 16384 B | 1336 B | 4052 B | 75% | ✅ |
| `testTask` | 8192 B | 736 B | — | 91% | ✅ |
| `timeSyncTask` | 2560 B | 1264 B | 616 B | 50% | ✅ |
| `adcTask` | 2560 B | 1320 B | 952 B | 48% | ✅ |
| `iec104Task` | 2560 B | 1456 B | 580 B | 43% | ✅ |
| `protectTask` | 2048 B | 872 B | 828 B | 57% | ✅ |
> **定栈规则**:改栈或加大栈内局部变量前后**必须**重跑 `scripts/stack_report.py`
> (Keil 静态调用图 × 串口 `[STACK]` 水位,判定取两者更坏者;余量须 ≥ max(1024 B ISR, 30%))。
> **LVGL 内存池** 16 KB(静态占用,2026-10-15 由 40 KB 收缩);**FreeRTOS 堆** 56 KB(CCM,水位 87.9% < 92% 门禁)。
> **卡点已从主 SRAM 移到 CCM**(余 ~7.9 KB)——再加任务或抬栈前先看
> `docs/内存管理/RAM使用分析与优化方案.md` §4。
### 7.4 可维护性(测试与门禁)
**测试分层**:
| 层 | 内容 | 运行位置 |
|---|---|---|
| 静态门禁 | pre-commit 钩子族:格式 / 编码 / 静态分析 / 文档链接 / 体积 / 生成物新鲜度 / 编号 | 本地 / CI |
| 主机侧纯逻辑单测 | MSVC 离线编译真实源码 | 本地 / CI |
| 板端测试套件 | 板端套件(testTask 串口 `[PASS]/[FAIL]` 汇总) | 本地(需真机 + 串口) |
| 主机侧回归 | 模拟器像素 / 池水位回归 + 真机 `/serial-boot-test` 闭环(含探针) | 本地(模拟器免硬件;`/serial-boot-test` 需真机 + ST-LINK) |
**构建 / 测试命令**:
```powershell
# 构建(/keil-build):0 错 0 警
& '.venv\Scripts\python.exe' .dsh/skills/keil-build/scripts/keil_build.py # 退出码 0=PASS / 1=构建失败 / 2=环境错误
# 自动化测试(/serial-boot-test):串口采集 → 烧录 → 日志校验
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py # 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法错误
# 全库门禁
pre-commit run --all-files
```
**pre-commit 门禁族**(钩子数量随套件增长,以 `.pre-commit-config.yaml` 为准):
| 族 | 代表钩子 | 作用 |
|---|---|---|
| 格式 / 仓库卫生 | `trailing-whitespace`、`end-of-file-fixer`、`check-added-large-files`、`check-merge-conflict`、`check-yaml`、`gitlint` | 行尾与空白、超大文件、冲突标记、提交信息规范 |
| 编码与源码纪律 | `no-mojibake`、`coding-standard`、`nested-comment`、`bom-guard`、`layer-deps`、`bsp-err` | 乱码、命名与常量位置、注释嵌套、BOM 漂移、分层依赖、BSP 错误码契约 |
| 静态分析 | `cppcheck`、`test-cppcheck` | 主机侧与测试代码静态检查 |
| 文档一致性 | `doc-links`、`id-check`、`task-table`、`mem-numbers`、`mem-budget`、`size-gate` | 链接与章节引用、编号、任务表、内存数字、体积门禁 |
| 生成物新鲜度 | `generated-fresh`、`font-charset`、`lv-conf-sync`、`ui-golden`、`setview-check`、`uinav-selftest` | 菜单/字体/像素黄金等生成物与源一致 |
| 主机侧单测 | `watchdog` / `sys-clock` / `cfg-file` / `phasor` / `adc-input` / `adc-health` / `adc-cal` / `meas` / `freq` / `prot-set` / `prot-elm` / `run-par` / `iec104` / `soe` / `nand-port` / `ui-data` 等 `*-unit-test` | MSVC 离线编译真实源码跑断言 |
| 结构与注册 | `registration-check`、`test-suite-switch`、`cubemx-survival` | 双端注册、套件互斥、CubeMX 保护区审计 |
**受管事实源(禁止手改,由脚本生成)**:
- 任务表(`README.md` / `AGENTS.md`)← `scripts/gen_task_table.py`
- 内存/Flash 快照 ← `scripts/check_mem_numbers.py`
- 菜单表 ← `tools/menu_gen/gen_menu.py`
- 中文字体 ← `tools/font_gen/make_font.py`
### 7.5 可移植性(双端)
| 项 | MCU | 模拟器 |
|---|---|---|
| 构建 | Keil MDK-ARM v5(ARMCC) | CMake + SDL2 + MSVC |
| 共用源码 | `Drivers/BSP/UI/*`、`Drivers/BSP/NETCFG/net_cfg.c`、`App/cfg/cfg_file.c` | 同左 |
| 平台后端 | `*_mcu.c`(CH395F/FatFS) | `*_sim.c`(Winsock/宿主文件) |
| 验证 | `/keil-build` 0/0 → `/serial-boot-test` 串口闭环 | `check_menu_rows.py` + `check_content_pages.py`(像素黄金参考) |
> **新源文件必须双端注册**(Keil `.uvprojx` + `simulator/CMakeLists.txt`),否则不参与编译。
### 7.6 安全与权限
| 项 | 现状 |
|---|---|
| FTP 鉴权 | **已鉴权**(2026-10-11):口令登录(`[ftp]` 段存 SHA-256 摘要,不存明文)+ 连续 5 次失败锁 60 s + `write=0` 可选只读;出厂默认 `admin`/`admin`,默认口令在用有启动告警。边界:FTP 明文传口令,挡未授权访问、不挡网络嗅探(详见 `docs/子系统设计/网络配置说明.md` §3.4) |
| UI 菜单权限 | 受保护叶子(行尾 `#P`)先经密码页;密码**仅编译期写入**(查看 `000` / 修改 `123`),不落 Flash、无运行期设置页 |
| 远程显示 | 无鉴权,单连接;现场同网段部署 |
| 104 上行 | **无应用层鉴权**(标准 104 本身不带认证):安全边界 = 现场同网段部署 + 单连接语义 + 首到优先。主站 IP 白名单**不实现**(单 socket 上"拒绝"= close + 重听,可被端口扫描打成 SPI 风暴,且配错即锁死真主站;属协议语义决定,非范围裁剪,见 §9 卷首)→ 需冗余/白名单时随 net 层立项 |
| 配置面 | 配置与定值只能经 FTP(需口令)或 104(同网段)改写;UI 侧修改需修改密码。**本地无串口配置命令通道** |
---
---
## 8. UI 规格
### 8.1 屏幕与渲染
| 项 | 值 |
|---|---|
| 屏幕尺寸 | 160 × 160 px |
| 色深 | **1bpp 单色**(MSB 优先),帧缓冲 3200 B;**无灰度、无彩色**——参考与本产品的链路都是单色,7003 `INIT` 的颜色字段已删(§5.16.5) |
| 渲染 | LVGL 离屏渲染到 `g_ui_fbuf`;MCU 无物理屏,经**串口屏板通道(主)+ TCP 7003(镜像)**双出口送出 |
| 内存池 | `LV_MEM_SIZE` = 16 KB(2026-10-15 由 40 KB 收缩,实测峰值占 40%) |
| 主题 | dark mono(单色深色) |
| 刷新 | `uiTask` 10 ms 循环 |
| 绘制缓冲 | I1 draw buffer 需 +8 B palette 头 |
### 8.2 菜单树(43 节点)
![菜单树总览(层级 · 绑页 · 密码门)](../images/菜单树总览.svg)
> 图里的节点数、绑页与密码门读数由 `tools/arch_diagram/gen_menu_tree_svg.py` 现算自真值 `ui_menu_tree.txt`(改菜单树 → 同步脚本内 `NODE_SRC` 重跑);级联框在屏上怎么摆(几何)→ `docs/UI设计/菜单页面设计.md` §4.1。
**规模**:菜单节点 **9** 个(含根)+ 叶子节点 **34** 个 = **43** 节点;最大深度 **4 级(主界面 / 一级 / 二级 / 三级菜单)**。
**绑定现状**(2026-10-29 复扫):**已绑内容页 30 叶 / 未绑 4 叶**(页面注册表 `UI_PAGE_MAP_COUNT` = 30,
与已绑叶数一致);未绑 4 叶 = 无开入硬件 3 + SNTP 1(取舍与重开条件见
docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 页 + 运行报告)已于 2026-10-28 落地绑页,
「手动录波」与「录波回放」两叶于 2026-10-29 随录波道绑页(判据见 docs/子系统设计/故障录波设计说明.md §4.1 与 §8.1)。
**根页(主界面)7 个一级条目**:装置信息 / 实时数据 / 参数定值 / 三遥设置 / 装置维护 / 通讯参数 / 记录查询。
**完整层级树**:
```text
主界面
├─ 装置信息 [叶子]
├─ 实时数据 ▶ [菜单]
│ ├─ 交流量 ├─ 直流量 └─ 遥信量
├─ 参数定值 ▶
│ ├─ 整定 ▶(参数 / 定值 / 控制字 / 软压板)
│ └─ 查看 ▶(参数 / 定值 / 控制字 / 软压板)
├─ 三遥设置 ▶(遥测死区 / 遥测系数 / 遥信类型 / 遥信防抖 / 双点遥信)
├─ 装置维护 ▶(时钟设置 / 强制复归 / 手动录波 / 清除记录)
├─ 通讯参数 ▶(通讯设置 / 网口设置 / SNTP设置)
└─ 记录查询 ▶(SOE记录 / 事故记录 / 操作记录 / 保护告警 / 保护启动 /
遥控记录 / 自检记录 / 运行记录 / 运行报告 / 录波回放)
```
**三层代码架构**:数据层 `ui_menu_tab`(生成物,`g_menu_tab[43]` 扁平父子索引表,≈0.7 KB Flash)
+ 逻辑层 `ui_menu`(唯一 **17 B** 导航状态)+ 显示层 `ui_menu_view`(持全部 LVGL 对象,零导航状态)。
**导航模型**:树形逐级进入——`ENTER`/`→` 进入子页,`ESC`/`←` 返回上一级;页面 = 对表的一次
`parent == 节点` 过滤(**现算不落地**)。
**显示模型**:横向级联子框——每级一个框,子框左缘 = 父框右缘(共享 1px 边框列);子框顶边对齐父框
"选中行"行顶,溢出时整框上移使底边对齐选中行行底;选中行整行反色(白带黑字),菜单行带 `▶`。
**改菜单流程**:编辑 `ui_menu_tree.txt`(缩进即层级)→ 重跑 `tools/menu_gen/gen_menu.py` → 导航/渲染代码**零改动**。
### 8.3 内容页
**已绑 30 页**(按分组;逐页形态见 [`docs/UI设计/页面补齐规划.md`](../UI设计/页面补齐规划.md)):
| 分组 | 页面 | 形态 |
|---|---|---|
| 装置信息 | 装置信息 | 静态键值行 6 行不分页;副标题"馈线自动化终端" |
| 实时数据 | 交流量 / 直流量 / 遥信量 | 数据列表(交流量 24 行 / 每页 8,共 3 页;直流量 3 行单页) |
| 参数定值 | 查看 4 页(参数 / 定值 / 控制字 / 软压板) | 只读列表 |
| 参数定值 | 整定 4 页(参数 / 定值 / 控制字 / 软压板) | 可编辑(写值 + 置脏 + 保存反馈) |
| 三遥设置 | 遥测死区 / 遥测系数 | 可编辑(运行参数段,`0x8030~0x804F`) |
| 装置维护 | 时钟设置 / 强制复归 / 清除记录 / 手动录波 | 操作页(执行类必须出真反馈,不得"点了没效果";手动录波触发后回读同一份计数,判据见 docs/子系统设计/故障录波设计说明.md §8.2) |
| 通讯参数 | 通讯设置 / 网口设置 | 可编辑(`[comm]` / `[network]` 段,保存后按各自生效时机) |
| 记录查询 | SOE 记录 / 自检记录 / 运行记录 | 记录列表(共用 reader) |
| 记录查询 | 事故记录 / 操作记录 / 保护告警 / 保护启动 / 遥控记录 | 记录列表(每记录 2 行、空态显「无记录」;写入点与判据见 App/log/README.md §五类记录) |
| 记录查询 | 运行报告 | 只读列表 13 行分 2 页;四个数逐个挂范围标签(自电池周期 / 本次开机),跨开机累计按裁定不做 |
| 记录查询 | 录波回放 | 单通道波形页(阶梯线自绘、8 路切换、单位徽标、削顶标记、空态与不可读态显式文案);显示契约见 docs/子系统设计/故障录波设计说明.md §8.1 |
**未绑 4 叶**(2026-10-28:记录 5 页与运行报告页已落地绑页,原「当前执行 6 项」清零;2026-10-29:
录波道落地,「手动录波」叶子随之一并绑页):
遥信类型 / 遥信防抖 / 双点遥信 = 等开入硬件(§9.1.1「出口与开入」);SNTP 设置 = 等 SNTP 对时(§9.1.2「SNTP 对时」)。
- 内容页为**独立全屏屏**,由框架一次性自绘(**零 LVGL 子对象**),`ESC` 返回菜单屏。
- 菜单导航状态**不压层**,返回即原样还原;同一时刻至多一个内容页打开。
- 节点→页映射在逻辑层 `menu_content_page_for(node)`;未绑叶子进入空白终点框(不伪造数据)。
- 数据供给走**注入通道**(rt / par / info / 遥测死区与系数第四表),页面不直接触算法层;
像素黄金用例当前 **66 张**(菜单 8 状态另计;2026-10-28 记录 5 页与运行报告 2 页落地 +7 张;2026-10-29 录波 UI 道 6 张:手动录波 3 态 + 录波回放 3 态)。
### 8.4 密码与权限
| 项 | 值 |
|---|---|
| 查看密码 | `000`(授只读权限) |
| 修改密码 | `123`(授修改权限) |
| 存储 | `ui_pwd_def.h` **编译期**写入,不落 Flash |
| 流程 | 受保护叶子先 arm → 开密码页 → 确认一致后回调 → **先关页再续做进入动作**(携授予权限) |
### 8.5 中文字体
- 字体 `ui_font_cn_12.c`:宋体 12px 1bpp **子集**(当前 **224 唯一字 / 319 字形**,2026-10-29 实测;
122/217、179/274、185/280 均为历史快照。字形数 = 汉字/符号数 + ASCII 0x20~0x7E 共 95 个)。
- 生成流程:`tools/font_gen/extract_cn.py`(扫 UI 源码 → `ui_cn_chars.txt`)→ `tools/font_gen/make_font.py`。
- **约束**:任何 UI 文案新增汉字后必须走此流程,否则显示为空白方块。
---
---
## 9. 缺口清单与推进顺序
> **口径**:本章条目全部来自 复刻明细 §1.2 的 🔶 / ⬜ 行——即"参考源码有、本产品还没做或只做了一部分"。
> **"不做"一词在本章不出现**:1:1 复刻口径下没有主动放弃的能力,只有尚未排期的能力。
> 唯二例外是 复刻明细 §1.7 标为"不适用"的两条(归一化值上送、主站 IP 白名单),那是**协议语义决定**,
> 不是范围裁剪。
>
> **命名**:本节条目一律用**语义短语**(编号规范 P-8 语义优先),不发字母+数字号——
> 避免与其它文档的 `S`/`A`/`P` 编号族撞名。
### 9.1 复刻缺口总表
按**缺口性质**分两类:**阻塞型**(缺外部资源或硬件,排期无用)与**规模型**(无外部依赖,只等排期)。
> 本节是**清单**;**逐项明细在 [`参考产品复刻明细.md`](参考产品复刻明细.md) §2~§10**——参考源码到底有哪几项、本产品有哪几项、
> 差额具体落在哪个定值 / 哪个点 / 哪个模块上、以及"怎么算做完了"。排期与估算以明细为准。
#### 9.1.1 阻塞型(等外部条件,排期不能解决)
| 条目 | 对应 复刻明细 §1.2 | 现状 | 缺的是 | 解除条件 |
|---|---|---|---|---|
| **标定闭锁** | 1~3 | 9 个元件、定值、闭锁链、SOE、104 上送全部就绪,但 `ADC_CAL_F_VALID` 未置位 → 判据整体停走 | **外部资源**:子板/端子板变比 + 标准源 | 端子变比确认 → 8 通道两点标定 → VALID 置位(届时自动解卡,无需改代码) |
| **出口与开入** | 7、8 | 无硬件;保护动作只体现为遥信 + SOE;遥控开关量否定确认 | **硬件 + 立项**:出口继电器、开入回路、回读自检 | 开入开出作为独立模块立项(不可移植项 X3 解除) |
| **三相与同期** | 4、12 | 建接口不使能(B/C 相与 `IN` 削顶、跨转换器无同刻) | **V0.2 硬件** | 8 路电气量全接 TPAFE + 中点偏置 |
| **电度计量** | 6 | 未实现(功率积分需跨转换器同刻) | **V0.2 硬件** | 同上(不可移植项 X4 解除) |
| **对下采集** | 17(子站侧) | `rs485_task.c` 空桩、未建任务、协议族未实现;整簇冻结 | **决策 + 门禁**:用户已决定暂缓 | 先修「单缓冲包撕裂」「TX 超时拉 DE 未等 TC」→ 接消费者 |
| **第三方送检** | 17 | 自有主站工具往返已通过 | **标定 + 第三方环境** | 标定闭锁解除后送检 |
#### 9.1.2 规模型(无外部依赖,只等排期)
| 条目 | 对应 复刻明细 §1.2 | 参考 | 现状 | 差额 |
|---|---|---|---|---|
| **定值铺开** | 1、2 | 249 项 / 23 单元 | 40 项(覆盖参考 35 + 本地 5)/ 5 组 | +214 项 / +18 单元 |
| **映射铺开** | 3 | 107 项 `0x8820~0x8891` | 40 项局部 | +67 项 |
| **元件铺开** | 12 | 45 个模块 | 8 个模块 / 9 个实例 | +37 个模块(过流类 6 / 电压类 5 / 重合闸 5 / 方向功率 4 / 涌流故障 5 / 自动控制 5 / 记录 5 / 录波采样 2) |
| **遥测铺开** | 4、5 | 31 点 + 模型 35 行 | 9 点 | +22 点 + 模型表 |
| **录波子系统** | 10、15 | 3 类型 / 6 段 / 65 通道 / RAM 16 个 / COMTRADE 64·64·16 | **最小闭环代码面已交付**(2026-10-29):1 kHz / 8 路原始码值 / 单段 320 点 / 轮转段文件 / 手动与保护启动 / 单通道回放页;**板级判据一轮未跑** | 仍差 4 kHz 全速与 80 点/周波、多段与 65 通道、COMTRADE、104 文件传输;口径偏离登记见 docs/子系统设计/故障录波设计说明.md §2 |
| **记录体系** | 9、13 | 13 类队列(128/256 条) | 8 类 | +5 类(越限 / 闭锁 / COS / 顺控 / 顺控执行) |
| **UI 记录页** | 9、13 | 参考有完整记录页与运行报告 | 六页已绑并纳入像素黄金回归(2026-10-28) | 只剩真机判据(docs/任务规划/登记簿.md §7) |
| **多规约栈** | 17 | 12 种 | 1 种(104) | +11 种(101 / 102 / 103 / CDT / ModBus / Whm_GB / 61850 / CAN + 子站侧 3 类) |
| **国密算法** | 18 | `Comm/Ecc/` 全套 | 0 | SM2 / SM3 / ECDSA |
| **软压板铺开** | 11 | 9 面(表容量 32) | 1 面 | +8 面 |
| **SNTP 对时** | 17 | 参考对时走 SNTP | 私有 TCP 8888 + 104 `C_CS` | SNTP 客户端 |
| **固有参数** | 1 | `8001H~800AH` 字符串类参数 | 未实现(无法短浮点上送) | 随送检点表设计 |
| **遥测增强** | 4、5 | k=1~9 谐波方和根 + 4 点平均 | 仅基波轨 | 谐波 / 等周波调频 / 零序自产外接切换 三条算法轨 |
| **屏板链路对齐** | 20 | **串口屏板通道**(**700000 8N1 / USART1 TTL 直连、无 DE**,帧 A 6 B 召唤、帧 B **4208 B** 应答:显存 3200 B + **状态灯 4 B 位掩码** + 4 B info)+ **7003 四命令**(INIT **16 B**,单色口径)+ 续传 / 拒绝握手 / 链路监视 / F1·F2 | **串口通道已交付**(4208 B 应答 / 状态灯掩码 / 键上行 / 双出口并发,屏板链路设计说明 §9.1);7003 四命令(INIT **8 B**);无续传、无 `bNeedFlash` 拒绝、无 1 分钟监视、键值 6 个 | 7003 报文口径 6 项(`参考产品复刻明细.md` §11 第 2~9 条);串口侧验收尾巴见屏板链路设计说明 §9.2 |
> **一句话**:**「标定闭锁」是唯一"再排期也没用"的缺口**(等标准源);「出口与开入」「三相与同期」
> 「电度计量」等 V0.2 硬件;「对下采集」等用户决策;其余规模型条目**全部可在现有硬件上推进**,
> 只取决于排期。
> 这张表与 [`docs/任务规划/登记簿.md`](../任务规划/登记簿.md) 一一对应;状态只在登记簿维护,
> 本节不另建状态。
### 9.2 推进顺序
> **顺序唯一权威 → [`docs/任务规划/README.md`](../任务规划/README.md) §2.0**(本表只是产品视角摘要,
> 冲突时以该节为准)。
| 顺序 | 工作 | 类别 | 当前状态 | 开始条件 |
|---|---|---|---|---|
| 1 | UI 补齐(记录 5 页 → 运行报告) | 规模型 | **清单已清空(2026-10-28)** | 六页已落地;余 4 叶属不做项(手动录波与录波回放两叶已随录波道绑页),重开条件见 docs/任务规划/UI补齐.md §5 |
| 2 | net 层跨上下文收口 | 平台 | **下一顺位(未开工)** | UI 清单已清空,切换条件已满足;**开工待用户点头** |
| 3 | 其余结构与可靠性评估 | 平台 | 排队 | net 收口后 |
| 4 | 端子/子板变比确认 → 两点标定 | 阻塞型 | 等外部资源 | 子板实物/原理图、标准源到位可插入 |
| 5 | 标准源送检与第三方 104 主站验收 | 阻塞型 | 等标定 | 保护 VALID 置位;送检窗口前 2 日冻结特性 |
| 6 | 定值铺开 / 映射铺开 / 遥测铺开 / 软压板铺开 | 规模型 | 等排期 | 标定后(工程量才有意义) |
| 7 | 元件铺开与遥测增强 | 规模型 | 等标准源/硬件 | 先解除未标定闭锁;三相类等 V0.2 |
| 8 | 录波子系统 → 记录体系 → 多规约栈 | 规模型 | **录波最小闭环已提前插入并交付代码面(用户裁定 2026-10-29 录波先行,设计与判据见 docs/子系统设计/故障录波设计说明.md §10);本行剩下的参考机完整形态(多段 / 65 通道 / COMTRADE / 104 文件传输)仍不排** | 各自重启条件 |
| 9 | 验证窗口事项(CI 远端、PC peer、负向注入) | 平台 | 逢条件顺带 | 条件到位即执行 |
| 10 | 出口与开入 / 三相与同期 / 电度计量 / 对下采集 | 阻塞型 | 不排 | V0.2 硬件 / 用户决策 |
> **不可颠倒的顺序(一)**:变比确认 → 两点标定 → 保护解卡 → 送检。未完成标定就送检,
> 保护表现为**不动作**;保持该闭锁,不用非标准源走捷径(用户裁定,2026-10-25)。
>
> **不可颠倒的顺序(二)**:**点表铺开必须晚于标定**——否则铺出去的全是 IV 点,
> 主站无法区分"没做"与"接线错",比不铺更糟。
### 9.3 未接线开关说明
| 开关 | 位置 | 现状 |
|---|---|---|
| 功能裁剪开关 | `App/app/app_cfg.h`(当前无宏) | **没有任何生效的裁剪开关**:原三个 `APP_*_ENABLE` 占位宏因全库零消费点已于 2026-10-22 删除,各模块一律编译进产品——ADC / RS-485 均为必需链路(ADC = 遥测/保护来源,RS-485 = 对下 ModBus RTU 采集)。将来确需裁剪时在真实初始化点接入门控,并同步登记回该文件 |
| `TEST_SUITE_*` | `test/board/test_config.h` | 测试套件**互斥**,一次构建只开一个;全注释 = 正常产品固件 |
| `rs485_task.c` | `App/task/`(未建任务、白名单登记) | 唯一的结构白名单缺口:RS-485 整簇冻结期间**刻意不接线**,随该簇一并出白名单 |
| `DTU_BENCH` | 构建宏(`protect_task.c` 等) | 台架构建下允许保护判据在未标定时动作(仅测试用);产品构建(`#ifndef DTU_BENCH`)下保护闭锁 |
> **逐项明细(定值 249 / 遥测 31 / 开入开出 / 动作事件 / 元件 / 录波 / 记录 / 规约 / 完成判据 / 屏板规划)已独立成档** → [`参考产品复刻明细.md`](参考产品复刻明细.md) §2~§11。
---
## 10. 验收判据
### 10.1 构建与静态门禁
| 项 | 判据 |
|---|---|
| 构建 | `/keil-build` → `BUILD_OK: 0 errors, 0 warnings`(退出码 0) |
| 静态门禁 | `pre-commit run --all-files` 全部通过(钩子族见 §7.4) |
| 主机侧单测 | **全部套件全绿**(看门狗、时钟、配置、相量、采集层、标定、遥测、测频、定值、元件、运行参数、104、SOE 等;套件与断言数随代码演进,以 `.pre-commit-config.yaml` 与 `test/host/` 为准) |
| 栈门禁 | `scripts/stack_report.py` 9/9 PASS(余量 ≥ max(1024 B, 30%)) |
| 体积门禁 | `check_size_gate.py --ci` PASS(体积凭证与 git 索引一致) |
| 双端构建 | Keil 与 CMake 均 0 错 0 警 |
| UI 回归 | 菜单行 + 内容页像素黄金 + 导航自检全 PASS |
### 10.2 上电自检(`/serial-boot-test`)
| 层 | 判据 |
|---|---|
| Required 标记(基础) | `App init OK` / `Network Init` / `net init: OK` / `PHY: 100M FULL` |
| Required 标记(采集链,可选启用) | `ADC input layer init: OK` / `phasor(first)` / `freq(hb)`(顺序锚点 `App init OK>phasor(first)>phasor(hb)`) |
| Required 标记(保护链,可选启用) | `protect task start` / `set=40 elm=9` 计数锚 |
| Required 标记(104,可选启用) | `[I104]` 监听/接入行 |
| Forbidden 标记 | 无 `FAIL` / `ERROR` / `HardFault` / `[ERR]`(四个 Fault 的崩溃日志都以 `[ERR]` 开头,出现即不通过) |
| 自检记录 | `selftest: no fault record` |
| 固件身份 | `fw build` 身份戳匹配 + 烧录后回读 `No difference found` |
| 退出码 | `/serial-boot-test` → 0(PASS) |
### 10.3 现场验收清单
| # | 项目 | 方法 | 通过标准 |
|---|---|---|---|
| 1 | 上电自启动 | 断电上电 + 串口采集 | `/serial-boot-test` PASS,无 Forbidden 标记 |
| 2 | 网络连通 | PC ping 设备 IP | 通(PHY 100M FULL) |
| 3 | 远程显示 | DTU-RemoteLCD 连 7003 | 出图(160×160 1bpp)+ 远端菜单导航可用 |
| 4 | FTP | FTP 客户端连 :21 | 列目录 / 读写 `/` 下文件 |
| 5 | UI 功能 | 远端操作 | 菜单 7 项导航、内容页、密码(查看 `000` / 修改 `123`)、网口设置保存后立即生效 |
| 6 | 对时 | PC 推送 8888 | 设备墙钟更新、日志时间戳正确 |
| 7 | 掉电保持 | 写文件 / 改网口配置 / 改定值 → 重启 | 数据仍在(`/config.ini`、`/prot_set.bin` 按新值生效) |
| 8 | LED 状态 | 目检 | 6 路 LED 随系统状态变化(低电平点亮) |
| 9 | 104 总召与变位 | PC 主站连工厂默认 :2404 → STARTDT → 总召 | 四组数据回全、`actterm` 正常;制造变位后 COT=3 上送 |
| 10 | 104 遥控与定值 | `YK12` 软压板投退、`C_SE` 写定值 | 两段式正答、压板行为可观测、定值跨复位保持 |
| 11 | 保护闭锁态 | 未标定状态下观察 `YX31` 与日志 | 判据停走且**明确可见**(不得静默不动作) |
| 12 | 记录可追溯 | FTP 下载 `/log/soe.log`、`/log/run.log`、`/log/selftest.log` | 记录连续、时标正确、解码校验通过 |
| 13 | 屏板链路一致性 | 串口屏板 + TCP 客户端同时接入,按 §5.16.10 **十四条**逐项验证(第 15 条 LOGO 出局) | 串口侧:275 ms 召唤 / ≤200 ms 回 4208 B、显存 3200 B 逐字节一致、**状态灯 4 B 掩码逐位一致**、7 键动作正确、自复位不误判离线;网络侧:INIT **16 B**、重复召唤 / 越界无应答、KEEPLIVE 静默、8 键(含组合)动作正确 |
### 10.4 台架验收(已闭环)
| 用例 | 内容 | 结论 |
|---|---|---|
| V2 | 任务饿死 → IWDG 复位 | ✅ 800 ms 声明 stall,≤2.5 s 复位(`reset cause: IWDG`) |
| V4 | 关中断死循环 → IWDG 复位 | ✅ 每 ~4.9 s 稳定复位(IWDG 独立于中断) |
| V5 | 30 min 真实负载长跑 | ✅ 120/120 FTP PASS,零 IWDG 复位 |
| 堆分配改造 | 配置引擎堆分配改造(真机) | ✅ `[CFG] load: OK`(经 FreeRTOS 堆);FTP 改 IP 复位即生效 |
| 存储/网络套件 | 16/16 PASS | ✅ |
| **采样节拍与相量** | 帧号 1000 Hz、5 实例产出、双区刷新 | ✅ 2026-10-11 |
| **ADC 前端注入** | 已知电压注入:零点 / 线性 / 斜率 / 映射 / 极性 | ✅ 2026-10-27 五项 PASS(斜率偏差 −0.14%,R²=0.9999997) |
| **104 主站往返** | STARTDT / 总召四组 / 变位 / 遥控 / 定值读回 | ✅ 2026-10-12 18/18 PASS |
| **定值与压板持久化** | `YK12` 投退 + 非默认定值跨复位 | ✅ 2026-10-12 21/21 + 13/13 + 4/4 + 4/4 PASS |
| **SOE 落盘** | 自发 `M_SP_TB_1` 带时标 + FTP 下载解码 | ✅ 2026-10-12 56 条记录全 PASS |
---
## 11. 相关文档
| 主题 | 文档 |
|---|---|
| 文档索引 | [`docs/README.md`](../../docs/README.md) |
| 软件架构与运行时规格(本文拆出的就绪位表/启动时序/线程通信权威) | [`docs/架构设计/软件架构与运行时规格.md`](../架构设计/软件架构与运行时规格.md) |
| 硬件规格与能力边界(本文拆出的引脚表与 H1~H5/量纲权威) | [`docs/架构设计/硬件规格与能力边界.md`](../架构设计/硬件规格与能力边界.md) |
| 参考产品复刻明细(能力基线与逐项差额) | [`docs/产品需求/参考产品复刻明细.md`](参考产品复刻明细.md) |
| 项目全景架构 | [`docs/架构设计/项目架构总览.md`](../../docs/架构设计/项目架构总览.md) |
| 存储架构 | [`docs/架构设计/STM32F4-Base存储架构说明.md`](../../docs/架构设计/STM32F4-Base存储架构说明.md) |
| 启动顺序 / 就绪屏障 | [`docs/架构设计/项目启动顺序管理.md`](../../docs/架构设计/项目启动顺序管理.md) |
| 配置引擎 | [`docs/子系统设计/配置文件设计说明.md`](../../docs/子系统设计/配置文件设计说明.md) |
| 网络配置 | [`docs/子系统设计/网络配置说明.md`](../../docs/子系统设计/网络配置说明.md) |
| 墙钟时间 | [`docs/子系统设计/系统墙钟时间维护说明.md`](../../docs/子系统设计/系统墙钟时间维护说明.md) |
| 运行/自检记录 | [`docs/子系统设计/运行与自检记录设计说明.md`](../../docs/子系统设计/运行与自检记录设计说明.md) |
| 控制台单写者日志 | [`docs/子系统设计/控制台单写者日志设计说明.md`](../../docs/子系统设计/控制台单写者日志设计说明.md) |
| 看门狗 | [`docs/子系统设计/看门狗设计说明.md`](../../docs/子系统设计/看门狗设计说明.md) |
| BSD Socket API | [`docs/网络/BSD_Socket_API_使用指南.md`](../../docs/网络/BSD_Socket_API_使用指南.md) |
| Net 层阻塞模型 | [`docs/网络/Net层阻塞模型架构设计.md`](../../docs/网络/Net层阻塞模型架构设计.md) |
| UI 架构 | [`docs/UI设计/UI_设计架构.md`](../../docs/UI设计/UI_设计架构.md) |
| 菜单页面 | [`docs/UI设计/菜单页面设计.md`](../../docs/UI设计/菜单页面设计.md) |
| 内容页 | [`docs/UI设计/内容页架构.md`](../../docs/UI设计/内容页架构.md) |
| 远程显示协议 | [`docs/UI设计/RemoDispBus协议与接收设计说明.md`](../../docs/UI设计/RemoDispBus协议与接收设计说明.md) |
| 采集与算法层(语义取数与有效性~保护判据) | [`docs/算法/采集与算法层设计说明.md`](../../docs/算法/采集与算法层设计说明.md) |
| 保护功能复现规划(产品定义 / 定值 / 路线) | [`docs/算法/继电保护功能复现规划.md`](../../docs/算法/继电保护功能复现规划.md) |
| 全周傅氏算法原理 | [`docs/算法/傅氏算法详解.md`](../../docs/算法/傅氏算法详解.md) |
| 采样有效性校验 | [`docs/算法/采样有效性校验设计说明.md`](../../docs/算法/采样有效性校验设计说明.md) |
| ADC 采样节拍与 DMA | [`docs/子系统设计/ADC采样节拍设计说明.md`](../../docs/子系统设计/ADC采样节拍设计说明.md) |
| ADC 通道映射与实测 | [`docs/硬件平台/ADC通道映射说明.md`](../../docs/硬件平台/ADC通道映射说明.md) |
| ADC 前端注入验证流程 | [`docs/测试说明/专项验证/ADC前端注入验证流程.md`](../测试说明/专项验证/ADC前端注入验证流程.md) |
| IEC 60870-5-104 上行 | [`docs/子系统设计/IEC104上行设计说明.md`](../../docs/子系统设计/IEC104上行设计说明.md) |
| 数据可信(标定 / 快照 / 运行参数) | [`docs/任务规划/数据可信.md`](../任务规划/数据可信.md) |
| 页面补齐规划 | [`docs/UI设计/页面补齐规划.md`](../../docs/UI设计/页面补齐规划.md) |
| 编号规范 | [`docs/编号规范.md`](../../docs/编号规范.md) |
| 部署环境 | [`docs/环境与部署/项目部署环境要求.md`](../../docs/环境与部署/项目部署环境要求.md) |
| 硬件网表 | [`docs/硬件平台/V0.1_网表详细分析报告.md`](../../docs/硬件平台/V0.1_网表详细分析报告.md) |
| 待办登记簿 | [`docs/任务规划/README.md`](../任务规划/README.md) |
| 实机测试指南 | [`docs/测试说明/板级测试/实机测试实施指南.md`](../测试说明/板级测试/实机测试实施指南.md) |
| 测试标准流程 | [`docs/测试说明/流程与规范/测试开发标准流程.md`](../测试说明/流程与规范/测试开发标准流程.md) |
| RAM 预算与优化 | [`docs/内存管理/RAM使用分析与优化方案.md`](../../docs/内存管理/RAM使用分析与优化方案.md) |
| AI 开发规范 | `AGENTS.md`(仓库根) |
| 开发者入门 | `README.md`(仓库根) |