# STM32F4-Base 产品需求设计文档 > **文档定位**:本项目**面向产品的需求基线**——把散落在架构、子系统、测试、问题记录各文档中的 > "产品做什么 / 做到什么程度 / 依据是什么"收敛成一份可评审、可追溯的需求说明,供甲方、产品、 > 开发、测试与评审者建立统一认知。 > **范围**:硬件规格、系统架构、功能需求(FR)、接口规范、非功能需求(NFR)、UI 规格、平台约束、 > 待实现项与路线图、验收判据。 > **事实来源**:本文所有数值与结论均取自仓库内**受管事实源**(代码、`.ioc`、`.map`、网表分析报告与 > 各模块设计文档),凡与其它文档冲突处,以本文标注的权威来源为准。 > **版本**:v1.9 **日期**:2026-10-28 **状态**:现状基线(含已实现项 + 待实现项) > **阅读前提**:建议先读 [`docs/README.md`](../../docs/README.md)(文档索引)与 > [`docs/架构设计/项目架构总览.md`](../../docs/架构设计/项目架构总览.md)(全景架构)。 > **范围口径:1:1 复刻**——本产品是参考产品 **CLS100**(`Scatter_P_HV03.01_SV01.01`,OMAP-L138 > ARM+DSP 双核 + VxWorks 配电保护测控装置)的**逐模块复刻**。产品范围 = 参考源码的能力全集, > 差异只允许来自物理不可移植项(§2.4.1)。§2.5 以**参考工程源码的常量表与模块文件**为唯一清单, > 给出 20 项能力对照与复刻进度;§9 的缺口一律表述为"复刻进度"而非"不做", > 其中 **§9.4~§9.13 是逐项明细**(每个定值 / 每个点 / 每个模块 / 每条链路的报文差在哪、怎么算做完)。 > 产品定义的技术细化见 [`docs/算法/继电保护功能复现规划.md`](../算法/继电保护功能复现规划.md)。 --- ## 1. 文档说明 ### 1.1 目的与范围 本文回答三个问题: 1. **产品是什么**——一块工业级嵌入式 DTU 平台固件,做什么、面向谁、部署在哪; 2. **产品要做到什么程度**——每一项功能/接口/性能/可靠性的**可验收要求**; 3. **产品当前做到哪一步**——已实现、已验收、待实现的**明确边界**。 **不含**:具体代码实现细节(见各模块设计文档)、测试用例步骤(见 `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/配置源码 | | **@build / @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 个新测量点越限才确认" | | **CLS100** | 参考产品(配电保护测控装置,ARM + DSP 双核 / VxWorks),仅作能力对照,非本产品代次 | ### 1.3 读者与相关文档 | 角色 | 建议读法 | |---|---| | 甲方 / 产品 | §2 产品概述 → §5 功能需求 → §9 待实现项与路线图 → §10 验收判据 | | 开发 | 全文;深入时按 §11 指向各模块设计文档 | | 测试 / 验证 | §5、§6、§7、§10 + `docs/测试说明/` | | 评审 | §2、§7、§8、§9 | --- ## 2. 产品概述 ### 2.1 产品定位 **STM32F4-Base 是一块工业级嵌入式 DTU 平台固件**,基于 STM32F407ZGTx,集成 **以太网通信、SPI NAND 存储、ADC 采集、RS-485 通信与 FTP 文件服务**,并自带 **远程显示(UI 推流)、墙钟对时、看门狗自恢复、运行/自检记录**等工业现场能力。 它的**产品目标是 1:1 复刻参考产品 CLS100**(配电保护测控装置)——按参考工程源码的 常量表与模块文件逐项对齐点表、定值、保护元件与规约栈(§2.4、§2.5)。在此过程中沉淀出的 分层底座(采集链 语义取数与有效性~出口与记录、配置引擎、就绪屏障、双端测试)同时是可复用的平台能力。 > **产品现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置) > **已实现并台架验收**;采集主线(语义取数与有效性采集 → 标定折算 → 全周傅氏相量 → 测频与遥测快照 → 保护判据 > → 出口与记录 SOE → IEC 60870-5-104 上送)**软件链路已全线贯通并台架闭环**;与参考产品的差距 > 集中在三处:① 保护判据处于**未标定闭锁**态(等标准源两点标定)、② **点表规模**远小于参考 > (定值 40 vs 249 项、遥测 9 vs 31 点、开入开出 0 vs 138/31 点)、③ **独立子系统未立项** > (故障录波、电度、多规约、SNTP)。即:**"数"与"判据"已可信到引脚电压口径, > 缺的是标定、点表铺开与一次设备接口**(明细见 §2.5.2、§9)。 ### 2.2 产品形态与部署 ```mermaid flowchart LR subgraph DEV["开发机(Windows)"] K[Keil MDK-ARM v5
编译/烧录] SIM[LVGL PC 模拟器
CMake + SDL2] PY[Python .venv
脚本/测试] 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 串口 USART1 @115200 调试日志" --> TGT TGT -- "以太网 CH395F
FTP:21 / 对时:8888 / 远程显示:7003" --> RMT ``` | 环境 | 角色 | 必需性 | |---|---|---| | ① 开发机 | 编译/烧录、UI 模拟器、脚本工具链 | 开发/测试必需 | | ② 目标板 | 运行固件的 DTU 硬件 | 上机必需 | | ③ 远端客户端 | 远程看图/按键、对时推送、FTP 文件操作 | 现场使用必需 | **关键产品特征**:**MCU 端无物理屏幕**——UI 画面以 160×160 单色位图(1bpp)经 TCP 推送到远端 客户端渲染,按键亦由远端下发(见 §5.6、§8)。 ### 2.3 设计目标与原则 | # | 目标 | 落地手段 | |---|---|---| | G1 | **可靠**:现场死机可自恢复、掉电不丢配置、错误可追溯 | IWDG + 任务心跳位图、原子写 + 迁移、错误码通道四要素 | | G2 | **可维护**:分层解耦、单一事实源、双端复用、门禁强制 | 六层架构、就绪屏障、生成器(菜单/字体/任务表)、pre-commit 钩子族 | | G3 | **可移植**:UI/配置源码 MCU 与 PC 模拟器共用 | 平台后端抽象(`*_mcu.c` / `*_sim.c`),核心零平台依赖 | | G4 | **可扩展**:新子系统/新配置段/新页面按模板接入 | 事件位扩展、段注册三步、页面插件描述符、`App/protocol/` 预留 | | G5 | **可验收**:每项能力有明确判据与自动化门禁 | `@build` / `@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)。 ### 2.4 产品范围(1:1 复刻口径) **产品范围 = 参考产品 CLS100 的可移植能力全集。** 本产品不是"在参考产品里挑一部分做", 而是**按参考工程源码逐模块复刻**:凡参考源码中存在的点表、定值单元、保护元件、记录类型、 规约栈,都属本产品目标范围;差异只允许来自**物理不可移植项**(§2.4.1)。 范围按**复刻完成度**分三档,判定口径见 §2.5.1: | 档 | 含义 | 处置 | |---|---|---| | ✅ **已对齐** | 参考源码能力在本产品上已实现,且有台架/单测证据 | 进 §10 验收判据 | | 🔶 **部分对齐** | 骨架、口径、地址编排已对齐,覆盖范围小于参考(通道数 / 元件数 / 点数) | 差额逐条进 §9 | | ⬜ **未开始** | 参考源码存在、本产品无对应实现 | 逐条进 §9,**不等于"不做"** | **当前分布**(20 项能力,明细见 §2.5.2):✅ **2 项**(采样节拍、自检类型)、🔶 **10 项**、 ⬜ **7 项**、— **1 项**(双核分工,不可移植)。 #### 2.4.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 无该硬件(§4.6 H3) | 硬件缺失 | | X4 | 独立计量通道 | 专用计量芯片(电度) | 无(跨转换器无同刻,§4.6 H1) | 硬件缺失 | > **X1~X4 是范围外项的全部。** 除此以外**不存在"参考有、我们决定不做"的能力**— ### 2.5 1:1 复刻基线(参考源码能力清单) **判定口径**:本节所有"参考能力"的数字,**一律从参考工程源 (实测已发现"89 个元件模块"实为 45 个、定值"约 260 项"实为 249 项两处偏差)。 #### 2.5.1 复刻基线三要素 1. **点表即契约**:参考源码用常量表(`g_t*Tab[]`)定义点表、定值、事件、压板。 这些表就是"要复刻什么"的**唯一清单**——表有几行,目标就有几个点。 2. **地址即接口**:104 的 IOA 由 `Rly_IEC104.h` 的基址宏划分段,**段基址必须逐字对齐**(§2.5.3)。 3. **模块即元件**:`RelayUnit/` 下一个 `.c` 对应一个保护元件,元件数 = 文件数(§2.5.5)。 #### 2.5.2 能力清单与复刻进度(源码实测) | # | 能力 | 参考源码出处 | 参考数量 | 本产品现状 | 状态 | |---|---|---|---|---|---| | 1 | 定值项 | `APP/EquipCfg/Const_Set.c` `g_tSetModelTab` | **249 项** | 40 项(`PS_ID_COUNT`) | 🔶 | | 2 | 定值单元 | `APP/EquipCfg/Const_Set.h` `EN_SET_CELL_*` | **23 个有项**(枚举 28) | 5 组(短路/接地/过负荷/电压/频率) | 🔶 | | 3 | 104 定值映射 | `Comm/Pub/Const_CommSetPara.c` `g_tCommSetParaTab` | **107 项**,`0x8820~0x8891` | 40 项(`0x8820~0x8890` 局部) | 🔶 | | 4 | 遥测 YC | `APP/EquipCfg/Const_Ana.c` `g_tYcIndexTab` | **31 点** | 9 点(`0x4011~0x4018` + `0x401D`) | 🔶 | | 5 | 遥测模型 | 同上 `g_tAnaModelTab` | 35 行 | 0 | ⬜ | | 6 | 电度 YM | 同上 `g_tYmIndexTab` | **8 种** | 0 | ⬜ | | 7 | 开入 KI | `APP/EquipCfg/Const_IO.c` `g_tInputModelTab` | **138 点** | 0(无硬件,X3) | ⬜ | | 8 | 开出 KO | 同上 `g_tOutputModelTab` | **31 点** | 0(无硬件,X3) | ⬜ | | 9 | 动作事件 | `APP/EquipCfg/Const_Rec.c` `g_tActRecTab` | **33 条**(421~453) | 7 类记录(`App/log/sys_log.h`) | 🔶 | | 10 | 录波测量通道 | 同上 `g_tMeasureModelTab` | **65 通道** | 0 | ⬜ | | 11 | 软压板 | `APP/EquipCfg/Const_Soft.c` `g_tSoftTab` | **9 面**(表容量 32) | 1 面(`PS_GLOBAL_EN`) | 🔶 | | 12 | 保护元件 | `RelayUnit/*.c` | **45 个模块** | 9 个实例 | 🔶 | | 13 | 记录队列 | `RecUnit/RecFile.c` + `GlobalDef/Const_Sys.h` | **13 类**(128/256 条) | 8 通道(7 文本 + SOE) | 🔶 | | 14 | 自检类型 | `GlobalUnit/SelfChk.c` | **3 类型 + 2 闭锁** | 3 类 | ✅ | | 15 | 故障录波 | `APP/EquipCfg/Const_Wave.h` | **3 类型 / 6 段 / RAM 16 个** | 0 | ⬜ | | 16 | 采样节拍 | `APP/EquipCfg/Const_VisEquip.h` | **20 点/周波**(50 Hz) | 20 点/周波(1000 Hz) | ✅ | | 17 | 上行规约 | `Comm/Pub/`、`Comm/Mon/` | **12 种** | 1 种(104) | 🔶 | | 18 | 加密算法 | `Comm/Ecc/` | SM2 / SM3 / ECDSA | 0 | ⬜ | | 19 | 双核分工 | `CLS100_Arm/`、`CLS100_Dsp/` | ARM + DSP | 单核(X1) | — | | 20 | 远端屏与按键链路 | `HMI/RemoeDisp/RemoDispBus.c`、`HMI/Dsp/Key.c`、`HMI/Dsp/Disp_Pub.c`;屏板侧 `DTU-LCD` | **串口屏板通道**(显存 3200 B + **状态灯 4 B 位掩码**下行 / 键号上行)+ **TCP 7003 镜像** | 7003 四命令已通;**串口屏板通道整条缺失**(协议已按屏板源码定版,见 §5.16.2);INIT 应答 8 B(参考 29 B,其中 13 B 颜色信息**不对齐**)、无断点续传、无链路监视、缺 F1/F2 | 🔶 | > **读法**:🔶 / ⬜ 行**全部是待办**,逐条对应 §9.1;`—` 只出现在 X1~X4 不可移植项上。 > 本表是范围判定的唯一出处,§9 不再重复解释"为什么缺"。 #### 2.5.3 104 地址空间(段基址逐字对齐) | 段 | 基址 | 范围 | 容量 | 本产品使用 | |---|---|---|---|---| | 遥信 YX | `0x0001` | `0x0001~0x1000` | 4096 | 定义 1~31,有效 6(YX15~18/23/31) | | 保护 BH | `0x1001` | `0x1001~0x4000` | 12288 | 0 | | 遥测 YC | `0x4001` | `0x4001~0x5000` | 4096 | `0x4011~0x4018`、`0x401D` | | 参数 PM | `0x5001` | `0x5001~0x6000` | 4096 | 0 | | 遥控 YK | `0x6001` | `0x6001~0x6200` | 512 | `0x600A`(信号复归)、`0x600B`(全局压板) | | 设定 YS | `0x6201` | `0x6201~0x6400` | 512 | 0 | | 电能 YM | `0x6401` | `0x6401~0x6600` | 512 | 0 | | 步位置 STEP | `0x6601` | `0x6601~0x6700` | 256 | 0 | > ⚠ **段基址是 `0x4001` / `0x6001`,不是 `0x4000` / `0x6000`**。参考源码 > `Comm/Pub/Rly_IEC104.h` 的 `CONST_104_BASEADDR_YC` / `_YK` 明确 1 起编 > (注释即写 `4001-5000H`、`6001-6200H`)。本仓 v1.1 及更早文档按 `4000H+` / `6000H+` 描述 > 属笔误,以本表为准;§5.15、§6.8 已同步更正。 #### 2.5.4 定值单元(23 个有项单元,合计 249 项) | 单元 | 语义 | 项数 | 本产品 | |---|---|---|---| | `EN_SET_CELL_WatchDog` | 看门狗功能 | 63 | ⬜ | | `EN_SET_CELL_SYS3` | 系统参数 | 45 | 🔶 | | `EN_SET_CELL_VolTimeRecv` | 电压时间型自愈 | 17 | ⬜ | | `EN_SET_CELL_AutoReCls` | 重合闸 | 16 | ⬜ | | `EN_SET_CELL_SYN` | 同期元件(FTU 型) | 13 | ⬜ | | `EN_SET_CELL_OverCurI0` | 接地告警 | 13 | 🔶 | | `EN_SET_CELL_VolPro` | 电压保护 | 13 | 🔶 | | `EN_SET_CELL_WAVESTART` | 录波启动 | 12 | ⬜ | | `EN_SET_CELL_MeaLimit` | 遥测越限 | 10 | ⬜ | | `EN_SET_CELL_OverCurSpeed` | 过流加速 | 8 | ⬜ | | `EN_SET_CELL_SYS1` | PT 参数 | 6 | 🔶 | | `EN_SET_CELL_SYS2` | CT 参数 | 4 | 🔶 | | `EN_SET_CELL_Auto_FG` | 自动分闸 | 4 | ⬜ | | `EN_SET_CELL_Auto_HH` | 自动合闸 | 4 | ⬜ | | `EN_SET_CELL_InRush` | 励磁涌流闭锁 | 3 | ⬜ | | `EN_SET_CELL_OverCur` | 短路告警 | 3 | 🔶 | | `EN_SET_CELL_VolQuaRate` | 电压合格率 | 3 | ⬜ | | `EN_SET_CELL_FZDDL` | 非遮断电流闭锁 | 3 | ⬜ | | `EN_SET_CELL_BHKO` | 保护出口展宽 | 2 | ⬜ | | `EN_SET_CELL_MeaRecord` | 遥测记录 | 2 | ⬜ | | `EN_SET_CELL_PowerSet` | 功率电压设置 | 2 | ⬜ | | `EN_SET_CELL_PTBreak` | PT 断线告警 | 2 | ⬜ | | `EN_SET_CELL_AutoCtrl` | 自动控制(活化/复归) | 1 | ⬜ | > 枚举中另有 4 个单元在参考源码表内**无项**(`EN_SET_CELL_YB_TY` 功能软压板、`OnceCch` > 一次重合闸、`TwiceCch` 二次重合闸、`OverLoad` 过负荷保护)——已被条件编译或注释掉, > 因此 249 项分布在 **23** 个单元而非枚举的 28 个。另有 3 个单元(`SYN` / `OverCurSpeed` / > `VolTimeRecv`)受 `CN_DEV_TYPE_FTU` 宏控制,属 FTU 型专属。 > > **104 定值地址编排**:107 项占 `0x8820~0x8891` 连续段中的 107 个地址,**8 个空洞** > (`0x8822`、`0x8823`、`0x883E`、`0x883F`、`0x8842`、`0x8851`、`0x8852`、`0x8853`) > 对应参考源码中被注释掉的 4 条映射(`ULTV_PARA_2`、`ULTV_Phase` 等)。 #### 2.5.5 保护元件(45 个模块 → 覆盖 8 个模块 / 9 个实例) 按功能归类(`RelayUnit/` 文件名去 `PRS_` 前缀): | 类别 | 元件模块 | 个数 | 本产品 | |---|---|---|---| | 过流类 | `OverCurrent`、`OverCurrent_I0`、`OverCurrent_SpeedUp`、`OverCurrentI0_SpeedUp`、`FZDOverCur`、`OverCurWarn`、`OverCurWarn_I0`、`OverLoad` | 8 | 🔶 过流 Ⅰ/Ⅱ/Ⅲ + 过负荷(4) | | 电压类 | `OverVol`、`OverVol_U0`、`LackVol`、`LineHaveVol`、`HaveVolCls`、`NoVolTrip`、`PTBreak`、`CTBreak` | 8 | 🔶 过压 / 欠压 / 零序过压(3) | | 频率类 | `OverFr`、`LackFr` | 2 | ✅ 过频 / 低频(2) | | 重合闸 | `AutoReCls`、`AutoReCls_JD`、`OnceCch`、`TwiceCch`、`ConnectBrkCls` | 5 | ⬜ | | 方向 / 功率 | `DirCell`、`PowDirJudge`、`PowerSet`、`YcOverLimit` | 4 | ⬜ | | 涌流 / 故障 | `InRushCur`、`FaultIsola`、`FaultRecov`、`BusEarthAlm`、`SgzAlarm` | 5 | ⬜ | | 自动控制 | `AutoCtrlTrip`、`ManualTrip`、`MusterRecov`、`ExvRec`、`ExvRecGW` | 5 | ⬜ | | 记录类 | `SOERecord`、`CORecord`、`FrzRecord`、`YcMeaRecord`、`YcMeaLimitRec`、`YcVolQuaRate` | 6 | 🔶 SOE 落盘(1,非判据元件) | | 录波 / 采样 | `WaveQdCell`、`RlyCommSam` | 2 | ⬜ | **判据结构对齐**(以 `PRS_OverCurrent.c` 为样板,三段过流共用一个核): ``` M_GRly_OverRelay(&bRelay, fAmp, fSetStart, fSetRet) 越限 + 返回系数滞回 ∧ bFyOpen ∧ bDirOpenX ∧ bNOVOpen 复压 / 方向 / 低压闭锁 GRly_TimeRelay(&bRelay, &tTimer, dwSetDelay, CN_T10MS) 定时限(10 ms 基准) tTimer_KeepKI 电流保持(返回后仍延时保持) tTimer_NoUI 无压无流判据(CN_T20MS / CN_T200MS) ``` > 本产品 `prot_elm.c` 的"越限 + 返回系数滞回 + 定时限"三元核与上述结构**同构**; > 差异在于本产品用**每元件独立实例表**替代参考的全局定时器数组——这是单核移植的必要调整, > 不改变判据语义。逐元件复刻计划见 > [`docs/算法/继电保护功能复现规划.md`](../算法/继电保护功能复现规划.md)。 #### 2.5.6 上行规约全集(12 种 → 已复刻 1 种;加密算法另计) | 方向 | 规约 | 参考源码 | 本产品 | |---|---|---|---| | 主站侧(对上) | IEC 60870-5-101 | `Comm/Pub/Rly_IEC101.c`、`Rly_IEC101_B.c` | ⬜ | | | IEC 60870-5-102(电度) | `Comm/Pub/Rly_IEC102.c` | ⬜ | | | IEC 60870-5-103 | `Comm/Pub/RLY_IEC103.c`、`RLY_IEC103-tcp.c`、`Rly_IEC103T.c` | ⬜ | | | **IEC 60870-5-104** | `Comm/Pub/Rly_IEC104.c` | ✅ | | | ModBus | `Comm/Pub/Rly_ModBus.c` | ⬜ | | | CDT | `Comm/Pub/Rly_Cdt.c` | ⬜ | | | 国标电度 `Whm_GB` | `Comm/Pub/Whm_GB.c` | ⬜ | | | IEC 61850(MMS / GOOSE / SV) | `Comm/Pub/struct61850.h`、`GlobalUnit/SttnLvlGoose.c` | ⬜ | | | CAN | `Comm/Pub/Rly_Can.h` | ⬜ | | 子站侧(对下) | IEC 101(含 ABB / 西门子 / 无优先级变体) | `Comm/Mon/Rtu_IEC101*.c` | ⬜ | | | IEC 104(含调试 / RIS 变体) | `Comm/Mon/Rtu_IEC104.c`、`Rtu_Dbg104.c`、`Rtu_RIS104.c` | ⬜ | | | BALA101 / ModBus | `Comm/Mon/Rtu_BALA101*.c`、`Rtu_ModBus.c` | ⬜ | | 加密 | SM2 / SM3 / ECDSA | `Comm/Ecc/` | ⬜ | > **排期判断**:104 已对齐;**对下 ModBus RTU 与 101 是"配网终端"的必要项**(接一次开关 / FTU), > 与 RS-485 簇同批解冻;61850 依赖以太网多播与 MMS 栈,单核 M4 资源上需单独评估,属最末批。 #### 2.5.7 差异归因与结论 | 归因 | 涉及条目 | 解除方式 | |---|---|---| | **外部资源**(标准源、端子变比) | 定值标定、保护判据解卡 | 两点标定(§9.2 第 4 步) | | **硬件缺失**(X3 / X4) | 开入 138、开出 31、电度 8、同期、三相通道 | V0.2 硬件 | | **立项未排** | 录波、多规约、SNTP、定值铺开 | 按 §9.2 顺序 | | **算力 / 形态**(X1 / X2) | 双核分工、VxWorks | 不解除。**屏板链路已从范围外项移入范围内待做**(规格 §5.16,执行规划 docs/任务规划/屏板链路.md):主机侧链路与参考 1:1 同构,不属"替代方案" | > **结论**:本产品与参考产品的差距**全部是"进度"而非"范围"**——没有一项是"参考有而我们不做"。 > 已对齐的是**软件骨架**(采样节拍、相量算法、定值模型、104 上行、SOE 落盘、自检闭锁); > 未对齐的是**点表规模**(249 / 31 / 138 / 31 / 33 / 65 vs 40 / 9 / 0 / 0 / 7 / 0) > 与**独立子系统**(录波、电度、多规约)。因此 §9 统一表述为"复刻进度",不再出现"不做"。 --- ## 3. 系统总体架构 ### 3.1 六层分层架构 ![STM32F4-Base 项目分层架构](../images/项目架构总览.svg) > 上图由 `tools/arch_diagram/gen_arch_svg.py` 生成(手绘 SVG,零连线交叉);改架构后编辑脚本顶部 > `ROWS` 数据重跑即可,源数据与下表一致。 分层原则:**上层只依赖相邻下层(单向)**;**① 业务层为顶层**(仅 `net_cfg`,被 UI 与基础服务共用), **② UI 层与 ③ 基础服务层同级并列**。 | 层 | 目录 | 职责 | 关键模块 | |---|---|---|---| | ① 业务层 | `Drivers/BSP/NETCFG/` | 顶层共享配置(被 UI 与基础服务共用) | `net_cfg` | | ② UI 层 | `Drivers/BSP/UI/` | LVGL 界面(与 PC 模拟器共用源码) | `ui_port` `ui_menu` `ui_menu_view` `ui_menu_tab` `ui_content_page` `ui_page_*` `ui_net_sink` `ui_font_cn_12` `ui_deco` | | ③ 基础服务层 | `App/`、`Drivers/BSP/NET/lftpd/` | 应用侧服务、采集算法与协议编排 | `app_main` `sys_events` `app_cfg` `task/*` `algo/*`(语义取数与有效性~保护判据)`protocol/iec104/*` `log/soe_store` `lftpd` `time_sync` | | ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `FatFs` `nand_ftl`(dhara) `LVGL` `sys_clock` | | ⑤ 驱动层 | `Drivers/BSP/{CH395F,GD5F2GQ5UE,TPAFE5160,SD2506,RS485}/` | 芯片寄存器 / 时序 / 中断 | `ch395f` `gd5f2gq5ue` `tpafe5160` `sd2506` `rs485` | | ⑥ 内核 / HAL | `Middlewares/`、`Drivers/STM32F4xx_HAL_Driver/`、`Drivers/CMSIS/` | 任务调度、外设 HAL、启动 | FreeRTOS V10.3.1 · STM32 HAL · CMSIS | | ⑦ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART4 / USART1 / DMA / GPIO / TIM7 | ### 3.2 运行时任务模型 **九个** FreeRTOS 任务(`Src/freertos.c` 创建,任务体在 `App/task/`、`App/sync/`): | 任务 | 栈(words / B) | 优先级 | 职责 | |---|---|---|---| | `defaultTask` | 1280 / 5120 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 卷健康探针/自愈 → `net_cfg_load` + 定值/运行参数/通讯参数装载(**配置引擎属主**)→ 置 `STORAGE_READY` → 日志落盘(唯一写者)+ 2 s 配置轮询 + 各模块脏位落盘(`prot_set` / `run_par` / `comm`)+ SOE 排空 + LED 闪烁 + 栈水位观测 + **看门狗唯一喂狗者** | | `netTask` | 960 / 3840 | Normal | 等存储 → `net_stack_bring_up()` → 置 `NET_READY` → `net_poll` 轮询 + CH395F 操作串行化(消息队列执行者) | | `ftpTask` | 1024 / 4096 | Normal | 等网络**且**存储(`WaitAll`)→ `lftpd_start("/", 21)` → 单客户端会话循环 | | `testTask` | 2048 / 8192 | BelowNormal | 测试套件入口(`test/board/test_config.h` 选择)。**产品构建不创建**(未选套件时立即 `vTaskDelete` 回收其 8 KB CCM 栈) | | `uiTask` | 4096 / 16384 | BelowNormal | 等网络 → `ui_port_init`/`ui_menu_init`/`ui_standby_init`(空闲 60 s 切待机屏)/`ui_net_sink` → 10 ms 刷新 + 数据注入 | | `timeSyncTask` | 640 / 2560 | Normal | 等网络 → TCP :8888 收 PC 推送时间 → 写 RTC | | `adcTask` | 640 / 2560 | AboveNormal | **采集主线唯一消费者**:每拍 `adc_input_poll()`(TPAFE 快照 + ADC1 DMA 帧)→ `adc_health` 有效性 → `phasor_run()` 相量 → `freq_run()` 测频 → 每 200 ms `meas_tick()` 发布遥测快照 | | `iec104Task` | 640 / 2560 | Normal | 等网络 → TCP :2404 监听 → 104 链路状态机(总召/变位/周期/遥控/定值/对时)+ 断链重听 + 网络代次自愈 | | `protectTask` | 512 / 2048 | Normal | **20 ms 判据节拍**:采样故障闭锁判定 → 定值校验和巡检 → `prot_elm_run()` 元件判据 → 动作边沿投递 SOE(**不做 NAND I/O**) | > `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 **56 KB** `heap_4` 堆 > (CCM)分配;CCM 水位 87.9%(<92% 门禁)。 > 任务表与 `Src/freertos.c` 的 `osThreadAttr_t` 一致,由 `scripts/gen_task_table.py` 生成并门禁校验; > **栈配置与实测余量**见 §7.3,改栈前后必须重跑 `scripts/stack_report.py`。 > > ⚠ **节拍同源**:TIM3 更新事件(1 kHz)同时触发 TPAFE `CONVST` 与(经 TRGO)ADC1 一轮 9 通道扫描, > 并在 ISR 内唤醒 `adcTask`——采集节拍与任务唤醒**不允许**各自计时。 > ⚠ **ADC1 必须在挂 DMA 的瞬间处于关闭态**:`app_main_init()` 步骤 4b-pre 的 `adc1_disable()` 不可删、不可后移, > 否则 DMA 乒乓缓冲整体旋转 2 格(表象与"映射表写错"一致)。证据见 > [`docs/子系统设计/ADC采样节拍设计说明.md`](../子系统设计/ADC采样节拍设计说明.md) §4.5。 ### 3.3 就绪屏障(Event Flags) ![就绪屏障(Event Flags)](../images/就绪屏障.svg) 用 CMSIS-RTOS2 Event Flags(`sys_evtHandle`)表达"子系统就绪",取代魔法延时与全局标志轮询。 生产者与消费者互不知晓,只经事件组解耦;新增子系统只需定义新 bit + 各自 wait/set。 | 位 | 名称 | 类型 | 语义 | |---|---|---|---| | bit0 | `SYS_EVT_NET_READY` | 状态位 | netTask:`net_init` + PHY 稳定完成 | | bit1 | `SYS_EVT_STORAGE_READY` | 状态位 | defaultTask:FTL/FatFS 挂载 + 配置就绪 | | bit2 | `SYS_EVT_FTP_READY` | 状态位 | ftpTask:`lftpd_start` 完成 | | bit3 | `SYS_EVT_TIMESYNC_READY` | 状态位 | timeSyncTask:首次时间同步完成 | | bit4 | `SYS_EVT_NET_RECONFIG` | 命令位 | UI:请求立即应用新网口配置 | | bit5 | `SYS_EVT_CFG_SAVE` | 命令位 | UI:网口配置保存请求(defaultTask 消费并写盘) | | bit6 | `SYS_EVT_CONSOLE_TX` | 命令位 | 控制台单写者:生产者入环形缓冲后置位 | | bit7 | `SYS_EVT_PROT_SET_SAVE` | 命令位 | 保护定值落盘请求:104 检测到定值脏后置位,defaultTask 原子写 `/prot_set.bin` | | bit8 | `SYS_EVT_RUN_PAR_SAVE` | 命令位 | 运行参数段落盘请求:104 写运行参数后置位,defaultTask 整档外科保存 | > ⚠️ **铁律**:状态位(bit0~3)表达**持久状态**,任何消费者都**不许消费**——必须 > `osFlagsWaitAny|osFlagsNoClear`。命令位(bit4~8)表达"请做一件事",必须**恰好被消费一次**(消费型等待)。 > 历史事故:就绪位被先到的等待者消费,导致 `ftpTask` 的 `WaitAll(bit0|bit1)` 永不成立、21 端口从未监听(静默)。 ### 3.4 启动时序 ```mermaid sequenceDiagram autonumber participant M as main participant A as app_main_init participant K as osKernelStart participant D as defaultTask participant N as netTask participant S as adcTask / protectTask participant F as ftpTask participant U as uiTask participant I as iec104Task / timeSyncTask M->>M: HAL_Init + CubeMX 外设初始化
(SPI1/SPI2/I2C1/UART4/USART1/DMA/GPIO/TIM3/TIM7) M->>A: app_main_init() Note over A: 时间子系统优先,使后续日志带时间戳 A->>A: sd2506_init() → sys_clock_init()(墙钟基准) A->>A: rs485_init() → gd5f2gq5ue_init()(BBT 扫描) A->>A: tpafe5160_init() → adc_input_init()(语义映射自检) A->>A: 配置段注册(adc_cal / run_par / prot_set / comm)→ 定值/元件初始化 A->>A: adc1_disable() → ADC1 挂 DMA(4b-pre,顺序不可变)→ 启动 TIM3 1 kHz 节拍 A-->>M: "App init OK" M->>K: osKernelStart() K->>D: 调度 D->>D: f_mount(无卷则 f_mkfs FAT32)→ 卷探针/自愈 → 配置装载
→ 定值整表原子装载(损坏则保出厂默认 + 自检留痕) D-->>N: set SYS_EVT_STORAGE_READY S->>S: adcTask:等 TIM3 节拍 → 采集 → 相量/测频 → 200 ms 遥测快照 S->>S: protectTask:20 ms 判据(未标定/采样故障则停走) N->>N: wait STORAGE_READY(Any|NoClear)→ net_init(ip,mask,gw,mac)
→ PHY 强制 100M 全双工 → 等链路 ≤3s N-->>F: set SYS_EVT_NET_READY F->>F: wait NET_READY|STORAGE_READY(All|NoClear)→ lftpd_start("/", 21) U->>U: wait NET_READY(Any|NoClear)→ ui_port_init → ui_menu_init → ui_net_sink_start(7003) I->>I: wait NET_READY(Any|NoClear)→ TCP listen :2404 / :8888 ``` > **顺序铁律**:① 配置段注册必须**先于** `cfg_file_load`(否则 FTP 上传的段不生效); > ② `adc1_disable()` 必须在挂 DMA 之前;③ `osKernelStart` 之前禁调任何内核 API(含 `osDelay` / 队列)。 ### 3.5 线程间通信 | 机制 | 对象 | 用途 | |---|---|---| | Event Flags | `sys_evtHandle` | 启动就绪屏障 + 命令位(§3.3) | | Message Queue | `netMsgQueue`(深度 8,`net_msg_t`) | 所有 CH395F 操作经队列**串行化**到 netTask 执行,保证 SPI 单线程访问 | | 双区 + 序号(无锁) | `phasor` 相量区、`meas` 遥测快照 | 写侧填满计算区后单字节翻转活跃索引;读者先取索引再整份拷贝,`seq` 判"是否换帧" | | SPSC 环(无锁) | `soe_store`(深度 8) | protectTask 只投递动作边沿,defaultTask 排空落盘——**保护路径零 NAND I/O** | | 脏位 + 事件位 | `prot_set` / `run_par` / `comm` | 写入方置脏;104 侧 peek 后投递事件位,配置属主(defaultTask)落盘,失败保留脏位下轮重试 | | 单字请求标志 | `prot_elm_request_reset()` | 跨上下文复归请求(104 遥控 YK11 在 iec104Task 落请求,protectTask 下一拍消费) | --- ## 4. 硬件规格 ### 4.1 主控与内存 | 项 | 值 | 说明 | |---|---|---| | MCU | STM32F407ZGTx | Cortex-M4(FPU),LQFP144 | | 主频 | 168 MHz | HSE 8 MHz → PLL M=4 / N=168 / P=2 | | Flash | 1 MB(0x08000000) | 片内 | | SRAM | 192 KB(128 KB 主 SRAM1+SRAM2 + 64 KB CCM) | 链接器映射 128 KB 主 SRAM;CCM 用于 FreeRTOS 堆(任务栈) | | RTOS | FreeRTOS V10.3.1(CMSIS-RTOS2) | HAL 时基 TIM7,tick = 1 ms;NVIC 4 位抢占,外设中断优先级 ≥5 | | 工具链 | Keil MDK-ARM v5(ARMCC v5) | 工程锁 `uAC6=0`,**不支持 armclang** | | CubeMX | 6.17.0 + STM32Cube FW_F4 V1.28.3 | 与 `.ioc` 记录一致,再生成须用此组合 | ### 4.2 外设与引脚分配 > **权威来源**:V0.1 网表分析报告 + `Inc/main.h`(代码已迁移至 V0.1)。下表为 **V0.1 硬件**的引脚。 | 外设 | 接口 | 引脚 | 说明 | |---|---|---|---| | **CH395F 以太网** | SPI2 | PB12 CS / PB13 SCK / PB14 SDO / PB15 SDI | 10/100M 以太网控制器;PHY 强制 100M 全双工;**SPI 分频系数必须为 8**(PCB 设计约束) | | **GD5F2GQ5UE NAND** | SPI1 | PE0 CS / PB3 SCK / PB4 MISO / PB5 MOSI / PB8 WP / PE1 HOLD | 256 MB SPI NAND(2 Gbit) | | **TPAFE5160 ADC** | 并行 16 位 | PG0~PG15 数据 / **PC6 FRSTDATA** / **PC7 BUSY**(EXTI) / **PC8 RD** / **PC9 CONVST** / **PA8 OS2** / **PA11 OS1** / **PA12 OS0** | 高精度多通道 ADC | | **SD2506 RTC** | I2C1 | PB6 SCL / PB7 SDA | 提供墙钟基准与日志时间戳 | | **USART1(调试串口 / U5 直连 TTL)** | UART | PA9 TX / PA10 RX | 115200 8N1;现为 printf 日志 + `@test` 采集通道。**端口重排后改作屏板链路**(700 kbaud,§5.16.2);PA10 双向均已接线 | | **UART4(RS-485)** | UART | PC10 TX / PC11 RX / **PA15 DE** | 半双工通信(V0.1 轮换后 RS485#4 = UART4,`docs/硬件平台/V0.1_网表详细分析报告.md` §4.2) | | **USART2(RS-232 #3)** | UART | PD5 TX / PD6 RX | 经 U10 隔离 → U13 TPT3232E → CN1;**端口重排后为日志口** | | **UART5(RS-232 #1)** | UART | PC12 TX / PD2 RX | 经 U9 隔离 → U13 → CN1;备选日志口 | | **状态 LED ×6** | GPIO | PC5 / PB0 / PB1 / PB2 / PF11 / PF12 | **低电平点亮** | | SWD | — | PA13 SWDIO / PA14 SWCLK | V0.1 已修复 V0.0 的 SWCLK 引脚缺陷 | | 调试口 | — | CN2(XH-3A,1=SWDIO/2=SWCLK/3=GND) | V0.1 由 H2 排针改为 XH 端子 | > ⚠️ **V0.0 / V0.1 差异**:V0.1 相比 V0.0 做了**接口裁剪(删 8080 LCD 与 8 键)+ U17 大面积引脚重排 + > ADC 前端通道置换(17 路中 13 路变化)+ 调试口改型**。固件 BSP 与 ADC 通道映射表**必须按 V0.1 重映射**; > 详见 `docs/硬件平台/V0.1_网表详细分析报告.md`。 ### 4.3 存储器件 | 器件 | 容量 | 组织 | 软件栈 | |---|---|---|---| | GD5F2GQ5UE | 2 Gbit(256 MB) | 2048 块 × 64 页 × 2048 B | FatFS(FAT32)→ dhara FTL(磨损均衡 / 坏块管理 / GC)→ NAND 驱动 | | SD2506 | — | I2C1 | RTC(时间/温度/电池诊断) | - **BBT 持久化**:坏块表存于保留块池(顶部 4 块),不交给 dhara 管理。 - **单写者纪律**:`defaultTask` 拥有存储;`netTask` 只读 RAM 配置副本,不触卷。 - **FatFs 约束**:`FF_USE_LFN=0`(严格 8.3 文件名);`FF_FS_REENTRANT=1` + `ffsystem.c` 互斥钩子(超时 1 s);`FF_FS_NORTC=0`(`get_fattime` 直引用墙钟)。 ### 4.4 硬件版本与兼容性 | 版本 | 状态 | 说明 | |---|---|---| | V0.0 | 历史 | 含 8080 LCD、8 按键;存在 2 处设计缺陷(SWCLK 引脚错误致 SWD 不通、ADC17 落在非 ADC 引脚) | | **V0.1** | **当前** | 架构与 V0.0 一致;裁剪 LCD/按键、U17 引脚重排、修复上述 2 处缺陷;固件已迁移 | | V0.2 | 规划关注 | 新增关注项:VCAP 电容值未标注、PC13/14/15 子板 LED 3 mA 限流、SPI1 无法双落点 | ### 4.5 平台约束 | 约束 | 内容 | |---|---| | **CubeMX 生成文件** | `Inc/`、`Src/` 中带 `USER CODE BEGIN/END` 标记;自定义代码**只能**写标记区内 | | **生成区外的定制** | 仅限 `Src/iwdg.c` 延后启动、4 个 Fault Handler 实体(`scripts/check_cubemx_survival.py` 审计兜底) | | **`osKernelStart` 前** | 禁止任何 FreeRTOS / CMSIS-RTOS API 调用 | | **BOM / 编码** | 部分源文件带 BOM(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`),被工具改写丢 BOM 会导致 ARMCC 按 CP936 解码、中文字面量编译失败 | | **SDIO** | V0.1 主板 SDIO 固定引脚组被 TPAFE/UART 全部占用 → **无 SDIO 能力**(当前设计不受影响,FatFs 卷在 SPI NAND 上) | | **新源文件** | 必须双端注册(Keil `.uvprojx` + `simulator/CMakeLists.txt`),否则不参与编译 | ### 4.6 V0.1 硬件能力边界(对产品的硬约束) 下列五条是**硬件事实**,不是软件缺陷;§5 的功能范围与 §9 的"等条件"项均由它们推导: | # | 约束 | 具体内容 | 产品影响 | 解除条件 | |---|---|---|---|---| | H1 | **双转换器无共同采样触发** | 8 路电气量分属 TPAFE5160(16 位、8 通道同步、±5 V 双极性)与 STM32 ADC1(12 位、顺序扫描、0~3.3 V 单极性)两片;两片无共同触发,STM32 侧 9 通道一轮约 67 µs | 跨转换器的**相位 / 功率 / 功率因数 / 对称分量 / 三相同时刻判据**一律不成立 → 方向元件、PT/CT 断线、三相过流、零序过流延后 | **V0.2 硬件**:8 路电气量全部接 TPAFE(与备用路对调串联电阻,料号不变,走线级改动);固件只改映射表内容 | | H2 | **STM32 侧无中点偏置** | 单极性 0~3.3 V,交流量负半周削顶 | `UB/UC/IB/IC/IN` 无有效值/相量,只能读码值**监视**;104 对应点恒置 IV | V0.2 补偏置 | | H3 | **无出口继电器与开入回路** | 无跳闸/告警出口、无硬压板与开入遥信 | 保护动作只体现为**遥信 + 日志 + SOE**;104 遥控 `YK1~4/13/14` 否定确认 | 开入开出独立立项 + 硬件 | | H4 | **无本地屏与按键** | 160×160 单色屏仅存在于远端客户端 | UI 以 TCP :7003 推流 + 远端按键回注(§8) | 产品形态即如此,不解除 | | H5 | **无 SDIO** | SDIO 固定引脚组被 TPAFE/UART 占用 | FatFs 卷只能落 SPI NAND(当前设计不受影响) | 不解除 | > **量纲口径**(由 H1/H2 派生):在端子/子板变比确认与两点标定完成前,遥测与保护读数的单位是 > **ADC 引脚电压**(`eng=0`),不是二次值;此时 `ADC_CAL_F_VALID` 未置位, > **保护判据整体停走**(§5.12)。这是刻意设计,不是"保护不动作"的缺陷。 --- ## 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)+ 远端菜单导航可用; 屏板实物接入后 275 ms 节拍出图 + 状态灯正确(§5.16.10 第 8~15 条)。 **FR-UI-2 菜单与内容页** - 需求:42 节点层级菜单(最大 4 级)+ 内容页(装置信息/交流量/直流量等)。 - 实现:三层拆分(数据层 `ui_menu_tab` 生成物 / 逻辑层 `ui_menu` 17 B 导航状态 / 显示层 `ui_menu_view` 级联框);内容页框架 + 页面插件描述符。 - 判据:`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**。 - 旧口径「2:1 分压 → 305.18 / 1611.3 µV/码」**作废**(那是芯片手册 ±10 V 档 LSB,不可混用)。 - 剩余未知量只有**子板/端子板变比**,因此系数只能推到"µV/码 @ 引脚"(§4.6 量纲口径)。 - 拓扑图 [`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 断线、涌流制动、重合闸、故障隔离——原因见 §4.6 的 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——**软件面已满足** (空态与四个范围标签由 60 张像素黄金钉住;模拟器侧这些文件没有写入方,只拍到空态)。 真记录翻页、掉电后保持、清除记录后归零、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` 起的遥测段(段基址见 §2.5.3);支持背景/总召、 周期(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 | ⬜ 未实现(录波未立项) | **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 区) | 规模型 | | 文件传输(目录 / 读文件 / 录波) | 录波子系统未开始 | 规模型 | | 固有参数 `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~10` | 映射为逻辑键值写入 `g_tKey` | | 网络显示总线(镜像) | `RDisp_Proc_SaveKey` → `g_tRemoteKey` | 键值位掩码 1 B | 写入 `g_tRemoteKey` | | 汇合点 | `Key_Read()`(`Key.c:199`) | — | **先取远程、再被本地覆盖**——两键同时挂起时**本地(串口)优先** | **串口通道的物理层与协议,交付源码里看不到**——屏板侧的 5 个入口全是**未实现的外部符号** (`KeyGet` / `WriteScreenByByte` / `LCD_LED_Update` / `getRs485Key` / `anyNewKey`), `Release/` 下的 `.o` 与 `.out` 由更完整的树编译而来。能确定的只有**调用契约**(协议本身已由屏板源码补齐,见 §5.16.2): > 注:上表"物理键号 `1~10`"是**参考主机侧的假设**。屏板固件实测只有 **7 个物理键**, > 键值 `1~9` 有入口、`10` 无入口,且 `9`/`10` 语义与参考的 `F1`/`F1|ENT` **不同**(§5.16.3 ②)。 | 契约项 | 已确定的内容 | 依据 | |---|---|---| | 下行粒度 | 3200 B 整帧 + 4 B LED,**一次调用一帧**(无分片参数) | `LCD_LED_Update(ptr,len,ledbuf,ledbuflen)` 签名 | | 下行长度 | `len = 3200` 为实参,非推导 | `Disp_Pub.c:8295-8298` | | 上行粒度 | 单字节物理键号,`0` 表示无键 | `getRs485Key()` 返回 `uint8_t`,`Key.c:176` 判 `key == 0` 即返回 | | 事件语义 | 有"新键事件"与"清除事件"两个动作 | `anyNewKey()` / `ClearNewKeyEvent()` | | 屏板地址 | 板件槽号 **0**(`CN_SLOT_DISP`) | `Key.c:8` 注释"显示板插槽号固定为0" | | 按键消息 TAG | `0xC22C`(`CN_MBUS_TAG_KEY`,注释"与开入 TAG 相同") | `Key.c:9`(该宏在本文件内**未被引用**,属备用/历史路径) | | 面板指示灯 | `WritePanelLED(pbyLedFlag)` 已在 BSP 中实现(`SR1200_peri.c:362`) | 与屏板同一块面板 | | 背光 / 对比度 | `Lcd_BackLight()`(`Lcd_BKOn/Lcd_BKOff` 走 GPIO)、`Lcd_SetContrast(值, 亮)` | `omapl138_lcd.c:240-247`、`Disp_Interface.c:3281` | | 屏板初始化 | `Lcd_init_display(0xC1F00000)` 由 `LocalDisp_StartTask()` 调用,**不经过任何 UART 打开动作** | `Disp_Main.c:56` | > ⚠ **原"四项未知"已由屏板固件消解**(2026-10-28,实测值见 §5.16.2): > ① 波特率 **700000 8N1**;② 无帧头,**整帧定长 + 尾部 CRC-16/MODBUS**; > ③ **软件 DE**(屏板侧 PA1,低 = 发送);④ **不分片**——一帧一整屏,屏板召唤、主机应答。 > 上表保留作为**取证过程**,说明"只看参考主机侧源码能确定什么、不能确定什么",不是待办。 #### 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 在两种候选时钟下都零误差**(2026-10-28 更正分频算法):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 %**。 > > ⚠ **旧版本本节写的"`42 000 000 / 700 000 = 60` → `USARTDIV = 60.0`、`BRR = 0x3C0`"是错的**—— > 漏掉了公式里的 16 倍过采样。照 `0x3C0` 写下去实际波特率是 **43 750**,不是 700 000。 > 若改时钟树或改用 HSI,必须重算——700 kbaud 下 2 % 误差已足以丢帧。 > > **端口裁定(用户决定,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), > 迁移在屏板链路跑通之后做,见下方"主机侧端口重排"。 > > ⚠ **"PA10 未接线"这条旧结论已撤销(2026-10-28 用户确认)**:`App/log/console_log.c` 与 > `docs/子系统设计/控制台单写者日志设计说明.md` §12 里的"PA10 悬空"是 2026-10-02 的**归因推断**, > 不是实测;PA10 一直是接线的。所以屏板链路**不需要额外飞线**。 > > 但 ORE 锁存的**真正充分条件与引脚悬不悬空无关**——**RX 使能了却从不读 `DR`**:线上只要出现 > 任意一个字节或一个噪声沿,`RXNE` 置位后没人读,下一个事件就把 **ORE 锁存**;而 ORE 只能用 > "先读 SR 再读 DR"清除,于是**永不自愈**。这条对本实现有两条硬要求: > ① 屏板链路要用 RX,就必须真的消费 `DR`,并在 `HAL_UART_ErrorCallback` 里**清 ORE 后重挂接收** > (否则一次噪声就永久锁死,症状与"屏板不回帧"一模一样);② 新日志口设 `UART_MODE_TX` > 仍然是对的——那是从机制上把 RX 摘掉,与 PA10 接不接线无关。 **帧 A:召唤 / 按键(屏板 → 主机,固定 6 B)** | 偏移 | 长度 | 字段 | 取值 | |---|---|---|---| | 0 | 1 | 从站地址 | 固定 `0x01`(**主机的从站地址**,不是屏板地址) | | 1 | 1 | 功能码 | 固定 `0x03`(读保持寄存器) | | 2 | 1 | 命令类型 | `0x00` = `KEY_CMD`(按键事件,**立即**召唤)
`0x01` = `RT_CMD`(周期刷新,275 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`、查表法 `auchCRCHi[]`/`auchCRCLo[]`), **只覆盖前 4 字节**,**高字节在前**(`cmdbuf[4] = crc>>8`、`cmdbuf[5] = crc`)。 **帧 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)
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]`,**高字节在前** | > ⚠ **帧长以代码为准:4208 B,不是 3208 B。** 屏板 readme 与 `BMP_LEN` 都写"3208 字节", > 但接收侧实参是 `Check_CRC(DR, UART_RX_LEN)`,`UART_RX_LEN = 4208` → CRC 取 `DR[4206]/DR[4207]`、 > 覆盖 `DR[0..4205]`;`rs485.h` 注释亦为"接收缓冲区长度,**与 Modbus 数据帧长度一致**"。 > 若主机只发 3210 B,屏板会在 `DR[4206]` 处读到陈旧字节,**CRC 必失败并静默丢弃**。 > 有效载荷只有前 3208 B,其后 998 B 为凑齐 CRC 窗口的填充。**此项须台架实测复核后固化。** > ⚠ **应答帧不是合法 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` | **275 ms** | 无按键时每 275 ms 召唤一次 | | 应答超时 | `RS485_ACK_COUNT_MAX = 40` | **200 ms** | 发帧后 200 ms 未收齐 → 本次超时 | | 连续超时 | `RS485_ACK_OVERTIME_MAX = 5` | 5 次 | 连续 5 次超时 → 强制允许重发(**不重启串口**,重初始化被注释) | | 屏板自复位 | `sysRunTime > 1 000 000` | **≈5000 s(83 min)** | 屏板**自己**无条件 `HAL_NVIC_SystemReset()` | **200 ms 是硬指标**:700 kbaud 8N1 → 10 bit/字节 → 4208 B ≈ 42 080 bit ≈ **60.1 ms**, 占预算 **30 %**,留给"取显存快照 + 组帧"约 140 ms,宽裕。 但若主机把整帧拆成多次 `HAL_UART_Transmit` 且中间被高优先级任务抢占,很容易越线—— **建议一次性 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) | ✅ **调试日志**(115200,屏板链路之后迁移) | | UART5 | PC12 / PD2 | **RS232#1**(U9 隔离 → U13 → CN1) | 备选日志口,与 USART2 二选一 | | 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 一致,**无需摘除**。 **这次重排带来两件事,必须在任务里体现:** 1. **日志口迁移不是一行改动**:`App/log/console_log.c` 里 `huart1` 出现 5 处(含 `huart1.Instance == NULL` 的空指针短路判据),自愈函数 `con_uart_reinit()` 还直接写 `USART1_IRQn`、`__HAL_RCC_USART1_CLK_*`、`MX_USART1_UART_Init()`。建议收口成**一处可配置端口宏**, 而不是逐处散改。 2. **新日志口必须设成只发不收**(`UART_MODE_TX`):USART1 控制台之所以要背一整套僵尸通道自愈, 根源就是"RX 悬空 + RX 使能 → ORE 锁存 → HAL 轮询 TX 恒快速失败"。日志口不需要收, 就别再开 RX,否则等于把同一场病搬到新口上。 > ⚠ **TTL 直连没有 DE**:主机侧不再有半双工方向控制,DE 极性、建立时间、等 TC 收尾全部不适用。 > 只有选"USART1 引脚 → 外部 485 模块 → 屏板 A/B"这种接法时才需要处理外部模块的 DE, > 此时**极性由那个模块决定,不可照抄屏板**(屏板的"低=发"是它自己那套收发器的接法)。 > ⚠ 主机 RX 只需吃下 **6 B 召唤帧**(不像屏板要吃 4208 B):16 B 缓冲 + 空闲中断断帧就够。 > TTL 直连**没有自发自收**问题(TX/RX 是两根独立线);只有走外部 485 模块时才会回灌。 #### 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"的一次性操作(建议放在屏板联调/出厂配置流程,不进常规刷新循环)。 **⑤ 屏板自身的健壮性机制** | 机制 | 实现 | 对本产品的影响 | |---|---|---| | CRC 错计数 | 连续 ≥3 次错 → 计数清零(**重初始化串口被注释掉**) | 错帧静默丢弃,链路**不会自愈**,只能靠下一次召唤重试 | | 应答超时 | 200 ms × 连续 5 次 → 强制允许重发 | 主机长时间不应答**不会锁死**屏板,恢复后自动续上 | | 系统自复位 | `sysRunTime > 1 000 000`(≈5000 s)→ `HAL_NVIC_SystemReset()` | **每 83 分钟屏板重启一次**,约 1 s 空窗 | | 看门狗 | 5 ms 定时器内 `Clear_Watchdog()` | 主循环卡死 → 屏板复位 | | DMA 接收 | 屏板提交记录明写"DMA 接收的问题没有解决" | 长帧接收可靠性**未经充分验证**,台架必须压测 | > ⚠ **5000 s 周期性自复位必须被主机容忍**:复位期间屏板约 1 s 不响应召唤。 > 主机的链路监视**不能**把这种周期性空窗判成"屏板掉线",更不能联动告警/保护。 > 判据建议:连续 **≥ 10 s** 无任何应答才置"屏板离线"。 **待与屏板固件方确认的 5 项**(不影响本产品先开工,但验收前必须闭环): 1. 线上一帧到底是 **4208 B**(代码实参)还是 **3210 B**(readme + `BMP_LEN`)——决定主机发多少字节。 2. `Check_CRC` 用缓冲区容量而非实际接收长度,**短帧会被误判**;是否计划改为传 `Size`。 3. 键值 9 / 10 是否要改成 `F1` / `F1|ENT`(对齐参考),还是保持"复位 / 工厂"语义。 4. `key_configs[]` 是否要补 ADD / DEC 物理键(当前 7 键,枚举 11 项)。 5. 屏板 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 在参考自己的构建里就是死字段**: > > 1. `RemoDispBus.h:46` `CN_RDISP_TYPE_MONO = TRUE` → `CN_RDISP_LCD_TYPE = CN_MODE_GREY_MONO`、 > `CN_RDISP_MEMSIZE = (X>>3)*Y`,即**链路显存本身就是 1 bpp**,不是彩色显存。 > 2. `RDisp_Form_InitInf` 里三个 RGB 值是**编译期常量** `CN_RGB_RED/GREEN/BLUE`, > **不是逐帧数据**,每次召唤都发同样的 12 B。 > 3. `RDisp_LcdColor_To_Mono()` 对 `CN_MODE_GREY_MONO` **直接 `return TRUE`**,不做任何转换; > 真正干活的 `RDisp_Lcd16B_To_Mono()`(RGB565 → 单色)挂在 `CN_MODE_COLOR_16B` 分支, > 本构建**永不进入**,是死代码。 > > 本产品的屏板同样是**单色 1 bpp**(`DTU-LCD` 的 `ScreenPrintf()` 只吃 1 bpp 点阵), > 所以这 13 B **既无接收方、也无语义**。**整段删除,不对齐多色协议**:29 − 13 = **16 B**。 > 这不是"少实现"——**参考能力全集里的多色分支连参考自己都没启用**(§2.4.1)。 **键值是位掩码,不是枚举**——可组合下发:`CN_KEY_F1|CN_KEY_ENT = 0x24`(`Key.c` 物理键号 `10` 即此值)。 `CN_KEY_ADD=0x82`、`CN_KEY_SUB=0x84` 只在 RS-485 通道使用,不进 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`)。
⚠ 本产品**不照抄**:屏板固件自己按 **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 兜底 | **无**(只有 7003 网络通道) | **新建**,按 §5.16.2 逐字节实现,见 §9.13 | | 12 | 双出口并发 | 同一份显存同时喂串口与网络,互不阻塞 | 只有网络一个出口 | 随第 11 条一并实现 | | 13 | 屏板状态指示灯 | 20 路(ID 0 电源常亮 + ID 1~19 位掩码),**4 B 小端**随每帧下发 | **无**;主板另有 6 路 GPIO LED(`LED1~6`) | **新建**;"位 → 业务状态"映射须自定(参考源码无此表,§5.16.3 ①) | | 14 | 背光 / 对比度 | 主机 BSP 有 `Lcd_BackLight` / `Lcd_SetContrast` + 空闲 120 s 熄灭 | 无 | **不做**——屏板固件自管背光,两条路径不能同时启用 | **来自屏板固件的新增约束(参考主机侧源码里看不出来,但对实现是硬约束)**: - **角色反转**:屏板是 **Modbus 主站(主动召唤)**,主机是**从站 `0x01`**;与网络通道"客户端召唤"方向相反。 - **帧长 4208 B**(有效载荷 3208 B + 998 B 填充 + 2 B CRC),不是 readme 写的 3208 B(§5.16.2)。 - **700000 8N1**,主机侧走 **USART1 TTL 直连**(无 DE);若改走外部 485 模块,DE 极性由该模块决定,**不可照抄屏板**。 - **屏板每 ≈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 不是同一口径**——13 B 颜色字段被有意删掉, > 客户端**不得**按 29 B 预留缓冲再截取前 16 B 使用(**从偏移 4 起含义即不同**: > 参考偏移 4 是颜色位,本产品偏移 4 是显存大小的高字节)。 > > 第 11 / 12 / 13 条是**整条链路缺失**,不是报文细节差异——它决定了本产品能否脱离 PC 独立带屏运行。 > > 第 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. 主机上电后,屏板按 **275 ms** 节拍发帧 A(6 B,形如 `01 03 01 xx CRC_H CRC_L`); 主机每帧在 **≤200 ms** 内回帧 B(**4208 B**,CRC 覆盖 `[0, 4205]`),屏板**连续 10 分钟无超时**。 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(`info[4..5] = 0x89 0x45`)→ 屏板写 Flash `0x0803F000` 并显示 2 s; 断电重启后仍显示该 LOGO,且**屏板 LED 状态未被这帧改动**。 --- ## 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。**以参考为准**,差异项按 §9.13 排期收敛。 > > 本节只讲**网络**通道(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 调试串口 | 项 | 值 | |---|---| | 端口 | USART1(PA9 TX / PA10 RX),115200 8N1 | | 用途 | printf 日志 + `@test` 采集通道 | | 配置源 | `.serialtest/config.json`(单一配置源) | | 工具 | 仓库内置 Python 串口助手(`.dsh/skills/serial-assistant/`,常驻 daemon + CLI) | **启动标记(`@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 段全表见 §2.5.3):遥信 `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)等整单元(§2.5.4)。 **链路参数**:k=12 / w=8 / t1=15 s / t2=10 s / t3=20 s;IOA 3 字节、公共地址 2 字节、传送原因 2 字节; 公共地址与端口可配(`[comm]` 段,工厂默认 1 / 2404,改后需重启生效)。 --- ## 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,零丢帧) | 台架 `@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]` 汇总) | 本地(需真机 + 串口) | | 主机侧回归 | 模拟器像素 / 池水位回归 + 真机 `@test` 闭环(含探针) | 本地(模拟器免硬件;`@test` 需真机 + ST-LINK) | **构建 / 测试命令**: ```powershell # 构建(@build):0 错 0 警 & '.venv\Scripts\python.exe' .dsh/skills/keil-build/scripts/keil_build.py # 退出码 0=PASS / 1=构建失败 / 2=环境错误 # 自动化测试(@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/宿主文件) | | 验证 | `@build` 0/0 → `@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 菜单树(42 节点) ![级联框总览](../images/级联框总览.svg) **规模**:菜单节点 **9** 个(含根)+ 叶子节点 **33** 个 = **42** 节点;最大深度 **4 级(L0~L3)**。 **绑定现状**(2026-10-28 实测):**已绑内容页 28 叶 / 未绑 5 叶**(页面注册表 `UI_PAGE_MAP_COUNT` = 28, 与已绑叶数一致);未绑 5 叶 = 无开入硬件 3 + 手动录波 1 + SNTP 1(取舍与重开条件见 docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 页 + 运行报告)已于 2026-10-28 落地绑页。 **根页(主界面)7 个一级条目**:装置信息 / 实时数据 / 参数定值 / 三遥设置 / 装置维护 / 通讯参数 / 记录查询。 **完整层级树**: ```text 主界面 (L0) ├─ 装置信息 [叶子] ├─ 实时数据 ▶ [菜单] │ ├─ 交流量 ├─ 直流量 └─ 遥信量 ├─ 参数定值 ▶ │ ├─ 整定 ▶(参数 / 定值 / 控制字 / 软压板) │ └─ 查看 ▶(参数 / 定值 / 控制字 / 软压板) ├─ 三遥设置 ▶(遥测死区 / 遥测系数 / 遥信类型 / 遥信防抖 / 双点遥信) ├─ 装置维护 ▶(时钟设置 / 强制复归 / 手动录波 / 清除记录) ├─ 通讯参数 ▶(通讯设置 / 网口设置 / SNTP设置) └─ 记录查询 ▶(SOE记录 / 事故记录 / 操作记录 / 保护告警 / 保护启动 / 遥控记录 / 自检记录 / 运行记录 / 运行报告) ``` **三层代码架构**:数据层 `ui_menu_tab`(生成物,`g_menu_tab[42]` 扁平父子索引表,≈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 内容页 **已绑 28 页**(按分组;逐页形态见 [`docs/UI设计/页面补齐规划.md`](../UI设计/页面补齐规划.md)): | 分组 | 页面 | 形态 | |---|---|---| | 装置信息 | 装置信息 | 静态键值行 6 行不分页;副标题"馈线自动化终端" | | 实时数据 | 交流量 / 直流量 / 遥信量 | 数据列表(交流量 24 行 / 每页 8,共 3 页;直流量 3 行单页) | | 参数定值 | 查看 4 页(参数 / 定值 / 控制字 / 软压板) | 只读列表 | | 参数定值 | 整定 4 页(参数 / 定值 / 控制字 / 软压板) | 可编辑(写值 + 置脏 + 保存反馈) | | 三遥设置 | 遥测死区 / 遥测系数 | 可编辑(运行参数段,`0x8030~0x804F`) | | 装置维护 | 时钟设置 / 强制复归 / 清除记录 | 操作页(执行类必须出真反馈,不得"点了没效果") | | 通讯参数 | 通讯设置 / 网口设置 | 可编辑(`[comm]` / `[network]` 段,保存后按各自生效时机) | | 记录查询 | SOE 记录 / 自检记录 / 运行记录 | 记录列表(共用 reader) | | 记录查询 | 事故记录 / 操作记录 / 保护告警 / 保护启动 / 遥控记录 | 记录列表(每记录 2 行、空态显「无记录」;写入点与判据见 App/log/README.md §五类记录) | | 记录查询 | 运行报告 | 只读列表 13 行分 2 页;四个数逐个挂范围标签(自电池周期 / 本次开机),跨开机累计按裁定不做 | **未绑 5 叶**(2026-10-28:记录 5 页与运行报告页已落地绑页,原「当前执行 6 项」清零): 遥信类型 / 遥信防抖 / 双点遥信 = 等开入硬件(§9.1.1「出口与开入」);手动录波 = 等录波子系统 (§9.1.2「录波子系统」);SNTP 设置 = 等 SNTP 对时(§9.1.2「SNTP 对时」)。 - 内容页为**独立全屏屏**,由框架一次性自绘(**零 LVGL 子对象**),`ESC` 返回菜单屏。 - 菜单导航状态**不压层**,返回即原样还原;同一时刻至多一个内容页打开。 - 节点→页映射在逻辑层 `menu_content_page_for(node)`;未绑叶子进入空白终点框(不伪造数据)。 - 数据供给走**注入通道**(rt / par / info / 遥测死区与系数第四表),页面不直接触算法层; 像素黄金用例当前 **60 张**(菜单 8 状态另计;2026-10-28 记录 5 页与运行报告 2 页落地,+7 张)。 ### 8.4 密码与权限 | 项 | 值 | |---|---| | 查看密码 | `000`(授只读权限) | | 修改密码 | `123`(授修改权限) | | 存储 | `ui_pwd_def.h` **编译期**写入,不落 Flash | | 流程 | 受保护叶子先 arm → 开密码页 → 确认一致后回调 → **先关页再续做进入动作**(携授予权限) | ### 8.5 中文字体 - 字体 `ui_font_cn_12.c`:宋体 12px 1bpp **子集**(当前 **218 唯一字 / 313 字形**,2026-10-28 实测; 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. 复刻进度与路线图 > **口径**:本章条目全部来自 §2.5.2 的 🔶 / ⬜ 行——即"参考源码有、本产品还没做或只做了一部分"。 > **"不做"一词在本章不出现**:1:1 复刻口径下没有主动放弃的能力,只有尚未排期的能力。 > 唯二例外是 §2.5.7 标为"不适用"的两条(归一化值上送、主站 IP 白名单),那是**协议语义决定**, > 不是范围裁剪。 > > **命名**:本节条目一律用**语义短语**(编号规范 P-8 语义优先),不发字母+数字号—— > 避免与其它文档的 `S`/`A`/`P` 编号族撞名。 ### 9.1 复刻缺口总表 按**缺口性质**分两类:**阻塞型**(缺外部资源或硬件,排期无用)与**规模型**(无外部依赖,只等排期)。 > 本节是**清单**;**§9.4~§9.12 是逐项明细**——参考源码到底有哪几项、本产品有哪几项、 > 差额具体落在哪个定值 / 哪个点 / 哪个模块上、以及"怎么算做完了"。排期与估算以明细为准。 #### 9.1.1 阻塞型(等外部条件,排期不能解决) | 条目 | 对应 §2.5.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 规模型(无外部依赖,只等排期) | 条目 | 对应 §2.5.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 | 0 | 整子系统 | | **记录体系** | 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 | 只有 7003 四命令(INIT **8 B**);**串口通道 0**;无状态灯下行;无续传、无 `bNeedFlash` 拒绝、无 1 分钟监视、键值 6 个 | 串口通道整条(协议已定版)+ 7003 报文口径 6 项(明细见 §9.13) | > **一句话**:**「标定闭锁」是唯一"再排期也没用"的缺口**(等标准源);「出口与开入」「三相与同期」 > 「电度计量」等 V0.2 硬件;「对下采集」等用户决策;其余规模型条目**全部可在现有硬件上推进**, > 只取决于排期。 > 这张表与 [`docs/任务规划/登记簿.md`](../任务规划/登记簿.md) 一一对应;状态只在登记簿维护, > 本节不另建状态。 ### 9.2 推进顺序 > **顺序唯一权威 → [`docs/任务规划/README.md`](../任务规划/README.md) §2.0**(本表只是产品视角摘要, > 冲突时以该节为准)。 | 顺序 | 工作 | 类别 | 当前状态 | 开始条件 | |---|---|---|---|---| | 1 | UI 补齐(记录 5 页 → 运行报告) | 规模型 | **清单已清空(2026-10-28)** | 六页已落地;余 5 叶属不做项,重开条件见 docs/任务规划/UI补齐.md §5 | | 2 | net 层跨上下文收口 | 平台 | **下一顺位(未开工)** | UI 清单已清空,切换条件已满足;**开工待用户点头** | | 3 | 其余结构与可靠性评估 | 平台 | 排队 | net 收口后 | | 4 | 端子/子板变比确认 → 两点标定 | 阻塞型 | 等外部资源 | 子板实物/原理图、标准源到位可插入 | | 5 | 标准源送检与第三方 104 主站验收 | 阻塞型 | 等标定 | 保护 VALID 置位;送检窗口前 2 日冻结特性 | | 6 | 定值铺开 / 映射铺开 / 遥测铺开 / 软压板铺开 | 规模型 | 等排期 | 标定后(工程量才有意义) | | 7 | 元件铺开与遥测增强 | 规模型 | 等标准源/硬件 | 先解除未标定闭锁;三相类等 V0.2 | | 8 | 录波子系统 → 记录体系 → 多规约栈 | 规模型 | 不排 | 各自重启条件 | | 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`)下保护闭锁 | ### 9.4 定值复刻明细(参考 249 项 → 本产品 40 项) 参考定值表的每一行固定 **21 个字段**:标识 ×2 / 类型(数值 | 枚举)/ 所属单元 / 单位组 / 值类型 / 最小值 / 最大值 / 步长 / 两个标志 / 上下限原始值 ×3(`0xAA` / `0xAAAA` / `0xFF`)/ 104 地址 / 单位 ×2 / 小数位 / 长度 / 显示名 / 结束标记。 本产品对应物是 `App/algo/prot_set.h` 的 `PS_*` 枚举(`PS_ID_COUNT = 40`)。 **40 项的去向必须能对上账**,否则"还差多少"无从谈起: | 去向 | 项数 | |---|---| | 覆盖参考定值项 | **35** | | 本产品本地扩展项(参考无对应行) | **5**(`PS_RET_OVER` / `PS_RET_UNDER` 返回系数,`PS_RET_OF` / `PS_RET_UF` 频率返回系数,`PS_GLOBAL_EN` 全局软压板) | | **合计** | **40** | 因此**定值差额 = 249 − 35 = 214 项**。 #### 9.4.1 23 个定值单元逐单元差额 | 单元 | 语义 | 参考项数 | 本产品覆盖 | 差额 | |---|---|---|---|---| | `WatchDog` | 过流三段 / 零序三段 / 过负荷 / 重载 / 过载 / 过频 / 低频 | 63 | 22 | **41** | | `SYS3` | 系统参数(零飘、死区、变比、防抖、脉宽、FA 模式…) | 45 | 0 | **45** | | `VolTimeRecv` | 电压时间型馈线自动化(失电分闸 / 得电合闸 / 联络) | 17 | 0 | **17** | | `AutoReCls` | 重合闸 | 16 | 0 | **16** | | `SYN` | 同期(FTU 型) | 13 | 0 | **13** | | `OverCurI0` | 接地告警(小电流接地选线) | 13 | 0 | **13** | | `VolPro` | 电压保护(失压 / 过压 / 零序过压) | 13 | 9 | 4 | | `WAVESTART` | 录波启动 | 12 | 0 | **12** | | `MeaLimit` | 遥测越限 | 10 | 0 | **10** | | `OverCurSpeed` | 过流 / 零序后加速 | 8 | 0 | **8** | | `SYS1` | PT 参数 | 6 | 2 | 4 | | `SYS2` | CT 参数 | 4 | 2 | 2 | | `Auto_FG` | 延时自动分闸 | 4 | 0 | 4 | | `Auto_HH` | 延时自动合闸(电池活化) | 4 | 0 | 4 | | `InRush` | 励磁涌流闭锁 | 3 | 0 | 3 | | `OverCur` | 短路告警 | 3 | 0 | 3 | | `VolQuaRate` | 电压合格率 | 3 | 0 | 3 | | `FZDDL` | 非遮断电流闭锁 | 3 | 0 | 3 | | `BHKO` | 保护出口展宽 | 2 | 0 | 2 | | `MeaRecord` | 遥测记录周期 | 2 | 0 | 2 | | `PowerSet` | 功率电压组 | 2 | 0 | 2 | | `PTBreak` | PT / 控回断线告警 | 2 | 0 | 2 | | `AutoCtrl` | 自动复归 | 1 | 0 | 1 | | **合计** | | **249** | **35** | **214** | > 单元名 `WatchDog` 有误导性:它装的是**全部保护元件的动作定值**(过流 / 零序 / 负荷 / 频率), > 不是看门狗参数。移植时不要按名字去找"看门狗定值"。 #### 9.4.2 已覆盖单元的逐项对照(35 项) **过流三段:参考 24 项,本产品 12 项**(每段缺 4 项,共缺 12) | 参考项(Ⅰ/Ⅱ/Ⅲ 段各一份) | 本产品 | 说明 | |---|---|---| | 过流 n 段投入 | `PS_OCn_EN` | ✅ | | 过流 n 段跳闸投入 | `PS_OCn_TRIP` | ✅ 语义 = 0 告警 / 1 跳闸 | | 过流 n 段定值 | `PS_OCn_CUR` | ✅ 二次值 A | | 过流 n 段延时 | `PS_OCn_DLY` | ✅ ms(参考为 S,换算在整定入口) | | 过流 n 段方向投入 | — | ❌ 无方向元件(`DirCell` / `PowDirJudge` 未复刻) | | 过流 n 段低压投入 | — | ❌ 无复压闭锁(`bNOVOpen` 无来源) | | 过流 n 段告警投入 | — | ❌ 参考把"告警投退"与"跳闸投退"拆成两项,本产品只有一项 | | 过流 n 段低压定值 | — | ❌ 同上 | > **这一处差额最容易被低估**:本产品的三段过流是"简化版三段",参考的三段每段还带 > **方向 / 低压 / 告警** 三个独立开关和一个低压定值。缺这四项时,三段过流只能做无方向、 > 无复压闭锁的纯过流保护——在环网或双电源线路上会误动。 **零序过流三段:参考 18 项,本产品 0 项** 参考在 `WatchDog` 内另有完整的零序三段(每段 6 项:投退 / 方向 / 告警投入 / 跳闸 / 定值 / 时限)。 本产品 `PS_3U0_*` 三项是**零序过压**(属 `VolPro`),**不是零序过流**——两者不可混为一谈: 零序过流看 `3I0` 幅值,零序过压看 `3U0` 幅值。因此零序过流保护目前**完全没有定值载体**, `OverCurrent_I0` 元件也无从判据。 **过负荷 / 重载 / 过载:参考 12 项,本产品 4 项** | 参考项组 | 本产品 | 差额 | |---|---|---| | 过负荷报警(投退 / 跳闸 / 定值 / 时限) | `PS_OL_*` 4 项 | ✅ 齐 | | 重载报警(同上 4 项) | — | ❌ 缺 4 | | 过载报警(同上 4 项) | — | ❌ 缺 4 | > 参考把"过负荷 / 重载 / 过载"分成三级(默认 150 A / 70 A / 100 A 步长,时限 9000 s / 0 / 0), > 对应事件 435 / 436 / 437。本产品只有一级过负荷。三级是**同一判据不同门槛**, > 复刻成本低(元件框架已具备),但需先补 `OverLoad` 的三个实例与三条事件映射。 **过频 / 低频:参考 8 项,本产品 6 项 + 2 本地项** | 参考项 | 本产品 | 说明 | |---|---|---| | 过频 / 低频 保护投退 | `PS_OF_EN` / `PS_UF_EN` | ✅ | | 过频 / 低频 保护定值 | `PS_OF_HZ` / `PS_UF_HZ` | ✅ 参考单位 HZ 存 5000~5500(0.01 Hz),本产品存 Hz 浮点 | | 过频 / 低频 保护时限 | `PS_OF_DLY` / `PS_UF_DLY` | ✅ | | 过频 / 低频 **跳闸**投退 | — | ❌ 缺 2:参考区分"保护投入"与"跳闸投入",本产品只有前者 | **电压保护:参考 13 项,本产品 9 项** | 参考项 | 本产品 | 说明 | |---|---|---| | 失压保护投退 / 定值 / 时限 | `PS_UV_EN` / `PS_UV_VOL` / `PS_UV_DLY` | ✅ | | 过压保护投退 / 定值 / 时限 | `PS_OV_EN` / `PS_OV_VOL` / `PS_OV_DLY` | ✅ | | 零序过压投退 / 定值 / 时限 | `PS_3U0_EN` / `PS_3U0_VOL` / `PS_3U0_DLY` | ✅ | | 失压 / 过压 / 零压 **跳闸**投退(3 项) | — | ❌ 缺 3 | | 零压 **告警**投退(1 项) | — | ❌ 缺 1 | **PT / CT 参数:参考 10 项,本产品 4 项** | 参考项 | 本产品 | 说明 | |---|---|---| | 母线一次额定(kV) | `PS_PT_PRI` | ✅ 本产品存 V | | 母线二次额定(线)(V) | `PS_PT_SEC` | ✅ | | 额定 TA 一次值(A) | `PS_CT_PRI` | ✅ | | 额定 TA 二次值(A) | `PS_CT_SEC` | ✅ | | 母线二次额定(相)(V) | — | ❌ 缺:线 / 相两种二次额定的换算关系需显式给出 | | 零序一次额定(kV) | — | ❌ 缺 | | 零序二次额定(V) | — | ❌ 缺 | | 零序一次额定(A) | — | ❌ 缺 | | 零序二次额定(A) | — | ❌ 缺 | | PT 接入数目(1~3) | — | ❌ 缺:决定用线电压还是相电压作判据源 | #### 9.4.3 完全未覆盖单元逐项清单(163 项) 214 项差额中,**51 项**落在 §9.4.2 已部分覆盖的四个单元里(过流 12 / 零序 18 / 重载与过载 8 / 频率 2 / 电压 4 / PT·CT 6 / 备用控制字 1),余下 **163 项**落在下面这些**一项都没有**的单元里。 逐项列出以便排期时按"整单元"认领,不要拆散。 **系统参数 `SYS3`(45 项)**——零飘门槛 10 项:额定电压零飘门槛、额定电流零飘门槛、直流电压零飘、 功率零飘、频率零飘、功率因数零飘、相角零飘门槛、备用 ×4;死区 7 项:电流死区、电压死区、 直流电压死区、功率死区、频率死区、功率因数死区、相角死区;开入开出 4 项:开入防抖(10~60000 ms)、 分闸脉宽、合闸脉宽、开入手动分合;变比 5 项:PT 变比、相 CT 变比、零序 CT 变比、功率变比、电量变比; 其余 19 项:开关类型、故障遥信保持时间、功率置负、单项接地选线投入、单相接地跳闸时间、FA 模式、 对时切换长延时、断线告警确认时间、事故总判小电流接地告警、加速段保护开放时间、 重合闸检有压等待时间、连续分闸时间、分闸次数、备用参数 01~08。 > 本产品的死区落在**运行参数段**(`0x8030~0x8038` 九点,RAM 副本 + 104 可写), > 变比落在 `prot_set` 的 PT/CT 四项。**语义已覆盖、载体不同**——复刻 `SYS3` 时是把这 > 两类合并进定值表,不是重新实现。 **电压时间型自愈 `VolTimeRecv`(17 项)**:就地 FA 模式、LS(0/1,1 = 联络)、 失电分闸投退、失电分闸时限(0~100000 s)、得电合闸投退、X 时间(1~100 s,默认 7)、 故障电流检测、故障电流定值(1~10000 A)、Y 时间(1~100 s,默认 5)、联络开关合闸、 联络充电时限(0~1800 s)、联络合闸时限(0~300 s)、正方向选择、残压定值(0~300 V)、 残压判定时限、停电时间(1~10 s)、人工闭锁。 > 这是**馈线自动化(FA)的核心**:失电分闸 → 得电合闸 → 联络转供。对应元件 > `NoVolTrip` / `HaveVolCls` / `ConnectBrkCls` / `FaultIsola` / `FaultRecov` 五个模块。 > 无开入(开关位置)与出口(分合闸)时**无法闭环**,属 X3 依赖项。 **重合闸 `AutoReCls`(16 项)**:重合闸投退、充电时间(0~100000 s)、重合闸次数(0~3)、 一次 / 二次 / 三次重合闸时间(0~60000 s)、重合闸闭锁时间(0~9900 s)、整组复归时间、 大电流闭锁投退、大电流闭锁定值、重合闸检同期、重合闸检无压、同期检无压定值(0~15000 V)、 同期检角差定值(1~30°)、过流禁止重合闸、零序禁止重合闸。 > 参考支持**三次重合**且每次时间独立可整定,充电时间与整组复归时间分离——这三项 > 是"重合闸能不能用"的关键(无充电时间 = 每次跳闸都立即重合,会连击)。 > 依赖出口(合闸)与开入(开关位置),属 X3 依赖项。 **同期 `SYN`(13 项,FTU 型专属)**:检无压投退、检同期投退、频差 / 角差 / 压差 / 滑差闭锁投退 (4 项)、检无压定值(1000~9999 V)、检频差定值(0~200,0.01 Hz)、检角差定值(1~180°)、 检压差定值(0~9999 V)、检频率滑差值(0~200 Hz/s)、开关合闸时限(0~200 s)、整组复归时间。 > 需**两路电压同刻**(母线侧 + 线路侧)才能算角差与频差——本产品 V0.1 只有一个转换器的 > 电压通道,属 X3 / H1 依赖项。 **录波启动 `WAVESTART`(12 项)**:电流启动投退 / 定值(0~10000 A)、低压启动投退 / 定值 (0~10000 V)、零压启动投退 / 定值(0~30000 V)、零流启动投退 / 定值(0~1000 A)、 零流突变投退 / 定值(0~1000 A)、录波启动延时、保护启动录波。 > 录波启动是**独立于保护判据的触发源**:即使保护不动作,只要越启动值也要录波。 > 本产品无录波子系统,这 12 项无处落地(见 §9.9)。 **遥测越限 `MeaLimit`(10 项)**:有压鉴别投退 / 线路有压定值(0~300 V)、无压鉴别投退 / 线路无压定值(0~300 V)、线路有压时限(2~100000 s)、电压越上限 / 越下限(0~100000 V)、 电流越上限(0~10000 A)、越上限延时 / 越下限延时。 > 越限记录(`OVER` 队列,128 条)的判据来源。本产品无越限记录类,故这 10 项也无载体。 **过流后加速 `OverCurSpeed`(8 项)**:后加速投退、线路 1 前加速投退、过流后加速电流值 (1~10000 A)、过流后加速时限(0~10000 s)、零序后加速投退、线路 1 前加速投退(重复项, 参考源码如此)、零序后加速电流值(1~1000 A)、零序后加速时限。 **接地告警 `OverCurI0`(13 项)**:线路接地告警投退、零序电流反极性、零序电压反极性、 接地备用 02、接地告警投退、接地跳闸投退、接地方向投退、接地定值(0~10000 A)、接地时限、 接地跳闸时限(0~600 s)、零压启动定值(1~10000 V)、接地重合投退、接地后加速投退。 > **反极性**两项是现场接线纠错开关(CT / PT 装反时不必改线,改定值即可), > 属"现场可维护性"需求,不是可选项。 **其余小单元(合计 29 项)**:励磁涌流闭锁 3(闭锁投退、涌流制动系数 1~100、闭锁复归时间); 短路告警 3(投退 / 时限 / 线路 01 短路电流定值);电压合格率 3(上限 / 下限定值 0~200 %、周期类型 0~4); 非遮断电流闭锁 3(电流闭锁投退、停电跳闸投退、非遮断电流值 1~10000 A); 出口展宽 2(跳闸出口时间、合闸出口时间,1~100000 ms); 遥测记录 2(遥测记录周期 0~2880、极值记录周期 0~360); 功率电压组 2(1~4 组);PT 断线 2(断线告警、控回断线告警);自动复归 1(自动复归投退); 延时自动分闸 4(延时复归投退、2:时 1:分 0:秒、延时复归时限 ×2); 延时自动合闸 4(自动活化投退、2:分 1:时 0:日、自动活化周期 0~360、自动活化时限 0~23000 s)。 --- ### 9.5 遥测复刻明细(参考 31 点 → 本产品 9 点) 参考 `g_tYcIndexTab` 的 31 点是**按"两回线 × 三相 + 线电压 + 零序 + 功率"组织的完整电气量集**。 本产品 9 点(`0x4011~0x4018`、`0x401D`)只覆盖第一回线的相量与频率。 | # | 参考遥测 | 参考枚举 | 本产品 | 差额原因 | |---|---|---|---|---| | 1 | 1 回 A 相电流 | `EN_YCMEA_IA01` | `0x4015` ✅ | | | 2 | 1 回 B 相电流 | `EN_YCMEA_IB01` | `0x4016` ✅ | 削顶 → 恒 IV | | 3 | 1 回 C 相电流 | `EN_YCMEA_IC01` | `0x4017` ✅ | 削顶 → 恒 IV | | 4 | 2 回 A 相电流 | `EN_YCMEA_IA02` | — | ❌ 无第二回线 | | 5 | 2 回 B 相电流 | `EN_YCMEA_IB02` | — | ❌ | | 6 | 2 回 C 相电流 | `EN_YCMEA_IC02` | — | ❌ | | 7 | 1 回 A 相电压 | `EN_YCMEA_UA1` | `0x4011` ✅ | | | 8 | 1 回 B 相电压 | `EN_YCMEA_UB1` | `0x4012` ✅ | 削顶 → 恒 IV | | 9 | 1 回 C 相电压 | `EN_YCMEA_UC1` | `0x4013` ✅ | 削顶 → 恒 IV | | 10 | 2 回 A 相电压 | `EN_YCMEA_UA2` | — | ❌ | | 11 | 2 回 B 相电压 | `EN_YCMEA_UB2` | — | ❌ | | 12 | 2 回 C 相电压 | `EN_YCMEA_UC2` | — | ❌ | | 13 | 1 回 AB 线电压 | `EN_YCMEA_UAB1` | — | ❌ 需两相相量之差 | | 14 | 1 回 BC 线电压 | `EN_YCMEA_UBC1` | — | ❌ | | 15 | 1 回 CA 线电压 | `EN_YCMEA_UCA1` | — | ❌ | | 16 | 2 回 AB 线电压 | `EN_YCMEA_UAB2` | — | ❌ | | 17 | 2 回 BC 线电压 | `EN_YCMEA_UBC2` | — | ❌ | | 18 | 2 回 CA 线电压 | `EN_YCMEA_UCA2` | — | ❌ | | 19 | 1 回零序电流 | `EN_YCMEA_3I01` | `0x4018` ✅ | 近似对齐(本产品为外部零序通道) | | 20 | 1 回零序电压 | `EN_YCMEA_3U01` | `0x4014` ✅ | 近似对齐 | | 21 | 2 回零序电压 | `EN_YCMEA_3U02` | — | ❌ | | 22 | 频率 | `EN_YCMEA_FR1` | `0x401D` ✅ | | | 23 | 直流 1 | `EN_YCMEA_DC_01` | — | ❌ 无直流采样通道 | | 24 | 直流 2 | `EN_YCMEA_DC_02` | — | ❌ | | 25 | A 相有功 | `EN_YCMEA_PA_1` | — | ❌ 需跨转换器同刻(H1) | | 26 | B 相有功 | `EN_YCMEA_PB_1` | — | ❌ | | 27 | C 相有功 | `EN_YCMEA_PC_1` | — | ❌ | | 28 | 总有功 | `EN_YCMEA_P_1` | — | ❌ | | 29 | 总无功 | `EN_YCMEA_Q_1` | — | ❌ | | 30 | 总视在 | `EN_YCMEA_S_1` | — | ❌ | | 31 | 功率因数 | `EN_YCMEA_COS_1` | — | ❌ | **缺的 22 点按依赖分三批**: | 批次 | 点数 | 内容 | 依赖 | |---|---|---|---| | 线电压与功率 | 13 | 6 线电压 + 7 功率/功率因数 | **H1**:电压在 TPAFE、电流在 STM32 ADC1,无共同触发,功率的 `U×I` 需同刻 | | 第二回线 | 7 | 2 回 6 相量 + `3U02` | 采样通道数(V0.2 硬件) | | 直流 | 2 | `DC_01` / `DC_02` | 无直流采样通道 | > 线电压本身**不一定要等 H1**:`Uab = Ua − Ub` 只需同一转换器内的两相相量, > 若两相都落在 TPAFE 侧即可算。当前 `Ub`/`Uc` 削顶(在 STM32 侧),所以连带算不出。 > **先解决"三相电压全落 TPAFE"这一件事,能一次解锁 6 个线电压点**。 **遥测模型 `g_tAnaModelTab`(35 行)**:参考为每个遥测点定义死区、变比、系数、越限档等属性行, 是"点表 + 整定"的合体。本产品的对应物拆成了三处:`iec104_pts.c` 的 YC 表(地址 + 死区系数)、 `run_par.c` 的死区九点、`adc_cal.c` 的 k/b。复刻模型表 = 把这三处收敛成一张可整定的表。 **电度 `g_tYmIndexTab`(8 种,本产品 0)**:正向有功 `Pt1`、正向无功 `Qt1`、Q1 象限、Q4 象限、 反向有功 `Pt0`、反向无功 `Qt0`、Q2 象限、Q3 象限。地址段 `0x6401~0x6600`。 电度 = 功率对时间积分,**与功率同批解锁**(同一 H1 依赖)。 --- ### 9.6 开入 / 开出复刻明细(138 + 31 → 本产品 0) 参考把"开入"分成四类,本产品**一个都没有**(无硬件,不可移植项 X3)。 **开入 138 点的构成**: | 类别 | 点数 | 含义 | |---|---|---| | 软遥信 `EN_KI_SOFTYX_*` | **104** | 由保护元件/逻辑产生的内部状态,不占物理端子(如"过流Ⅰ段动作""重合闸充电完成""FA 投入") | | 硬件开入 `EN_KI_*` / `EN_KIA_*` | **27** | 物理端子:开关合位 / 分位 / 未储能 / 就地远方 / 接地位置 / 隔离位置 / 停用重合闸 / 保护投入 / 检修压板 / 远方信号 / 就地 FTU 手合手分 / 电源输入 / 活化状态 / 电池欠压 / 活化投入 | | 双点遥信 `EN_KI_TWIN_YX_*` | **6** | 开关位置用双点表达(00 中间态 / 01 分 / 10 合 / 11 无效) | | 多态遥信 `EN_KI_INT8U_*` | **1** | 8 位状态量(如"设备状态"枚举) | > **104 点 ≠ 104 个端子**。真正要接线的是 27 个硬件开入;104 个软遥信是**元件输出**, > 只要元件复刻出来就自然存在。这个区分决定了工作量估算:不是"买 138 路开入板", > 而是"27 路端子 + 104 个元件状态映射"。 **开出 31 点的构成**: | 类别 | 点数 | 明细 | |---|---|---| | 出口类 | **11** | 保跳 1(`BH_TZ1`)、保合 1(`BH_HZ1`)、遥分 1(`YT01`)、遥合 1(`YH01`)、遥分 2(`YT02`)、遥合 2(`YH02`)、电池活化(`DCHH`)、活化退出(`DCHH_Esc`)、信号复归(`FG`)、开入变位(`KICHANGE`)、录波启动(`WAVE_START`) | | 信号类 | **20** | 事故信号 `SignTV`、运行 `SignRun`、通信中断 `SignCOMM`、硬件故障 `SignHW`、储能 `SignCN`、电池欠压 `SignBatQY`、失电 `SignSD`、过负荷 `SignGL`、接地 `SignJD`、闭锁 `SignBM`、备用 `SignBS`、自检 `SignCHK`、重合 `SignCH`、备用 3 个 `SignBAK1~3`、告警 `SignALM`、保护动作 `SignPro`、就地 FA `SignJDFA`、集中 FA `SignJZFA` | > 本产品当前**保护动作只体现为遥信 + SOE**,没有任何物理出口。这在"保护测控装置"的 > 定义里是硬缺口:保护判据做得再准,不跳闸就等于没保护。X3 解除前,`YX` 里的 > 动作类遥信必须**如实标 IV**,不能让主站误以为已跳闸。 --- ### 9.7 动作事件复刻明细(参考 33 条 → 本产品 9 条) 参考 `g_tActRecTab` 的 33 条动作事件编号 **421~453**,是"保护动作 → 事件码 → 记录 → 上送"的 中间层。本产品用 `App/log/sys_log.h` 的七类记录 + SOE,**没有事件码表**。 | 事件码 | 参考事件 | 本产品 | 说明 | |---|---|---|---| | 421 | 线路励磁涌流闭锁 | ❌ | `InRushCur` 未复刻 | | 422 | 线路Ⅰ段过流保护动作 | ✅ | `PS_OC1_*` | | 423 | 线路Ⅱ段过流保护动作 | ✅ | `PS_OC2_*` | | 424 | 线路Ⅲ段过流保护动作 | ✅ | `PS_OC3_*` | | 425 | 线路零序Ⅰ段过流保护动作 | ❌ | 无零序过流定值载体 | | 426 | 线路零序Ⅱ段过流保护动作 | ❌ | 同上 | | 427 | 线路零序Ⅲ段过流保护动作 | ❌ | 同上 | | 428 | 线路后加速保护动作(过流) | ❌ | `OverCurrent_SpeedUp` 未复刻 | | 429 | 线路后加速保护动作(零序) | ❌ | `OverCurrentI0_SpeedUp` 未复刻 | | 430 | 零序过压保护动作 | ✅ | `PS_3U0_*` | | 431 | 线路欠压保护动作 | ✅ | `PS_UV_*` | | 432 | 线路过压保护动作 | ✅ | `PS_OV_*` | | 433 | 高频保护动作(过频) | ✅ | `PS_OF_*` | | 434 | 低频保护动作 | ✅ | `PS_UF_*` | | 435 | 线路过负荷保护动作 | ✅ | `PS_OL_*` | | 436 | 线路重载保护动作 | ❌ | 三级负荷只做了一级 | | 437 | 线路过载保护动作 | ❌ | 同上 | | 438 | 线路接地保护动作 | ❌ | `OverCurI0` 未复刻 | | 439 | 一次重合动作 | ❌ | 重合闸未复刻 | | 440 | 二次重合动作 | ❌ | 同上 | | 441 | 三次重合动作 | ❌ | 同上 | | 442 | 接地重合动作 | ❌ | 同上 | | 443 | 接地后加速动作 | ❌ | 同上 | | 444 | 失电分闸动作 | ❌ | `NoVolTrip` 未复刻 | | 445 | 得电合闸动作 | ❌ | `HaveVolCls` 未复刻 | | 446 | 闭锁合闸动作 | ❌ | 需开入(闭锁压板) | | 447 | 闭锁分闸动作 | ❌ | 同上 | | 448 | 联络开关合闸动作 | ❌ | `ConnectBrkCls` 未复刻 | | 449 | 联络开关分闸动作 | ❌ | 同上 | | 450 | 保护动作备用 02 | — | 参考即备用 | | 451 | A 相故障 | ❌ | 需分相判别(三相全接) | | 452 | B 相故障 | ❌ | 同上 | | 453 | C 相故障 | ❌ | 同上 | > **本产品覆盖 9 / 33 条**。注意 428 与 429 在参考源码里**同名**(都叫"线路后加速保护动作"), > 靠事件码区分过流与零序——移植时不能按名字去重。 > > 事件码缺失的连带后果:**事故报告(`CN_NUM_QUE_REC_RPT` 队列)无法生成**。 > 参考的事故报告是按事件码索引动作时序的,没有事件码表就没有"保护动作报告"这一页。 --- ### 9.8 保护元件复刻明细(参考 45 个模块 → 本产品 8 个) **模块级口径**:参考 `RelayUnit/` 45 个 `.c` 文件 = 45 个元件模块。本产品覆盖 **8 个模块** (7 个判据模块 + 1 个记录模块),以 **9 个实例**落地(`OverCurrent` 一个模块出三段实例)。 | 类别 | 参考模块 | 本产品 | 缺 | |---|---|---|---| | 过流类 | 8 | 2(`OverCurrent`、`OverLoad`) | **6** | | 电压类 | 8 | 3(`OverVol`、`OverVol_U0`、`LackVol`) | **5** | | 频率类 | 2 | 2(`OverFr`、`LackFr`) | 0 | | 重合闸 | 5 | 0 | **5** | | 方向 / 功率 | 4 | 0 | **4** | | 涌流 / 故障 | 5 | 0 | **5** | | 自动控制 | 5 | 0 | **5** | | 记录类 | 6 | 1(`SOERecord`) | **5** | | 录波 / 采样 | 2 | 0 | **2** | | **合计** | **45** | **8** | **37** | **缺的 37 个模块,逐个给出"缺它意味着什么"**: | 模块 | 类别 | 判据要点 | 缺它的后果 | 依赖 | |---|---|---|---|---| | `OverCurrent_I0` | 过流 | 零序电流三段 + 方向 | 接地故障无保护 | 定值载体(零序 18 项) | | `OverCurrent_SpeedUp` | 过流 | 后加速:重合后短延时无选择跳闸 | 重合于故障时靠 Ⅰ 段延时切除,慢一拍 | 重合闸 | | `OverCurrentI0_SpeedUp` | 过流 | 零序后加速 | 同上(接地) | 重合闸 | | `FZDOverCur` | 过流 | 非遮断电流闭锁(负荷电流以下不允许跳闸) | 小电流跳闸可能拉弧不熄 | 定值 3 项 | | `OverCurWarn` | 过流 | 过流告警(只告警不跳闸) | 过流只能跳不能告警 | 定值(告警投入) | | `OverCurWarn_I0` | 过流 | 零序过流告警 | 同上 | 定值 | | `LineHaveVol` | 电压 | 线路有压鉴别 | 无法区分"有压合闸"与"无压合闸" | 定值(MeaLimit 有压鉴别) | | `HaveVolCls` | 电压 | 得电合闸 | FA 的"来电自动合闸"缺失 | 开入 + 出口 | | `NoVolTrip` | 电压 | 失电分闸 | FA 的"失电自动分闸"缺失 | 开入 + 出口 | | `PTBreak` | 电压 | PT 断线(电压消失但电流正常) | 断线时电压类保护可能误动/拒动 | 定值 2 项 | | `CTBreak` | 电压 | CT 断线 | 同上(电流侧) | 定值 | | `AutoReCls` | 重合闸 | 充电 → 跳闸 → 延时重合 → 整组复归 | 瞬时故障必须人工合闸 | 开入 + 出口 + 16 项定值 | | `AutoReCls_JD` | 重合闸 | 接地故障重合 | 同上(接地) | 同上 | | `OnceCch` | 重合闸 | 一次重合(表内无定值,条件编译) | — | 同上 | | `TwiceCch` | 重合闸 | 二次重合(表内无定值,条件编译) | — | 同上 | | `ConnectBrkCls` | 重合闸 | 联络开关合闸(转供) | 环网无法转供 | 开入 + 出口 + 17 项定值 | | `DirCell` | 方向 / 功率 | 方向元件(功率方向判别) | 双电源/环网会反送电误动 | 三相电压 + 三相电流同刻(H1) | | `PowDirJudge` | 方向 / 功率 | 功率方向(有功/无功象限) | 同上 | 同上 | | `PowerSet` | 方向 / 功率 | 功率电压组切换(1~4 组) | 多组 PT/CT 变比无法切换 | 定值 2 项 | | `YcOverLimit` | 方向 / 功率 | 遥测越限判据 | 越限记录无来源 | 定值(MeaLimit 10 项) | | `InRushCur` | 涌流 / 故障 | 二次谐波制动(励磁涌流闭锁) | 变压器空投时过流误动 | 谐波算法(见 §9.12 遥测增强) | | `FaultIsola` | 涌流 / 故障 | 故障隔离 | FA 无法定位故障区段 | 开入 + 出口 | | `FaultRecov` | 涌流 / 故障 | 故障恢复供电 | 同上 | 同上 | | `BusEarthAlm` | 涌流 / 故障 | 母线接地告警 | 母线接地无告警 | 零序电压 | | `SgzAlarm` | 涌流 / 故障 | 事故总告警 | 事故总信号缺失 | 事件码表 | | `AutoCtrlTrip` | 自动控制 | 自动控制跳闸(电池活化等) | 活化流程无法跳闸 | 出口 | | `ManualTrip` | 自动控制 | 手动跳闸 | 就地手动分闸缺失 | 开入 + 出口 | | `MusterRecov` | 自动控制 | 总复归(复归所有保持态) | 动作态只能靠重启清 | 无(**可先做**) | | `ExvRec` | 自动控制 | 事故记录 | 事故报告缺失 | 事件码表 | | `ExvRecGW` | 自动控制 | 事故记录(网关侧) | 同上 | 同上 | | `CORecord` | 记录 | COS 变位记录 | 开入变位无记录 | 开入 | | `FrzRecord` | 记录 | 冻结记录(整点冻结电量) | 电度无冻结 | 电度 | | `YcMeaRecord` | 记录 | 遥测定时记录 | 无遥测历史 | 定值 2 项 | | `YcMeaLimitRec` | 记录 | 遥测越限记录 | 无越限记录 | 越限判据 | | `YcVolQuaRate` | 记录 | 电压合格率统计 | 无合格率 | 定值 3 项 | | `WaveQdCell` | 录波 / 采样 | 录波启动判据 | 录波无触发源 | 录波子系统 | | `RlyCommSam` | 录波 / 采样 | 保护通信采样(跨装置采样同步) | 多装置差动类保护不可能 | 通信 + 对时 | > **唯一"现在就能做"的是 `MusterRecov`(总复归)**:它不依赖任何硬件,只把各元件的 > 保持态与出口标志清一遍。本产品已有 `prot_elm_request_reset` 单字请求标志(§3.5), > 接上即可。其余 36 个模块都至少依赖定值载体、开入、出口、事件码四者之一。 --- ### 9.9 故障录波子系统规格(本产品 0) 录波是**唯一一个"完全独立的子系统"级缺口**:它不在保护链路上,但有独立的采样率、 独立的存储格式、独立的文件传输通道。参考实现的硬指标如下,可直接作为复刻验收判据。 | 指标 | 参考值(`APP/EquipCfg/Const_Wave.h`) | 说明 | |---|---|---| | 录波采样率 | `CN_SAM_RATE × CN_WAVE_RATIO` = 20 × 4 = **80 点/周波**(50 Hz 下 4000 Hz) | 保护计算用 20 点/周波,录波要 4 倍过采样才能看清波形 | | 录波段数 | `CN_WAVE_SECT_NUM` = **6 段** | 每段独立缓冲,支持连续多次录波 | | 启动前缓冲 | `CN_WAVE_CYCLE_SUM_REFRE_BUF` = **5 周波**(100 ms) | 循环刷新,保证故障前有数据 | | 动作后 | `CN_WAVE_CYCLE_SUM_ACTED` = **10 周波**(200 ms) | | | 返回段 | `CN_WAVE_CYCLE_SUM_RET` = **2 周波** | 返回过程也要录 | | 手动录波 | `CN_WAVE_CYCLE_SUM_HAND` = **6 周波** | | | 单次最长 | `CN_WAVE_MAX_TIME` = **9000 ms** | 超长故障(如持续性接地)按此截断 | | RAM 录波个数 | `CN_WAVE_RAM_NUM` = **16** | RAM 里最多同时存 16 份,满了转存文件 | | COMTRADE 保存数 | 动作 **64** / 启动 **64** / 手动 **16** | 三类独立配额,满了循环覆盖 | | 单份最大开关变位 | `CN_WAVE_MAX_RECSWI` = **64** | | | 布尔量通道 | `CN_WAVE_NUM_BOOLFLG` = **320** | 全局布尔变量按位录波 | | 模拟量通道 | 原始 + 计算 = `CN_WAVE_ALL_ANA_NUM` | 本产品对应 **65 通道**(`g_tMeasureModelTab`) | | 录波类型 | 动作 `TYPE_ACT`=1 / 启动 `TYPE_START`=2 / 手动 `TYPE_HAND`=3 | | | 文件格式 | COMTRADE(`.cfg` + `.dat`) | 主站侧通用格式,非私有 | | 上送方式 | 104 文件传输(`F_FR_NA_1` 目录 / `F_SR_NA_1` 读文件) | 本产品 104 未实现文件传输 | **复刻所需的四件东西**(缺一不可): 1. **4 倍过采样缓冲**:当前 `adcTask` 以 1000 Hz 出样本,录波要 4000 Hz。要么提高采样率 再抽取给保护(改变整条链节拍),要么在 TPAFE 侧单独开一条 4 倍速通路(改驱动)。 2. **环形缓冲 + 触发点定位**:5 周波前缓冲 + 10 周波后缓冲,触发时刻要能反算出缓冲内偏移。 3. **COMTRADE 编码器**:`.cfg`(通道定义、采样率、时标)+ `.dat`(二进制采样值)+ `.hdr`。 4. **文件存储与配额管理**:SPI NAND 上按 64/64/16 配额循环覆盖,需与现有 FatFs + dhara 层对接。 > **排期判断**:录波不是"加个功能",是**新增一条 4 倍速采样通路 + 一套文件格式 + 一个配额管理器**。 > 它排在定值/元件铺开之后,因为录波通道表(65 路)本身依赖元件输出。 --- ### 9.10 记录体系复刻明细(参考 13 类 → 本产品 8 类) 参考用 13 个定长队列分类存记录,容量按类分配(`GlobalDef/Const_Sys.h`): | 参考队列 | 容量 | 语义 | 本产品 | |---|---|---|---| | `REC_ACT` | 128 | 动作记录(保护动作) | ✅ 事故记录 `/log/accid.log` | | `REC_ALM` | 128 | 告警记录 | ✅ 保护告警 `/log/alarm.log` | | `REC_OVER` | 128 | 越限记录 | ❌ | | `REC_START` | 128 | 启动记录(元件启动但未动作) | ✅ 保护启动 `/log/start.log` | | `REC_LOCK` | 128 | 闭锁记录(保护被闭锁) | ❌ | | `REC_SOE` | **256** | SOE(带时标的开入变位) | ✅ `soe_store`(SPSC 深度 8 → 落盘) | | `REC_COS` | **256** | COS(无时标的状态变位) | ❌ | | `REC_CHK` | 128 | 自检记录 | ✅ `/log/selftest.log` | | `REC_RUN` | 128 | 运行记录(上电/重启) | ✅ `/log/run.log` | | `REC_OPT` | 128 | 操作记录(复归/对时/定值保存) | ✅ `/log/oper.log` | | `REC_YK` | 128 | 遥控记录 | ✅ `/log/yk.log` | | `REC_SC` | 128 | 顺控记录 | ❌ | | `REC_SCSTEPINFO` | 128 | 顺控执行明细 | ❌ | **缺的 5 类及其依赖**: | 缺的队列 | 依赖 | 能否先做 | |---|---|---| | 越限记录 | 越限判据(`MeaLimit` 10 项定值 + `YcOverLimit` 元件) | 可先做判据、后做记录 | | 闭锁记录 | 闭锁源已具备(四源闭锁,§5.12),**只缺记录落点** | ✅ **最该先补**——现在闭锁发生了但无记录,现场排障靠猜 | | COS 记录 | 开入(X3) | ❌ | | 顺控记录 / 顺控执行明细 | 顺控功能(参考 `YKRly.c` 63 KB 的顺控执行引擎) | ❌ 整功能未立项 | > **闭锁记录是本表里性价比最高的一项**:闭锁源(未标定 / 采样故障 / 定值校验和 / 软压板退出) > 已经全部实现,但闭锁的**发生与解除都没有留痕**。加一个 `REC_LOCK` 等价队列, > 只需在 `protect_task` 的闭锁判定处加一条写入,不依赖任何硬件。 **格式差异**:参考是**二进制定长记录 + 独立队列 + 循环覆盖**;本产品是**文本行(64 B 定长)+ 文件**。 文本行的优点是 FTP 直接可读、现场排障友好;缺点是容量利用率低、无结构化解码。 复刻时建议**保留文本格式**(本产品优势),只补队列分类与配额。 --- ### 9.11 规约栈复刻明细(参考 12 种 → 本产品 1 种) | 规约 | 参考源码 | 链路 | 用途 | 工作量特征 | |---|---|---|---|---| | **IEC 60870-5-104** | `Comm/Pub/Rly_IEC104.c` | TCP | 主站接入(本产品已实现) | ✅ 已完成 | | IEC 60870-5-101 | `Rly_IEC101.c`、`Rly_IEC101_B.c` | 串口 | 主站接入(老站) | 与 104 共享 ASDU 层,**只换链路层**(平衡/非平衡链路、可变帧长) | | IEC 60870-5-102 | `Rly_IEC102.c` | 串口 | 电度采集 | 独立 ASDU(电度专用类型),依赖电度 | | IEC 60870-5-103 | `RLY_IEC103.c`、`RLY_IEC103-tcp.c`、`Rly_IEC103T.c` | 串口 / TCP | 保护装置信息接口 | 三种变体(串口 / TCP / T 型),ASDU 与 101/104 不同族 | | ModBus | `Rly_ModBus.c` | 串口 / TCP | 通用工业设备 | 简单,**对下采集首选** | | CDT | `Rly_Cdt.c` | 串口 | 循环式远动(老国标) | 帧结构简单,无确认机制 | | `Whm_GB` | `Whm_GB.c` | 串口 | 国标电度 | 依赖电度 | | IEC 61850 | `struct61850.h`、`GlobalUnit/SttnLvlGoose.c` | 以太网多播 | MMS / GOOSE / SV | **最大工作量**:需 MMS 栈 + ASN.1 + 多播组管理 | | CAN | `Rly_Can.h` | CAN 总线 | 内部总线 | 依赖 CAN 硬件 | | 子站 IEC 101 | `Comm/Mon/Rtu_IEC101*.c`(含 ABB / 西门子 / 无优先级变体) | 串口 | 对下接 FTU / 开关 | **对下主站侧**,与对上从站侧是不同角色 | | 子站 IEC 104 | `Rtu_IEC104.c`、`Rtu_Dbg104.c`、`Rtu_RIS104.c` | TCP | 对下 | 含调试与 RIS 变体 | | BALA101 / ModBus | `Rtu_BALA101*.c`、`Rtu_ModBus.c` | 串口 | 对下 | 与 RS-485 簇同批 | | 加密 | `Comm/Ecc/`(SM2 / SM3 / ECDSA) | — | 安全加固 | 国密算法库,纯软件可移植 | **排期分层**: | 层 | 规约 | 理由 | |---|---|---| | 先做 | ModBus(对上 + 对下)、IEC 101 | 与 104 共享 ASDU 或结构简单;对下采集是配网终端必备 | | 次做 | IEC 103、CDT、BALA101 | 老设备兼容,结构清晰 | | 后做 | IEC 102、`Whm_GB` | 依赖电度(H1) | | 末做 | IEC 61850 | 需 MMS/ASN.1/多播,单核 M4 资源需专项评估 | | 并行 | SM2 / SM3 / ECDSA | 纯软件,无硬件依赖,可随时插入 | > **一个容易漏的点**:参考同时有**对上(`Comm/Pub`,本机作从站)**与**对下(`Comm/Mon`,本机作主站)** > 两套规约。本产品只有对上 104。做"配网终端"时对下那套(101/104/ModBus 主站侧) > 是**必须**的——否则接不了一次开关。 --- ### 9.12 复刻完成判据(逐缺口可验证) 每一条缺口给出"怎么算做完了",避免"实现了但没验收"。 | 缺口 | 完成判据 | |---|---| | 定值铺开 | `PS_ID_COUNT = 249`(或按单元分批:每批新增项在 104 上可读写、跨复位保持、校验和正确);`check` 脚本比对参考表逐项一致 | | 104 定值映射 | 107 个地址(`0x8820~0x8891` 含 8 空洞)全部可读可写;空洞地址返回"未知信息体地址" | | 元件铺开 | 45 个模块对应 45 个实例;每个实例有:定值项齐全、启动/动作/返回三态可观测、事件码正确、单测覆盖判据边界 | | 遥测铺开 | 31 点全部非 IV(除物理不可达点明确标 IV);线电压与相量满足 `Uab = Ua − Ub` 误差 ≤ 0.5 % | | 开入开出 | 27 路硬件开入可读、31 路开出可驱动;开入防抖可整定;双点遥信四态正确(00/01/10/11) | | 录波 | 4000 Hz 采样;三类录波各生成一份 COMTRADE 且被第三方工具打开;配额 64/64/16 循环覆盖正确 | | 记录体系 | 13 类队列各有落点;每类容量达标;断电不丢已确认记录 | | 多规约 | 每种规约与对应主站/子站工具完成一次完整交互(连接 / 总召 / 变位 / 遥控 / 断链重连) | | 国密 | SM2 签名验签、SM3 摘要与标准测试向量一致 | | 软压板 | 9 面全部可投退且行为可观测(投退后对应功能确实停走/恢复) | | 出口与开入 | 保护动作 → 出口继电器动作 → 开入回读确认,全链路 ≤ 120 ms | | 标定解卡 | 8 通道两点标定后 `ADC_CAL_F_VALID` 全置位;注入标准源,保护动作值与定值误差 ≤ 3 % | | 屏板链路 | **串口通道**:屏板 275 ms 召唤、主机 ≤200 ms 回 **4208 B**,连续 10 min 无超时;显存 3200 B 与 `g_ui_fbuf` 逐字节一致;7 个物理键(`1/2/3/4/7/8/9`)动作正确;**状态灯位掩码逐位一致、LED 0 恒亮**;屏板 ≈5000 s 自复位不误判离线;LOGO 下发后断电重启仍在;**网络通道**:INIT 应答 **16 B** 逐字段一致且无颜色字段、`LCDMEM` 偏移 0 得 3204 B、重复召唤无应答、偏移 ≥3200 无应答、`KEEPLIVE` 无应答且 1 分钟不掉链、8 键(含 `F1\|ENT` 组合)动作正确(§5.16.10 十五条) | --- ### 9.13 屏板链路复刻任务规划 > **口径**:条目一律用**语义短语**(编号规范 P-8),执行序即表序。 > 改动范围只含本产品固件与 `DTU-RemoteLCD` 客户端;**屏板固件不在本仓库**, > 其行为以 §5.16 为契约做黑盒验收。 > > **两种部署形态,两条通道都要**:① **串口直连屏板**(参考的形态,第 10~15 条)——屏板直接挂在设备 > 串口上,无需任何网络设备;② **串口服务器透传 / 网络镜像**(第 2~9 条)——屏板或调试软件经以太网连 7003。 > 现场用串口服务器把屏板接到网上时,两种形态等价,但**固件侧不能只实现后者**: > 只有 7003 时,设备离开网络就没有任何本地显示,与参考产品不等价。 > > **方向相反,别写错**:网络通道是"**客户端召唤、设备应答**";串口通道是"**屏板召唤、设备应答**"。 > 两条通道共用同一份显存快照,但**帧格式、校验、时序完全不同**(网络用 `0xAA/0xBB` + XOR, > 串口用裸二进制 + CRC-16/MODBUS),不能共用组帧代码。 > > **执行细则另立**:逐项任务的改动点、判据、代码骨架、台架联调步骤与风险清单在 > `docs/任务规划/屏板链路.md`;本节只保留**需求视角**的缺口与顺序,不复制执行细节。 | 序 | 任务 | 改动点 | 判据 | 跨端 | |---|---|---|---|---| | 1 | **报文口径定版** | 本节 + §5.16 + §6.2 按参考源码成文 | 文档门禁全绿(已完成) | — | | 2 | **INIT 应答补足 16 字节** | `ui_net_sink.c` 的 `remo_send_init()` 组帧由 8 B 扩到 **16 B**:补 8 B 键值表;**不加颜色位与 RGB**(单色屏不对齐多色协议,§5.16.5) | 16 B 逐字节比对通过,且报文内**无颜色字段** | **是** | | 3 | **键值表补 F1 / F2** | `ui_net_sink.h` 键值宏补 `0x04` / `0x80`;`ui_net_sink_inject_key()` 映射;UI 键处理接受组合键 | 下发 `0x04` / `0x80` / `0x24` 界面响应正确 | 否 | | 4 | **显存断点续传** | `CMD_LCDMEM` 分支去掉"仅偏移 0"限制:按偏移取 `g_ui_fbuf + off`,长度 `min(9800, 3200 − off)` | `off=1600` 得 `4+1600` B,内容与整帧尾部逐字节一致 | 否 | | 5 | **召唤拒绝握手** | 新增"帧代次 + 已上送"等价状态:新图未就绪时重复召唤**不应答**;偏移 ≥3200 不应答 | §5.16.10 第 2、3 条通过 | **是** | | 6 | **链路监视** | 1 分钟通信中断计时(收到帧 / 发送成功即重置);超时置状态位并落一行日志 | 断链 60 s 有明确日志与状态;重连后自动恢复推流 | 否 | | 7 | **KEEPLIVE 静默** | 去掉空帧回显,改为仅重置计时 | §5.16.10 第 4 条通过 | **是** | | 8 | **发送重试评估** | 对比参考"BUSY + 200 ms 放弃"与现有 `net_send` 阻塞模型,出结论并回填 §5.16.9 第 7 行 | 结论有实测数据支撑(实现 / 明确不实现) | 否 | | 9 | **客户端一致性回归** | `DTU-RemoteLCD` 按 **16 B** 解析;把 §5.16.10 第 1~7 条做成自动化用例 | 七条判据全 PASS | **是** | | 10 | **串口物理层落地** | 按 §5.16.2 定版:**700000 8N1、`BRR = 0x0078`**(USART1,APB2 84 MHz)、**无 DE**(TTL 直连,不走收发器)、RX 缓冲 16 B + 空闲中断断帧、发送走 **DMA 一次性发完**(禁逐块阻塞,`DMA2_Stream7` TX / `DMA2_Stream2` RX 已在 `.ioc` 配好但控制台未用) | 环回 + 示波器实测 700 kbaud 分频零误差;屏板能收到帧 A 并回帧 B | 否 | | 11 | **帧 A 解析 / 帧 B 组帧** | 新增屏板通道模块:识别 6 B 请求(`01 03 type key CRC_H CRC_L`)、校验 CRC-16/MODBUS(**高字节在前**);组 **4208 B** 帧 B(3200 显存 + 4 B LED + 4 B info 保留 + 998 B 填充 + 2 B CRC) | 屏板出图与 `g_ui_fbuf` 逐字节一致;`info[0..3]` 与下发的 LED 掩码一致 | 否 | | 12 | **状态灯位映射表** | 定义"**业务状态 → 屏板 LED 位**"映射(参考源码**无此表**,须自定);`info[0..3]` 按 **32 位小端**填,bit0 恒 0、bit20~31 恒 0 | §5.16.10 第 11 条通过 | 否 | | 13 | **串口上行按键** | 帧 A 的 `key` 字节按**屏板 `KEY_TYPE`** 解释——实际可达值只有 **`1/2/3/4/7/8/9`**(`5`/`6`/`10` 无物理入口);映射为逻辑键值后与 7003 注入**同一入口**合并 | §5.16.10 第 10 条通过;`9`/`10` 语义按 §5.16.3 ② 处置 | 否 | | 14 | **双通道并发 + 自复位容忍** | 两出口各自独立发送上下文;串口 4208 B 阻塞不拖 7003;链路监视按"**连续 ≥10 s 无应答**"判离线,**忽略屏板 ≈5000 s 周期自复位** | §5.16.10 第 12、13、14 条通过 | 否 | | 15 | **LOGO 下发流程** | 一次性下发 LOGO 帧(`info[4..5] = 0x89 0x45`,宽高大端);放在出厂 / 联调流程,**不进常规刷新循环** | §5.16.10 第 15 条通过;断电重启后 LOGO 仍在 | 否 | | 16 | **日志口迁移(USART1 → RS-232 通道)** | ① `console_log.c` 的 `huart1`、`USART1_IRQn`、`__HAL_RCC_USART1_CLK_*`、`MX_USART1_UART_Init()` 收口成**一处端口宏**,指向 RS232#3(`huart2` / USART2,默认)或 RS232#1(`huart5` / UART5);② 该实例改 **`UART_MODE_TX`**(只发不收,避免重演 ORE 锁存);③ **不动** UART4 的 `g_rs485` 绑定 | 日志在 RS-232 侧(CN1)可读且内容与迁移前一致;USART1 **完全空出**;连续 10 分钟无 ORE 锁存、无 `[ERR]` | 否 | **批次划分**: | 批 | 任务 | 理由 | |---|---|---| | 第一批(无跨端风险,可独立发版) | 3、6、8 | 只动本端,旧客户端不受影响 | | 第二批(跨端,须与客户端同步发版) | 2、4、5、7、9 | 报文口径变更,两端必须同版本 | | 第三批(**独立立项**,不随 RS-485 冻结) | **16**、10、11、12、13、14、15 | 屏板串口通道是**独立需求**,与 §5.10 冻结的"对下采集簇"不是一回事。**第 16 条必须排在 10 之前**:日志不腾出 USART1,屏板链路就没法独占这条通道(两者共用同一个 `USART1->DR`,同时用就是互相插字节) | > ⚠ **唯一破坏性变更**:第 2 条把 INIT 应答从 8 B 改成 **16 B**。**旧客户端会把 16 B 的前 8 B 当全部、 > 把后 8 B 当作下一帧的 TAG 去找**,解析状态机会被这 8 B 拖住直到超时——表现为"连上了但不出图"。 > 因此第 2、5、7、9 条必须**同批发布**,且发版说明中明确"客户端需升级"。 > > ⚠ **16 B 与参考的 29 B 不可互换**:删掉 13 B 颜色字段后,偏移 4 之后全部前移—— > 参考的"显存大小在偏移 5、键值表在偏移 21"在本产品变成"偏移 4、偏移 8"。 > 客户端**不得**复用参考的 29 B 解析器再截前 16 B(会读出颜色位当显存高位)。 > > **屏板固件边界**:本表不覆盖屏板侧固件(帧解析 / 显存写入 / 按键扫描 / 状态灯驱动)。 > 屏板固件工程在 `D:\Code\DTU Code\DTU-LCD`,**不在本仓库**,其契约已按源码逐字节成文(§5.16.2 / §5.16.3)。 > 本产品只要满足 §5.16.10 的十五条判据即为兼容。 > > **第三批的前置阻塞**:第 11~15 条需要**屏板实物**才能验收(无实物只能做到"帧发出去了")。 > 第 11 条的帧长仍有一处待实测复核——代码实参是 **4208 B**,屏板 readme 写 **3208 B**(§5.16.2); > 台架抓到第一帧真实报文后**立即固化**,在此之前以代码实参为准,不得按 readme 实现。 > > **一条物理前置 + 一条实现纪律**: > ① **台架串口线挪位**——现在的 CH340/`COM6` 接在 USART1 调试口上,日志迁到 RS-232 侧后 > 必须改接到 CN1(或 U14),否则 `@test` 与 `serial-assistant` 全部抓不到日志,等于把台架弄瞎。 > **这条要在第 16 条动代码之前完成。** > ② **RX 要真的读、ORE 要真的清**——PA10 一直是接线的(原"未接线"结论已撤销,§5.16.2), > 但"RX 使能却不读 `DR`"本身就足以让 ORE 永久锁存。第 11 条实现时必须在 > `HAL_UART_ErrorCallback` 里清 ORE 并重挂接收,否则一次噪声就永久静默。 > > **已消解的旧阻塞**:原"屏板协议拿不到、不得按猜测定契约"的告警**不再适用**——物理层、帧格式、 > 键值、状态灯、时序均已从 `DTU-LCD` 源码确定。剩下的只是"实物联调确认",不是"协议未知"。 --- ## 10. 验收判据 ### 10.1 构建与静态门禁 | 项 | 判据 | |---|---| | 构建 | `@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 上电自检(`@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` | | 退出码 | `@test` → 0(PASS) | ### 10.3 现场验收清单 | # | 项目 | 方法 | 通过标准 | |---|---|---|---| | 1 | 上电自启动 | 断电上电 + 串口采集 | `@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 **十五条**逐项验证 | 串口侧: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`](../../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`](../../docs/测试说明/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`](../../docs/测试说明/实机测试实施指南.md) | | 测试标准流程 | [`docs/测试说明/测试开发标准流程.md`](../../docs/测试说明/测试开发标准流程.md) | | RAM 预算与优化 | [`docs/内存管理/RAM使用分析与优化方案.md`](../../docs/内存管理/RAM使用分析与优化方案.md) | | AI 开发规范 | `AGENTS.md`(仓库根) | | 开发者入门 | `README.md`(仓库根) | --- ## 12. 变更记录 | 日期 | 变更 | 说明 | |---|---|---| | 2026-10-05 | 初版 | 基于项目全量内容(架构 / 子系统 / UI / 测试 / 硬件网表 / 待办)编写完整产品需求设计文档:产品定位、六层架构与任务模型、V0.1 硬件规格、10 类功能需求(含 2 项待实现)、接口规范、非功能需求、UI 规格、待实现项与路线图、验收判据 | | 2026-10-10 | FR-CFG-4 旧档迁移**取消** | 删除旧固件 `/cfg/netcfg.bin`(NCFG 二进制)的首启自动迁移:旧档若存在则被忽略(不读、不删、不迁移),配置回落工厂默认。理由:该一次性路径把整条配置写链挂进启动期调用图,defaultTask 静态最坏栈链 +600 B,而本仓库不存在携带旧档的设备;`net_cfg_legacy.c/h` 与 `cfg_store_legacy_path()` 一并移除。§7.1「掉电不丢配置」同步 | | 2026-10-27 | **v1.1 全面刷新(以参考产品 CLS100 为横向基准)** | ① 新增 §2.5「与参考产品 CLS100 的能力对照」(12 个能力域逐域给出参考实现 / 本产品现状 / 结论),并据此重写 §2.1 现状口径与 §2.4 产品范围(分"已验收 / 已实现但受制约 / 非本期"三档);② 新增 §4.6「V0.1 硬件能力边界」(H1 双转换器无共同采样触发 / H2 无中点偏置 / H3 无出口开入 / H4 无本地屏 / H5 无 SDIO)作为范围裁剪的硬依据;③ 任务模型由 6 任务更正为 **9 任务**(补 `adcTask` / `iec104Task` / `protectTask`,栈配置改为 words/B 双列),事件位补至 bit8,启动时序补配置段注册与 `adc1_disable` 铁律,线程间通信补双区快照 / SPSC 环 / 脏位;④ **功能需求新增五节**:§5.9 ADC 采集与标定(FR-ADC,含标定折算与前端物理口径)、§5.11 遥测与相量(FR-MEAS)、§5.12 保护功能(FR-PROT,9 元件 + 四源闭锁)、§5.13 定值与运行参数(FR-SET)、§5.14 SOE 与动作记录(FR-SOE)、§5.15 IEC 104 上行(FR-IEC104,含点表推荐响应时间);⑤ 新增 §6.8 104 点表摘要,§6.7 对外 API 补 10 个模块;⑥ §7.2 补采样/相量/遥测/保护/上送七项实时指标,§7.3 内存与栈预算全部换为 2026-10-24 受管快照与 2026-10-22 栈快照,§7.4 门禁改为按族描述,§7.6 补 104 与配置面安全边界;⑦ §8 菜单绑定现状(22/33/42)与内容页分组重写;⑧ §9 改为「缺口总表(G1~G10)+ 推进顺序 + 未接线开关」,删除已闭环的 ADC/104 缺口;⑨ §10 验收补栈/体积门禁、采集与 104 锚点、六条现场项与五项台架闭环用例。**本次只改本文件** | | 2026-10-27 | **v1.2 改为 1:1 复刻口径(以参考工程源码为唯一清单)** | ① **范围口径反转**:产品范围由「按硬件裁剪」改为「参考源码能力全集」,差异只允许来自 5 项物理不可移植项(X1 双核分工 / X2 VxWorks / X3 本地屏 / X4 一次设备接口 / X5 计量通道,§2.4.1);② §2.5 由「12 域能力对照 + 取舍依据」重写为 **「1:1 复刻基线」**,判定依据从参考项目的分析文档改为 **参考工程源码的常量表与模块文件**(§2.5.1 三要素:点表即契约 / 地址即接口 / 模块即元件);③ 新增 **§2.5.2 19 项能力清单**(源码实测):定值 **249 项 / 23 单元**(`g_tSetModelTab`)、104 定值映射 **107 项 `0x8820~0x8891`**(`g_tCommSetParaTab`)、遥测 **31**、遥测模型 35、电度 **8**、开入 **138**、开出 **31**、动作事件 **33**、录波通道 **65**、软压板 **9**、保护元件 **45 模块**、记录队列 **13 类**、自检 3 类型、录波 3 类型 / 6 段、规约 **12 种**、加密 SM2/SM3/ECDSA;④ 新增 **§2.5.3 104 地址空间 8 段表**并 **更正段基址笔误**——参考 `Rly_IEC104.h` 为 1 起编,遥测 `0x4001`、遥控 `0x6001`(v1.1 及更早写 `4000H+` / `6000H+`),§5.15 与 §6.8 同步更正;⑤ 新增 **§2.5.4 定值 23 单元分布表**(WatchDog 63 / SYS3 45 / VolTimeRecv 17 / AutoReCls 16 …,并说明枚举 28 个中 4 个表内无项的原因)与 **§2.5.5 45 元件九类归集表**(含 `PRS_OverCurrent.c` 判据结构对照);⑥ 新增 **§2.5.6 规约全集 13 行表**(对上 9 种 / 对下 3 类 / 加密)与 **§2.5.7 差异归因表**,结论改为「差距全部是进度而非范围」;⑦ **§9 重写为「复刻进度与路线图」**:删除全部「不做」表述,缺口分 **阻塞型**(标定闭锁 / 出口与开入 / 三相与同期 / 电度计量 / 对下采集 / 第三方送检)与 **规模型**(定值 40→249、映射 40→107、元件 9→45、录波子系统、规约 1→12、国密等)两表,条目一律用**语义短语**命名以免与其它文档的 `S`/`A`/`P` 编号族撞名(编号规范 P-8),推进顺序补「点表铺开必须晚于标定」铁律;⑧ §2.1 产品定位补 1:1 复刻目标与三项差距量化(定值 40 vs 249 / 遥测 9 vs 31 / 开入开出 0 vs 138·31);⑨ §6.8 点表摘要逐段补「与参考的差额」说明(遥测缺第二回线与线电压、遥信缺 138 点开入、定值缺整单元)。**本次只改本文件** | | 2026-10-28 | **v1.3 缺口逐项细化(新增 §9.4~§9.12)** | ① **定值**:给出 40 项去向对账(覆盖参考 35 + 本地扩展 5),差额由「+209」更正为 **+214 项**;新增 23 单元逐单元差额表、已覆盖 35 项逐项对照(过流三段每段缺方向 / 低压 / 告警 / 低压定值 4 项;零序过流三段 18 项全缺,注意 `PS_3U0_*` 是零序**过压**不是过流;三级负荷只做一级;频率缺「跳闸投退」;电压缺 4 项跳闸 / 告警投退;PT·CT 缺 6 项)、完全未覆盖 163 项逐项清单(`SYS3` 45 / `VolTimeRecv` 17 / `AutoReCls` 16 / `SYN` 13 / `WAVESTART` 12 / `MeaLimit` 10 / `OverCurSpeed` 8 / `OverCurI0` 13 / 小单元 29);② **遥测**:31 点逐点对照,缺 22 点按依赖分三批(线电压与功率 13 / 第二回线 7 / 直流 2),指出「三相电压全落 TPAFE」可一次解锁 6 个线电压点;③ **开入开出**:138 = 软遥信 104 + 硬件 27 + 双点 6 + 多态 1,澄清「104 点 ≠ 104 个端子」;31 开出 = 出口 11 + 信号 20;④ **动作事件**:33 条(421~453)逐条对照,覆盖 9 条;记录参考源码 428/429 **同名**需按事件码区分;⑤ **保护元件**:口径改为模块级 45 → 8,37 个缺失模块逐个给出「缺它的后果 + 依赖」,差额由「+35」更正为 **+37**,指出 `MusterRecov` 总复归是唯一无硬件依赖、可立即开工的元件;⑥ **录波**:14 项硬指标(80 点/周波 = 4000 Hz、6 段、前 5 后 10 返回 2 手动 6 周波、9000 ms、RAM 16、COMTRADE 64/64/16、65 通道)与复刻所需四件东西;⑦ **记录体系**:13 类队列容量表,覆盖 8 类,缺越限 / 闭锁 / COS / 顺控 / 顺控执行;指出「闭锁记录」性价比最高(四源闭锁已全实现却无留痕);⑧ **规约栈**:12 种逐种链路 / 用途 / 工作量特征 + 四层排期,指出对上与对下是两套角色;⑨ 新增 **§9.12 复刻完成判据**(12 条可验证判据)。**本次只改本文件** | | 2026-10-28 | **v1.4 屏板链路口径更正 + 逐字节复刻规格(新增 §5.16、§9.13)** | ① **口径更正**:v1.3 及更早把参考写成"**板载** LCD + 按键"、把本产品远端推流说成"等价替代",**两处都错**。参考源码中主机侧 5 个碰硬件的函数(`KeyGet` / `WriteScreenByByte` / `LCD_LED_Update` / `getRs485Key` / `anyNewKey`)**全树只有声明没有实现**,按键轮询函数名为 `Key_Rs485_Process()`(注释"轮询从485传来的按键值"),屏板插槽号 `CN_SLOT_DISP = 0`(注释"显示板插槽号固定为0")——**参考的屏和键同样在独立板件上、同样靠链路传输**,本产品与之**同构而非替代**;§2.4.1 X3 由「本地人机」改为「**就地屏板(板件本体)**」,并补口径更正说明段;② **§2.5.2 补第 20 项「远端屏与按键链路」**(状态 🔶),能力计数 19 → **20 项**、🔶 9 → **10 项**,§2.5.7 差异归因同步(X3 不再计为"已用等价方案替代");③ **新增 §5.16 远端屏与按键链路(FR-HMI)**:两条链路对照(RS-485 屏板通道 / TCP 7003 总线)、帧格式逐字节、CRC 覆盖范围、接收定界算法、**INIT 应答 29 B 逐字段偏移表**、键值位掩码语义、`bNeedFlash` 续传与拒绝语义(`-1` / `-2`)、按键状态机与合并优先级、三个定时器(500 ms / 1 分钟 / 200 ms)、发送状态机、`0x55`/`0xaa` 刷新互斥、任务参数(prio 111 / 栈 0x35000);④ **§5.16.9 给出 10 项与参考的差异**,其中 5 项为客户端可见的报文口径差(INIT **8 B vs 29 B** 为最大一条);⑤ **§6.2 按参考源码重写**:更正 INIT 应答长度(原写 8 B)、KEEPLIVE 改为**不应答**(原写"原样回显")、补最长帧 9800 B / 收发缓冲 10240 B / 按键码补 `F1`·`F2` 与位掩码可组合说明;⑥ **新增 §9.13 屏板链路复刻任务规划**:10 条语义短语任务 + 三批划分 + 唯一破坏性变更警示(INIT 长度变更必须与客户端同批发版);⑦ §9.1.2 补「屏板链路对齐」行、§9.12 补完成判据、§10.3 补现场验收第 13 项。**本次只改本文件** | | 2026-10-28 | **v1.5 补串口屏板通道(显示信息不只走网络)** | ① **口径再更正**:v1.4 把串口通道写成"链路 A,随 RS-485 簇冻结",等于把它降级成可选——**错**。参考的架构是**一份显存、两个出口**:**串口屏板通道是主通道**(`LCD_LED_Update(显存 3200 B, LED 4 B)` 变化驱动 + `WriteScreenByByte` 500 ms 兜底),**TCP 7003 是镜像**;上行同理(`getRs485Key()` 物理键号 1~10 为主,`g_tRemoteKey` 为镜像,两者在 `Key_Read()` 汇合且**本地优先**)。§5.16 标题改为「**屏板链路:串口主通道 + 网络镜像**」并重写开篇;② **§5.16.1 重写为「一份显存、两个出口」**:显存物理基址 `CN_VIDEO_ADDRESS_BASE = 0xC1F00000`(非 cache 区)、三条下行路径与两条上行路径的归属任务 / 调用 / 载荷 / 节拍对照表;③ 新增 **§5.16.2「本产品的串口通道(待建)」**:V0.1 串口资源盘点(USART1 调试占用 / UART4 被冻结 RS-485 占用 / **UART5、USART2、USART3 空闲**),默认选 **USART3(PB10/PB11)**,若屏板为 RS-485 半双工则改 UART4 并先解冻该簇;④ 新增 **§5.16.3「参考实现的屏板侧接口一览」**,并**明确列出交付源码推不出的四项**(波特率与数据格式 / 帧头与校验 / 半双工方向控制 / 3200 B 是否分片与应答)——在拿到实测前不得声称"按参考协议实现";⑤ **§5.16.9 差异表由 10 行扩到 14 行**,新增「串口屏板通道整条缺失」「双出口并发缺失」「面板指示灯语义不同」「背光对比度」四项,并注明第 11/12 条是**整条链路缺失**而非报文细节;⑥ **§5.16.10 判据由 7 条扩到 12 条**,新增串口侧 5 条(键号注入 / 500 ms 内出帧 / 双通道并发不丢帧不串帧 / 拔插恢复 / 背光);⑦ **§9.13 任务由 10 条扩到 13 条**:原「RS-485 屏板通道」一条拆为「串口通道物理层定版 / 串口下行显存 / 串口上行按键 / 双通道并发回归」四条,批次表第三批改为**独立立项、不随 §5.10 冻结**,并补前置阻塞(需屏板实物验收;拿不到固件或抓包时不得把猜测定成契约);⑧ §5.16.8 `tTimerLCD` 行由"此路不实现"改为"串口通道周期兜底节拍(待建)",§2.5.2 第 20 项、§9.1.2、§9.12、§10.3 同步。**本次只改本文件** | | 2026-10-28 | **v1.6 屏板源码到手:串口通道协议定版 + 状态灯下行** | ① **屏板固件已获得**(`D:\Code\DTU Code\DTU-LCD`,STM32F103ZE + UC1698U 160×160 单色 + **20 路状态指示灯** + 7 个物理按键),v1.5 列的"四项未知"**全部消解**;② **§5.16.2 重写为「串口通道:物理层与帧格式(屏板源码实测)」**:**700000 8N1**(APB1 42 MHz 分频零误差、`BRR = 0x3C0`)、软件 DE、**角色反转(屏板是 Modbus 主站、主机是从站 `0x01`)**、帧 A 6 B 召唤(`01 03 type key CRC_H CRC_L`,`KEY_CMD=0` / `RT_CMD=1`,CRC-16/MODBUS 只覆盖前 4 B、**高字节在前**)、帧 B **4208 B** 图像应答(3200 显存 + 4 B 状态灯 + 4 B info + 998 B 填充 + 2 B CRC,覆盖 `[0, 4205]`)、时序 275 ms / 200 ms / 5 次超时、200 ms 预算下 4208 B 约 60.1 ms(占 30 %)、主机端口默认 **USART3 + 另配 DE GPIO**(**不必解冻 UART4**);③ **§5.16.3 重写为「屏板侧行为:状态灯 / 按键 / 背光 / LOGO」**,新增**状态灯下行**:`info[0..3]` 为 **32 位小端位掩码**、**ID 0 = 电源灯硬件常亮、ID 1~19 对应 bit1~19**,给出 20 路 LED 引脚表,并指出"**位 → 业务状态映射表参考源码没有、须由本产品自定**";键值 `KEY_TYPE` 表(`0` 无键 / `1` ENTER / `2` UP / `3` DOWN / `4` ESC / `5` ADD / `6` DEC / `7` LEFT / `8` RIGHT / `9` RESET / `10` FACTORY),并**指出与参考主机 `getRs485Key()` 的 `9` / `10` 语义冲突**(参考是 `F1` 与 `F1+ENT`)、`key_configs[]` 仅 7 项且无 ADD/DEC 入口;背光自管、LOGO(Flash `0x0803F000`、魔数 `0x89 0x45`)、屏板健壮性(CRC 错 ≥3 仅清计数**不自愈**、**≈5000 s 自复位**、DMA 接收问题未解决);④ **§5.16.9 差异表**第 8 / 11 / 12 / 13 / 14 行按屏板实测重写,并新增"来自屏板固件的新增约束"5 条;⑤ **§5.16.10 判据由 12 条扩到 15 条**(275 ms 召唤 / ≤200 ms 回 4208 B、显存逐字节、7 键、**状态灯逐位**、双通道并发、**自复位容忍**、拔插恢复、**LOGO 下发**);⑥ **§9.13 任务由 13 条改为 15 条**:第 10 条由"物理层定版"改为"**串口物理层落地**"(参数已定)、新增第 12 条"**状态灯位映射表**"与第 15 条"**LOGO 下发流程**"、第 14 条并入"自复位容忍",批次表第三批改 10~15 并注明**不必解冻 UART4**,删除"拿不到固件不得定契约"告警(已消解),保留"**4208 vs 3208 待台架复核**";⑦ **§5.8 补屏板 20 路指示灯与主板 6 路 LED 是两套独立输出**;§5.6 FR-UI-1 改为"**双出口推流**";§6.2 口径注改为"串口通道是 Modbus RTU 风格裸帧、**不能共用组帧代码**";§2.4.1 X3、§2.5.2 第 20 项、§5.16.1、§5.16.8 `tTimerLCD` 行、§9.1.2、§9.12、§10.3 第 13 项同步。**本次只改本文件** | | 2026-10-28 | **UI 现状同步(不 bump 版本号)** | ① 绑定叶数 22→28、未绑 11→5(记录 5 页与运行报告页落地绑页,§8.2 / §8.3 / §9.2);② 像素黄金 53→60 页(新增 7 张,含运行报告第 2 页);③ 字库当前态改 218 唯一字 / 313 字形(122/217、179/274、185/280 均为历史快照,§8.5);④ FR-SOE-3 由「展示层待补」转「软件面已落地,真机判据待台架」(§5.14);⑤ §2.4 立项未排去掉已交付的「记录 5 页」、§9.1 差距表 UI 记录页行改写现状与去向。真机判据登记 docs/任务规划/登记簿.md §7;模拟器结构性照不到的部分见 docs/测试说明/模拟器差距清单.md §2.3。 | | 2026-10-28 | **v1.7 单色口径:删除多色协议,INIT 由 29 B 改 16 B** | 用户裁定"屏幕板都是单色的,多色协议可以删除、不必对齐"。**核查后确认这不是让步,而是删死字段**:① **三处源码证据**——`RemoDispBus.h:46` `CN_RDISP_TYPE_MONO = TRUE` → `CN_RDISP_LCD_TYPE = CN_MODE_GREY_MONO`、`CN_RDISP_MEMSIZE = (X>>3)*Y`(链路显存本就是 1 bpp);`RDisp_Form_InitInf` 里 `CN_RGB_RED/GREEN/BLUE` 是**编译期常量**而非逐帧数据;`RDisp_LcdColor_To_Mono()` 对 `CN_MODE_GREY_MONO` **直接 `return TRUE` 不转换**,真正干活的 `RDisp_Lcd16B_To_Mono()`(RGB565→单色)挂在 `CN_MODE_COLOR_16B` 下**本构建永不进入**(死代码);② **§5.16.5 INIT 应答表由 29 B 重写为 16 B**(宽 2 + 高 2 + 显存 4 + 8 键值表),删掉 1 B 颜色位 + 12 B RGB,并补"为什么是 16 B"的证据块;③ **§6.2 命令集 INIT 行**同步改 16 B 并加"单色口径"注(颜色位与 RGB 整段删除、`RDisp_LcdColor_To_Mono()` 不存在);④ **§5.16.6 第 6 条注**改为"该函数在参考单色构建下就是空转",避免误以为本产品漏了一步转换;⑤ **§5.16.9 第 1 行**由"补 21 B"改为"**补 8 B 键值表 → 16 B**,颜色 13 B 不对齐";"新增约束"补"单色"一条;末尾警示改为 **16 B vs 29 B 不可互换**(从偏移 4 起含义即不同,客户端不得复用参考 29 B 解析器截前 16 B);⑥ **§5.16.10 判据第 1 条**改 16 B 且"不得出现颜色位或 RGB";⑦ **§9.13 第 2 条**改"INIT 应答补足 **16** 字节 / 不加颜色位与 RGB"、第 9 条客户端按 16 B 解析、破坏性变更警示改 16 B(后 8 B 而非后 21 B)并补"不可与参考 29 B 互换";⑧ **§2.4.1 补"多色显示不在本表内"**——参考与本产品都是单色链路,删的是死字段,**不属于"决定不做"的能力**;§2.5.2 第 20 项、§9.1.2、§9.12、§10.3 第 13 项同步;⑨ **§8.1 色深行**补"无灰度、无彩色,7003 INIT 颜色字段已删",渲染行改为"串口屏板通道(主)+ TCP 7003(镜像)双出口"。**本次只改本文件** | | 2026-10-28 | **v1.8 端口重排:屏板占 USART1,日志迁 RS-232 通道** | 用户裁定"把现在的日志 UART 让给屏幕,RS-232 通道后面用来输出日志"。**这条裁定把 v1.6/v1.7 的头号阻塞直接消掉了**:① **§5.16.2 新增「端口裁定」块**——屏板改走 **USART1(PA9/PA10,MCU 直出 TTL、不经任何收发器)**,因此 `U15 = TP8485E-SR` 只有 250 kbps 这件事**对屏板链路不再构成约束**;日志迁 **RS-232 通道**(V0.1 板上 RS232#3 = USART2 PD5/PD6 为默认,RS232#1 = UART5 PC12/PD2 为备选,两者都经隔离器 → U13 TPT3232E → CN1);② **纠正分频算法**——旧文写"`42 000 000 / 700 000 = 60` → `USARTDIV = 60.0`、`BRR = 0x3C0`"**漏了 16 倍过采样**,照此写实际波特率只有 43 750;正确值:USART1(APB2 84 MHz)`USARTDIV = 7.5` → **`BRR = 0x0078`**,APB1 实例(42 MHz)`USARTDIV = 3.75` → **`BRR = 0x003C`**,两者误差均为 0;③ **§5.16.2「主机侧端口选型」整表重写为「主机侧端口重排」**,按 `docs/硬件平台/V0.1_网表详细分析报告.md` §4.2 的三对 UART 轮换列明 RS485#4 = UART4(DE = PA15)、RS232#1 = UART5、RS232#3 = USART2,并补两条实现约束(`console_log.c` 里 `huart1` 5 处 + 自愈函数直写 `USART1_IRQn` / `MX_USART1_UART_Init()` 需收口成一处端口宏;新日志口必须 `UART_MODE_TX`,否则重演 ORE 锁存);④ **DE 口径改为「TTL 直连无 DE」**,仅"经外部 485 模块接 A/B"时才需处理,且极性由该模块决定;⑤ **主机 RX 口径**由"吃下 4208 B"改为"只需 6 B(16 B 缓冲够)",并说明 TTL 直连无自发自收;⑥ **§4.2 外设接口表**:USART1 行补"端口重排后改作屏板链路 + PA10 目前未接线",新增 USART2(RS-232 #3,日志口)与 UART5(RS-232 #1,备选)两行,UART4 行注明 V0.1 轮换依据;⑦ **§5.16.9「新增约束」**与 **§9.12** 的 700000 行同步为"USART1 TTL 直连、无 DE";⑧ **§9.13 新增第 16 条「日志口迁移」**并置于第三批首位,批次表注明 **16 必须排在 10 之前**(两者共用 `USART1->DR`);第 10 条改为 `BRR = 0x0078`、无 DE、`DMA2_Stream7/2` 现成;**前置阻塞**补两条物理前置(PA10 接线、台架串口线从 USART1 挪到 CN1)。**本次只改本文件** | | 2026-10-28 | **v1.9 撤销"PA10 未接线",ORE 机理改正** | 用户反馈"PA10 这个端口我接线了,当时分析出这个问题来,我怀疑是错误的"。核查后确认:**`App/log/console_log.c`(文件头与 `con_uart_reinit()` 注释)与 `docs/子系统设计/控制台单写者日志设计说明.md` §12 里的"PA10 悬空 / 未接线"是 2026-10-02 的归因推断,不是实测**,且与用户确认的接线状态相反。改动:① **§4.2 外设接口表** USART1 行删掉"PA10 目前未接线、作屏板链路前必须接上",改为"PA10 双向均已接线";② **§5.16.2** 原"USART1 的 RX 目前没有接线"警示块整段重写为「旧结论已撤销 + ORE 真正机理」——ORE 的充分条件是**RX 使能却从不读 `DR`**(任意一个字节或噪声沿使 `RXNE` 置位后无人读,下一个事件即锁存 ORE,且只能靠"读 SR 再读 DR"清除 ⇒ **永不自愈**),与引脚悬空无关;据此补两条硬要求(屏板链路必须真的消费 `DR` 并在 `HAL_UART_ErrorCallback` 里清 ORE 后重挂接收;新日志口设 `UART_MODE_TX` 仍正确,因为那是从机制上摘掉 RX);③ **§9.13 前置阻塞**由"两条物理前置"改为"一条物理前置(台架串口线挪位)+ 一条实现纪律(RX 要真读、ORE 要真清)",并写明原"PA10 接线"结论已撤销;第 11 条的验收隐含"拔插 TX 线制造噪声后能自动恢复"。**当年那场 ORE 风暴的真因仍待定**——接线正常时更常见的是对端波特率不匹配或适配器 TX 在 USB 掉线时浮空,下次复现先量空闲电平与波特率,不要再默认"引脚悬空"。**本次只改本文件** | | 2026-10-28 | **像素黄金口径更正(不 bump 版本号)** | 正文两处当前态由「60 页」改报「60 张」:判据本体 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 60 = `check_content_pages.py` 的 `PAGES` 条目数 60,其中 2 张是分页页多拍的第 2 屏 ⇒ 张数 ≠ 页数(同一句里「+7 张」与「60 页」并存就是口径混用的后果)。历史行 53 → 60 与更早的 27 / 40 / 47 一律按当日事实冻结,本行只追加。 |