From 8f8d97b3d8e07598d07249d4409b79f8e883b59d Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Wed, 28 Oct 2026 18:57:03 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E7=BC=96=E5=8F=B7):=20=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=E5=85=AD=E4=BD=99=E8=B4=A6=E6=96=87=E6=A1=A3=E9=9D=A2=E2=80=94?= =?UTF-8?q?=E2=80=94CubeMX=20=E5=B1=82=E5=8F=B7=E3=80=81=E5=8F=8C=E5=86=99?= =?UTF-8?q?=E7=96=A4=E3=80=81=E9=87=87=E9=9B=86=E9=93=BE=E6=97=A7=E5=B1=82?= =?UTF-8?q?=E5=90=8D=E5=85=B1=2061=20=E7=AB=99=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CubeMX 防丢失三层优先级 L1/L2/L3(批次 3c 漏网 30 处):看门狗设计说明 25、 门禁说明/cubemx-survival 3(含「优先 L1/L2」→「优先写进 .ioc 与搬进 USER CODE 区」)、ADC通道映射 2。 - 改名遗留的中文重叠双写 14 处:菜单页面设计「一级菜单菜单」×6、「二级菜单菜单」 ×2;README「语义取数与有效性」重复;存储架构「3字节字节地址」;链路延迟 「队列往返队列往返」;继电保护「出口出口与记录层」;产品需求「全周傅氏相量」重复。 - 采集链旧层名 5 处:「L2 相量层」→ 全周傅氏相量、「L0 采样驱动」→ 采样驱动层、 「L2-x 前缀」→ 相量输出标签前缀、「L1 层 v2.3」→ 语义取数与有效性层 v2.3。 - UI 菜单层级 2 处:产品需求「4 级(L0~L3)」→「4 级(主界面 / 一级 / 二级 / 三级菜单)」、「主界面 (L0)」→「主界面」。 - 登记簿条目改名「L5 队列往返基线待重验」→「队列往返基线待重验」+ 引用侧 5 处 同步(登记簿 §1/§7、实机测试实施指南 §7.3 与步骤表、编号规范 §1 好例子清单—— 那个混在短语里的 L5 恰恰是个坏例子)。归档里的历史 note 原文不动。 - 归档 §10.3:四族收口记录 + 边界与裁定(peer JSON 键属冻结证据 schema 不改; REQ2FIRST / FULLFRAME 不并入 FRAME_RT;L1-3/L1-4 属不可还原死号,就地删号留 事实;「原 L2 基线 3/10」按上下文读作主机侧回归的早期基线)。 - 判族保留(永久):硬件位号与封装尺寸 27 处(网表 L1~L3 磁珠电感、 SOT-23_L2.9-W1.3、CRYSTAL-L3.2)、芯片手册原文 3 处双写;伪命中 4 条没删 (「遥测量 测量值」「已登记登记簿」「谁来监视监视者」×2)——那些是正常词组。 - 门禁:check_doc_links --all --sections-strict 通过(195 篇、章节引用 3292 处); git diff --check 干净;逐文件 BOM / U+FFFD / CRLF 与 HEAD 一致。 --- docs/README.md | 2 +- docs/UI设计/菜单页面设计.md | 18 +++++----- docs/产品需求/产品需求设计文档.md | 6 ++-- docs/任务规划/登记簿.md | 4 +-- docs/子系统设计/看门狗设计说明.md | 40 +++++++++++------------ docs/架构设计/STM32F4-Base存储架构说明.md | 2 +- docs/测试说明/实机测试实施指南.md | 4 +-- docs/测试说明/相量层单元测试说明.md | 2 +- docs/测试说明/自动化测试流程.md | 2 +- docs/测试说明/链路延迟测试说明.md | 4 +-- docs/硬件平台/ADC通道映射说明.md | 6 ++-- docs/算法/继电保护功能复现规划.md | 6 ++-- docs/编号规范.md | 2 +- docs/门禁说明/cubemx-survival.md | 6 ++-- scripts/archive/id_gate/编号治理历史.md | 34 +++++++++++++++++++ 15 files changed, 86 insertions(+), 52 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1671f2cf..752f7f08 100644 --- a/docs/README.md +++ b/docs/README.md @@ -104,7 +104,7 @@ docs/ | 文档 | 内容 | |---|---| | [`傅氏算法详解.md`](算法/傅氏算法详解.md) | 全周波傅氏(单频 DFT)原理与工程实现:正交性、梳状带通、滑动窗、系数表折算有效值;保护/遥测双轨有效值的算法基础 | -| [`采集与算法层设计说明.md`](算法/采集与算法层设计说明.md) | **`App/algo/` 的现状设计说明**:语义取数与有效性语义取数与有效性判定、全周傅氏相量采样环/相量/测频、保护判据定值模型与元件框架的契约、关键机制(双区原子切换、乒乓半区、自适应测频窗、定值封存-自检-闭锁)、精度判据 J1~J6 与资源足迹 | +| [`采集与算法层设计说明.md`](算法/采集与算法层设计说明.md) | **`App/algo/` 的现状设计说明**:语义取数与有效性判定、全周傅氏相量采样环/相量/测频、保护判据定值模型与元件框架的契约、关键机制(双区原子切换、乒乓半区、自适应测频窗、定值封存-自检-闭锁)、精度判据 J1~J6 与资源足迹 | | [`继电保护功能复现规划.md`](算法/继电保护功能复现规划.md) | **保护功能复现规划**:把参考系统(CLS100)保护链路映射到本项目(STM32F4)的分层功能/性能/参数清单、平台适配(「双转换器无共同采样触发」等)与分阶段路线图 | | [`采样有效性校验设计说明.md`](算法/采样有效性校验设计说明.md) | **`App/algo/adc_health.c` 单文件深设计**:四类判据(静默 / 丢帧 / 序号倒退 / 错误计数增量)的判据推导与 100 ms 门限依据、电平-锁存-事件三种位语义与位号对齐、闭锁与看门狗兜底的责任划分、观测盲区、主机侧验证分工与现场判读手册(含三张配图生成器) | diff --git a/docs/UI设计/菜单页面设计.md b/docs/UI设计/菜单页面设计.md index a5562824..97442903 100644 --- a/docs/UI设计/菜单页面设计.md +++ b/docs/UI设计/菜单页面设计.md @@ -297,37 +297,37 @@ const ui_menu_node_t g_menu_tab[UI_MENU_TAB_COUNT] = { /* parent name 下标 */ { MENU_ROOT, "主界面" }, /* 0 主界面菜单(根) */ { 0, "装置信息" }, /* 1 一级菜单叶子 */ - { 0, "实时数据" }, /* 2 一级菜单菜单 */ + { 0, "实时数据" }, /* 2 一级菜单 */ { 2, "交流量" }, /* 3 二级菜单叶子 */ { 2, "直流量" }, /* 4 二级菜单叶子 */ { 2, "遥信量" }, /* 5 二级菜单叶子 */ - { 0, "参数定值" }, /* 6 一级菜单菜单 */ - { 6, "整定" }, /* 7 二级菜单菜单 */ + { 0, "参数定值" }, /* 6 一级菜单 */ + { 6, "整定" }, /* 7 二级菜单 */ { 7, "参数" }, /* 8 三级菜单叶子 */ { 7, "定值" }, /* 9 三级菜单叶子 */ { 7, "控制字" }, /* 10 三级菜单叶子 */ { 7, "软压板" }, /* 11 三级菜单叶子 */ - { 6, "查看" }, /* 12 二级菜单菜单 */ + { 6, "查看" }, /* 12 二级菜单 */ { 12, "参数" }, /* 13 三级菜单叶子 */ { 12, "定值" }, /* 14 三级菜单叶子 */ { 12, "控制字" }, /* 15 三级菜单叶子 */ { 12, "软压板" }, /* 16 三级菜单叶子 */ - { 0, "三遥设置" }, /* 17 一级菜单菜单 */ + { 0, "三遥设置" }, /* 17 一级菜单 */ { 17, "遥测死区" }, /* 18 二级菜单叶子 */ { 17, "遥测系数" }, /* 19 二级菜单叶子 */ { 17, "遥信类型" }, /* 20 二级菜单叶子 */ { 17, "遥信防抖" }, /* 21 二级菜单叶子 */ { 17, "双点遥信" }, /* 22 二级菜单叶子 */ - { 0, "装置维护" }, /* 23 一级菜单菜单 */ + { 0, "装置维护" }, /* 23 一级菜单 */ { 23, "时钟设置" }, /* 24 二级菜单叶子 */ { 23, "强制复归" }, /* 25 二级菜单叶子 */ { 23, "手动录波" }, /* 26 二级菜单叶子 */ { 23, "清除记录" }, /* 27 二级菜单叶子 */ - { 0, "通讯参数" }, /* 28 一级菜单菜单 */ + { 0, "通讯参数" }, /* 28 一级菜单 */ { 28, "通讯设置" }, /* 29 二级菜单叶子 */ { 28, "网口设置" }, /* 30 二级菜单叶子 */ { 28, "SNTP设置" }, /* 31 二级菜单叶子 */ - { 0, "记录查询" }, /* 32 一级菜单菜单 */ + { 0, "记录查询" }, /* 32 一级菜单 */ { 32, "SOE记录" }, /* 33 二级菜单叶子 */ { 32, "事故记录" }, /* 34 二级菜单叶子 */ { 32, "操作记录" }, /* 35 二级菜单叶子 */ @@ -887,7 +887,7 @@ bool ui_content_page_is_open(void); /* 查询是否有内容页 | 2026-09-04 | 改名 `menu_children` → `menu_get_children`、`menu_name` → `menu_get_name`(裸名词改"动词+对象",API 统一 `has_`/`get_` 风格);§6.4 建页片段改用 `s_path`/`s_depth`/`s_cont` 现算模型(条目不落地) | | 2026-09-04 | §6.4 补"签名约定"说明:`node`/`out` 取 `uint16_t` 因与 `parent`(须装 `0xFFFF` 根哨兵)同域、**非容量所需**(42 下标 8 位够);`max`=出参缓冲容量(防越界 + 对齐单页行数,今日 9≤16 不触发截断) | | 2026-09-04 | **索引位宽 16→8 位**:`MENU_ROOT` 由 `0xFFFF` 降为 `0xFF`,`parent`/`node`/`out`/`s_path[]` 均改 `uint8_t`(菜单上限 255 项,远够);路径历史 13→9 B、省 ~20 B RAM,Flash 不变(结构体因指针仍 8 B/项);双护栏=生成器 `节点数>255 报错` + 头文件 C99 断言 `menu_tab_count_check`。生成器已改并重生成 `ui_menu_tab.c/.h`,MSVC /W4 编译 + 导航测试(根 7 / 整定 4 / 查看 4 / 记录查询 9)通过;同步 §四/§6.2/§6.4/§6.5/§七。取代上一条"签名约定(uint16_t)"决定 | -| 2026-09-04 | 修正 §四"进入/返回示例":返回行节点顺序原与进入行相反(参数在左)致 `←` 被读成"前进"方向;改为与进入行**同序**(主界面主界面 在左…参数 三级菜单在右)+ `←`,并补"怎么读"注(`→`/`←` = 进/退动作方向;返回 = 从最右 参数(三级菜单) 沿 `←` 一级级退回最左 主界面) | +| 2026-09-04 | 修正 §四"进入/返回示例":返回行节点顺序原与进入行相反(参数在左)致 `←` 被读成"前进"方向;改为与进入行**同序**(主界面 在左…参数 三级菜单在右)+ `←`,并补"怎么读"注(`→`/`←` = 进/退动作方向;返回 = 从最右 参数(三级菜单) 沿 `←` 一级级退回最左 主界面) | | 2026-09-04 | **子页面显示模型定稿(横向级联子框)**:§四 新增 ⑧"显示模型"——横向树,每级一框在上一级**右侧**、顶边对齐上级"选中行"行顶;水平**右锚定**(最深框钉右缘、父框超宽滚出左、行按屏上/外建毁)+ 框宽文字自适应 + **无动画**;垂直每框独立(顶边=上级选中行行顶、框高按内容、**仅底边>160 才上移**贴屏底、比整屏高则 `↑`/`↓` 滚);焦点=最右框(`↑`/`↓` 移选/滚、`→`/`ENTER` 进、`←`/`ESC` 关最右框)。§七 同步:行对象按层 `s_row[d][i]` 仅屏上框建、去掉逐框页头(`s_header`/`s_back_btn`/`s_title`)、新增每框滚动 `s_scroll[]`(8 B)、"存 vs 算"增 滚动=存/位置=算、持久态 9→17 B、内存预算(静态 ~0.75→~1.3 KB、LVGL 池=屏上 2~3 框)、对照表更新 | | 2026-09-04 | **§四⑧ 按参考图 02~07 像素实测校准**(设备 px,误差 ≤3px):层级树/命名/箭头/选中态全部与图一致 ✓;显示模型两处修正——水平由"右锚定(钉屏幕右缘)"改为"**子框左缘=父框右缘**(共享边框列、零间隙)+ 宽自适应"(实测各子框均贴父框右缘 x≈67/106,最深框止于 x≈113~127 而非屏右缘);垂直溢出由"上移使底边贴屏底"改为"**上移使子框底边对齐父选中行行底**"(实测三遥 top=26/装置维护 top=54/通讯参数 top=82/记录查询 top=12 均吻合,且可叠到屏顶状态行区、顶部不钳制)。本菜单最深路径 ≈155<160 不会溢出;>160 行为与"根页框宽 99 vs 68"列为待确认。§7.2/§7.3/§7.4 "右锚定"表述同步 | | 2026-09-04 | **实现落地,本文档与代码同步**:三层拆分实现(`ui_menu_tab` 生成物 + 新增 `ui_menu_view` 显示层、`ui_menu` 瘦身为纯状态机,§8.1);§四⑦/§6.4/§6.5/§6.6/§6.7 函数与 API 名同步为实际 `ui_menu_tab_*`/`ui_menu_view_*`/`menu_open\|menu_back\|menu_key_handle`;§五"待实现"→"已实现";§七 修正为实际字段(容器从不切 HIDDEN、`s_style_frame`、新增框几何缓存 `s_frame[]`,§7.2 G);§四⑧ 实测值区分"参考图/实现"两套(实现:根框 66、子框 42/46/54/6,顶 28/58/88/13,偏差入 §8.8 注 5),>160 溢出"待确认"落定为实现行为"屏缘裁切";新增 **§八 实现架构与关键实施细节**(布局常量/几何公式/对象模型/键路径/初始化时序/分级验证/6 条陷阱与保真度说明);验证:模拟器 7 状态对照参考图 01~07 + `@build` 0 错 0 警 | diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index be4f50f0..fb63323b 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -1608,7 +1608,7 @@ mac=00:00:00:00:00:00 | `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` | 全周傅氏相量全周傅氏相量(双区) | +| `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` | 保护判据定值模型(原子写 + 封存) | @@ -1818,7 +1818,7 @@ pre-commit run --all-files ![级联框总览](../images/级联框总览.svg) -**规模**:菜单节点 **9** 个(含根)+ 叶子节点 **33** 个 = **42** 节点;最大深度 **4 级(L0~L3)**。 +**规模**:菜单节点 **9** 个(含根)+ 叶子节点 **33** 个 = **42** 节点;最大深度 **4 级(主界面 / 一级 / 二级 / 三级菜单)**。 **绑定现状**(2026-10-28 实测):**已绑内容页 28 叶 / 未绑 5 叶**(页面注册表 `UI_PAGE_MAP_COUNT` = 28, 与已绑叶数一致);未绑 5 叶 = 无开入硬件 3 + 手动录波 1 + SNTP 1(取舍与重开条件见 docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 页 + 运行报告)已于 2026-10-28 落地绑页。 @@ -1828,7 +1828,7 @@ docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 **完整层级树**: ```text -主界面 (L0) +主界面 ├─ 装置信息 [叶子] ├─ 实时数据 ▶ [菜单] │ ├─ 交流量 ├─ 直流量 └─ 遥信量 diff --git a/docs/任务规划/登记簿.md b/docs/任务规划/登记簿.md index d1d48ff7..69053de8 100644 --- a/docs/任务规划/登记簿.md +++ b/docs/任务规划/登记簿.md @@ -10,7 +10,7 @@ | # | 事项 | 发现 | 证据与现状 | 出处 | |---|---|---|---|---| -| L5 队列往返基线待重验 | **L5 队列往返基线(1223/988µs)的 L3 后重验**——L5 测 DONTWAIT 队列全程;L3 对该路径逐位不变 → 机制上无影响,但基线数值属分片时代,严格重锁需跑一轮 Phase 13(需 PC peer) | 2026-09-22(L3 收尾留档) | 低风险:10ms/100ms 阈值余量 >8×;下次 Phase 13 窗口顺带重锁即可 | `链路延迟测试说明.md` §6.4 L5 注¹ | +| 队列往返基线待重验 | **队列往返基线(1223/988µs)在发送分块握手改造后的重验**——队列往返测 DONTWAIT 队列全程;发送分块握手对该路径逐位不变 → 机制上无影响,但基线数值属分片时代,严格重锁需跑一轮 Phase 13(需 PC peer) | 2026-09-22(发送分块握手收尾留档) | 低风险:10ms/100ms 阈值余量 >8×;下次 Phase 13 窗口顺带重锁即可 | `链路延迟测试说明.md` §6.4 队列往返行的注¹ | | 网络负向场景台架验证 | **负向场景台架验证待办**(拔线断链 / 芯片级 `net_init` 失败分支 / 短写压测;正常路径已实测零回归):① PHY 拔线负向测试——断链需断 **DTU 侧**线缆或关**受管交换机端口**;② 芯片级 `net_init` 失败分支(需 SPI 故障注入);③ 短写压测(需远显客户端 + UI 推流压力) | 2026-10-03(台架拓扑 = CH395↔交换机,拔线操作受限) | 待专项台架窗口 | `实机测试实施指南.md` §10 | | reconcile 突发饿死 uiTask | **对时 reconcile/relisten 突发(0~4s 窗)把唯一 BelowNormal 的 uiTask 压成同一任务 800ms+ 单串缺席(饿死型,非等待型——F3 返回点喂位射程外)**:8 连窗级 UI 缺席期间喂狗面本身未断(cf=0 seg=1)、uisn=0(不在 sink 段内)、uip 在途 307→1083ms 而 uis 峰仅 183ms——被拖的 1s 在 feed/port 纯计算段墙钟。**2026-10-25 台架两轮分箱定案**:同负载换 :7003 churn 2200 周期 stall=0(阻塞型已被 F3 治愈),探针轮 3 事件 8 行全为该签名(漏检零、误报零,v1.1 口径 FAIL 面精确收缩)。处置方向 = 自愈设计 §11.3 脆弱点 1 实测样本并档「net 层跨上下文收口」项:优先级矩阵复核与 reconcile 突发分片让路两案并评;**排期 = UI 完成后的第一项**,与裸指针收口同批 | 2026-10-25(F2/F3 验收轮②,v1.1 判据下捕获) | `docs/测试说明/boot_log_data/f3rounds_r2_probe_30min.log.gz`(3 事件 8 行 + STALL 证据行 + 同行 PROF);对照轮 `f3rounds_r1_7003_churn_30min.log.gz`;机理归档 `../网络/网络自愈架构设计.md` §4.6、`../子系统设计/看门狗设计说明.md` §11.3、`../UI设计/UI_设计架构.md` §5.8 | `../算法/继电保护功能复现规划.md`(裸指针收口项属主文档指针) | | relisten 沉降段原语化受阻 | **relisten 沉降段统一到驱动 `sock_reset` 原语受阻**——"去重复实现"的收口已实测回退:原语版 `ftp_abort_probe` RST **0/10**、FIN **0/5**(中止后首笔复连仍被截断到 ≤2048B),另新增 `sock_reset_sync timeout (600ms)` 与 `close: sock_reset not done` 两条 `[ERR]`;同场 net 层自建序列 **10/10 + 5/5** | 2026-10-14(同一块板 A/B,判据 `test/host/ftp_abort_probe.py`;快复连 50/50、背靠背 30/30 两版都过 → 失效面精确落在"中止后首笔复连") | **现状 = `net_listener.c` 保留自建排渣序列**(排空 + CLOSE + 排事件 + 等 CLOSED,30 ms 重试轮持续清)。重开前置三条语义:① 隔离窗内**持续**排空/关闭(残渣在 CLOSE 之后才落进 RX,一次排空接不住)② **复用让位**(复位机不得拆上层刚在同一芯片 Socket 上重建的监听)③ `net_sock_reset_sync` 改"已在运行 = 加入等待"(`begin` 返 -1 ≠ 失败) | `../网络/网络自愈架构设计.md` §8 延后项 1、§5.2 E16;`../问题记录/CH395F_Trap_Records.md` TRAP-NET-18 | @@ -62,7 +62,7 @@ |---|---|---|---| | **数据可信两项** | 子板实物或原理图;标准源 | 端子/子板变比确认 → 8 通道两点标定 | 唯一任务卡与验收在 `数据可信.md` §5 | | **标准源送检与第三方 104 验收** | 两点标定完成、保护 VALID 置位、第三方主站 | 标准源注入判据全部通过;总召、周期、变位、遥控全流程通过;证据归档。送检窗口前 2 日冻结特性 | `../子系统设计/IEC104上行设计说明.md` §7/§8 | -| **L5 队列往返基线待重验** | 台架 + PC peer | 下次 Phase 13 窗口重锁基线 | 本文件 §1 | +| **队列往返基线待重验** | 台架 + PC peer | 下次 Phase 13 窗口重锁基线 | 本文件 §1 | | **网络负向场景台架验证** | 拔线、SPI 故障注入、远显压力条件 | PHY 拔线、芯片级 `net_init` 失败、短写压测 | 本文件 §1 | | **串口助手正路径补验** | CH340 接入 | 补齐尚未覆盖的正路径 | `../测试说明/串口助手设计说明.md` | | **CI 远端闭环未验证** | runner 与第二台机器 | 验证远端两 job 闭环和不同工具路径 | 本文件 §4 | diff --git a/docs/子系统设计/看门狗设计说明.md b/docs/子系统设计/看门狗设计说明.md index 7aed61cc..6f69b370 100644 --- a/docs/子系统设计/看门狗设计说明.md +++ b/docs/子系统设计/看门狗设计说明.md @@ -106,7 +106,7 @@ T_任务循环 < 监视窗口(100ms) < 停喂判定(同一任务连续缺席 800 | 层 | 落点 | 内容 | |---|---|---| -| **L1(`.ioc`)** | `IWDG.Prescaler=IWDG_PRESCALER_64` / `IWDG.Reload=1249` | CubeMX 生成 `Src/iwdg.c` 的 `hiwdg.Init.*`,重生成不丢 | +| **写进 `.ioc`** | `IWDG.Prescaler=IWDG_PRESCALER_64` / `IWDG.Reload=1249` | CubeMX 生成 `Src/iwdg.c` 的 `hiwdg.Init.*`,重生成不丢 | | **代码(判定侧)** | `WD_IWDG_PRESCALER=4` / `WD_IWDG_RELOAD=1249` | **真正写寄存器的是这两只宏**(见 §2.4) | | **喂狗/启动** | 裸寄存器 `IWDG->KR`(不经 HAL 封装) | 100 ms 级高频路径,HAL 封装的 `State` 检查无收益 | @@ -124,7 +124,7 @@ IWDG 是"写键值"式外设(KR 只写,读回恒 0): CubeMX 把 `MX_IWDG_Init()` 插在 `app_main_init()` **之前**,而 `HAL_IWDG_Init()` 第一句就是 `__HAL_IWDG_START()`(立即启动)。启动段(NAND/RTC/ADC)耗时可达数秒且无喂狗点 → 按最坏 1.7 s 必然复位 → **开机死循环**(2026-09-29 实测)。 -**现做法(L2,2026-10-09 起)**:`Src/iwdg.c` 的 `USER CODE BEGIN 0` 里放空实现 + 宏改名,把**本编译单元内**那句生成调用变成空操作——生成区保持 CubeMX 原样,**重生成零 diff**,不必再手工删那一句: +**现做法(搬进 USER CODE 区,2026-10-09 起)**:`Src/iwdg.c` 的 `USER CODE BEGIN 0` 里放空实现 + 宏改名,把**本编译单元内**那句生成调用变成空操作——生成区保持 CubeMX 原样,**重生成零 diff**,不必再手工删那一句: ```c static HAL_StatusTypeDef iwdg_deferred_start(const IWDG_HandleTypeDef *a_h) { (void)a_h; return HAL_OK; } @@ -238,7 +238,7 @@ static HAL_StatusTypeDef iwdg_deferred_start(const IWDG_HandleTypeDef *a_h) { (v ### 4.4 断言与崩溃钩子 -`Inc/FreeRTOSConfig.h:165`(L2,`USER CODE BEGIN 1` 区): +`Inc/FreeRTOSConfig.h:165`(搬进 `USER CODE BEGIN 1` 区): ```c #define configASSERT( x ) if ((x) == 0) { vApplicationAssertFailed(__FILE__, __LINE__); } @@ -258,7 +258,7 @@ static HAL_StatusTypeDef iwdg_deferred_start(const IWDG_HandleTypeDef *a_h) { (v | **`__builtin_return_address(0)` 不可用** | AC5 前端接受但编成库函数调用,MicroLIB 无该符号 → `L6218E`(2026-10-12 台架实证,勿再尝试);ARMCC `__asm` 只有整函数形态且取不到 C 符号(§10.4)。→ **失败点靠"最后一条成功日志"与 `main()` 的 `MX_*_Init` 顺序之间定位**(GPIO→DMA→USART1→SPI2→SPI1→I2C1→UART4→UART5→USART2→USART3→IWDG→ADC1→TIM3→`app_main_init`) | | **日志路径必须内核前安全** | 调用点全在 `osKernelStart` 之前:控制台写者未注册 → `con_append` 走 `dbg_console_write_raw`(DWT/计数有界轮询,`huart1` 未就绪即返回),`sys_clock_get_str` 未初始化打占位串——全程无内核 API(§4.7 与 `AGENTS.md`「平台约束」同源) | -成本 = 仅这 1 条日志(≈90 B Flash,见 `AGENTS.md` 日志成本)。重生成存活:函数体在 `USER CODE Error_Handler_Debug` 区(L2,`KeepUserCode` 逐字保留)+ `check_cubemx_survival.py` 审计项兜底(L3)。 +成本 = 仅这 1 条日志(≈90 B Flash,见 `AGENTS.md` 日志成本)。重生成存活:函数体在 `USER CODE Error_Handler_Debug` 区(`KeepUserCode` 逐字保留)+ `check_cubemx_survival.py` 审计兜底。 ### 4.5 复位原因锁存与优先级 @@ -311,7 +311,7 @@ static HAL_StatusTypeDef iwdg_deferred_start(const IWDG_HandleTypeDef *a_h) { (v | `Drivers/BSP/NET/lftpd/*` | 弱钩子 `lftpd_task_wd_beat()` 的节拍点(事件循环、传输分片、连接等待、卷锁等待) | | `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.{c,h}` | 弱钩子 `nand_ftl_wd_beat()` / `nand_ftl_wd_feed()` 的调用点(FTL 入口与 `wait_busy` 轮询) | | `App/log/sys_log_mcu.c` | `sys_log_evidence()` → `/log/wdev.log` | -| `Inc/FreeRTOSConfig.h` | `configASSERT` 改造(L2) | +| `Inc/FreeRTOSConfig.h` | `configASSERT` 改造(USER CODE 区) | | `test/host/watchdog/` | 判定状态机主机侧单测(`#include` 生产源码 + 强符号桩) | | `MDK-ARM/STM32F407-Demo.uvprojx` | 挂载 `task_watchdog.c` 与 `stm32f4xx_hal_iwdg.c` | | `MDK-ARM/STM32F407-Demo.sct` | `UNINIT_CCM`(0x1000FF00,256 B)承载 `.noinit` 取证槽 | @@ -493,7 +493,7 @@ snapshot == required → miss_streak=0、证据门闩复位、喂狗 看门狗是本项目**第一次**把 CubeMX 生成流程引入可靠性关键路径,由此沉淀出与看门狗本身无关的通用方法论。`AGENTS.md`「平台约束」指向本节,改动 `.ioc`/`Src/`/`Inc/` 前请先读。 -> **2026-10-26(道G task-26)一句话登记,机制不在本文复述**:四个 Fault 与栈溢出 / 断言钩子的**崩溃上报发送口已归一**进控制台单写者(`dbg_console_write_raw` + 一次整行独占,不再自行调用 `HAL_UART_Transmit`),机制正文与残余见 `docs/子系统设计/控制台单写者日志设计说明.md` §4.4.1。本节只关心一件事:这些实现仍须整体待在 `Src/freertos.c` 的 `USER CODE BEGIN 4/5` 区与 `Src/stm32f4xx_it.c` 的 `USER CODE BEGIN 0` 区(§10.2 的 L2),改完跑 `scripts/check_cubemx_survival.py`(§10.3)。同族另一笔只登记指针:断言钩子与临界区在控制台路径上互达会构成**幻影环**,armlink 的 `In Cycle` 记账随环形状波动(同一 port.o 三棵树实测每链差 0~80 B,休眠≠解耦),三树取证、量化表与「configASSERT 裁空宏 ≠ 优化收益」的钉死句见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.7(道G task-38)。 +> **2026-10-26(道G task-26)一句话登记,机制不在本文复述**:四个 Fault 与栈溢出 / 断言钩子的**崩溃上报发送口已归一**进控制台单写者(`dbg_console_write_raw` + 一次整行独占,不再自行调用 `HAL_UART_Transmit`),机制正文与残余见 `docs/子系统设计/控制台单写者日志设计说明.md` §4.4.1。本节只关心一件事:这些实现仍须整体待在 `Src/freertos.c` 的 `USER CODE BEGIN 4/5` 区与 `Src/stm32f4xx_it.c` 的 `USER CODE BEGIN 0` 区(§10.2 的「搬进 USER CODE 区」条),改完跑 `scripts/check_cubemx_survival.py`(§10.3)。同族另一笔只登记指针:断言钩子与临界区在控制台路径上互达会构成**幻影环**,armlink 的 `In Cycle` 记账随环形状波动(同一 port.o 三棵树实测每链差 0~80 B,休眠≠解耦),三树取证、量化表与「configASSERT 裁空宏 ≠ 优化收益」的钉死句见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.7(道G task-38)。 ### 10.1 覆盖史(三次重生成) @@ -501,7 +501,7 @@ snapshot == required → miss_streak=0、证据门闩复位、喂狗 |---|---|---|---| | 1(2026-09-29) | 勾选 IWDG | 4 处:`Src/iwdg.c` 开机死循环(P0)、`HardFault_Handler` 采集被打回空(P1)、`FreeRTOSConfig.h` 两个加固宏整块删除(P1)、`freertos.c` 栈注释(P2) | 覆盖**有限且可枚举**,"重生成 → 逐项恢复 → 编译验证"可固化 | | 2(2026-09-29) | 再次重生成 | 审计脚本检出 5 处;**关键发现**:CubeMX 换了一代中断函数模板(旧自造区 `HardFault_Handler`/`HardFault_Report` → 函数体内 `HardFault_IRQn 0`/`W1_HardFault_IRQn 0`),**生成区之外的自造标记区连同内容一起消失** | 🔴 **只信 CubeMX 当前模板真实生成的区名**,别凭记忆;优先用 `USER CODE BEGIN 0` 这类从未变过的标准位 | -| 3(2026-10-09) | 启用 ADC1 | 同一批定制再次全被抹;`FREE_RTOS_CONFIG_HARDENING` 自造区**第二次**消失;`freertos.c` 三处栈注释第四次丢失 | 加固宏改走 **L1**(`.ioc` 的 `FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2` / `configUSE_MALLOC_FAILED_HOOK=1`,GUI 在 FreeRTOS → **Config parameters** → **Hook function related definitions**);启用后 CubeMX 生成 `USER CODE BEGIN 4/5` **空桩** → 钩子实现必须整体搬进 4/5 区(否则同名两处定义 = 编译错),helper `hook_ptr_ok()` 放 `PD` 区 | +| 3(2026-10-09) | 启用 ADC1 | 同一批定制再次全被抹;`FREE_RTOS_CONFIG_HARDENING` 自造区**第二次**消失;`freertos.c` 三处栈注释第四次丢失 | 加固宏改走 **写进 `.ioc`**(`FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2` / `configUSE_MALLOC_FAILED_HOOK=1`,GUI 在 FreeRTOS → **Config parameters** → **Hook function related definitions**);启用后 CubeMX 生成 `USER CODE BEGIN 4/5` **空桩** → 钩子实现必须整体搬进 4/5 区(否则同名两处定义 = 编译错),helper `hook_ptr_ok()` 放 `PD` 区 | > ⚠ **别凭印象断言"界面里没有该项"**——先翻标签页或查 `db/mcu/IP/*_Modes.xml`:那两个加固宏一直躺在 "Config parameters" 里,因此白丢了两次。 @@ -509,20 +509,20 @@ snapshot == required → miss_streak=0、证据门闩复位、喂狗 | 层 | 手段 | 适用 | 可靠性 | |---|---|---|---| -| **L1 写进 `.ioc`** | 让 CubeMX 自己生成 | 界面能表达的配置 | ⭐⭐⭐ 根治——重生成只会照写 | -| **L2 搬进 `USER CODE` 区** | 移到 `USER CODE BEGIN/END` 之间 | 有真实标记区(**当前模板生成的**)的文件 | ⭐⭐ 有效(`KeepUserCode=true` 时逐字保留) | -| **L3 审计 + 基线** | 脚本定期核对 | 标记区外的存量 | ⭐ 兜底——不防丢,但丢了一定知道 | +| **写进 `.ioc`** | 让 CubeMX 自己生成 | 界面能表达的配置 | ⭐⭐⭐ 根治——重生成只会照写 | +| **搬进 `USER CODE` 区** | 移到 `USER CODE BEGIN/END` 之间 | 有真实标记区(**当前模板生成的**)的文件 | ⭐⭐ 有效(`KeepUserCode=true` 时逐字保留) | +| **审计 + 基线** | 脚本定期核对 | 标记区外的存量 | ⭐ 兜底——不防丢,但丢了一定知道 | -**本项目已落 L1 的项**:任务栈/优先级/名称(`FREERTOS.Tasks01`,**改栈请改 `.ioc`,不要改 `freertos.c`**)、队列 `netMsgQueue`、事件组 `sys_evt`、堆 `configTOTAL_HEAP_SIZE`、FPU、两个加固宏、**IWDG 分频/重载**、时钟树(含 LSI)、`KeepUserCode=true`(必须为 true,否则 L2 整体失效)。 +**本项目已写进 `.ioc` 的项**:任务栈/优先级/名称(`FREERTOS.Tasks01`,**改栈请改 `.ioc`,不要改 `freertos.c`**)、队列 `netMsgQueue`、事件组 `sys_evt`、堆 `configTOTAL_HEAP_SIZE`、FPU、两个加固宏、**IWDG 分频/重载**、时钟树(含 LSI)、`KeepUserCode=true`(必须为 true,否则 USER CODE 区这一层整体失效)。 -**生成区内那句"必须去掉"的调用 → 用宏改名升级为 L2**(2026-10-09 定,本案两处): +**生成区内那句"必须去掉"的调用 → 用宏改名升级为「搬进 USER CODE 区」**(2026-10-09 定,本案两处): - `Src/iwdg.c`:`USER CODE BEGIN 0` 放空实现 + `#define HAL_IWDG_Init(a_h) iwdg_deferred_start(a_h)` → 生成区原样、**重生成零 diff**(§2.4)。 - `Src/stm32f4xx_it.c`:4 个 `__asm void X_Handler` 放 `USER CODE BEGIN 0`,**必须写在 4 个 `#define X X_Gen` 之前**(写在之后会被自己的宏改名);生成实体经宏改名让位,链接器自动移除(`.map` 各 `Removing … (2 bytes)`),向量表仍指向我们的 `__asm` 版。 -> **L3 清单已清空**:所有定制要么在 `.ioc`(L1)、要么在 `USER CODE` 区(L2)——**重生成应当零 diff**。剩余 1 项 WARN 是 `Src/freertos.c` 的 `timeSyncTask` 手工定义(属性块在 `USER CODE BEGIN Variables` 区,本身受保护,WARN 只提示"它不在 `.ioc` 里")。**`adcTask`(B1,2026-10-10)同为手工定义**——属性块在同区、`osThreadNew` 在 `USER CODE BEGIN RTOS_THREADS` 区,同样受保护、同样不在 `.ioc` 里(B1 原计划走 L1,实际按 timeSyncTask 先例落 L2:避免为单任务触发全工程重生成)。 +> **审计兜底清单已清空**:所有定制要么写进 `.ioc`、要么搬进 `USER CODE` 区——**重生成应当零 diff**。剩余 1 项 WARN 是 `Src/freertos.c` 的 `timeSyncTask` 手工定义(属性块在 `USER CODE BEGIN Variables` 区,本身受保护,WARN 只提示"它不在 `.ioc` 里")。**`adcTask`(B1,2026-10-10)同为手工定义**——属性块在同区、`osThreadNew` 在 `USER CODE BEGIN RTOS_THREADS` 区,同样受保护、同样不在 `.ioc` 里(B1 原计划走「写进 `.ioc`」,实际按 timeSyncTask 先例落「搬进 USER CODE 区」:避免为单任务触发全工程重生成)。 -**为什么 `fault_handler_enable()`(SHCSR 使能三位)写不进 `.ioc`**(2026-10-01 查模板源码三重证据):① `tpl_main_c.ftl` 的 `MX_NVIC_Init()` 只在 `systemHandler=="false"` 时生成 `HAL_NVIC_EnableIRQ`,而三个 Fault 在 `NVIC-STM32F417_Modes.xml` 都标 `Y`;② 全部模板中 `SHCSR` 只出现 1 次且是 MPU 清理的 `&= ~MEMFAULTENA`(禁用),无任何 `|=`;③ `.ioc` 无 MPU,且 `HAL_MPU_Enable` 只置 `MEMFAULTENA` 一位。⚠ `.ioc` 的 `NVIC.MemoryManagement_IRQn=true` **不代表已使能**(只是向量可见)。现走 L2(`USER CODE BEGIN 1`)+ 审计项兜底。 +**为什么 `fault_handler_enable()`(SHCSR 使能三位)写不进 `.ioc`**(2026-10-01 查模板源码三重证据):① `tpl_main_c.ftl` 的 `MX_NVIC_Init()` 只在 `systemHandler=="false"` 时生成 `HAL_NVIC_EnableIRQ`,而三个 Fault 在 `NVIC-STM32F417_Modes.xml` 都标 `Y`;② 全部模板中 `SHCSR` 只出现 1 次且是 MPU 清理的 `&= ~MEMFAULTENA`(禁用),无任何 `|=`;③ `.ioc` 无 MPU,且 `HAL_MPU_Enable` 只置 `MEMFAULTENA` 一位。⚠ `.ioc` 的 `NVIC.MemoryManagement_IRQn=true` **不代表已使能**(只是向量可见)。现走 USER CODE 区(`USER CODE BEGIN 1`)+ 审计项兜底。 ### 10.3 审计脚本 @@ -541,7 +541,7 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建 ### 10.4 ARMCC `__asm` 的三条语法约束(2026-09-29 踩坑) -> **出处**:自 `AGENTS.md`「ARMCC 内联汇编语法」条目迁入(AGENTS.md 瘦身,不再当事实存放地)。4 个 Fault Handler 是手工 `__asm`(§10.2 的 L2 条);ARMCC 的内联汇编只有一种合法形态,以下三种写法全部实测踩坑: +> **出处**:自 `AGENTS.md`「ARMCC 内联汇编语法」条目迁入(AGENTS.md 瘦身,不再当事实存放地)。4 个 Fault Handler 是手工 `__asm`(§10.2 的「搬进 USER CODE 区」条);ARMCC 的内联汇编只有一种合法形态,以下三种写法全部实测踩坑: | # | 尝试写法 | ARMCC 实际报错 | 根因 | |---|---|---|---| @@ -558,7 +558,7 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建 ## 11. 任务级饥饿的诊断方法与实测基线(2026-10-10 结案沉淀) > **来源**:`[ERR][WD] task stall: mask=0x00000067 req=0x00000077`(缺 bit4 = `TASK_WD_UI`) -> 的完整归因与处置。故障本身已结案——**累计 38 次上板启动 0 复现**(原 L2 基线 3/10), +> 的完整归因与处置。故障本身已结案——**累计 38 次上板启动 0 复现**(主机侧回归的早期基线 3/10), > 原始分析文档按登记簿"完成即销项"规则删除。本节只留**可复用的判读口径、诊断方法、 > 实测基线与教训**;ADC 侧的处置细节在 [`ADC采样节拍设计说明.md`](ADC采样节拍设计说明.md) §6 与 §9, > UI 推流门控在 `../UI设计/RemoDispBus协议与接收设计说明.md` §3,就绪门控 API 在 @@ -696,15 +696,15 @@ enter/exit 段还能读出"此刻仍在段内已多久"——**永久阻塞的 | 2026-09-29 | 创建 | 调研 + 设计方案:IWDG + 任务心跳位图;含选型、九项设计决策、验证计划与风险清单 | | 2026-09-29 | 修正实施路径 | 实测发现 HAL IWDG 驱动源码不在仓库且未编入 MDK 工程 → 初稿改为**直接寄存器操作**(§2.2 路径 C);超时数值按公式核算修正;补 `RCC_CSR`/`IWDG` 寄存器定义的实测行号 | | 2026-09-29 | **路径再变更 + 实施落地** | 决策改走 **CubeMX 图形配置**(§2.2 路径 A):`HAL_IWDG_MODULE_ENABLED` 改为经 CubeMX 生成(避免"取消注释即编译失败")。同时落地 §5 骨架到 `App/util/task_watchdog.{c,h}`,各任务埋点(default/net/ui/time_sync),`RCC_CSR` 复位原因锁存(`Src/main.c` + `App/app/app_main.c`),`configASSERT` 改为 `vApplicationAssertFailed()`。实测 CubeMX 重生成覆盖 4 处既有定制(P0×1 + P1×2 + P2×1),已全部恢复,`@build` 0 错误 0 警告 | -| 2026-09-29 | **新增 §10.2 三层手段 + 审计脚本** | 沉淀通用方法论(L1 `.ioc` / L2 `USER CODE` 区 / L3 审计+基线);新建 `scripts/check_cubemx_survival.py`(16 项清单 + 漂移检测)+ `.cubemx_survival_baseline.json` + pre-commit `cubemx-survival` 钩子;`AGENTS.md` 与 `docs/代码规范/pre-commit使用说明.md` 同步 | +| 2026-09-29 | **新增 §10.2 三层手段 + 审计脚本** | 沉淀通用方法论(写进 `.ioc` / 搬进 `USER CODE` 区 / 审计+基线);新建 `scripts/check_cubemx_survival.py`(16 项清单 + 漂移检测)+ `.cubemx_survival_baseline.json` + pre-commit `cubemx-survival` 钩子;`AGENTS.md` 与 `docs/代码规范/pre-commit使用说明.md` 同步 | | 2026-09-29 | **第二次重生成验证 + 机制修正** | 审计脚本**实测有效**(准确检出 5 处覆盖)。⚠️ **关键发现**:CubeMX 换了一代中断函数模板(旧 `HardFault_Handler`/`HardFault_Report` 自造区 → 新函数体内 `HardFault_IRQn 0`/`W1_HardFault_IRQn 0`),**"生成区外的自造标记区"同样不被保留**——上一轮假设被证伪。据此升级恢复方案:报告实现迁入 `USER CODE BEGIN 0`(标准保护位);Handler 本体改为生成区整函数替换(`#define` 改名保留生成实体,链接器自动移除,`.map` 已验证)+ `HARD-FAULT-ANCHOR` 锚点。同步修正审计脚本(`hardfault-handler` 改 `wholefile` + 锚点)、`UNPROTECTED_LEGACY` 清单、§10.1/§10.2 全文。`@build` 0 错误 0 警告 | | 2026-09-29 | **3 个 Fault 升级为崩溃采集** | `MemManage` / `BusFault` / `UsageFault` 由空 `while(1)`(死机静默)升级为与 `HardFault` 同一套采集:生成区整函数替换(各带独立锚点 `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)+ `#define X X_Gen` 改名保留生成实体(链接器各移除 2 字节)。新增 `fault_handler_enable()` 使能 `SHCSR` 后三者(默认只开 HardFault,不开则故障被升级、看不出根因),声明入 `Inc/stm32f4xx_it.h`、调用在 `app_main_init()` 第一行。因 ARMCC `__asm` 取不到 C 数据符号,改为 per-fault C 入口 `fault_entry_{hard,mem,bus,usage}` 带故障名调 `hard_fault_report(tag, frame)`。**刻意不启用** `UNALIGN_TRP` / `DIV_0_TRP`(避免把普通 bug 升级成死机)。审计脚本新增 `fault-handlers-3` / `fault-handler-enable` 两项、`UNPROTECTED_LEGACY` 清掉 3 个 Fault(WARN 2→1)、清单 16→18 项。`@build` 0 错误 0 警告;审计 18 通过 / 1 警告 / 0 失败 | -| 2026-10-01 | **查证 `fault_handler_enable()` 的 CubeMX 可行性** | 用户问"能否通过 CubeMX 配置"→ **否**。查 CubeMX 安装目录模板源码得三重证据:① `tpl_main_c.ftl` 的 `MX_NVIC_Init()` 只在 `systemHandler=="false"` 时生成 `HAL_NVIC_EnableIRQ`,而 `NVIC-STM32F417_Modes.xml` 中三个 Fault 都标 `Y`(=systemHandler)→ 永不生成使能代码;② **全部模板中 `SHCSR` 只出现 1 次**且是 MPU 清理的 `&= ~MEMFAULTENA`(禁用),**无任何 `\|=` 置位**;③ `.ioc` 无 MPU(`Mcu.IP` 列表),且 `HAL_MPU_Enable` 只置 `MEMFAULTENA` 一位。实测印证:`HAL_MspInit()` 里三个 Fault 一行代码都没生成。结论:只能手工写,但属 **L2**(`USER CODE BEGIN 1` 区)→ 重生成不丢,有审计项兜底。另新增警示:`.ioc` 的 `NVIC.MemoryManagement_IRQn=true` **不代表已使能**(第 1 段只是"向量可见")。写入 §10.2 与四个 Fault 采集小节 | +| 2026-10-01 | **查证 `fault_handler_enable()` 的 CubeMX 可行性** | 用户问"能否通过 CubeMX 配置"→ **否**。查 CubeMX 安装目录模板源码得三重证据:① `tpl_main_c.ftl` 的 `MX_NVIC_Init()` 只在 `systemHandler=="false"` 时生成 `HAL_NVIC_EnableIRQ`,而 `NVIC-STM32F417_Modes.xml` 中三个 Fault 都标 `Y`(=systemHandler)→ 永不生成使能代码;② **全部模板中 `SHCSR` 只出现 1 次**且是 MPU 清理的 `&= ~MEMFAULTENA`(禁用),**无任何 `\|=` 置位**;③ `.ioc` 无 MPU(`Mcu.IP` 列表),且 `HAL_MPU_Enable` 只置 `MEMFAULTENA` 一位。实测印证:`HAL_MspInit()` 里三个 Fault 一行代码都没生成。结论:只能手工写,但属 **USER CODE 区**(`USER CODE BEGIN 1` 区)→ 重生成不丢,有审计项兜底。另新增警示:`.ioc` 的 `NVIC.MemoryManagement_IRQn=true` **不代表已使能**(第 1 段只是"向量可见")。写入 §10.2 与四个 Fault 采集小节 | | 2026-10-01 | **新增 §7.3 主机侧单元测试** | 硬件访问收敛到薄平台层(`App/util/task_watchdog.h` 末段声明 3 个 `__weak` 钩子:喂狗/启动/取 tick),MCU 端行为零变化(`@build` 0/0)。新建 `test/host/watchdog/`(`watchdog_test.c` 39 用例 + `run_unit_test.py` MSVC 运行器 + `stub/` 三桩头),`#include` 生产源码以保证测的是真代码。挂 pre-commit `watchdog-unit-test` 钩子。新增 `docs/测试说明/看门狗单元测试说明.md`;`App/util/README.md`、`test/host/README.md`、`测试开发标准流程.md`(新增主机侧纯逻辑单测层)同步。顺带改善:`s_last_tick` 由函数局部 static 提为文件 static(可测性 + 模块状态归一)。§5.1 文件表与 §10 步骤表登记;§7.2 S1 优先级降低(位图逻辑已由 U1 覆盖) | | 2026-10-02 | **P0 根因修复:IWDG 超时被静默配成复位默认值(比设计短 5 倍)** | round 3~8 反复出现的"空 STOR(0 字节截断)→ IWDG 误复位"**根因不是"poller 自身停摆"**。真因:`Src/iwdg.c` 为实现**延后启动**(§4.5 决策 C)删掉了 `HAL_IWDG_Init()`(`IWDG-DEFER-START-ANCHOR`),而 `HAL_IWDG_Init()` 是**唯一**会把 `hiwdg.Init.Prescaler/Reload` 写进 IWDG 寄存器的路径——删掉后 `MX_IWDG_Init()` 只填结构体,`WD_IWDG_PRESCALER`/`WD_IWDG_RELOAD` 沦为**死宏**,寄存器停在**复位默认值** `PR=0`(÷4)/`RLR=4095`。实测(SWD 回读)实际超时 = 4×4096/LSI ≈ **349~512 ms**,而设计是 64×1250/LSI ≈ **1701~2498 ms**。两个后果:① `WD_MISS_TOLERANCE`(8×100ms=800ms)**永远等不到**就被 IWDG 复位 → **停喂判定分支成死代码**(这正是 §7"STALL 从未执行"的真正原因,当时据此误判为"poller 停摆");② 任何 >400ms 的心跳缺口都直接误复位(空 STOR 的 FTL flush 使 NET/FTP/UI 心跳缺 ~400ms)。**修复**:`wd_platform_start()`(`__weak`,**不在 CubeMX 生成区**,重生成不丢)补上按设计值配置:解锁(`KR=0x5555`) → `PR` → 等 `SR.PVU` 清 → `RLR` → 等 `SR.RVU` 清 → `KR=0xCCCC`(不等同步会静默丢弃后写的那只,SWD 实测)。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`,故这是全固件**唯一**写 PR/RLR 的点。**验证**:SWD 现场改回设计值后,同一空 STOR 由 **5/5 复位 → 100% 成功**;固件烧录后 3 次开机回读均为 `PR=4/RLR=1249`。`@build` 0/0;本地 CI 静态门禁+主机侧纯逻辑单测全绿(278/278 断言) | | 2026-10-03 | **V2/V4/V5 台架验收闭环(N-03 销项)** | 三用例全部通过。**V2 任务饿死**:`netTask` 循环插 `while(1)` → 800ms 声明 `[WD] task stall: mask=0x00000001 req=0x00000037`,≤2.5s 触发 IWDG 复位(`reset cause: IWDG`,`RCC_CSR=0x24000000`)。⚠️ 首轮实测**不复位**(stall 已声明、`miss` 累至 1157≈115s):根因是**停摆证据落盘自身喂狗**——`wd_stall_evidence()`→`sys_log_evidence()` 在停喂窗口里每 100ms 轮都写 NAND(`/log/wdev.log` + `SYSLOG_RUN` flush 诱导),经 `nand_ftl_wd_feed()` **直接重装 IWDG**,"停喂"语义被证据机制抵消。修复:加 `s_stall_evidenced` **每 episode 门闩**(每轮停摆只落盘一次,此后不再触碰 NAND)→ 复测复位正常。**V4 关中断死循环**:`__disable_irq()` 后插 `while(1)` → 设备每 ~4.9s 稳定复位(20+ 周期,`reset cause: IWDG`),证实 IWDG **独立于中断**(PRIMASK 屏蔽 TIM7/SysTick/PendSV 仍能复位)。**V5 长期不误杀**:FTP 512KB 上/下传 + `e02_probe` 网络探针持续 **30 min** → **120/120 次 FTP 传输全 PASS(MATCH)、零 IWDG 复位**(唯一复位为起始烧录的 `software (NVIC_SystemReset)`)。Step 4 堆告警全程未触发(`min_ever=4728 B` > 3072 B)。**遗留观察(已登记 `待处理内容.md`)**:V5 期间 44 次 `task stall`(`mask=0x00000001` 持续 ≥800ms)**100% 与 `e02_probe` 的 30s 挂起客户端→`time_sync` 掉线+监听重建(`listen FAIL`×2,约 4s)重合**;隔离实验证实「仅 FTP(无探针)120s = 0 次 stall」→ 该停摆为**探针场景专属**,非一般负载误杀。因缺口(~1s)< IWDG 最坏超时(1701ms)而未复位,与 §4.3 包含关系一致 | -| 2026-10-09 | **IWDG 延后启动由 L3 升级为 L2:不再需要每次重生成后手工删那一句** | 起因是提问"能否在初始化函数下方的自定义区里先反初始化"。查证:**反初始化不可行**——IWDG 一旦启动就**无法停止**(参考手册:唯一停止手段是复位),故不能在 `IWDG_Init 2` 区撤销。改用等价且免手工的做法:`Src/iwdg.c` 的 `USER CODE BEGIN 0`(标准保护位)放空实现 `iwdg_deferred_start()` + `#define HAL_IWDG_Init(a_h) iwdg_deferred_start(a_h)`,把**本编译单元里**那句生成调用 `HAL_IWDG_Init(&hiwdg)` 改名到空实现 → 生成区保持 CubeMX 原样、**重生成零 diff**。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`、`iwdg.o(i.MX_IWDG_Init)` 只引用 `.bss`(空实现被编译器消掉)。审计项 `iwdg-defer-start` 相应由 `wholefile`+锚点注释改为 `protected`(区 `0`)+ 代码符号断言(`iwdg_deferred_start` / `#define HAL_IWDG_Init`——`protected` 比对前会剥掉注释,锚点注释不能当断言)。§10.2 硬教训②与 L3 存量清单同步:L3 只剩 4 个 Fault Handler 实体(同一招可根治,尚未做)。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 | -| 2026-10-09 | **4 个 Fault Handler 由 L3 升级为 L2:重生成完全零 diff** | 用与 iwdg 同一招。`Src/stm32f4xx_it.c` 的 4 个 `__asm void X_Handler` 从**生成区**搬进 `USER CODE BEGIN 0`,且必须写在文件头那 4 个 `#define X X_Gen` **之前**(写在之后会被自己的宏改名);配套的 `#undef X` 随之删除(不再需要)。生成区恢复 CubeMX 原样(`void HardFault_Handler(void)` + `HardFault_IRQn 0` / `W1_HardFault_IRQn 0` 标记区),4 个锚点注释(`HARD-FAULT-ANCHOR` / `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)全部退役。`.map` 复核:`startup_stm32f407xx.o(RESET) refers to stm32f4xx_it.o(.emb_text) for HardFault_Handler`(向量表仍指向我们的 `__asm` 版)、`.emb_text → i.fault_entry_* → i.hard_fault_report`、4 个 `*_Gen` 各 `Removing ... (2 bytes)`。审计项 `hardfault-handler` / `fault-handlers-3` 由 `wholefile`+锚点改为 `protected`(区 `0`)+ 代码符号断言。**L3 清单清空**,重生成预期零 diff。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 | +| 2026-10-09 | **IWDG 延后启动由审计兜底升级为 USER CODE 区:不再需要每次重生成后手工删那一句** | 起因是提问"能否在初始化函数下方的自定义区里先反初始化"。查证:**反初始化不可行**——IWDG 一旦启动就**无法停止**(参考手册:唯一停止手段是复位),故不能在 `IWDG_Init 2` 区撤销。改用等价且免手工的做法:`Src/iwdg.c` 的 `USER CODE BEGIN 0`(标准保护位)放空实现 `iwdg_deferred_start()` + `#define HAL_IWDG_Init(a_h) iwdg_deferred_start(a_h)`,把**本编译单元里**那句生成调用 `HAL_IWDG_Init(&hiwdg)` 改名到空实现 → 生成区保持 CubeMX 原样、**重生成零 diff**。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`、`iwdg.o(i.MX_IWDG_Init)` 只引用 `.bss`(空实现被编译器消掉)。审计项 `iwdg-defer-start` 相应由 `wholefile`+锚点注释改为 `protected`(区 `0`)+ 代码符号断言(`iwdg_deferred_start` / `#define HAL_IWDG_Init`——`protected` 比对前会剥掉注释,锚点注释不能当断言)。§10.2 硬教训②与审计兜底存量清单同步:审计兜底只剩 4 个 Fault Handler 实体(同一招可根治,尚未做)。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 | +| 2026-10-09 | **4 个 Fault Handler 由审计兜底升级为 USER CODE 区:重生成完全零 diff** | 用与 iwdg 同一招。`Src/stm32f4xx_it.c` 的 4 个 `__asm void X_Handler` 从**生成区**搬进 `USER CODE BEGIN 0`,且必须写在文件头那 4 个 `#define X X_Gen` **之前**(写在之后会被自己的宏改名);配套的 `#undef X` 随之删除(不再需要)。生成区恢复 CubeMX 原样(`void HardFault_Handler(void)` + `HardFault_IRQn 0` / `W1_HardFault_IRQn 0` 标记区),4 个锚点注释(`HARD-FAULT-ANCHOR` / `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)全部退役。`.map` 复核:`startup_stm32f407xx.o(RESET) refers to stm32f4xx_it.o(.emb_text) for HardFault_Handler`(向量表仍指向我们的 `__asm` 版)、`.emb_text → i.fault_entry_* → i.hard_fault_report`、4 个 `*_Gen` 各 `Removing ... (2 bytes)`。审计项 `hardfault-handler` / `fault-handlers-3` 由 `wholefile`+锚点改为 `protected`(区 `0`)+ 代码符号断言。**审计兜底清单清空**,重生成预期零 diff。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 | | 2026-10-10 | **正文按当前代码重写(清理立项前的描述)** | 正文原样保留了大量**立项前(2026-09-29)**的状态取证与已废弃的"路径 C 考古",与现状矛盾,故重写为现状设计说明(1144 → 571 行;**历史与逐次决策全部保留在下方变更记录,未删**)。主要更正:① §1.1"现状:无任何硬件看门狗"改为**历史背景**(IWDG 已在 `.ioc`/`hal_conf`/工程三处就位);② **ftpTask 早已纳入监视**——经 `lftpd_task_wd_beat()` 弱钩子(事件循环 / RETR·STOR 分片 / 连接与卷锁等待),旧正文"首期不埋点、由掩码剔除"及 R3/O1 已作废;③ 补齐正文缺失的三块现状实现:**FTL 心跳路由**(`task_wd_bind()` + `nand_ftl_wd_beat()`)、**FTL 直接喂狗**(`nand_ftl_wd_feed()`,含"设备存活但功能冻结"边界)、**round 7/8/8b 取证机制**(`/log/wdev.log`、`.noinit` 槽 + `UNINIT_CCM`、`PREVRUN`/`TICKRUN`/`MISS start`/`STALL`);④ 掩码改为按代码实况列表(产品 `0x37` / `ENABLE_HW_LAYER_TESTS` `0x35` / **testTask 任何构建都不入掩码**)并写明"精确相等"语义;⑤ 复位原因优先级按 `reset_cause_str()` 实际顺序改写(旧表把 POR 排第一,代码是 **IWDG 最优先**);⑥ §4.6 更正为**已集成**:看门狗复位写 `selftest.log`(旧正文"未改");⑦ §8 预算改用 `.map` 实测(`task_watchdog.o` 1098 B 代码 / 44+104 B RAM / `.noinit` 48 B;`iwdg.o` 仅 28 B,`HAL_IWDG_Init` 94 B 被移除);⑧ 附录 B 自相矛盾修正(旧表把"借助 CubeMX"列为否决,实际已选它);⑨ §7 由"验证计划"改为**验证记录**(V2/V4/V5 ✅、U1 39 断言 ✅,V3/V6/V7 标为未单独注入、V8 未做)。连带同步:`Src/iwdg.c`、`Src/freertos.c`、`App/task/default_task.c` 三处注释的章节引用(旧 §4.5/§4.6 → 新 §4.1/§4.4),`scripts/check_cubemx_survival.py` 提示语(§10.1 → §10.2/§10.3),`实机测试实施指南.md`(§10 → §7 ×3)、`看门狗单元测试说明.md`(§10 → §7)、`模拟器差距清单.md` H-1(§4.9 → §4.7,并更正"模拟器不编译本模块、无 IWDG 桩")、`docs/README.md`("实施中/待验证" → 已闭环)。⚠️ **下方历史行里的 §x.y 引用指当时编号**,不再回溯改写 | | 2026-10-10 | **adcTask 纳入监视(B1):必需掩码 `0x37`→`0x77`** | B1(ADC 采集链路)落地时新增 `adcTask`(`App/task/adc_task.c`:1 ms 采样节拍触发 CONVST + 消费 `tpafe5160_snapshot()`)并纳入 IWDG 监视:`task_wd_id_t` 新增 `TASK_WD_ADC = 6`;`wd_required_mask()` 产品 `0x37`→`0x77`、`ENABLE_HW_LAYER_TESTS` `0x35`→`0x75`(adcTask **不依赖任何就绪屏障** → 任何构建都置位)。adcTask 主循环每 1 ms `task_wd_beat(TASK_WD_ADC)`;**不做 NAND I/O → 不登记 FTL 心跳路由**(无需 `task_wd_bind()`)。主机侧单测 `beat_all()` 与掩码断言(`tc_003`/`tc_005`/`tc_011` 的 `0x37`→`0x77`)同步,39 断言全绿。任务由 `Src/freertos.c` 的 `USER CODE` 区手工创建(同 timeSyncTask,不在 `.ioc` 里),栈 512 words、优先级 Normal,`stack_report.py` 实测最坏链 504 B(余 1544 B / 75%)。见 `docs/算法/继电保护功能复现规划.md` §7 | | 2026-10-10 | **B1 `@test` 实机闭环:adcTask 掩码真机验证** | 台架闭环(`.dsh/skills/serial-boot-test/scripts/run_boot_test.py`)确认 `adcTask` 纳入掩码后**无 adc 相关误复位**:`[STACK] adc peak_used=496/2048 free_min=1552 (75%)`、`frames/seq` 每 10 s 严格 +10000(=1000 Hz)、`idle=0`/`frst_err=0`、全片回读 `No difference found`。启动早期两次 `MISS start snap=0x67 req=0x77` 仅缺 bit4(`timeSync` 未启动,属正常就绪窗;adc 的 bit6 自始置位),掩码随后收敛、全程无复位。同轮另修 `dbg_console_writer_ready()` 门控(adcTask 周期日志在写者注册前抑制,见 `控制台单写者日志设计说明.md`) | diff --git a/docs/架构设计/STM32F4-Base存储架构说明.md b/docs/架构设计/STM32F4-Base存储架构说明.md index 59f7fa27..1ea3466f 100644 --- a/docs/架构设计/STM32F4-Base存储架构说明.md +++ b/docs/架构设计/STM32F4-Base存储架构说明.md @@ -146,7 +146,7 @@ WRITE_ENABLE → PROGRAM_LOAD (02h + 列地址 + 数据) → PROGRAM_EXEC (10h + **块擦除**(最小擦除单位 128KB,参数是字节地址而非块编号): ``` -WRITE_ENABLE → BLOCK_ERASE (D8h + 3字节字节地址) → 等待完成 → 检查 E_FAIL +WRITE_ENABLE → BLOCK_ERASE (D8h + 3字节地址) → 等待完成 → 检查 E_FAIL ``` ### 4.5 DMA 传输策略(2026-07-21 新增) diff --git a/docs/测试说明/实机测试实施指南.md b/docs/测试说明/实机测试实施指南.md index 626474d4..bec40446 100644 --- a/docs/测试说明/实机测试实施指南.md +++ b/docs/测试说明/实机测试实施指南.md @@ -26,7 +26,7 @@ | **步骤 3** | IWDG 故障注入 V2/V4 + 长跑 V5(P1-A) | ✅ 已闭环 | §4 | | **步骤 4** | 堆低水位告警实测(P1-B) | ✅ 已闭环 | §5 | | **步骤 5** | GD5F 错误通道回归(P2-A) | ✅ 已闭环 | §6 | -| **步骤 6** | 网络稳定性与基线重锁(P2-B / 日志静默观察项 + L5 队列往返基线待重验) | ◐ 部分闭环(7.1/7.2 ✅;7.3 待 PC peer) | §7 | +| **步骤 6** | 网络稳定性与基线重锁(P2-B / 日志静默观察项 + 队列往返基线待重验) | ◐ 部分闭环(7.1/7.2 ✅;7.3 待 PC peer) | §7 | | **步骤 7** | 工程卫生(P3 + 待核实项) | ✅ 已闭环 | §8 | > **每步出口**:`@build` 0/0 → 一轮 `@test` PASS → ST-LINK 全片回读 `No difference found` → @@ -299,7 +299,7 @@ $cli = if ($env:DTU_STLINK_CLI) { $env:DTU_STLINK_CLI } else { (Get-Command ST-L **执行步骤**:长跑(§4.2 V5)期间周期性打点 + 对照 `CH395F` 无关的 SD2506 侧日志判别卡点。 -### 7.3 L5 队列往返基线待重验 · ⬜ 待 PC peer +### 7.3 队列往返基线待重验 · ⬜ 待 PC peer **现状**:L5 基线(1223/988µs)属**分片时代**;人工目检对该路径逐位不变 → 机制上无影响, 但严格重锁需跑一轮 Phase 13(**需 PC peer**)。 diff --git a/docs/测试说明/相量层单元测试说明.md b/docs/测试说明/相量层单元测试说明.md index b5a8354f..4abc02f4 100644 --- a/docs/测试说明/相量层单元测试说明.md +++ b/docs/测试说明/相量层单元测试说明.md @@ -93,7 +93,7 @@ test/host/phasor/phasor_test.c ## 2. 测试用例 -> 命名 `TC-PH-NNN`(PH = PHasor)。用例内的输出标签使用 `L2-x` 前缀,对应 +> 命名 `TC-PH-NNN`(PH = PHasor)。用例内的输出标签使用相量输出标签前缀,对应 > `docs/算法/继电保护功能复现规划.md` §3.2 的设计点编号。部分用例直接观测静态量(白盒)。 ### 2.1 全周傅氏系数表 diff --git a/docs/测试说明/自动化测试流程.md b/docs/测试说明/自动化测试流程.md index 9536cc8e..16e0924d 100644 --- a/docs/测试说明/自动化测试流程.md +++ b/docs/测试说明/自动化测试流程.md @@ -89,7 +89,7 @@ | `ADC init:` | `App/app/app_main.c` | | `SD2506 ID:` / `time:` / `temp:` / `battery:` | `App/app/app_main.c` | | `Network Init` / `net init: OK` / `MAC:` / `PHY:` | `App/task/net_task.c` | -| `phasor(first)` / `phasor(hb)` | `App/task/adc_task.c`(L2 相量层,2026-10-11 起常开;`hb` 每 10 s 一次,采集窗口需 ≥10 s——默认 `max_wait 30 s` 覆盖) | +| `phasor(first)` / `phasor(hb)` | `App/task/adc_task.c`(全周傅氏相量,2026-10-11 起常开;`hb` 每 10 s 一次,采集窗口需 ≥10 s——默认 `max_wait 30 s` 覆盖) | **默认 Forbidden 模式**:`FAIL;ERROR;HardFault;[ERR]` diff --git a/docs/测试说明/链路延迟测试说明.md b/docs/测试说明/链路延迟测试说明.md index 0f69393d..b001ccea 100644 --- a/docs/测试说明/链路延迟测试说明.md +++ b/docs/测试说明/链路延迟测试说明.md @@ -444,7 +444,7 @@ med 65.0ms/max 133.5ms(133.5 = 压力启动瞬态单帧);**挂起 recv 化 ¹ 队列往返两行为 2026-09-22 上午分片时代(M2 B2 档)数值:发送分块握手对 DONTWAIT 快速路径**逐位不变**(dispatch → `net_poll` → `get_recv_len` 读,无 readable 门控),pending tick 无 pending 时为纯指针读零 SPI -(µs 级)——机制上不影响队列往返;M2 重验未在本轮执行(低风险,留待 Phase 13 下次窗口,见 `docs/任务规划/README.md` 条目「队列往返队列往返基线待重验」)。 +(µs 级)——机制上不影响队列往返;M2 重验未在本轮执行(低风险,留待 Phase 13 下次窗口,见 `docs/任务规划/README.md` 条目「队列往返基线待重验」)。 数据来源:SPI 字节命令/SPI DMA = §6.1;发送分块握手/队列往返/请求→首字节/整帧 3209B 往返 = M2 B2 档(200ms、per-recv 日志已删)串口 `[LAT]` 行 (`m2_data/m2_quiet_r200.json`);生产路径帧周期/连接/重监听/uiTask 节拍 = **挂起 recv 构建 2026-09-22 16:42/16:43 重锁** @@ -549,7 +549,7 @@ peer 侧 = `m3_data/m3_pending_*.json`(发送分块握手重锁)与 `m3_data ### 7.5 队列与调度:静默档不存在台阶 -- **队列往返队列往返**:收 1223µs / 发 988µs(P0-b 后;旧值 5997/987µs)——收段 ≈ 1 个 netTask 循环周期量级, +- **队列往返**:收 1223µs / 发 988µs(P0-b 后;旧值 5997/987µs)——收段 ≈ 1 个 netTask 循环周期量级, 属**调度周期**而非排队拥塞(旧收值 5997µs 含 per-recv 日志税 4.4ms/行,已移除); `queue_rt_send` 988µs ≈ 1 个循环周期,发送路径没有额外排队; - **发送分块握手 chunk 级**:219µs ≈ 每 chunk SPI 成本的 1/7 → **芯片 TX 缓冲不是瓶颈**,SPI 搬运才是; diff --git a/docs/硬件平台/ADC通道映射说明.md b/docs/硬件平台/ADC通道映射说明.md index 8aea4e89..d6fe7631 100644 --- a/docs/硬件平台/ADC通道映射说明.md +++ b/docs/硬件平台/ADC通道映射说明.md @@ -130,9 +130,9 @@ STM32(U17)为 LQFP144 脚号。 |---|---|---| | **通道归属(硬件缺陷)** | 8 路电气量分属两片转换器:UA/UN/IA → TPAFE,UB/UC/IB/IC/IN → STM32 ADC | **V0.1 硬件缺陷**(非有意分配,理由见 §1)→ V0.1 上禁止跨转换器做相位/功率/功率因数/矢量类运算;**V0.2 改版要求 8 路电气量全部走 TPAFE**(登记簿 N-18) | | STM32 ADC 外设 | **已生成(2026-10-09,CubeMX 6.17 + FW_F4)**:`Src/adc.c`/`Inc/adc.h` 已入库并挂进 `MDK-ARM/*.uvprojx`;`Inc/stm32f4xx_hal_conf.h` 的 `HAL_ADC_MODULE_ENABLED` 已生效;HAL `stm32f4xx_hal_adc.c/_ex.c` + `stm32f4xx_ll_adc.h` 已补入仓库(HAL 原本是裁剪子集);`Src/main.c` 里 `MX_ADC1_Init()` 排在 `MX_IWDG_Init()` 之后。生成配置核对无误:9 通道 / Rank 1~9 / 全部 144 cycles / `ScanConvMode=ENABLE` / `ContinuousConvMode=DISABLE` / `ExternalTrigConv=ADC_SOFTWARE_START` + `EDGE_NONE` / `EOCSelection=ADC_EOC_SINGLE_CONV` / `DataAlign=RIGHT` / 12 位;`HAL_ADC_MspInit()` 已把 PA0~PA7+PC4 配成 `GPIO_MODE_ANALOG` | 硬件通路已打通(`hadc1` 可用);**消费者已落地**——`App/algo/adc_input.c` 的 `stm32_take()`(「STM32 ADC1 启动与扫描」,v2.0,2026-10-10):ADC1 由 **TIM3 TRGO 硬件触发**一轮 9 通道 + **DMA2_Stream4 循环搬运**进乒乓双缓冲,任务侧只消费刚完成的半区(零忙等),9 通道一轮 ≈ 67 µs | -| ADC1 上电自校准 | **已补(2026-10-10)**:`Src/adc.c` 的 `USER CODE BEGIN ADC1_Init 2` 调用 `adc1_calibrate()`(实现同文件 `USER CODE BEGIN 1`,按 RM0090 §13.3.6:清 ADON→等生效→置 ADON→置 CAL→等 CAL 由硬件清零)。⚠ **CubeMX 不生成这一步**,且本仓库裁剪版 HAL **没有** `HAL_ADCEx_Calibration_Start`、裁剪版 CMSIS 头**没有** `ADC_CR2_CAL`(bit3,代码里按 RM 自建 `ADC1_CAL_BIT`)→ 属 L2 手工定制,审计项 `adc1-calibration-call` / `adc1-calibration-impl` 兜底 | 实测有效:校准前后同一拍 STM32 侧 6 路**同向**变化 `UB 898→961`、`UC 978→1038`、`IB 906→922`、`IC 1061→1071`、`IN 1017→1055`、`T 790→846`(+10~+63 LSB ≈ 0.2~1.5 % FS);TPAFE 侧 `UA/UN/IA` 一字不变 → 变化确来自 ADC1 校准。缺这一步不报错,只是**读数带失调** | +| ADC1 上电自校准 | **已补(2026-10-10)**:`Src/adc.c` 的 `USER CODE BEGIN ADC1_Init 2` 调用 `adc1_calibrate()`(实现同文件 `USER CODE BEGIN 1`,按 RM0090 §13.3.6:清 ADON→等生效→置 ADON→置 CAL→等 CAL 由硬件清零)。⚠ **CubeMX 不生成这一步**,且本仓库裁剪版 HAL **没有** `HAL_ADCEx_Calibration_Start`、裁剪版 CMSIS 头**没有** `ADC_CR2_CAL`(bit3,代码里按 RM 自建 `ADC1_CAL_BIT`)→ 属 USER CODE 区手工定制,审计项 `adc1-calibration-call` / `adc1-calibration-impl` 兜底 | 实测有效:校准前后同一拍 STM32 侧 6 路**同向**变化 `UB 898→961`、`UC 978→1038`、`IB 906→922`、`IC 1061→1071`、`IN 1017→1055`、`T 790→846`(+10~+63 LSB ≈ 0.2~1.5 % FS);TPAFE 侧 `UA/UN/IA` 一字不变 → 变化确来自 ADC1 校准。缺这一步不报错,只是**读数带失调** | | ADC 时钟 | CubeMX 的 F4 ADC 面板**有** Clock Prescaler 一项(此前"面板没有该项、固定 DIV2"的判断有误),已选 **PCLK2/4 → ADCCLK = 21 MHz** ✓。面板里 **DIV2 是灰的、选不到**:DB 里 `FadcConstraint Max=36000000`,APB2 = 84 MHz → DIV2 = 42 MHz 超限,该候选值被 `Action="DISABLE"` 禁掉,超规值进不来 | **不设额外防护(2026-10-09 决定)**:约束已由 CubeMX 把关;`.ioc` 虽不保存 `ADC1.ClockPrescaler` 键,重生成也只会回落到约束允许的值。按 144+12 cycles 算 ≈ 7.4 µs/通道、9 通道一轮 ≈ 67 µs | -| `adc_task` | **已实现(B1,2026-10-10;L1 层 v2.3;P1-5 事件驱动消费,2026-10-10)**:`App/task/adc_task.c` 每拍调 `adc_input_poll()`(`App/algo/adc_input.c`)= TPAFE `tpafe5160_snapshot()` 原子快照(8 通道)+ 消费一帧 STM32 ADC1 DMA 乒乓缓冲,**经 `adc_input_raw()` 按语义取数**(不再直接索引驱动缓冲);**消费唤醒由 TIM3 ISR 置位 `ADC_WAKE_FLAG`**(不再 1 ms 轮询);任务在 `Src/freertos.c` 的 `USER CODE` 区创建为 `adcTask`(512 words / **AboveNormal**) | 数据路径已通(台架 `@test`:`frames` 严格 1000 Hz 递增、**`idle=0`**、`adc1_rc=0` / `adc1_err=0`) | +| `adc_task` | **已实现(B1,2026-10-10;语义取数与有效性层 v2.3;P1-5 事件驱动消费,2026-10-10)**:`App/task/adc_task.c` 每拍调 `adc_input_poll()`(`App/algo/adc_input.c`)= TPAFE `tpafe5160_snapshot()` 原子快照(8 通道)+ 消费一帧 STM32 ADC1 DMA 乒乓缓冲,**经 `adc_input_raw()` 按语义取数**(不再直接索引驱动缓冲);**消费唤醒由 TIM3 ISR 置位 `ADC_WAKE_FLAG`**(不再 1 ms 轮询);任务在 `Src/freertos.c` 的 `USER CODE` 区创建为 `adcTask`(512 words / **AboveNormal**) | 数据路径已通(台架 `@test`:`frames` 严格 1000 Hz 递增、**`idle=0`**、`adc1_rc=0` / `adc1_err=0`) | | TPAFE 采样触发 | **周期 CONVST 已实现(2026-10-10)**:`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback` 在 **TIM3 更新事件(1 kHz)** 内调 `tpafe5160_start_conv_irq()`;`app_main_init()` 用 `HAL_TIM_Base_Start_IT(&htim3)` 启动节拍(在 `tpafe5160_init()` 之后)。同一 TIM3 更新事件还经 **TRGO** 硬件触发 ADC1 一轮 9 通道(DMA 搬运),并调 `adc_tick_irq_notify()` **唤醒消费者**(P1-5 方案 a,2026-10-10;内含"消费者就绪"门)→ 两片转换器由同一硬件事件同源对齐 | 连续波形(1 kHz);详见 `docs/子系统设计/ADC采样节拍设计说明.md` §4(DMA 乒乓搬运)与 §6(事件驱动唤醒) | | 前端偏置 | 每路 1k 串联 + 滤波 C 跨接差分输入;差分负端(`*_GND` 网)经 1k 拴 AGND。**不是对地分压**(2026-10-27 网表追线定死,拓扑图见 §7.1),**无中点偏置** | TPAFE 输入阻抗 **1 MΩ 恒定**(`../芯片手册/TPAFE5160.md` "Analog Input Impedance" 节),故端子对 AGND 的直流通路 = 1k + 1 MΩ + 1k ≈ **1.002 MΩ**,不是 1 kΩ。两侧各 1 kΩ 是手册要求的**源阻抗匹配**(AIN_nP / AIN_nGND 外接源阻抗相等以抵消失调),**不是分压**。STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若要算 RMS/功率需硬件补偏置或明确算法口径 | | TPAFE 量程(**2026-10-25 量程口径冻结定死**) | 板上 RANGE 接 GND → **±5 V 双极性量程**(芯片手册输入量程表:RANGE = 0 → −5 ~ +5 V),16 位二进制补码,**±5 V 满量程 = 10 V 跨度**,LSB = 10 V / 65536 ≈ **152.59 µV/码(@ 转换器引脚)**。“±10 V” 是芯片的另一可选档(RANGE = HIGH,跨度 20 V、LSB ≈ 305.18 µV),**本板未用**——历史版本曾把本板误标为“±10 V 双极性”,与 §7 本行矛盾、且会让 TPAFE 侧系数差一倍,更正记录见 §9 | 用 `tpafe5160_to_voltage()` 换算;端到端口径见 §7.1 | @@ -227,7 +227,7 @@ STM32(U17)为 LQFP144 脚号。 | 2026-10-09 | 建档:固化 V0.1 语义分配(UA/UB/UC/UN、IA/IB/IC/IN、V_ZL2=温度、其余备用)与 17 路端到端映射;确立"主板只做监视/辅助测量、计量在子板"口径;勘误 PC4 通道号(IN12→IN14)与 PB0 定性(=IN8) | | 2026-10-09 | **CubeMX 生成 ADC1(9 通道)落地**:新增 `Src/adc.c`/`Inc/adc.h` + HAL adc 三文件并挂进 uvprojx、`HAL_ADC_MODULE_ENABLED` 生效、`MX_ADC1_Init()` 入 `main.c`;分频 PCLK2/4 = 21 MHz——面板里 DIV2 已被 `FadcConstraint`(36 MHz)约束禁掉,**故不加 `.ioc` 键 / 代码守卫 / 审计项等额外防护**(曾加过,按决定撤除,理由见 §7"ADC 时钟"行);修正 PA0 标签笔误 `AD9_RES`→`ADC9_RES`。⚠ 本次重生成打回了 5 处定制(IWDG 延迟启动锚点、HardFault + 3 个 Fault 手工 Handler、`FreeRTOSConfig.h` 加固区、`freertos.c` 三处栈核算注释),已全部复原;存活审计新增 1 项 `adc1-hal-enabled`(共 19 项),现 19 通过 / 1 警告(timeSyncTask 手工定义)/ 0 失败,`@build` 0 错 0 警 | | 2026-10-10 | **口径更正**:8 路电气量分属 TPAFE / STM32 两片转换器**定性为 V0.1 硬件缺陷**,撤销 2026-10-09 建档时"有意分配、不要修正"的说法;补记缺陷实质(跨转换器无同一时刻性、16 位双极性 vs 12 位单极性、语义与归属交叉)与 **V0.2 改版要求**(8 路电气量全部接 TPAFE,与 5 路备用路对调串联电阻目的地,BOM 不变);§7 新增"通道归属"行,明确 V0.1 上禁止跨转换器做相位/功率类运算。同步更正 `V0.1_网表详细分析报告.md` §1.4(新增 V10)/ §6-3,登记簿新增 **N-18** | -| 2026-10-10 | **STM32 ADC1 消费者落地(L1-3/L1-4)**:新增 `App/algo/adc_input.c/h`——语义映射表 `k_adc_map[]` 按本文件 §3/§5 逐条落地(`UA/UN/IA`→TPAFE `buf[6]/[5]/[2]`,`UB/UC/IB/IC/IN/TEMP`→`CH14/3/6/1/2/5`),并提供 `adc_input_raw()` **按语义取数**;`stm32_scan()` 实现 9 通道软件触发顺序扫描;`adc_input_init()` 增**映射自检**(STM32 映射通道必在扫描序列内)。§7 的"STM32 ADC 外设 / `adc_task` / TPAFE 采样触发"三行同步更新(后两者原为陈旧描述:TIM6 周期触发、L1 层取数)。§8 步骤 2 标注**引脚级注入验证尚未执行**。台架 `@test` PASS(`adc1_rc=0` / `adc1_err=0`、`ADC input layer init: OK`;首帧 `UA=11429 UN=11454 IA=11452`、`UB=1018 UC=1044 IB=943 IC=1069 IN=1052 T=850`——互异非饱和,且两次开机读数不同 → 实时转换而非常量),证据 `测试说明/boot_log_data/20261010_prod_adc_l1_boot_log.txt` | +| 2026-10-10 | **STM32 ADC1 消费者落地**:新增 `App/algo/adc_input.c/h`——语义映射表 `k_adc_map[]` 按本文件 §3/§5 逐条落地(`UA/UN/IA`→TPAFE `buf[6]/[5]/[2]`,`UB/UC/IB/IC/IN/TEMP`→`CH14/3/6/1/2/5`),并提供 `adc_input_raw()` **按语义取数**;`stm32_scan()` 实现 9 通道软件触发顺序扫描;`adc_input_init()` 增**映射自检**(STM32 映射通道必在扫描序列内)。§7 的"STM32 ADC 外设 / `adc_task` / TPAFE 采样触发"三行同步更新(后两者原为陈旧描述:TIM6 周期触发、语义取数与有效性层取数)。§8 步骤 2 标注**引脚级注入验证尚未执行**。台架 `@test` PASS(`adc1_rc=0` / `adc1_err=0`、`ADC input layer init: OK`;首帧 `UA=11429 UN=11454 IA=11452`、`UB=1018 UC=1044 IB=943 IC=1069 IN=1052 T=850`——互异非饱和,且两次开机读数不同 → 实时转换而非常量),证据 `测试说明/boot_log_data/20261010_prod_adc_l1_boot_log.txt` | | 2026-10-23 | **验证现状核查与排期**:确认 §8 步骤 1/2 与 `V0.0_网表详细分析报告.md` §5.1 第 6 条的"首次功能级 ADC 验证"**均无执行记录**——现有台架判据只证"通路活"(9 通道转换成功、零超时、互异非饱和、跨次开机不同),不证读数正确,连极性都未验。新增 §8 步骤 4:**"端子电压 → ADC 引脚电压"的变比在仓库内无任何文档**(无子板原理图),且 `1k 串联 + 1k 到地` 已被 §4.7 定性为"源阻抗匹配、非分压",故撤销"按 2:1 换算二次值"的用法。验证方法同步降为**不需要示波器 / 信号源**:`BUSY` 脉冲改用 `tpafe5160_frame_seq()` 帧计数,幅值改用万用表 + 三点线性拟合。排期见 `docs/任务规划/数据可信.md`(已知电压注入验证 / 端子/子板变比确认) | | 2026-10-25 | **量程口径定死(本波)**:文首摘要行“TPAFE5160(16 位、8 通道同步、±10 V 双极性)”更正为“±5 V 满量程 = 10 V 跨度、LSB ≈ 152.59 µV(板上 RANGE 接 GND)”;§1“缺陷实质 2”补“满量程 = 10 V 跨度”;§7“TPAFE 量程”行重写并括注“±10 V 是芯片可选档(RANGE=HIGH)、本板未用”;新增 §7.1 固化“端子 → 子板变比 → 网名 AINn → 电阻网络 → 引脚 → 码值 → 满量程”链路分段口径——② 引脚侧 LSB 系数(TPAFE 152.59 / STM32 805.66 µV/码)**定死**,③ 1k+1k 网络“2:1 分压 vs 源阻抗匹配”两读法待已知电压注入验证斜率判别,④ 端子变比为仓库缺口待「端子/子板变比确认」;“板级系数 305.18 / 1611.3 µV/码”定性为读法 A 条件值、判别前禁入代码;新增内容全部收进 §7.1 子节、本章编号刻意保持 §9 不动(`docs/算法/继电保护功能复现规划.md` §4 有指向本文件 §9 的既有跨文档引用,挪号会打断 doc-links 章节校验)。实测数值本轮按用户指示未采(台架未跑),相关段均标“待台架实测” | | 2026-10-27 | **③ 段定死为 1:1(网表追线)**:逐通道追 `V0.1.tel`,`ADCn` 节点(如 UA 的 `ADC7`,第 241 行 `ADC7 ; C44.2 R31.2 U3.61`)只有 {串阻.2, 滤波 C, 转换器引脚} 三者,**无对地元件**——"1k 到地"实际接在差分负端(`ADC7_GND` → R32 → AGND,第 151 行),属差分滤波。故 ③ 段不是分压器、变比 1:1,旧"读法 A(2:1 分压 → 305.18 / 1611.3 µV/码)"**作废**,`docs/任务规划/数据可信.md` §2.1 旧表同步改为引脚侧系数。§7.1 新增拓扑 SVG(`docs/images/ADC前端电路.svg`,生成器 `tools/arch_diagram/gen_adc_frontend_svg.py`),含 STM32 侧对照面板;§7"前端偏置"行、§8 步骤 4 同步改写。已知电压注入验证的判据随之从"判别 A/B 读法"降为"确认转换器 LSB 与逐通道映射" | diff --git a/docs/算法/继电保护功能复现规划.md b/docs/算法/继电保护功能复现规划.md index cdb5762f..450bd4d3 100644 --- a/docs/算法/继电保护功能复现规划.md +++ b/docs/算法/继电保护功能复现规划.md @@ -182,7 +182,7 @@ | 层 | 职责 | 参考对应 | 本项目落点(拟) | | --- | --- | --- | --- | -| **L0 采样驱动** | 触发转换、读出原始码值 | AD 芯片 + 总线 | `Drivers/BSP/TPAFE5160/` + STM32 ADC1 HAL | +| **采样驱动层** | 触发转换、读出原始码值 | AD 芯片 + 总线 | `Drivers/BSP/TPAFE5160/` + STM32 ADC1 HAL | | **语义取数与预处理** | 快照取数、语义映射、标定、有效性校验、等周波调频、零序接入 | `DataPreRecv` / `RealDataMerge` / `CalSoftFr` / `GetZoreSamData` | `adc_task` + `App/algo/`(拟) | | **全周傅氏相量计算** | 全周傅氏基波/谐波、线/负序/零序合成、双区交换 | `CalFFTVecFull` / `CalfLineVec` / `CalfNegVec` | `App/algo/`(拟) | | **保护判据元件** | 各保护判据(过流/零序/过压/频率/…) | `RelayUnit/PRS_*.c` | `App/protect/`(拟) | @@ -609,8 +609,8 @@ B0 产品定义 ✅(2026-10-10 已确认,见 §1.5) | 2026-10-11 | **全周傅氏相量台架闭环:P0 收尾** | `@build` 0 错 0 警 → `@test` 闭环把 `phasor(first)`/`phasor(hb)` 列为 Required 并校验顺序 `App init OK>phasor(first)>phasor(hb)`(⚠ `net init: OK` **不在**该序内——adcTask 与 netTask 并行,`phasor(first)` ≈+500 ms 早于 `net init: OK` ≈+574 ms 属正常)→ **PASS**:5 实例相量开机即产出、心跳 5 实例持续刷新、`frames` 每 10 s +10000(1000.0 Hz)、`idle=0`、`frst_err=0`、`adc1_err=0`、`adc1_hw=0`、`fw build` 身份戳匹配、回读一致、锚点 ×1、零 `[ERR]`/HardFault;证据 `boot_log_data/20261011_prod_phasor_boot_log.txt`。栈复测:adcTask 静态 832 B / 实测峰值 **928/2048(余 54%)**,7 任务全 PASS(`docs/内存管理/RAM使用分析与优化方案.md` §2.3 已同步)。§3.1 语义取数与有效性层置 ✅(`C04-IA-F2`/`C05-IA-F5` 台架产出);§3.2 全周傅氏相量层置 ✅(精度判据保留"待标准源"注),**全周傅氏相量层更正为"V0.1 不适用、随 V0.2"**(跨转换器合成被 N-18 禁止、零序走外部专用通道——原表下注"全周傅氏相量层实现完成"对全周傅氏相量层系误记);§7.1 P0 里程碑置 ✅(遗留语义取数与有效性层)。下一步 = **P1**(A 相过流/过负荷 + 定值模型)与 **语义取数与有效性层采样有效性校验** | | 2026-10-11 | **语义取数与有效性层采样有效性校验落地** | 新增 `App/algo/adc_health.c/h`(**纯逻辑判定层**,零平台依赖免桩单测):① 静默——两侧帧序号连续 ≥100 ms 无前进(电平位,可自愈;`adc_health_flags()` 为动作留痕层闭锁预留);② 丢帧——TPAFE 序号一次前进 >1 计 `tgap`(与 STM32 侧 `adc1_gap` 对称,边沿事件不告警——消费者超拍是已知现象,告警会淹没真故障);③ 单调性——序号倒退锁存位;④ 错误增量——FRSTDATA / HAL 错误回调 / STM32 停摆计数增量(事件位与锁存位**编号对齐**,消费者按位图"每类每次开机留痕一次")。`adc_task.c` 只做告警编排:严重事件/上升沿 `DBG_ERROR` + `SYSLOG_SELFTEST`("adc sampling fault"),下降沿 `DBG_INFO`,心跳行增 `tgap`。主机单测 `test/host/adc_health/` **31 用例全绿**(100 ms 门限两侧、tick 回绕、边沿语义、NULL 守卫、init 幂等;`adc-health-unit-test` 钩子登记)。台架 `@test` PASS:零 `[ERR]`(健康不误报)、`tgap=1→2` 与 `adc1_gap` 同步可见、`frames` 1000.0 Hz;证据 `boot_log_data/20261011_prod_adc_health_boot_log.txt`。⚠ **抬栈**:`adc_health_report`→`syslog_post` 控制台回显尾使 adcTask 静态最坏链 832→**1080 B**(512 words 余 968 B < 1024 B 底线)→ **512→640 words**,复测余 **1480 B / 57%**、堆 min_ever 7152→6640 B(AGENTS 任务表 / `docs/内存管理/RAM使用分析与优化方案.md` §2.3 已同步)。§3.1 语义取数与有效性层置 ✅ | | 2026-10-11 | **P1/P2 软件侧:定值模型 + 保护元件框架** | 新增 `App/algo/prot_set.c/h`(**定值模型**:Q10 定点与参考 2^10 口径一致;31 项元数据表**通讯地址逐项对照参考 `Comm/Pub/Const_CommSetPara.c`**——过流 Ⅰ/Ⅱ/Ⅲ = 0x8856~0x8861、过负荷 = 0x8871~0x8874、零序过压 = 0x888E~0x8890、过压/欠压沿用告警段 0x8027~0x802A;范围校验 + 整表装载 all-or-nothing + **sum32 正/反码封存自检闭锁**(参考 `dwTagSum/dwTagSumN` 模式,动作留痕层);⚠ 默认值/范围为**拟定**,参考 EEPROM 出厂表不在本仓库,待对照修订)+ `App/algo/prot_elm.c/h`(**元件框架**:通用"越限 + 返回系数滞回 + 定时限"核,数据驱动实例表 7 行 = OC1/OC2/OC3/OL(IA)+ OV/UV(UA)+ 3U0(UN);`prot_elm_eval()` 纯判据可主机单测,`prot_elm_run()` 为 phasor→测量薄适配(相量未就绪整拍跳过);定值闭锁 → 全元件复位)。主机单测 `test/host/prot_set/`(30 用例)+ `test/host/prot_elm/`(27 用例:1.01× 启动 / 100 ms 到点 / 滞回保持与释放 / 欠量方向 / 投退 / 闭锁联动 / 时基回绕)全绿,钩子 `prot-set-unit-test` / `prot-elm-unit-test` 登记。`@build` 0 错 0 警(**未接线 → 链接器整对象移除,零 Flash/RAM 足迹**);板端接线随 protectTask 立项(栈预算 + IWDG 门禁),标准源注入验收随其后。§3.3 保护判据层/3/4/6 置 🔶、保护判据层/5 注明框架就绪待通道/频率算法;§3.5 动作留痕层/2/4 置 🔶;§5.4 落实进度注 | -| 2026-10-12 | **B5 板端接线:protectTask 立项落地** | 新增 `App/task/protect_task.c/h`(**纯编排**,判据仍在 `App/algo/`):20 ms 节拍三件事——① **动作留痕层采样故障闭锁**:`adc_health_flags()` 电平位(TPAFE/STM32 stall + 序号倒退)命中 → `prot_elm_init()` 复位 + 本拍不 eval(stall 自愈自动恢复;锁存类事件位不闭锁——非"当前测量不可信");② **动作留痕层定值校验和巡检**(每拍 `prot_set_selfcheck()`,31 项 sum32 开销可忽略;⚠ 与 104 定值写的竞态窗口 µs 级、同次写 seal 即清锁,如实登记,P4 定值持久化收口);③ `prot_elm_run()` + 动作位图边沿日志(`[PROT]`,V0.1 无出口 → 动作 = 遥信 + 日志)。**线程契约收口**:`prot_elm_request_reset()`(单字标志,任意上下文)——104 YK11 在 iec104Task 落请求、下一拍 eval 头部消费,杜绝与 protectTask eval 竞态(`iec104_pts.c` 改走此口);定值/元件初始化移入 `app_main_init`(内核前,phasor_init 之后)。**门禁(E-59 同款)**:`TASK_WD_PROTECT = 8` 入 `wd_required_mask()` 两分支(产品 `0xF7`→`0x1F7`、HW 层 `0xF5`→`0x1F5`,看门狗单测 39 断言同步);栈 **512 words**,`stack_report.py` 实测静态最坏链 **872 B / 余 1176 B / 57%**;**FreeRTOS 堆 48→56 KB**(`.ioc` L1 参数 + `FreeRTOSConfig.h` 同步,CCM 75.3%→87.9% < 92% 门禁,`RAM预算台账.md` §6 登记配额 48→56 KB)。任务手写于 `Src/freertos.c` USER CODE 区(Normal,V0.1 无出口不需 AboveNormal;§2.2 表已注"1 ms/高"为 P2 瞬时值口径)。单测 +1(TC-PE-010 复归请求:不当拍生效、下一拍消费,滞回保持区可区分复位与否)→ prot_elm 28 用例全绿。`@build` 0 错 0 警(Code=204348,ZI +8,196 = 堆 +8 KB);台架 `@test`(PROT 锚点 + `[STACK] protect` 水位 + 104 YX 联动)待做。§3.3 保护判据层/3 注板端落地、§3.5 动作留痕层置 ✅、§7 P1 里程碑更新 | +| 2026-10-12 | **B5 板端接线:protectTask 立项落地** | 新增 `App/task/protect_task.c/h`(**纯编排**,判据仍在 `App/algo/`):20 ms 节拍三件事——① **动作留痕层采样故障闭锁**:`adc_health_flags()` 电平位(TPAFE/STM32 stall + 序号倒退)命中 → `prot_elm_init()` 复位 + 本拍不 eval(stall 自愈自动恢复;锁存类事件位不闭锁——非"当前测量不可信");② **动作留痕层定值校验和巡检**(每拍 `prot_set_selfcheck()`,31 项 sum32 开销可忽略;⚠ 与 104 定值写的竞态窗口 µs 级、同次写 seal 即清锁,如实登记,P4 定值持久化收口);③ `prot_elm_run()` + 动作位图边沿日志(`[PROT]`,V0.1 无出口 → 动作 = 遥信 + 日志)。**线程契约收口**:`prot_elm_request_reset()`(单字标志,任意上下文)——104 YK11 在 iec104Task 落请求、下一拍 eval 头部消费,杜绝与 protectTask eval 竞态(`iec104_pts.c` 改走此口);定值/元件初始化移入 `app_main_init`(内核前,phasor_init 之后)。**门禁(E-59 同款)**:`TASK_WD_PROTECT = 8` 入 `wd_required_mask()` 两分支(产品 `0xF7`→`0x1F7`、HW 层 `0xF5`→`0x1F5`,看门狗单测 39 断言同步);栈 **512 words**,`stack_report.py` 实测静态最坏链 **872 B / 余 1176 B / 57%**;**FreeRTOS 堆 48→56 KB**(`.ioc` 参数 + `FreeRTOSConfig.h` 同步,CCM 75.3%→87.9% < 92% 门禁,`RAM预算台账.md` §6 登记配额 48→56 KB)。任务手写于 `Src/freertos.c` USER CODE 区(Normal,V0.1 无出口不需 AboveNormal;§2.2 表已注"1 ms/高"为 P2 瞬时值口径)。单测 +1(TC-PE-010 复归请求:不当拍生效、下一拍消费,滞回保持区可区分复位与否)→ prot_elm 28 用例全绿。`@build` 0 错 0 警(Code=204348,ZI +8,196 = 堆 +8 KB);台架 `@test`(PROT 锚点 + `[STACK] protect` 水位 + 104 YX 联动)待做。§3.3 保护判据层/3 注板端落地、§3.5 动作留痕层置 ✅、§7 P1 里程碑更新 | | 2026-10-12 | **B7 定值持久化 + 全局软压板 + 竞态收口(采集保护收尾任务卡)** | **定值模型增项** `PS_GLOBAL_EN`(全局软压板,枚举尾追加,编号不回收);**原子写**:`prot_set_set_sealed()` 影子区拷贝→改→重封→**单字交换活跃区**(双缓冲 +124 B 静态),`prot_set_selfcheck()` 改 seqlock 式复核(封存和两读不一致 = 提交撞见 → 本轮跳过)——B5 登记的"104 写 vs 巡检"µs 级误锁窗口就此消除(§3.5 动作留痕层注收口)。**持久化**:新增 `App/cfg/prot_set_store.c/h`(双端注册 + 模拟器豁免点名)——blob = magic`PSET`+版本+计数+值区+sum32 正/反码对(144 B),`/prot_set.bin` 走 `cfg_store_write_atomic`(临时文件+rename,掉电安全);**写路径** = C_SE/YK12 执行段置脏 → iec104Task `dirty_peek` 投递 `SYS_EVT_PROT_SET_SAVE`(事件位 bit7)→ defaultTask(配置引擎属主)落盘,写失败脏回置 = 下轮重试;**装载** = storage_bring_up 在 STORAGE_READY 前整表原子 parse(损坏/越界/版本换代 → 拒绝保出厂默认 + 自检留痕 `prot set corrupt`;文件缺失 = 首启正常静默)。**YK12 转真**:`iec104_pts.c` 两段式(选择段校验 DPC、执行段 set_sealed),protectTask 下一拍读退出态 → 复位元件 + 停判据(`global plate OUT/IN` 边沿日志);本地定值项 `comm_addr=0` 显式不可寻址(防 IOA=0 误命中)。单测:prot_set 63 断言(+33:软压板/原子写/脏握手/blob 往返/坏 magic/截断/坏版本/**伪造自洽越界**对抗样本)、iec104 94 断言(+7:YK12 两段转真/置脏/本地项 C_RD EVI)。`@build` 0 错 0 警(Code=205500 +1152 B,ZI +280 = 双缓冲+blob 缓冲,`ram_baseline` 已刷)。**台架腿完成(2026-10-12)**:`@test` PASS + PC 主站 `test/host/iec104_master.py --b7-pre` **21/21**(YK12 两段 OFF→0.05 A 低定值不动作 / ON→YX15·16 即动作,C_SE→C_RD 回读)、`--b7-post` **13/13**(**压板退出态跨复位保持**:开机日志 `global plate OUT -> protection disabled` + 低定值仍不动作、重启后 YK12 ON 联动在位)、`--b7-mark/--b7-check` **4/4+4/4**(**非默认定值 7.5 跨复位保持**,C_RD=7.500,与出厂默认可区分);开机日志两次 `[CFG] prot set loaded (/prot_set.bin)`;证据 `docs/测试说明/boot_log_data/`(20261012_B7_* 两份) | | 2026-10-12 | **B6 台架 `@test` 闭环复测(B4/B5 验收窗口,采集保护收尾任务卡)** | 首跑(HEAD `d3107f3`)**FAIL**:`protect task start` 锚点行字节级交错损坏(`st[art: cycle=220ms elm=70`,两次开机同形复现)= protectTask 启动行违反控制台写者注册前门控纪律(B5 台架腿未跑故未暴露,机理与实证见 `docs/子系统设计/控制台单写者日志设计说明.md` §11 2026-10-12 行);其余 8 Required / 零 `[ERR]` / 锚点×1 / 回读一致全过。**修复** = 启动锚点行改 `dbg_console_writer_ready()` 首拍补打(adcTask 首帧门控同款,+16 B)。**复测 PASS**(B7 `1fe163b`+fix):锚点行完好(+577 ms,attach 后);**首采水位** `protect` 828/2048(free_min 1220,59%)、`iec104` 424/2560(83%)、新 56 KB 堆 free=17936 **min_ever=10000**(>3072 告警线零告警,**N-33 抬栈 min_ever 背书就绪**);采样闭锁不误报。**PC 主站往返**:新增 `test/host/iec104_master.py`(纯标准库主站客户端)STARTDT→总召四组+actterm→C_SE 两段写临时压低 OC1 定值**确定性**触发 YX15·16 变位(COT=3)→恢复复归→YK11 两段正答→C_RD 读回 10.000,**18/18 PASS**(`d3107f3` 与 B7+fix 两版固件各一轮),串口侧 `[I104] accepted`/`[PROT] elm OC1-IA` 边沿/YK11 复位→UV-UA +10 s 重返联一致。证据 `docs/测试说明/boot_log_data/20261012_B6_*`(含缺陷复现 MISS 快照)。B5 行"台架 `@test` 待做"遗留**闭环**;B7 台架腿的 C_SE 写路径已随本窗验证(断电重启保持 + YK12→YX 仍待窗) | | 2026-10-12 | **B8 频率测量层 + 过频/低频保护(保护判据层,W1 任务卡)** | 新增 `App/algo/freq.c/h`(测频:共轭乘积相位增量 + 解缠绕 + **自适应窗长** K_eff=round(8·Fs/(2·f̂))——定长窗受 20 点矩形窗**负序泄漏纹波**限制(实测最坏 0.317 Hz)、60 拍 Kay 复数和 0.197 Hz(交叉项和角不按 1/K² 衰减),自适应窗把窗长对齐纹波周期 fs/(2f) 整数倍 → **0.0101 Hz**;假值钳制带 35~65 Hz 与系统合理带分离(贴边钳制会非对称丢纹波半周 → 45 Hz 点 +0.16 Hz 偏置);幅值门 <50 码 → valid=0 保持;UA→IA 优先级;`freq_run()` 挂 adcTask `phasor_run()` 后,心跳 `freq(hb)` 行)。**定值 32→40**:尾追加 OF/UF 8 项(51.0/49.0 Hz、dly 1000 ms、返回系数 0.99/1.01;全部本地项 `comm_addr=0`,参考逐项地址不在本仓库待对照);`bad_mask`→`uint64_t`(TC-PS-011 断言 bit39 可指认);**blob VER 1→2**(144→176 B;升级语义换代/损坏分流:旧档 → `prot_set_store_load()` **rc=2 换代** INFO ≠ **rc<0 真损坏** ERROR+自检,defaultTask 不误报)。**prot_elm 7→9**:`PE_OF`/`PE_UF` + `pe_cfg_t.src`(0=相量有效值 / 1=freq 测频;freq 无读数按方向填"必不启动"值,不整拍跳过);OF/UF 暂无独立 YX 点,动作仅经 YX15 总聚合可见(独立分点 = B10 送检点表);YC `0x401D` 频率点仍 QDS=IV(接真值 + 独立分点归 B10)。单测:`test/host/freq/` **46 断言**(J1 扫频 45~55 Hz ≤0.02 实测 0.0101 / J2 噪声 1% ≤0.10 实测 0.0160 / J3 启动收敛 ≤40 ms 实测 20 / J4 阶跃 50→51 ≤120 ms 实测 98——**精度优先于响应**显式取舍:98 ms ≪ OF/UF 动作延时定值默认 1000 ms / J5 浮空 valid=0 漂移 0.13 / J6 双跳变 60 ms 回 ±0.05;三代弯路推导写 `freq.c` 文件头防重蹈)+ prot_set **90**(+27:40 项/VER 2/64 位 bad_mask/旧档拒绝)+ prot_elm **47**(+OF/UF 滞回·方向隔离·测频接线);回归 phasor 57 / iec104 94 无退化。`@build` 0 错 0 警(Code=207468);RW_IRAM1 +632 B(73.3%→73.8%)、CCM 不变、Flash +2,244 B(21.6%);**adcTask/protectTask 栈链不变**(freq_run 帧未进最坏链,无需抬栈)。§3.3 保护判据层置 ✅(软件侧)、§7 P2 更新;`freq-unit-test` 钩子登记(31→32) | -| 2026-10-12 | **B9 动作记录 + 时标 SOE(出口与记录层,W1 任务卡)** | 新增 `App/log/soe_rec.c/h`(64 B 定长 SOE 记录纯编解码:类型 1~6 = 元件动作/复归、软压板投/退、采样数据块/恢复 + 元件号 + IV 位 + seq(u32) + CP56Time2a + epoch_s + value + sum8)与 `App/log/soe_store.c/h`(板端胶水:SPSC 无锁环 8 深惰性创建 + `soe_store_drain()` 挂 `syslog_drain()` 尾,defaultTask 唯一落盘写者)。**动作路径零 NAND I/O**:protectTask 只投递(post **禁 DBG**——曾把静态链从 872 垫到 1192 FAIL,丢弃计数留痕挪 drain 侧),**protectTask 静态链 872 B / 57% 改前后逐字节不变**(DoD 硬约束)。**104 自发单点改 M_SP_TB_1(type 30,带 CP56 时标)**——idx≥4 且时标有效;总召/周期上送保持 M_SP_NA_1 不变;主站工具 `iec104_master.py` 同步解析 type 1/30。单测 `test/host/soe/` **32 断言**(往返/截断/坏校验和/IV 位/seq 连续性)+ iec104 **94→97**(TB 编解码 + pts 自发选型 + IV);`soe-unit-test` 钩子登记(32→33)。**台架腿**(sync 追平 B8/N33 后重跑,覆盖将被合并的代码):`@test` PASS(锚点 `set=40 elm=9`、无 `[ERR]`)+ **顺带兜住 B8 板端面**(`freq(hb): f=0 valid=0` 浮空不发散、`prot set blob version change (ver=1 cnt=32), defaults in use` = 换代 INFO ≠ CORRUPT 实证)+ `--b7-pre` 21/21 / `--b7-post` 13/13 不回归 + 自发 YX M_SP_TB_1 带 ts(动作/复归/重启后联动三段)+ FTP 下载 `SOE.LOG` **56 条** `decode_soe.py` PASS(boot 段内 seq 严格 +1、CP56↔epoch UTC 交叉一致)。RAM:RW_IRAM1 +1,088 B(SPSC 环 512 + 静态 FIL 528 + 计数器,74.7%);Flash +1,856 B。§3.4 出口与记录层置 ✅、§7 P4 更新;出口出口与记录层仍 ⬜(V0.1 无出口硬件,R7 否定确认口径不变) | +| 2026-10-12 | **B9 动作记录 + 时标 SOE(出口与记录层,W1 任务卡)** | 新增 `App/log/soe_rec.c/h`(64 B 定长 SOE 记录纯编解码:类型 1~6 = 元件动作/复归、软压板投/退、采样数据块/恢复 + 元件号 + IV 位 + seq(u32) + CP56Time2a + epoch_s + value + sum8)与 `App/log/soe_store.c/h`(板端胶水:SPSC 无锁环 8 深惰性创建 + `soe_store_drain()` 挂 `syslog_drain()` 尾,defaultTask 唯一落盘写者)。**动作路径零 NAND I/O**:protectTask 只投递(post **禁 DBG**——曾把静态链从 872 垫到 1192 FAIL,丢弃计数留痕挪 drain 侧),**protectTask 静态链 872 B / 57% 改前后逐字节不变**(DoD 硬约束)。**104 自发单点改 M_SP_TB_1(type 30,带 CP56 时标)**——idx≥4 且时标有效;总召/周期上送保持 M_SP_NA_1 不变;主站工具 `iec104_master.py` 同步解析 type 1/30。单测 `test/host/soe/` **32 断言**(往返/截断/坏校验和/IV 位/seq 连续性)+ iec104 **94→97**(TB 编解码 + pts 自发选型 + IV);`soe-unit-test` 钩子登记(32→33)。**台架腿**(sync 追平 B8/N33 后重跑,覆盖将被合并的代码):`@test` PASS(锚点 `set=40 elm=9`、无 `[ERR]`)+ **顺带兜住 B8 板端面**(`freq(hb): f=0 valid=0` 浮空不发散、`prot set blob version change (ver=1 cnt=32), defaults in use` = 换代 INFO ≠ CORRUPT 实证)+ `--b7-pre` 21/21 / `--b7-post` 13/13 不回归 + 自发 YX M_SP_TB_1 带 ts(动作/复归/重启后联动三段)+ FTP 下载 `SOE.LOG` **56 条** `decode_soe.py` PASS(boot 段内 seq 严格 +1、CP56↔epoch UTC 交叉一致)。RAM:RW_IRAM1 +1,088 B(SPSC 环 512 + 静态 FIL 528 + 计数器,74.7%);Flash +1,856 B。§3.4 出口与记录层置 ✅、§7 P4 更新;出口与记录层仍 ⬜(V0.1 无出口硬件,R7 否定确认口径不变) | diff --git a/docs/编号规范.md b/docs/编号规范.md index 3fdf5fbe..8a600570 100644 --- a/docs/编号规范.md +++ b/docs/编号规范.md @@ -54,7 +54,7 @@ ### 3.3 语义标识(默认形态,怎么写) - 一行短语就是标识:≤20 汉字、说清"是什么事"、**同一登记表内不撞名**(同义异写算撞名)。 -- 好的例子:`双转换器无共同采样触发`、`FTP 鉴权`、`写回缓存空闲 flush`、`L5 队列往返基线待重验`。 +- 好的例子:`双转换器无共同采样触发`、`FTP 鉴权`、`写回缓存空闲 flush`、`队列往返基线待重验`。 坏的例子:`优化项 3`、`遗留问题`、`重要缺陷`——换个上下文就指代不明,等于又造了个号。 - 登记处就是那张表:表首格写短语,**不要再配一个字母号**;跨文档引用写成 `` `docs/任务规划/登记簿.md` 「双转换器无共同采样触发」 ``,同篇后续直接用短语。 diff --git a/docs/门禁说明/cubemx-survival.md b/docs/门禁说明/cubemx-survival.md index 810c8ba4..5a4006a7 100644 --- a/docs/门禁说明/cubemx-survival.md +++ b/docs/门禁说明/cubemx-survival.md @@ -24,7 +24,7 @@ - 实测记录:`.ioc` 里 `ProjectManager.KeepUserCode=true` **只**保护 `USER CODE BEGIN/END` 之间; 2026-09-29 一次重生成覆盖了 **4 处定制**(P0×1 + P1×2 + P2×1)。典型形态:崩溃采集函数被打回 空 `while(1)`、加固宏被整块删除——**不报错,只在真崩的时候才发现没日志**。 -- AGENTS 把防丢失排成三层(L1 写进 `.ioc` → L2 搬进 `USER CODE` 区 → **L3 本门当审计兜底**)。 +- AGENTS 把防丢失排成三层(写进 `.ioc` → 搬进 `USER CODE` 区 → **本门当审计兜底**)。 - 那一次被覆盖的具体内容:`HardFault_Handler` 的崩溃采集**被打回空 `while(1)`**、**两个 FreeRTOS 加固宏被整块删除**——两处都不报编译错,只在真出故障时才暴露。 @@ -44,7 +44,7 @@ CubeMX 重生成存活审计.......Failed ``` - 单独跑:`--report` 只看审计报告(不比对基线);确认改动无误后 `--write` 重建基线。 -- 正确动作:**先把定制搬回正确位置(优先 L1/L2),再 `--write`**;直接 `--write` 等于宣布 +- 正确动作:**先把定制搬回正确位置(优先写进 `.ioc` 与搬进 `USER CODE` 区),再 `--write`**;直接 `--write` 等于宣布 "被覆盖掉的样子是新的正确答案"。 - 每次重生成后**立刻**跑(AGENTS 铁律),别等到提交时才发现。 @@ -82,7 +82,7 @@ CubeMX 重生成存活审计.......Failed ## 8. 改这个门禁本身 -- 新增受保护项:先想能不能落到 `USER CODE` 区(L2)或 `.ioc`(L1)——清单越长,人工维护越重; +- 新增受保护项:先想能不能落到 `USER CODE` 区或 `.ioc`——清单越长,人工维护越重; - 自造标记区(自定义 `BEGIN/END` 名字)会被官方模板换代抹掉,别拿它当保护手段。 ## 9. 变更记录 diff --git a/scripts/archive/id_gate/编号治理历史.md b/scripts/archive/id_gate/编号治理历史.md index 9731000c..d6e4924c 100644 --- a/scripts/archive/id_gate/编号治理历史.md +++ b/scripts/archive/id_gate/编号治理历史.md @@ -316,6 +316,40 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 历史文档、变更记录行、提交信息、`.gitea` 历史 run 里出现的 `L0`/`L0.5` 一律按本表换算来读;不要拿它们当"现行名"去搜代码或 CI(现行 CI 里搜 `L0` 只会命中别的族)。本表所在目录不参与编号门禁(`check_ids_new.py` 排除 `scripts/archive/`)。 +### 10.3 批次六余账收口(同日;清完层级号后顺带收掉的四个同形族) + +用户指令「排队的两条余账,做完」。这四族与批次六同源(都是"机械替换留下的半改状态"),一起收: + +| 族 | 旧形态 | 现在 | 站点 | +|---|---|---|---| +| 链路时延分段 | `L1`~`L11`(含 C 宏名 `LAT_P13_OP_L5_RECV`、`P13_ST_L3`,peer 的 `OP_L5_RECV`、局部量 `l5_t0`) | 权威 ASCII 段名(表见 §10);文档侧仍写中文段名 | test/board 111 + ch395f.h 1 + latency_peer.py 46 | +| 日志自愈层号 | 「L1 文件级自愈」「L2 错误去重」 | 文件级自愈 / 错误去重(正文本来就这么叫) | App/log 4 + test/host/sys_log_mcu 6 | +| timeSync 挂起 | 「L3 pending 化」 | 挂起请求 / pending 化(既有裁定:写平实语言,不留层号) | App/sync/time_sync.c 3 | +| CubeMX 防丢失三层 | 「优先 L1/L2」「L3 兜底」 | 写进 `.ioc` / 搬进 USER CODE 区 / 审计兜底 | docs 30 + check_cubemx_survival.py 6 | +| 采集链旧层名 | 「L2 相量层」「L0 采样驱动」「`L2-x` 前缀」「L1 层 v2.3」 | 全周傅氏相量 / 采样驱动层 / 相量输出标签前缀 / 语义取数与有效性层 | docs 5 + App 4 | +| UI 菜单层级 | 「4 级(L0~L3)」「主界面 (L0)」 | 「4 级(主界面 / 一级 / 二级 / 三级菜单)」「主界面」 | docs/产品需求 2 | + +**四条边界与裁定,写给下一个动这块的人**: + +1. **peer 的 JSON 键不改(本族清退到 schema 边界为止)**:`test/host/latency_peer.py` 的 + `l5_recv` / `l5_send` / `l3_drain` / `l7_crc` / `seg_l6` / `seg_l7_first_byte` / `seg_l7_total` / + `frames_l7` 与内部字典键 `l3`/`l6`/`l7`(共 48 token)是 `docs/测试说明/m2_data/*.json` 五份 + **冻结实测证据**的 schema。改名要么让代码与归档数据漂移,要么得回头改证据文件(等于篡改 + 测量产物)——两条都不接受。这是**有记录的刻意例外**,不是漏网。 +2. **`REQ2FIRST` / `FULLFRAME` 不并入 `ECHO_RT` / `FRAME_RT`**:它们是「整帧往返」段内两个不同 + 测量(报告标签本就叫 `req2first_mcu` / `fullframe_mcu`),并入会撞名并丢掉区分度。 + 判据:**段名可以共用,测量名必须各自可辨**。 +3. **两处死号就地删掉**(按「就地删号留事实、禁补号」):ADC通道映射 变更记录行的 `(L1-3/L1-4)` + ——全仓无别名登记、无第二个引用点,指向已不可还原;看门狗设计说明「原 L2 基线 3/10」按上下文 + (38 次上板启动复现)读作主机侧回归的早期基线,改写为「主机侧回归的早期基线 3/10」。 +4. **登记簿条目 `L5 队列往返基线待重验` 改名**为「队列往返基线待重验」:这里的 `L5` 曾**长在语义 + 短语里**(`id_aliases.json` 当年就是这么登记的),属"短语里混着号"的坏例子——`docs/编号规范.md` + §1 的好例子清单里正是它。引用侧 5 处同批改(登记簿 §1/§7、实机测试实施指南 §7.3 标题与步骤表、 + 编号规范好例子清单);归档里那条历史 note 原文不动,它是当时决定的记录。 + +另修一处上一批路径残留:`net_latency_test.c/.h` 三处注释把 peer 少写一级目录(正确路径是 +`test/host/latency_peer.py`),已改对。 + ## 变更记录(2026-10-28 瘦身以前的全量流水) | 日期 | 变更 | 说明 |