From f5902bcc21075a35d7684e385a37e831796c31ac Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Sun, 15 Nov 2026 21:30:07 +0800 Subject: [PATCH] =?UTF-8?q?docs(design):=20=E8=BF=90=E8=A1=8C/=E8=87=AA?= =?UTF-8?q?=E6=A3=80=E8=AE=B0=E5=BD=95=E4=B8=8E=20ADC=20=E8=8A=82=E6=8B=8D?= =?UTF-8?q?=E4=B8=A4=E7=AF=87=E6=94=B9=E4=B8=BA=E7=8E=B0=E7=8A=B6=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=B9=B6=E5=8E=BB=E6=8E=89=E6=A1=A3=E5=90=8D"?= =?UTF-8?q?=E8=AF=B4=E6=98=8E"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两篇规划期文档改成对当前系统的描述,档名统一去掉"设计说明"的"说明"。 运行与自检记录: - 旧档重写为现状版(10 节:概览 / 记录布局 / 写入路径 / 读取与呈现 / 事件分类 / 控制台日志 / 双端差异 / 内存 / 异常恢复 / 自测要点), 删掉需求表、调研摘要、模块清单动作列、实施步骤与开放问题。 - 按代码纠正:七类记录齐备(原文只写了两类)、自检描述串换成实际 落盘的 28 条、待机阈值以 ui_tokens.h 为准、记录页已走 ui_w_records 共享组件、容量满只删该文件而非整卷清空。 - 待机屏移出本篇:它不属于日志,归 UI 侧;内容页架构补"待机屏 (覆盖层)"一节,产品需求 FR-LOG-3 拆成单纯的上电/重启分类, 待机显示归口已有的 FR-UI-3。 ADC 采样节拍: - §4.5 从 45 行压到 24 行,只留规则 / 违反的代价 / 为什么 / 兜底与 档案指针;九路置换实测表、A/B 对照、探针计数等取证归 ADC_Trap_Records.md 的 TRAP-ADC-01(那里已有完整档案)。 - adc1_gap 字段去掉"2026-10-21 起、此前固件为次数"的修订记录式括号。 - app_main.c 的注释改为指向 TRAP-ADC-01 取证、§4.5 设计口径。 引用同步:两个旧档名全仓替换(含代码注释、角色档、归档 id 表), 旧名残留 0;对 ADC 篇的 § 引用逐条复核落在真实章节上。 验证:48 个提交期钩子全通过,check_doc_links --sections-strict 通过。 --- .dsh/roles/algo-specialist.md | 2 +- .../reference/clean-room-measure.md | 2 +- .../task-worktree/reference/gates-in-tree.md | 2 +- AGENTS.md | 2 +- App/README.md | 2 +- App/algo/README.md | 4 +- App/algo/adc_input.c | 2 +- App/algo/adc_input.h | 2 +- App/algo/phasor.h | 2 +- App/algo/sample_ring.h | 2 +- App/app/app_main.c | 3 +- App/log/README.md | 2 +- App/log/sys_log.h | 2 +- App/log/sys_log_mcu.c | 4 +- App/task/adc_task.c | 4 +- Drivers/BSP/UI/ui_page_runlog.c | 2 +- Drivers/BSP/UI/ui_page_runlog.h | 2 +- Drivers/BSP/UI/ui_page_selftest.c | 2 +- Drivers/BSP/UI/ui_page_selftest.h | 2 +- Drivers/BSP/UI/ui_standby.c | 2 +- Drivers/BSP/UI/ui_standby.h | 2 +- Inc/adc.h | 2 +- README.md | 2 +- Src/adc.c | 2 +- Src/freertos.c | 2 +- Src/main.c | 4 +- docs/README.md | 4 +- docs/UI设计/内容页架构.md | 29 +- docs/UI设计/页面扩充详细方案.md | 4 +- docs/产品需求/产品需求设计文档.md | 10 +- ...采样节拍设计说明.md => ADC采样节拍设计.md} | 58 +-- docs/子系统设计/控制台单写者日志设计说明.md | 6 +- docs/子系统设计/故障录波设计说明.md | 6 +- docs/子系统设计/看门狗设计说明.md | 8 +- docs/子系统设计/运行与自检记录设计.md | 287 ++++++++++++++ docs/子系统设计/运行与自检记录设计说明.md | 351 ------------------ docs/架构设计/软件架构与运行时规格.md | 2 +- docs/架构设计/项目架构总览.md | 6 +- docs/测试说明/单元测试/主机单测覆盖率口径.md | 8 +- docs/硬件平台/ADC通道映射说明.md | 2 +- docs/算法/继电保护功能复现规划.md | 2 +- docs/算法/采样有效性校验设计说明.md | 14 +- docs/算法/采集与算法层设计说明.md | 12 +- docs/问题记录/ADC_Trap_Records.md | 6 +- scripts/archive/id_gate/id_aliases.json | 2 +- simulator/sim/sim_syslog.c | 2 +- test/host/sys_log_mcu/run_unit_test.py | 2 +- 47 files changed, 413 insertions(+), 469 deletions(-) rename docs/子系统设计/{ADC采样节拍设计说明.md => ADC采样节拍设计.md} (88%) create mode 100644 docs/子系统设计/运行与自检记录设计.md delete mode 100644 docs/子系统设计/运行与自检记录设计说明.md diff --git a/.dsh/roles/algo-specialist.md b/.dsh/roles/algo-specialist.md index 4a182042..efc89b93 100644 --- a/.dsh/roles/algo-specialist.md +++ b/.dsh/roles/algo-specialist.md @@ -132,6 +132,6 @@ onToolCallBudget: wrap-up - `docs/算法/采集与算法层设计说明.md` - 设计与判据详解 - `App/algo/README.md` - 目录索引 -- `docs/子系统设计/ADC采样节拍设计说明.md` - 采样节拍与通道映射 +- `docs/子系统设计/ADC采样节拍设计.md` - 采样节拍与通道映射 - `docs/硬件平台/ADC通道映射说明.md` - 网名/板级/转换器通道对照 - `docs/架构设计/项目架构总览.md` - 数据主线全景 diff --git a/.dsh/skills/task-worktree/reference/clean-room-measure.md b/.dsh/skills/task-worktree/reference/clean-room-measure.md index bd484b37..86b30691 100644 --- a/.dsh/skills/task-worktree/reference/clean-room-measure.md +++ b/.dsh/skills/task-worktree/reference/clean-room-measure.md @@ -28,7 +28,7 @@ git -C "$WT" status --porcelain # 应为空 2. **一次只注入一份在飞文件 → 跑同一个扫描器 → 与基线差分**(得到的是每个文件的**独立**贡献,不是合计): ```bash -for f in "App/log/sys_log_mcu.c" "test/host/sys_log_mcu/sys_log_mcu_test.c" "docs/子系统设计/运行与自检记录设计说明.md"; do +for f in "App/log/sys_log_mcu.c" "test/host/sys_log_mcu/sys_log_mcu_test.c" "docs/子系统设计/运行与自检记录设计.md"; do mkdir -p "$WT/$(dirname "$f")" cp "$MAIN/$f" "$WT/$f" # 拷进净室;主树那份只读、一字节不动 echo "== $f =="; "$PY" "$WT/scripts/archive/id_gate/check_ids.py" 2>&1 | tail -25 # ← 与第 1 步同一条命令 diff --git a/.dsh/skills/task-worktree/reference/gates-in-tree.md b/.dsh/skills/task-worktree/reference/gates-in-tree.md index 70538fc4..c7b20721 100644 --- a/.dsh/skills/task-worktree/reference/gates-in-tree.md +++ b/.dsh/skills/task-worktree/reference/gates-in-tree.md @@ -57,5 +57,5 @@ git log -1 --pretty=%B | grep -ci '\bDE\b' # 同主题的次要关键词:同 `ci-artifacts/merge-msg-.txt` 落盘后**同样 grep 一遍**再 `git merge --no-ff --no-edit -F`(C2 第 3 步)。 自审发现消息里的行数 / 统计算错而**尚未推送** → `git commit --amend -F <改好的文件>` 就地修(实测就这么修掉一次 off-by-one);**别追加一笔「更正」提交**——那等于把禁词和被更正的错数一起写回历史。 -6. **合并提交期 pre-commit 只查冲突文件(2026-10-29 实测)**:合并那一次提交的钩子日志首行是 `[INFO] Checking merge-conflict files only.`,其余钩子全打 `(no files to check) Skipped` —— 于是「合并提交通过了 30 项钩子」这句话**只覆盖冲突文件**,别拿它当全量绿。正解:合并落主干后立刻在主工作树跑全量 `scripts/ci_local.py`(静态 + 提交信息 + 主机侧单测 + manual 段),实测全量这一步才会抓到「表格断列」「章节引用悬空」这类跨文件问题。合并解冲突时若上游把 Markdown 表格重排过,注意**单元格里的竖线必须写成反斜杠竖线**(先例 `docs/子系统设计/运行与自检记录设计说明.md` 的 `FA_WRITE\|FA_SEEKEND`),否则 `FA_WRITE|FA_OPEN_APPEND` 会被当列分隔符打断,而 `doc-links` 不查表格列数——要自己数(第 4 条)。 +6. **合并提交期 pre-commit 只查冲突文件(2026-10-29 实测)**:合并那一次提交的钩子日志首行是 `[INFO] Checking merge-conflict files only.`,其余钩子全打 `(no files to check) Skipped` —— 于是「合并提交通过了 30 项钩子」这句话**只覆盖冲突文件**,别拿它当全量绿。正解:合并落主干后立刻在主工作树跑全量 `scripts/ci_local.py`(静态 + 提交信息 + 主机侧单测 + manual 段),实测全量这一步才会抓到「表格断列」「章节引用悬空」这类跨文件问题。合并解冲突时若上游把 Markdown 表格重排过,注意**单元格里的竖线必须写成反斜杠竖线**(先例 `docs/子系统设计/运行与自检记录设计.md` 的 `FA_WRITE\|FA_SEEKEND`),否则 `FA_WRITE|FA_OPEN_APPEND` 会被当列分隔符打断,而 `doc-links` 不查表格列数——要自己数(第 4 条)。 7. **跨层改动拆不成「按层分笔」提交(2026-10-29 实测,四笔改一笔)**:钩子看到的世界 = HEAD + 本笔 staged——pre-commit 会 stash 已跟踪文件的未暂存改动,而**未跟踪文件不在 `git diff-index` 里、不会被藏**(`git add -N` 也救不了,pre-commit 会把它撤下索引、文件仍在盘上)。后果:把双端登记(`MDK-ARM/*.uvprojx` / `simulator/CMakeLists.txt`)、生成器产物、互锁引用的文档分到别的笔里,本笔必被 `doc-links`(引用了还没入册的小节)、`registration-check`(源文件登记在另一笔)、`generated-fresh`(`#page=` 找不到页面描述符、字库清单与源不一致)、`mem-numbers`(受管区还是旧 .map 的数)四道**整仓**门禁一起拦。判据:**一笔提交必须自洽**——互锁的改动(代码 + 登记 + 生成物 + 文档)走同一笔;机制与三条处置结论(半成品要么先落地要么先挪出工作树)→ `docs/代码规范/pre-commit使用说明.md` §2「stash 覆盖面」。 diff --git a/AGENTS.md b/AGENTS.md index 3a0c6490..826236f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -169,7 +169,7 @@ | 架构全景 / 启动时序 | `docs/架构设计/项目架构总览.md`、`项目启动顺序管理.md` | | 产品需求基线 | `docs/产品需求/产品需求设计文档.md` | | 看门狗 + CubeMX + ARMCC __asm | `docs/子系统设计/看门狗设计说明.md` §10 | -| ADC 采样 / 通道映射 / V0.1 缺陷 | `docs/子系统设计/ADC采样节拍设计说明.md`、`docs/硬件平台/ADC通道映射说明.md` | +| ADC 采样 / 通道映射 / V0.1 缺陷 | `docs/子系统设计/ADC采样节拍设计.md`、`docs/硬件平台/ADC通道映射说明.md` | | 存储栈 / FatFs 陷阱 | `docs/架构设计/STM32F4-Base存储架构说明.md` | | RAM / 栈预算 | `docs/内存管理/RAM使用分析与优化方案.md`、`RAM预算台账.md` | | 每个门禁详解 | `docs/门禁说明/README.md` | diff --git a/App/README.md b/App/README.md index c1bff395..41a89132 100644 --- a/App/README.md +++ b/App/README.md @@ -34,4 +34,4 @@ FreeRTOS/CMSIS-RTOS API**(见 `AGENTS.md`「平台约束」)。 - 架构全景:`docs/架构设计/项目架构总览.md` - 启动顺序:`docs/架构设计/项目启动顺序管理.md` - 配置引擎:`docs/子系统设计/配置文件设计.md` -- 日志:`docs/子系统设计/控制台单写者日志设计说明.md`、`docs/子系统设计/运行与自检记录设计说明.md` +- 日志:`docs/子系统设计/控制台单写者日志设计说明.md`、`docs/子系统设计/运行与自检记录设计.md` diff --git a/App/algo/README.md b/App/algo/README.md index c5f7b2ce..9a1beb36 100644 --- a/App/algo/README.md +++ b/App/algo/README.md @@ -3,7 +3,7 @@ > 本目录的**索引**:文件清单、依赖、逐文件的"改代码必须知道"、构建与测试入口、新增模块动作。 > **详细设计——机制、契约、判据、为什么这样设计 → `docs/算法/采集与算法层设计说明.md`**(本文不复制其内容)。 > 分层归属与功能项状态 → `docs/算法/继电保护功能复现规划.md` §2.1; -> 通道映射唯一权威 → `docs/硬件平台/ADC通道映射说明.md`;采样节拍 → `docs/子系统设计/ADC采样节拍设计说明.md`。 +> 通道映射唯一权威 → `docs/硬件平台/ADC通道映射说明.md`;采样节拍 → `docs/子系统设计/ADC采样节拍设计.md`。 ## 1. 文件清单(一眼版) @@ -262,7 +262,7 @@ adcTask 现取现填后交棒,本层不复制第二个事实源)。两份录 - 通道映射权威口径:`docs/硬件平台/ADC通道映射说明.md` - 分层与功能项清单:`docs/算法/继电保护功能复现规划.md` - 傅氏算法与有效值口径:`docs/算法/傅氏算法详解.md` -- 采样节拍(TIM3 1 kHz + TRGO 触发 ADC1 / DMA):`docs/子系统设计/ADC采样节拍设计说明.md` +- 采样节拍(TIM3 1 kHz + TRGO 触发 ADC1 / DMA):`docs/子系统设计/ADC采样节拍设计.md` - **故障录波**(数据面 / 段格式 / 现场回放契约):docs/子系统设计/故障录波设计说明.md §6.2 是段文件 字节布局的属主,同一篇的 docs/子系统设计/故障录波设计说明.md §6.3 是字段口径裁定——两节合起来是板端 编码器与 PC 读段脚本两份独立实现的共同依据;板级判据与执行顺序 → docs/子系统设计/故障录波设计说明.md §10 diff --git a/App/algo/adc_input.c b/App/algo/adc_input.c index 110d53ff..87d4c542 100644 --- a/App/algo/adc_input.c +++ b/App/algo/adc_input.c @@ -111,7 +111,7 @@ static int stm32_take(void) { * 看到"计数没前进"(那个完成已被上一拍消费掉)。这种伪影**只会连续 1 拍**—— * DMA 若真停,第 2 拍仍不会前进。故只有连续 >= ADC_INPUT_STALL_RUN_MAX 拍 * 无新帧才计入故障计数(实测开机日志突发期约 21~26 次伪影、稳态每 10 s 心跳 1 次, - * 全部为单拍;见 docs/子系统设计/ADC采样节拍设计说明.md §8) */ + * 全部为单拍;见 docs/子系统设计/ADC采样节拍设计.md §8) */ if (s_stall_run < 255u) { s_stall_run++; } diff --git a/App/algo/adc_input.h b/App/algo/adc_input.h index eb9c765e..e507d8ad 100644 --- a/App/algo/adc_input.h +++ b/App/algo/adc_input.h @@ -18,7 +18,7 @@ * v2.0 2026-10-10 王建锋 STM32 侧改 TIM3 TRGO 硬件触发 + DMA 双缓冲消费: * 消除逐通道轮询的 2 ms 忙等;节拍源 TIM6 -> TIM3 * (换源判据:F4 上 ADC1 收不到 TIM6/TIM7 的 TRGO,见 - * docs/子系统设计/ADC采样节拍设计说明.md §3.2);新增 + * docs/子系统设计/ADC采样节拍设计.md §3.2);新增 * adc_input_stm32_seq() / _hw_errs() * v2.1 2026-10-21 王建锋 adc_input_stm32_gaps() 单位由「次数」改为「帧点数」, * 与判定层 adc_health_tpafe_gaps() 同单位 diff --git a/App/algo/phasor.h b/App/algo/phasor.h index 4bb8dbd5..921f463f 100644 --- a/App/algo/phasor.h +++ b/App/algo/phasor.h @@ -27,7 +27,7 @@ extern "C" { #endif -/* 每周波采样点数(1000 Hz @ 50 Hz,见 docs/子系统设计/ADC采样节拍设计说明.md) */ +/* 每周波采样点数(1000 Hz @ 50 Hz,见 docs/子系统设计/ADC采样节拍设计.md) */ #define PHASOR_N 20U /* 相量结果(**有效值口径**)。字段名沿用参考系统 `{fRe, fIm, fAm, fAmSQ}` diff --git a/App/algo/sample_ring.h b/App/algo/sample_ring.h index 8513d9db..d3675ca7 100644 --- a/App/algo/sample_ring.h +++ b/App/algo/sample_ring.h @@ -24,7 +24,7 @@ extern "C" { #endif -/* 每周波采样点数(1000 Hz @ 50 Hz,见 docs/子系统设计/ADC采样节拍设计说明.md) */ +/* 每周波采样点数(1000 Hz @ 50 Hz,见 docs/子系统设计/ADC采样节拍设计.md) */ #define SAMPLE_RING_CYCLE_N 20U /* 环容量(周波数)——与参考系统 CN_BUFSAM_CYCLE 一致 */ #define SAMPLE_RING_CYCLES 2U diff --git a/App/app/app_main.c b/App/app/app_main.c index 565efcbe..9099fc5a 100644 --- a/App/app/app_main.c +++ b/App/app/app_main.c @@ -250,7 +250,8 @@ int app_main_init(void) { * 整体错开 2 位。表象是"接地 AIN2、UB 却读到别的通道的值",极易被误判成 * 映射表写错或夹子位置不对。 * 把使能与挂 DMA 压到同一时刻后旋转消失(台架 4/4 次,同开机对照差值 ≤4 码)。 - * 证据与复现方法见 docs/子系统设计/ADC采样节拍设计说明.md §4.5。 + * 证据与复现方法见 docs/问题记录/ADC_Trap_Records.md 的 TRAP-ADC-01 + * (设计口径见 docs/子系统设计/ADC采样节拍设计.md §4.5)。 * * ⚠ 顺序不可调换:本调用必须严格早于下面的 adc_input_init()。 * 若 ADC 在本步之后仍处于使能态,旋转会重新出现。 */ diff --git a/App/log/README.md b/App/log/README.md index 3197bcd3..f44dd8b7 100644 --- a/App/log/README.md +++ b/App/log/README.md @@ -114,5 +114,5 @@ ## 相关文档 - `docs/子系统设计/控制台单写者日志设计说明.md` -- `docs/子系统设计/运行与自检记录设计说明.md` +- `docs/子系统设计/运行与自检记录设计.md` - FatFs 陷阱:`AGENTS.md`「平台约束」 diff --git a/App/log/sys_log.h b/App/log/sys_log.h index dc36973e..23642226 100644 --- a/App/log/sys_log.h +++ b/App/log/sys_log.h @@ -6,7 +6,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §4) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §4) * v1.2 2026-10-26 王建锋 新增「记录删除」API(UI 装置维护「清除记录」的前置, * 道F task-4;判据 docs/UI设计/内容页架构.md §7.1 * 「不允许只做一个没有实际效果的按钮」): diff --git a/App/log/sys_log_mcu.c b/App/log/sys_log_mcu.c index 1aa03922..c3fb2e58 100644 --- a/App/log/sys_log_mcu.c +++ b/App/log/sys_log_mcu.c @@ -37,7 +37,7 @@ * 本任务 = 看门狗喂狗点绝不阻塞。 * 新增静态 4 B .bss(s_clear_req[2] + s_clear_st[2]), * 零新增缓冲、零堆分配。 - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §4.4) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §4.4) * v1.1 2026-09-21 王建锋 消息队列改为内核启动后由 syslog_drain 惰性创建 * (修复 osKernelStart 前创建 FreeRTOS 内核对象导致的启动失败) * v1.2 2026-10-02 王建锋 s_append 落盘序列纳入卷锁 try 语义(vol_lock_wait(0)): @@ -128,7 +128,7 @@ static uint8_t s_batch[SYSLOG_BATCH_MAX * SYSLOG_REC_SIZE]; * 一整批 12 条会全部别名到最后一次写入的内容(落盘变成 1~5 + 12 七份);叠加 A 项 * 整改后的「卷忙整批回投」,条目最长会滞留到卷空闲 —— 生产者一旦投栈缓冲,函数返回 * 后就是悬垂读。故改为条目内嵌定长缓冲,长度按 SYSLOG_DESC_MAX 截断。 - * RAM 代价(实测见 docs/子系统设计/运行与自检记录设计说明.md §4.4):s_preboot 与 + * RAM 代价(实测见 docs/子系统设计/运行与自检记录设计.md §4.4):s_preboot 与 * s_batch_src 各 8 条由 8 B 变 32 B(.bss 合计 +384 B),消息队列存储由堆多要 192 B * (FreeRTOS 堆已迁 CCM)。 */ typedef struct { diff --git a/App/task/adc_task.c b/App/task/adc_task.c index a6282829..4a41bedf 100644 --- a/App/task/adc_task.c +++ b/App/task/adc_task.c @@ -14,7 +14,7 @@ * 轮询改为 **TIM3 ISR 置位 `ADC_WAKE_FLAG` 的事件驱动**——唤醒与生产同源锁定, * 消除 0~1 ms 的 tick 量化相位抖动与"超拍永久落后 → 空转"风险;本任务优先级 * 同步由 Normal 提到 **AboveNormal**(唤醒即抢占)。详见 - * docs/子系统设计/ADC采样节拍设计说明.md §6。 + * docs/子系统设计/ADC采样节拍设计.md §6。 * **语义取数与有效性层**:取数改走 App/algo/adc_input.c—— * 本任务每拍调 adc_input_poll()(TPAFE 快照 + STM32 ADC1 9 通道扫描), * 经 adc_input_raw() **按语义**读通道(STM32 ADC1 启动与扫描 / 通道语义映射),不再直接触碰驱动缓冲。 @@ -67,7 +67,7 @@ * ISR 不得在此之前对内核动手(同 Src/main.c 里 TIM7 的 * 2026-10-02 实测坑)。优先级 Normal → AboveNormal(见 freertos.c) * v2.8 2026-10-10 王建锋 节拍源换 TIM3(F4 上 ADC1 收不到 TIM6/TIM7 的 - * TRGO,换源判据见 docs/子系统设计/ADC采样节拍设计说明.md + * TRGO,换源判据见 docs/子系统设计/ADC采样节拍设计.md * §3.2);STM32 侧改 DMA 双缓冲 * 消费,adc_input_poll() 不再忙等;心跳日志增 * adc1_seq / adc1_hw(adc1_err 语义改为"本拍无新帧") diff --git a/Drivers/BSP/UI/ui_page_runlog.c b/Drivers/BSP/UI/ui_page_runlog.c index 601c6373..5f7db556 100644 --- a/Drivers/BSP/UI/ui_page_runlog.c +++ b/Drivers/BSP/UI/ui_page_runlog.c @@ -9,7 +9,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.3) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.1) * v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6:合并两页重复实现) * v1.2 2026-10-25 王建锋 记录 reader 注入(道B task-6):不再 include sys_log.h, * 数据经组件内注入的 ui_rec_reader_t 取;日志 ID 换 diff --git a/Drivers/BSP/UI/ui_page_runlog.h b/Drivers/BSP/UI/ui_page_runlog.h index 3febc822..69557b91 100644 --- a/Drivers/BSP/UI/ui_page_runlog.h +++ b/Drivers/BSP/UI/ui_page_runlog.h @@ -8,7 +8,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.3) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.1) */ #ifndef UI_PAGE_RUNLOG_H #define UI_PAGE_RUNLOG_H diff --git a/Drivers/BSP/UI/ui_page_selftest.c b/Drivers/BSP/UI/ui_page_selftest.c index f8b97bc3..8b79ca52 100644 --- a/Drivers/BSP/UI/ui_page_selftest.c +++ b/Drivers/BSP/UI/ui_page_selftest.c @@ -9,7 +9,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.4) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.2) * v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6:合并两页重复实现) * v1.2 2026-10-25 王建锋 记录 reader 注入(道B task-6):不再 include sys_log.h, * 数据经组件内注入的 ui_rec_reader_t 取;日志 ID 换 diff --git a/Drivers/BSP/UI/ui_page_selftest.h b/Drivers/BSP/UI/ui_page_selftest.h index 2f139002..2a8a0070 100644 --- a/Drivers/BSP/UI/ui_page_selftest.h +++ b/Drivers/BSP/UI/ui_page_selftest.h @@ -8,7 +8,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.4) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.2) */ #ifndef UI_PAGE_SELFTEST_H #define UI_PAGE_SELFTEST_H diff --git a/Drivers/BSP/UI/ui_standby.c b/Drivers/BSP/UI/ui_standby.c index e7bd3117..a2b6923a 100644 --- a/Drivers/BSP/UI/ui_standby.c +++ b/Drivers/BSP/UI/ui_standby.c @@ -8,7 +8,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.2) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.2) * v1.1 2026-11-13 DTU 项目组 v1 占位(6 行电流恒 0.00A)→ UA/UB/UC/IA/IB/IC 实时值: * ui_data 通道一按 seq 门控取帧;无首帧/坏质显 "--";定点 * 格式化复用 ui_w_editor_fmt_scaled;单位随 eng(引脚档不挂) diff --git a/Drivers/BSP/UI/ui_standby.h b/Drivers/BSP/UI/ui_standby.h index 6128fda4..3bea5991 100644 --- a/Drivers/BSP/UI/ui_standby.h +++ b/Drivers/BSP/UI/ui_standby.h @@ -8,7 +8,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.2) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §5.2) * v1.1 2026-11-13 DTU 项目组 6 路电流占位 → UA/UB/UC/IA/IB/IC 实时值(口径同 ui_page_ac.c) */ diff --git a/Inc/adc.h b/Inc/adc.h index 6d2f1588..a4195c0f 100644 --- a/Inc/adc.h +++ b/Inc/adc.h @@ -49,7 +49,7 @@ int adc1_calibrate(void); /* ADC1 关闭(清 ADON 并等其生效)。实现见 Src/adc.c 的 USER CODE BEGIN 1。 * **必须在挂 DMA 之前调用**:把"使能 ADC"推迟到 HAL_ADC_Start_DMA 内部完成, * 使能与搬运之间不留可观测间隔。返回 0 = 已关闭,-1 = 等待超时。 - * 动机与台架证据见 docs/子系统设计/ADC采样节拍设计说明.md §4.5(DMA 缓冲 +2 格旋转)。 */ + * 动机与台架证据见 docs/子系统设计/ADC采样节拍设计.md §4.5(DMA 缓冲 +2 格旋转)。 */ int adc1_disable(void); /* USER CODE END Prototypes */ diff --git a/README.md b/README.md index d1ba4b0f..5d27b246 100644 --- a/README.md +++ b/README.md @@ -419,7 +419,7 @@ pre-commit run --all-files | `docs/网络/Net层阻塞模型架构设计.md` | NET 阻塞模型(挂起请求表五路语义,recv 一期 / SEND 二期已落地) | | `docs/网络/网络自愈架构设计.md` | 网络自愈架构(监听重建 / 沉降隔离 / 周期对账) | | `docs/子系统设计/IEC104上行设计说明.md` | IEC 60870-5-104 上行(工厂默认 :2404 监听) | -| `docs/子系统设计/ADC采样节拍设计说明.md` | TIM3 1kHz 采样节拍(两片转换器同源硬件驱动) | +| `docs/子系统设计/ADC采样节拍设计.md` | TIM3 1kHz 采样节拍(两片转换器同源硬件驱动) | | `docs/UI设计/UI_设计架构.md` | LVGL UI 系统架构(菜单 / 内容页 / 内存预算) | | `docs/UI设计/菜单页面设计.md` | 菜单导航行为、数据流与内容页设计 | | `docs/UI设计/UI_界面制作流程.md` | 参考图 → 设计规格 → LVGL 代码管线(P0~P4 门禁) | diff --git a/Src/adc.c b/Src/adc.c index 0321d22b..8c93a900 100644 --- a/Src/adc.c +++ b/Src/adc.c @@ -329,7 +329,7 @@ int adc1_calibrate(void) * 调用本函数后 ADC 处于关闭态,HAL_ADC_Start_DMA 内部会自己 * __HAL_ADC_ENABLE + 等 Tstab(~20 µs),然后紧接着开 CR2.DMA 并启流, * 使能与搬运之间不再有可观测的间隔 → 旋转消失(台架 4/4 次,差值 ≤4 码)。 - * 证据、复现方法与机制分析见 docs/子系统设计/ADC采样节拍设计说明.md §4.5。 + * 证据、复现方法与机制分析见 docs/子系统设计/ADC采样节拍设计.md §4.5。 */ int adc1_disable(void) { diff --git a/Src/freertos.c b/Src/freertos.c index 149e542a..4289cf60 100644 --- a/Src/freertos.c +++ b/Src/freertos.c @@ -152,7 +152,7 @@ const osThreadAttr_t timeSyncTask_attributes = { * Normal 任务",唤醒延迟固定为 µs 级。 * 代价:本任务单拍最坏 adcm≈2 ms 会在 AboveNormal 上挤占 net/ftp/default/timeSync; * 实测峰值来自每 10 s 心跳日志而非采集(STM32 侧已由 DMA 搬运,单拍取数 µs 量级) - * → 耗时特征与代价见 docs/子系统设计/ADC采样节拍设计说明.md §9、§10。 */ + * → 耗时特征与代价见 docs/子系统设计/ADC采样节拍设计.md §9、§10。 */ osThreadId_t adcTaskHandle; const osThreadAttr_t adcTask_attributes = { .name = "adcTask", diff --git a/Src/main.c b/Src/main.c index 27713d4d..8a3c68ae 100644 --- a/Src/main.c +++ b/Src/main.c @@ -260,9 +260,9 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) * → 两片转换器由同一硬件事件驱动,采样时刻对齐。 * TIM3 与 BUSY EXTI(EXTI9_5_IRQn) 同为抢占优先级 6 → 互不抢占, * 读 RD 时序不会被下一次 CONVST 脉冲打断。详见 - * docs/子系统设计/ADC采样节拍设计说明.md §3.3。放 USER CODE 区(L2)。 + * docs/子系统设计/ADC采样节拍设计.md §3.3。放 USER CODE 区(L2)。 * ⚠ 节拍源必须能触发 ADC1:F4 上 ADC1 的外部触发清单里没有 TIM6/TIM7 的 TRGO - * (只接 DAC),判据见 docs/子系统设计/ADC采样节拍设计说明.md §3.2。 + * (只接 DAC),判据见 docs/子系统设计/ADC采样节拍设计.md §3.2。 * ⚠ 唤醒必须走 adc_tick_irq_notify(),**不要**在这里直接 osThreadFlagsSet: * TIM3 在 app_main_init() 就启动,远早于建任务与 osKernelStart, * "消费者就绪"门在 adc_task.c 里(同本文件 TIM7 分支 2026-10-02 的实测坑)。 */ diff --git a/docs/README.md b/docs/README.md index 8838fde9..6056e53a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -91,9 +91,9 @@ docs/ | [`网络配置说明.md`](子系统设计/网络配置说明.md) | 网口设置页:IP/掩码/网关,UI 保存即时生效 | | [`系统墙钟时间维护说明.md`](子系统设计/系统墙钟时间维护说明.md) | `sys_clock`:RTC 基准、tick 回绕防护、30 天惰性再基准化 | | [`控制台单写者日志设计说明.md`](子系统设计/控制台单写者日志设计说明.md) | 控制台日志单写者模型(避免多任务交错输出) | -| [`运行与自检记录设计说明.md`](子系统设计/运行与自检记录设计说明.md) | `/log/run.log`·`selftest.log` 定长记录与待机显示 | +| [`运行与自检记录设计.md`](子系统设计/运行与自检记录设计.md) | `/log/*.log` 七类 64B 定长记录、SOE 二进制记录、控制台日志、待机屏与七个记录页 | | [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5)。**§11 = 任务级饥饿的诊断方法与 `[PROF]` 实测基线**(诊断沉淀:判读口径、量化归因、三条脆弱点、明确不做的四条) | -| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍(现状说明)**:TIM3 1 kHz 更新事件同时触发 TPAFE CONVST 与(经 TRGO)STM32 ADC1 + DMA 乒乓缓冲;节拍源为什么是 TIM3、DMA 硬约束、事件驱动消费与就绪门、实现落点、观测计数、性能特征与设计边界 | +| [`ADC采样节拍设计.md`](子系统设计/ADC采样节拍设计.md) | **ADC 采集链路采样节拍(现状说明)**:TIM3 1 kHz 更新事件同时触发 TPAFE CONVST 与(经 TRGO)STM32 ADC1 + DMA 乒乓缓冲;节拍源为什么是 TIM3、DMA 硬约束、事件驱动消费与就绪门、实现落点、观测计数、性能特征与设计边界 | | [docs/子系统设计/故障录波设计说明.md](子系统设计/故障录波设计说明.md) | **故障录波设计与口径属主**:1 kHz 起步与 80 点口径的偏离登记、存原始码值的理由、四段所有权与四条并发陷阱、段文件字节布局、现场单通道回放显示契约、预算与 R6 前置 | | [`IEC104上行设计说明.md`](子系统设计/IEC104上行设计说明.md) | **IEC 104 上行规约**:IEC 60870-5-104 被控站(TCP 工厂默认 2404)范围/点表(V0.1 有效子集)/链路流程(k/w/t1~t3、总召、COV、遥控两段式、定值)/netTask 接入决策/预算与测试方案;协议核心纯逻辑 + 主机侧纯逻辑单测全栈单测 | | [`屏板链路设计说明.md`](子系统设计/屏板链路设计说明.md) | **屏板串口链路(主机侧,`Drivers/BSP/ScreenLink/`)设计**:链路与角色、USART1 700 kbaud 配置与中断收发、物理接线(含台架日志口与 PC 顶替屏板)、协议实现口径(CRC 字节序 / 整帧 DMA / 时序)、键值与状态灯映射表、资源与耗时、对外接口表、验证状态与未闭环项、坑账本 | diff --git a/docs/UI设计/内容页架构.md b/docs/UI设计/内容页架构.md index 00f4ef26..09d8c8d2 100644 --- a/docs/UI设计/内容页架构.md +++ b/docs/UI设计/内容页架构.md @@ -480,7 +480,34 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash 先肉眼确认新效果正确,再 `check_content_pages.py --update` 刷参考(`ref/*.bin` 已入库,刷了会进 diff,必须一并提交并说明) - 新增页面后同步在 `check_content_pages.py` 增加该页的按键序列、文本断言与参考图 -## 九、变更记录 +## 九、待机屏(覆盖层) + +待机屏不是内容页,而是盖在活动屏之上的**覆盖层**(`Drivers/BSP/UI/ui_standby.c`,与内容页框架平级的显示端口模块)。它由 `uiTask` 在 `ui_menu_init` 之后调 `ui_standby_init(UI_STANDBY_IDLE_MS, ui_task_time_cb)` 装好。 + +**进入条件**:60 s 内无任何按键(`UI_STANDBY_IDLE_MS = 60000`,定义在 `Drivers/BSP/UI/ui_tokens.h`)→ 切待机屏。空闲检测 = 1 s 周期的 `lv_timer`(MCU 与模拟器都经 `lv_timer_handler` 驱动,无 OS 依赖);活动通知挂在 `menu_key_cb` / `cp_key_cb` 上,每收到一个键调 `ui_standby_notify_activity()` 重置计时。 + +屏上是 UA / UB / UC / IA / IB / IC 六路实时值与底部时钟: + +``` +UA = 0.341 ← 六路实时值,取自 ui_data 通道一的 yc[],下标 = 语义序 +UB = 0.338 UA/UB/UC = 0..2,IA/IB/IC = 4..6;UN/IN 不上屏 +UC = 0.339 +IA = 0.021 ← 现在的显示值是引脚电压档:数字照显、不挂单位 +IB = 0.020 (挂单位等于替用户完成没做的标定换算) +IC = 0.021 ← 升到二次/一次值档后,电压挂 V、电流挂 A +────────────────── ← 分隔线 +YYYY-MM-DD HH:MM:SS ← 底部实时时钟(每秒重绘) +``` + +- 数值为空或品质带 `IV|NT|RAW|SB|BL|OV` 时显示 `--`;3 位小数定点格式化复用 `ui_w_editor_fmt_scaled`,不用 `%f`。口径的唯一真源是交流量页 `ui_page_ac.c`。 +- 帧读取按 `ui_data_rt_seq()` 门控整帧拷贝;1 s 定时器整屏 invalidate 就是刷新节拍,没有新增定时器。 +- **键语义**:只有 ENTER 进菜单,其余按键忽略(但同时重置空闲计时)。 +- **覆盖层做法**:进入时保存当前活动屏 + 组内焦点对象,加载待机屏并聚焦其容器;ENTER 时还原。因此内容页打开期间超时同样切待机,返回后原样恢复,编辑态不丢。 +- 布局常量(`ui_standby.c` 头部):首行 `SB_ROW_Y0=28`、行距 `SB_PITCH=18`、分隔线 `SB_LINE_Y=132`、时钟 `SB_TIME_Y=140`。 +- 无头回归:模拟器 `--standby` 注键后经 `ui_standby_force_enter()` 直接切入并截图;窗口模式 `--idle <秒>` 走真超时路径。 +- 真机串口标记:Required `UI standby: ready (idle=60s)`。 + +## 十、变更记录 | 日期 | 变更 | |---|---| diff --git a/docs/UI设计/页面扩充详细方案.md b/docs/UI设计/页面扩充详细方案.md index 10c832e6..5c7b05d7 100644 --- a/docs/UI设计/页面扩充详细方案.md +++ b/docs/UI设计/页面扩充详细方案.md @@ -446,7 +446,7 @@ int run_par_set_deadband(uint8_t yc_idx, float v); /* 范围校验 */ > **本表只是页面侧的需求清单**(记录页要消费哪几类),**不是记录层的登记表**: > 5 个 ID 的定名、记录格式、条数与字节上限、写入时机、以及是否真的落地,一律以 -> `App/log` 侧为准,权威口径 `docs/子系统设计/运行与自检记录设计说明.md`; +> `App/log` 侧为准,权威口径 `docs/子系统设计/运行与自检记录设计.md`; > 实施进度由记录侧任务回报后回填本文(本道不代记,免得同一件事在两份文档里各说一遍—— > 同一事实只在一处详述,见 `docs/文档规范.md`)。 @@ -482,7 +482,7 @@ void ui_w_soe_refresh(ui_w_soe_t *inst, uint16_t row, char *name, char *valu **运行报告(最小可用版)**:3 行——`最近动作`(元件名)、`动作时间`、`当前状态`。数据取最后一条 `SOE_T_ELM_ASSERT`。完整版(故障前后有效值 + 波形摘要)依赖录波,不在本批。 -**记录容量**:新增 5 类日志需各自定上限(参考现有:运行 1024 条/64 KB、自检 512 条/32 KB)。按"事故/告警/启动各 512 条 + 操作/遥控各 256 条"计 ≈ **128 KB**(64 B/条),NAND 余量需按 `docs/子系统设计/运行与自检记录设计说明.md` 的容量口径核对。 +**记录容量**:新增 5 类日志需各自定上限(参考现有:运行 1024 条/64 KB、自检 512 条/32 KB)。按"事故/告警/启动各 512 条 + 操作/遥控各 256 条"计 ≈ **128 KB**(64 B/条),NAND 余量需按 `docs/子系统设计/运行与自检记录设计.md` 的容量口径核对。 ### 3.7 装置信息(回炉) diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index 9296877e..fd2c6e80 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -374,9 +374,9 @@ flowchart LR - 实现:4 KB 环形缓冲 + `defaultTask` 唯一发送(行级原子);内核前/ISR/异常钩子有界直发回退。 - 判据:日志无交错;崩溃时能输出归因日志。 -**FR-LOG-3 上电/重启分类与待机显示** -- 需求:区分上电与重启;空闲切待机屏。 -- 实现:`run.log` 末条 epoch 时间差推断;硬件复位原因(`RCC_CSR`)优先;`ui_standby` 空闲 60 s 切入。 +**FR-LOG-3 上电/重启分类** +- 需求:区分上电与重启。 +- 实现:`run.log` 末条 epoch 时间差推断;硬件复位原因(`RCC_CSR`)优先。 ### 5.6 UI 与远程显示(FR-UI)✅ @@ -1808,7 +1808,7 @@ pre-commit run --all-files | 配置引擎 | [`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) | @@ -1821,7 +1821,7 @@ pre-commit run --all-files | 保护功能复现规划(产品定义 / 定值 / 路线) | [`docs/算法/继电保护功能复现规划.md`](../../docs/算法/继电保护功能复现规划.md) | | 全周傅氏算法原理 | [`docs/算法/傅氏算法详解.md`](../../docs/算法/傅氏算法详解.md) | | 采样有效性校验 | [`docs/算法/采样有效性校验设计说明.md`](../../docs/算法/采样有效性校验设计说明.md) | -| ADC 采样节拍与 DMA | [`docs/子系统设计/ADC采样节拍设计说明.md`](../../docs/子系统设计/ADC采样节拍设计说明.md) | +| ADC 采样节拍与 DMA | [`docs/子系统设计/ADC采样节拍设计.md`](../../docs/子系统设计/ADC采样节拍设计.md) | | ADC 通道映射与实测 | [`docs/硬件平台/ADC通道映射说明.md`](../../docs/硬件平台/ADC通道映射说明.md) | | ADC 前端注入验证流程 | [`docs/测试说明/专项验证/ADC前端注入验证流程.md`](../测试说明/专项验证/ADC前端注入验证流程.md) | | IEC 60870-5-104 上行 | [`docs/子系统设计/IEC104上行设计说明.md`](../../docs/子系统设计/IEC104上行设计说明.md) | diff --git a/docs/子系统设计/ADC采样节拍设计说明.md b/docs/子系统设计/ADC采样节拍设计.md similarity index 88% rename from docs/子系统设计/ADC采样节拍设计说明.md rename to docs/子系统设计/ADC采样节拍设计.md index 02302c65..d0cfae4d 100644 --- a/docs/子系统设计/ADC采样节拍设计说明.md +++ b/docs/子系统设计/ADC采样节拍设计.md @@ -200,50 +200,30 @@ const uint16_t *src = &s_stm32_dma[half * ADC_INPUT_STM32_CH_NUM]; - 取帧状态码:`ADC_INPUT_RC_OK` = 0 / `ADC_INPUT_RC_START` = -1 挂载失败 / `ADC_INPUT_RC_STALL` = -2 本拍无新帧 / `ADC_INPUT_RC_NOT_RUN` = -3 尚未取过帧。 -### 4.5 使能时机与 DMA 缓冲相位(+2 格旋转,2026-10-27 实测) +### 4.5 使能时机:挂 DMA 那一瞬 ADON 必须为 0 -**这条是本节最反直觉的一条:DMA 缓冲会整体错开 2 格,而寄存器配置全对、拷贝循环也全对。** +**规则**:`app_main_init()` 步骤 4b-pre 先调 `adc1_disable()` 把 ADC 关掉,再调 `adc_input_init()` +挂 DMA;使能动作交给 `HAL_ADC_Start_DMA()` 内部完成——它自己 `__HAL_ADC_ENABLE` + 等 Tstab +(约 20 µs),然后紧接着开 `CR2.DMA` 并启流。 -**现象。** 台架把某个端子接地,读数却出现在**另一个通道**上——例如接地 `AIN2`(`UB` / PC4 / CH14), -`UB` 读出 784 码(浮空值),而 `IB` 读出 0。表象与"映射表写错"完全一致,但 -`SQR1/SQR2/SQR3` 实测为 CH0..CH7 + CH14、`L=8`,`k_stm32_rank_ch[]` 与拷贝循环也逐条核过,全对。 +⚠ **顺序不可调换**:`adc1_disable()` 必须严格早于 `adc_input_init()`。 -**证据。** 同一次开机内,先用无 DMA 单通道方式(逐路关扫描、软件触发、直接读 `DR`)取真值, -再与 DMA 路径的同一批输入逐路对照,得到 9/9 完美置换: +**违反的代价**:不报任何错,只是 9 路通道按 rank 序**整体错开 2 格**(槽 `i` 装的是 +rank `((i+1) mod 9)+1`)。表象与"映射表写错""注入点标签不对"**完全一样**, +而 `s_stm32_raw[k_stm32_rank_ch[i]] = src[i]` 的恒等性**照常成立**——旋转后的缓冲被原样拷贝, +看起来完美自洽,所以拿它判映射会得出相反结论。 -| 槽(rank) | 通道 | DMA 值 | 实际装的是 | 真值 | 差 | -|---|---|---|---|---|---| -| 1 | CH0 | 850 | CH2 | 850 | 0 | -| 2 | CH1 | 932 | CH3 | 931 | 1 | -| 3 | CH2 | 853 | CH4 | 852 | 1 | -| 4 | CH3 | 819 | CH5 | 817 | 2 | -| 5 | CH4 | 946 | CH6 | 945 | 1 | -| 6 | CH5 | 954 | CH7 | 953 | 1 | -| 7 | CH6 | 0 | CH14 | 0 | 0 | -| 8 | CH7 | 690 | CH0 | 691 | 1 | -| 9 | CH14 | 790 | CH1 | 785 | 5 | +**为什么**:触发条件是「ADC 被使能之后闲置了数秒才挂 DMA」。本工程 `MX_ADC1_Init()` 在 `main()` +里就调用(使能 ADC),而挂 DMA 在 `app_main_init()` 步骤 4b,中间隔着 GD5F 全片 BBT 扫描(数秒), +ADC 在这段时间里处于 `ADON=1` 的空转态。不需要整个闲置期 `ADON` 都为 0,只要**使能与搬运之间不留 +可观测间隔**,旋转就不出现。微架构层面为何恰好错开 2 格尚未证出——不要为"解释清楚"去猜, +也不要据此改动 ADC 配置。 -即**槽 i 装的是 rank ((i+1) mod 9)+1 —— 整个缓冲从 rank 3 开始搬,固定错开 2 格**。 -差值全在 5 码以内(两种读法的噪声),不是随机抖动。 +**兜底**:改动被删时由审计项 `adc1-calibration-impl` 的基线漂移拦下(它整段守护 `Src/adc.c` +的 `USER CODE BEGIN 1`)。 -**判据上的陷阱。** `s_stm32_raw[k_stm32_rank_ch[i]] = src[i]` 的恒等性**不能**用来证明映射正确: -DMA 从序列中途开始搬时,缓冲整体旋转,拷贝照样是恒等的。判映射只能靠 -**按通道号索引的独立真值**(无 DMA 单通道读),或按 §4.5 的修法消除旋转后再比对。 - -**根因。** 触发条件是「ADC 被使能之后**闲置了数秒**才挂 DMA」。本工程 `MX_ADC1_Init()` 在 `main()` -里调用,而挂 DMA 在 `app_main_init()` 步骤 4b,中间隔着 GD5F 全片 BBT 扫描。台架对照: -"使能后闲置数秒才挂" → 7/7 次都是 +2 旋转;"使能与挂 DMA 在同一时刻" → 6/6 次旋转为 0 -(探针固件 5/5 + 同一次开机内重新挂 DMA 1/1)。 -**微架构层面的确切原因(为何恰好丢 2 拍)尚未证出**,但工程结论是确定的:使能与搬运不该有可观测间隔。 - -**修法。** `app_main_init()` 步骤 4b-pre 调 `adc1_disable()`(实现见 `Src/adc.c` 的 `USER CODE` 区) -先把 ADC 关掉,使能动作交给 `HAL_ADC_Start_DMA()` 内部完成——它自己 `__HAL_ADC_ENABLE` + -等 Tstab(约 20 µs),然后紧接着开 `CR2.DMA` 并启流。台架验证:在 `MX_ADC1_Init()` 末尾直接关闭 -4/4 次、在步骤 4b-pre 调本 API 3/3 次,均 `UB=0`,同开机对照差值 ≤4 码。 -⚠ **顺序不可调换**:`adc1_disable()` 必须严格早于 `adc_input_init()`;若 ADC 在挂 DMA 时仍处于使能态, -旋转会重新出现。改动被删时会由审计项 `adc1-calibration-impl` 的基线漂移拦下 -(它整段守护 `Src/adc.c` 的 `USER CODE BEGIN 1`)。 -踩坑经过、判读陷阱与回归判据另见 `docs/问题记录/ADC_Trap_Records.md` 的 `TRAP-ADC-01`。 +完整档案(九路置换实测表、A/B 对照、判读陷阱、回归判据)→ `docs/问题记录/ADC_Trap_Records.md` +的 `TRAP-ADC-01`。 --- ## 5. TPAFE 侧:CONVST 脉冲与 BUSY 读帧 @@ -327,7 +307,7 @@ t = 1000 µs 下一个更新事件 | `ring` | `sample_ring_frames()` | `ring == frames` | 不等 → 有 `idle` / `skip` 拍被压进环(回归恒等式) | | `adc1_rc` | 最近一拍取帧状态 | 0(`ADC_INPUT_RC_OK`) | -2 本拍无新帧;-1 DMA 挂载失败 | | `adc1_seq` | `s_dma_seq`(半传输 + 整传输共用) | ≈ `seq`,**恒定小偏移** | 偏移持续增大 → STM32 侧在丢 | -| `adc1_gap` | Σ(`seq` 每次前进的超出部分) = **丢掉的帧点数**(2026-10-21 起;此前固件为"次数") | 每 10 s 心跳 +1 点 | 快速上涨 → 本任务单拍 > 1 ms(看 `adcm`) | +| `adc1_gap` | Σ(`seq` 每次前进的超出部分) = **丢掉的帧点数** | 每 10 s 心跳 +1 点 | 快速上涨 → 本任务单拍 > 1 ms(看 `adcm`) | | `adc1_err` | **连续 ≥ 2 拍**无新帧 | 0 | > 0 → 节拍源 / DMA 真停 | | `adc1_hw` | `HAL_ADC_ErrorCallback()` 次数 | 0 | > 0 → ADC / DMA 硬件报错 | | `tgap` | `adc_health_tpafe_gaps()`(「采样有效性校验」) | 极少 | TPAFE 侧丢帧,与 `adc1_gap` **同单位(帧点数)**,可直接对比 | diff --git a/docs/子系统设计/控制台单写者日志设计说明.md b/docs/子系统设计/控制台单写者日志设计说明.md index 4d7ee598..9bf21988 100644 --- a/docs/子系统设计/控制台单写者日志设计说明.md +++ b/docs/子系统设计/控制台单写者日志设计说明.md @@ -3,7 +3,7 @@ > **版本**:v1.8(已实施;修订史见文末 §11「实施时间线」) > **日期**:2026-10-30(v1.8:控制台端口由 USART1 改为 **UART5/RS232#1**,全文端口口径同步——物理口与台架端子见 `屏板链路设计说明.md` §2 / §3.2;v1.7 于 2026-10-26:崩溃上报归一进单写者 + 多段裸发收口,道G task-26;v1.6 同日:未注册期裸发加「行级单一所有者」;v1.5 于 2026-10-24;初版 v1.0 于 2026-09-25) > **状态**:已实施并验证——`/keil-build` 0 错误 0 警告、`/serial-boot-test` 硬件闭环 PASS(2026-09-25,见 §10);设计 2026-09-24 加固实施(当时位于 `Src/usart.c` USER CODE 区),2026-09-25 实现迁出至 `App/log/`(HAL 生成文件不含应用代码);同日写者任务体自 `Src/freertos.c` 迁至 `App/task/default_task.c`(与其余 5 任务文件同址归位) -> **关联文档**:`项目架构总览.md` · `运行与自检记录设计说明.md`(姊妹模块:持久化日志,同一写者任务) · `docs/测试说明/专项验证/链路延迟测试说明.md`(D6 日志税 / 步骤 8 非阻塞日志,需求出处) · AGENTS.md「平台约束」 +> **关联文档**:`项目架构总览.md` · `运行与自检记录设计.md`(姊妹模块:持久化日志,同一写者任务) · `docs/测试说明/专项验证/链路延迟测试说明.md`(D6 日志税 / 步骤 8 非阻塞日志,需求出处) · AGENTS.md「平台约束」 ## 1. 背景:实测故障与根因 @@ -64,7 +64,7 @@ HAL 并发语义(本仓库实现实测,`stm32f4xx_hal_uart.c:1135`):轮 | 子系统 | 通道 | 写者 | 持久化 | |--------|------|------|--------| | 控制台单写者(本文档) | UART5 串口(RS232#1) | defaultTask | 无(易失,复位丢失)——实时调试流 | -| sys_log 运行/自检记录 | FatFS `/log/*.log` | defaultTask(同一任务) | 是(定长 64B 记录,`运行与自检记录设计说明.md`) | +| sys_log 运行/自检记录 | FatFS `/log/*.log` | defaultTask(同一任务) | 是(定长 64B 记录,`运行与自检记录设计.md`) | | 配置引擎 | FatFS `/config.ini` | defaultTask(同一任务) | 是 | defaultTask 主循环按序处理:控制台排空 → 配置保存执行 → 2s 配置轮询 → LED 心跳 → syslog 落盘 → 栈水位观测——同一任务串行化三类 I/O,任务间零竞争。 @@ -408,7 +408,7 @@ sequenceDiagram ## 14. 相关文档 - `项目架构总览.md` —— 六层分层 / 启动时序 / 6 任务(写者任务与就绪屏障位置) -- `运行与自检记录设计说明.md` —— sys_log 持久化日志(姊妹模块:同写者任务、不同通道) +- `运行与自检记录设计.md` —— sys_log 持久化日志(姊妹模块:同写者任务、不同通道) - `docs/测试说明/专项验证/链路延迟测试说明.md` —— D6 日志税 / 步骤 8 非阻塞日志(需求出处与 09-22 丢行实证) - `docs/测试说明/流程与规范/自动化测试流程.md` —— `/serial-boot-test` 标记体系(Required / Forbidden 模式,`[ERR]` 语义) - AGENTS.md —— 平台约束(内核前禁内核对象 / C 堆禁令 / 事件位语义 / 内存预算) diff --git a/docs/子系统设计/故障录波设计说明.md b/docs/子系统设计/故障录波设计说明.md index 1b4d50e9..ce7d698e 100644 --- a/docs/子系统设计/故障录波设计说明.md +++ b/docs/子系统设计/故障录波设计说明.md @@ -4,7 +4,7 @@ > **文档定位**:本波的**设计属主**——口径、通道集、数据形态、触发模型、文件格式、预算与所有权边界、显示契约、二期不返工的结构约束在此定稿。**板级判据与本波执行顺序的属主即本文 §9 与 §10**——原任务规划下的《故障录波》卡片登记簿已于 2026-10-30 随销项并入本文后删除(沿革见 §14);排期顺位在 `docs/任务规划/README.md` §2.0「独立」行。 > **状态**:设计已定稿,**数据面 + 触发与落盘接线 + UI 两个页面(手动录波、录波回放)的代码面已交付(2026-10-29)**——主机单测 113 / 170 / 110 全 rc=0、双端编译 0 错误 0 警告、板端编码器与 PC 侧解码器两份独立实现字节级互验通过。**没做完的只有板级判据那一轮**(注量对表、FTP 拉回、与 `meas:` 对表、CRC 不符必须拒收的实测、回放页放一段真波、两个待台架核数值),逐条列在本文 §10 板级各行。三条已裁定口径见 §1.2。**2026-10-31 远程半轮**已把其中不需要到场与注入的六样跑过(落盘道、触发时序、冷却恢复、轮转、PC 侧坏段拒收、104 并发)——结果与回放页新缺陷「段表枚举恒空」全在 §10「板级远程半轮记录」小节;**该缺陷已于 2026-11-02 远程修复并板级闭环**(根因 = 读侧 `wave_store_seg_path()` 返回值判错 + 键处理缺失效,证据与前后对拍见 §10「回放页枚举恒空缺陷修复记录」)。 > **相关文档**: -> `docs/子系统设计/ADC采样节拍设计说明.md`(节拍与时序账的属主;本文 §3 的数据源就是它) +> `docs/子系统设计/ADC采样节拍设计.md`(节拍与时序账的属主;本文 §3 的数据源就是它) > `docs/硬件平台/ADC通道映射说明.md`(网名 `AINn` 不等于板级 `ADCn`,也不等于转换器通道号;链路分段与削顶归属) > `docs/算法/采集与算法层设计说明.md §13.10`(动态波形验证判据;录波落地后它的读数通路升级,见 §11) > `docs/架构设计/STM32F4-Base存储架构说明.md` §10.1(FatFs 两条实证陷阱与 8.3 短名限制) @@ -54,7 +54,7 @@ | 项 | 内容 | |---|---| -| 为什么先做 1 kHz | 抬到 4 kHz 的第一块骨头不在录波,而在采集主线:一拍时序账要重算(现在 TPAFE 转换约 23 µs、EXTI 读 8 通道约 0.5 µs、ADC1 第 9 点约 67 µs、读窗与下次 CONVST 相隔约 977 µs,见 `docs/子系统设计/ADC采样节拍设计说明.md` 的时序账),半区语义与「恒定小偏移」都要重推;算法层还有两处按 1 kHz 写死的假设(`App/algo/phasor.h` 的 `PHASOR_N 20U`、`App/algo/freq.h` 的 `FREQ_FS_HZ 1000.0f`)会被一起牵动 | +| 为什么先做 1 kHz | 抬到 4 kHz 的第一块骨头不在录波,而在采集主线:一拍时序账要重算(现在 TPAFE 转换约 23 µs、EXTI 读 8 通道约 0.5 µs、ADC1 第 9 点约 67 µs、读窗与下次 CONVST 相隔约 977 µs,见 `docs/子系统设计/ADC采样节拍设计.md` 的时序账),半区语义与「恒定小偏移」都要重推;算法层还有两处按 1 kHz 写死的假设(`App/algo/phasor.h` 的 `PHASOR_N 20U`、`App/algo/freq.h` 的 `FREQ_FS_HZ 1000.0f`)会被一起牵动 | | 芯片支持吗 | **支持**:TPAFE5160 是 16 bit / 350 kSPS 全通道同时采样(`docs/芯片手册/TPAFE5160.md` 首行)→ 4 kHz 是纯固件与算法的工程账,不是硬件上限 | | 1 kHz 能看到什么 | 基波形状、削顶、直流偏置、明显畸变、频率(数过零)。**看不到**:2/5 次谐波的形状细节(每周期只剩 10 / 4 个点)、暂态前沿 | | 偏离登记 | 落码时在产品需求那一行补一句指向本文;「看清波形」这一目标在 1 kHz 下**只算部分达成**,不许拿 1 kHz 的图当满足 80 点口径的证据 | @@ -678,7 +678,7 @@ Before/After 表与串口原文。 |---|---| | 原任务规划《故障录波》卡片(2026-10-30 销项删除) | 卡片判据与顺序图已并入本文 §9 与 §10;排期顺位归 `docs/任务规划/README.md` §2.0「独立」行——判据不再另立文件 | | `docs/算法/采集与算法层设计说明.md §13` | 上游:静态与动态信度、削顶归属由它定义;本文 §11 记录因录波而变的三处口径 | -| `docs/子系统设计/ADC采样节拍设计说明.md` | 数据源与节拍属主;本波**不改**它,二期改节拍时必须回头改它 | +| `docs/子系统设计/ADC采样节拍设计.md` | 数据源与节拍属主;本波**不改**它,二期改节拍时必须回头改它 | | `docs/架构设计/STM32F4-Base存储架构说明.md` §10.1 | FatFs 陷阱与 8.3 短名的属主;本文 §5.2 只是消费者 | | `docs/UI设计/页面补齐规划.md` | 「手动录波」叶子旧状态是「不做(无录波模块)」;P2 与 P3 落地时该处的叶数记录要同步(**两叶已绑,现值 43 节点 / 34 叶 / 已绑 31 / 未绑 3**,数法见 `docs/UI设计/菜单页面设计.md` §3.9) | | `docs/问题记录/采集与数据处理短板评测.md` | 记录链缺口(波形数据面 / COMTRADE / 104 文件服务)的登记处;P1 落地后该行改指本文 | diff --git a/docs/子系统设计/看门狗设计说明.md b/docs/子系统设计/看门狗设计说明.md index 2077a4da..df81583c 100644 --- a/docs/子系统设计/看门狗设计说明.md +++ b/docs/子系统设计/看门狗设计说明.md @@ -6,7 +6,7 @@ > **日期**:2026-09-29 创建;**2026-10-10 按当前代码重写正文**(旧正文含大量立项前的 > 描述,已清理;实施史与逐次决策见 §10 与文末变更记录,未删)。 > **相关文档**: -> [`运行与自检记录设计说明.md`](运行与自检记录设计说明.md)(复位记录与 `selftest.log`)、 +> [`运行与自检记录设计.md`](运行与自检记录设计.md)(复位记录与 `selftest.log`)、 > [`../测试说明/单元测试/看门狗单元测试说明.md`](../测试说明/单元测试/看门狗单元测试说明.md)(主机侧纯逻辑单测,判定状态机)、 > [`../测试说明/板级测试/实机测试实施指南.md`](../测试说明/板级测试/实机测试实施指南.md)(故障注入台架流程)、 > [`../架构设计/项目启动顺序管理.md`](../架构设计/项目启动顺序管理.md)(启动时序与就绪屏障)、 @@ -560,7 +560,7 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建 > **来源**:`[ERR][WD] task stall: mask=0x00000067 req=0x00000077`(缺 bit4 = `TASK_WD_UI`) > 的完整归因与处置。故障本身已结案——**累计 38 次上板启动 0 复现**(主机侧回归的早期基线 3/10), > 原始分析文档按"完成即销项"规则删除。本节只留**可复用的判读口径、诊断方法、 -> 实测基线与教训**;ADC 侧的处置细节在 [`ADC采样节拍设计说明.md`](ADC采样节拍设计说明.md) §6 与 §9, +> 实测基线与教训**;ADC 侧的处置细节在 [`ADC采样节拍设计.md`](ADC采样节拍设计.md) §6 与 §9, > UI 推流门控在 `../UI设计/RemoDispBus协议与接收设计说明.md` §3,就绪门控 API 在 > `../网络/BSD_Socket_API_使用指南.md` §10.1。 @@ -592,7 +592,7 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建 |---|---|---| | 1 | **uiTask 是 `required_mask` 里唯一的 `osPriorityBelowNormal`**(其余五个全 `Normal`)。FreeRTOS 数值越大优先级越高 → 任一 Normal 任务持续就绪,uiTask 就**完全拿不到 CPU**;`configUSE_TIME_SLICING=1` 只保护**同优先级**公平 | **至今未变**(刻意不提优先级,理由见 §11.6)。风险由"心跳自产 + 往返门控 + 采集零忙等"三面压住 | | 2 | **UI 心跳曾依赖 netTask 往返**:`ui_net_sink_poll()` → `net_accept()` → `osMessageQueuePut(osWaitForever)` + `xTaskNotifyWait(portMAX_DELAY)`,**之后**才 `task_wd_beat(TASK_WD_UI)` → netTask 慢一拍、UI 心跳晚一拍。这正是"**只有 UI 位缺失**"的根因(别的任务没这条依赖) | ✅ 已断两处:心跳提到 poll **之前**(`App/task/ui_task.c`);无客户端时门控让往返 = **0 次**(`uis` 实测 1 µs) | -| 3 | **自定截止期一旦错过就不可恢复**:`next += 1; osDelayUntil(next)` 且**丢弃返回值** → 单拍 >1 ms 后 `next` 相对当前时刻永久落后 → 该任务从此不再阻塞、以 Normal 优先级空转(**连续**慢拍才锁死,单次慢拍会自己追平)。返回值本是现成的重同步钩子 | ✅ 已消:adcTask 改**事件驱动唤醒**(`osThreadFlagsWait`),"落后"这个概念不存在了(`ADC采样节拍设计说明.md` §6) | +| 3 | **自定截止期一旦错过就不可恢复**:`next += 1; osDelayUntil(next)` 且**丢弃返回值** → 单拍 >1 ms 后 `next` 相对当前时刻永久落后 → 该任务从此不再阻塞、以 Normal 优先级空转(**连续**慢拍才锁死,单次慢拍会自己追平)。返回值本是现成的重同步钩子 | ✅ 已消:adcTask 改**事件驱动唤醒**(`osThreadFlagsWait`),"落后"这个概念不存在了(`ADC采样节拍设计.md` §6) | ⚠ 顺带揪出的两个**独立正确性**缺陷(与停摆无关,但同源发现,别只当性能问题): @@ -600,7 +600,7 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建 等效采样间隔被拉长。当时 `idle` 占 3.2% → 窗长从 20.0 ms 漂到 ≈20.6 ms ≈ **3% 频率失配** → 幅值/相位**系统性误差**(不是随机噪声)。现仅 `r == 1` 压帧,上板印证 `ring == frames`。 - **失败拍静默沿用上一拍值**:失败支路不清 `s_stm32_valid` → 照样压环、`skip` 抓不到。 - 现失败即清标志走整帧跳过(`ADC采样节拍设计说明.md` §4.4;单测 TC-ADCIN-070..078 钉死)。 + 现失败即清标志走整帧跳过(`ADC采样节拍设计.md` §4.4;单测 TC-ADCIN-070..078 钉死)。 ### 11.4 `[PROF]` / `[PROF2]` 字段语义与当前基线 diff --git a/docs/子系统设计/运行与自检记录设计.md b/docs/子系统设计/运行与自检记录设计.md new file mode 100644 index 00000000..65b72187 --- /dev/null +++ b/docs/子系统设计/运行与自检记录设计.md @@ -0,0 +1,287 @@ +# 运行与自检记录设计 + +> **读者**:要改日志记录、加自检检查点、调记录页,或排查"设备上为什么没记下来"的人。 +> **读这一篇能得到什么**:`/log` 下那七个文件与 SOE、控制台日志的**现状设计**——记录长什么样、谁在什么时候写、满了怎么办、屏上怎么呈现、出问题时退到哪儿。 +> **相关文档**:`docs/架构设计/STM32F4-Base存储架构说明.md`(卷与 FTL)· `docs/子系统设计/系统墙钟时间维护说明.md`(时标来源)· `docs/子系统设计/控制台单写者日志设计说明.md`(串口侧)· `docs/UI设计/内容页架构.md`(记录页框架) + +本文只描述**现在**的系统。 + +## 1. 概览 + +装置有两类日志、三条落盘通道,都遵守**单写者**约定(谁写盘只有一个任务,别的任务只投递): + +| 通道 | 载体 | 写者 | 内容 | +|---|---|---|---| +| 运行日志 | `/log` 下七个文本文件 | defaultTask(经 `syslog_drain()`) | 上电/重启、自检故障、事故、告警、启动、操作、遥控 | +| SOE 事件顺序记录 | `/log/soe.log`(二进制 64B) | defaultTask(`syslog_drain()` 尾部) | 保护元件的动作/复归边沿 | +| 控制台日志 | 串口(RS232#1 / UART5) | defaultTask(`dbg_console_drain()`) | `DBG_*` 调试行,不落盘 | + +三条通道各有自己的模块,互不借用: + +| 模块 | 端 | 职责 | +|---|---|---| +| `App/log/sys_log.h` | 双端共享 | 运行日志 API、64B 记录布局常量、行拆装 | +| `App/log/sys_log_mcu.c` | MCU | 待发队列、defaultTask 落盘、容量轮转、上电/重启分类、取证文件 `wdev.log` | +| `simulator/sim/sim_syslog.c` | 模拟器 | 宿主文件后端(七份 `sim_*.log`),无队列无丢弃 | +| `App/log/soe_rec.c/h` | 双端共享 | SOE 记录编解码(纯逻辑,零 I/O) | +| `App/log/soe_store.c/h` | MCU | SOE 投递(protectTask)与排空落盘(defaultTask) | +| `App/log/console_log.c/h` | MCU | 控制台环形缓冲 + 唯一发送者 | +| `Drivers/BSP/UI/ui_page_*.c`、`ui_w_records.c` | 双端共享 | 七个记录页 + 记录列表共享组件 | + +**一条记录的诞生**:任意任务 `syslog_post()` / `syslog_post_quiet()` → 条目进 queue(内核未启动时进静态数组)→ defaultTask 在 `syslog_drain()` 里取出、按文件合并成一次追加写 → 落 NAND。业务任务**从不碰文件系统**。 + +## 2. 一份记录长什么样 + +### 2.1 文本记录:定长 64 B,一行一条 + +七个运行日志文件用同一套布局,可以直接 `RETR` 下载后按固定偏移解析: + +``` +偏移 长度 内容 +[0..18] 19 "YYYY-MM-DD HH:MM:SS"(墙钟无效时 = "1970-01-01 08:00:00") +[19] 1 ' ' +[20..29] 10 epoch 秒,ASCII "%010u"(墙钟无效 = "0000000000") +[30] 1 ' ' +[31..58] 28 描述 UTF-8(≤28 B = 9 个汉字),不足补 '\0' +[59..63] 5 '\0' 填充 +``` + +一行实际内容只到约 59 字节,不足 64 补零,所以 ftp 下载看到的是一串带空白的定长记录。示例(描述之后是 NUL 填充): + +`2026-09-14 16:48:34 1789465714 装置上电记录` + +选定长的三个理由:条数 = `文件大小 / 64`(O(1),不用扫描);第 k 条偏移 = `k×64`(翻页零解析);掉电写了半条,`floor(size/64)` 会自然把它排除掉。 + +epoch 定宽 10 位是因为 2001-09-23 起 Unix 秒已是 10 位;uint32 到 2106 年才溢出。描述限制 28 B 是被内容页框架的行缓冲 `CP_ROW_BUF_LEN = 32`(含 NUL)卡住的,超长在投递侧就截断。 + +### 2.2 七类文本记录与容量 + +**记录类型 = 日志 ID = 哪个文件**。记录体内没有类型字段——见 [App/log/README.md](../../App/log/README.md)「记录类型 = 日志 ID」,那里有完整的判据与逐类写入点清单;本节只说有哪些文件、各多大。 + +| 文件 | 日志 ID | 记录什么 | 上限 | 满时 | +|---|---|---|---|---| +| `/log/run.log` | `SYSLOG_RUN` | 上电、重启 | 1024 条(64 KB) | 删除重建 | +| `/log/selftest.log` | `SYSLOG_SELFTEST` | 自检故障(有问题才记) | 512 条(32 KB) | 同上 | +| `/log/accid.log` | `SYSLOG_ACCIDENT` | 事故(跳闸型元件动作) | 512 条(32 KB) | 同上 | +| `/log/alarm.log` | `SYSLOG_ALARM` | 保护告警(告警型元件动作) | 1024 条(64 KB) | 同上 | +| `/log/start.log` | `SYSLOG_START` | 保护启动(越限电平上升沿) | 1024 条(64 KB) | 同上 | +| `/log/oper.log` | `SYSLOG_OPER` | 操作(改动了装置状态的运维动作) | 512 条(32 KB) | 同上 | +| `/log/yk.log` | `SYSLOG_YK` | 遥控(104 命令的选择/执行/拒绝) | 256 条(16 KB) | 同上 | + +- **上限是逻辑上限,不是预分配**:文件按追加写自然增长,一条不写就不占簇;到顶才删除重建(串口打 `cap full, recreate`)。七类全满的名义峰值 = 4864 条 × 64 B = **304 KB**,相对 NAND 容量可忽略,且这些只在事件边沿写。 +- 告警/启动给到 1024 条,是因为一次故障会连片触发多个元件,别让最能说明故障过程的两类最先被截掉。 +- 名字全部是严格 8.3(`FF_USE_LFN=0`:主名 ≤8、扩展 ≤3);`/log` 目录在首次落盘前 `f_mkdir`(已存在则忽略)。 +- **`syslog_id_t` 只准尾部追加**:枚举序就是两端 `s_paths[]` / `s_caps[]` 的下标,中间插值会让某类记录安静地写进别的文件——不报错,屏上照常翻页。 +- 记录只增不改;删除只有两条路径——容量满时重建,以及 UI「清除记录」(§4.5)。 + +### 2.3 SOE 记录:二进制 64 B + +`/log/soe.log` 与上面七个文件**不是同一套格式**,也不共用读者:它是结构化的二进制记录,给程序解析用。 + +``` +[0] 版本号 +[1] 类型(soe_type_t) +[2] 元件号(prot_elm_id_t;0xFF = 非元件事件) +[3] 标志位(bit7 = 记录时时钟无效) +[4..7] 单调序号 seq(uint32 小端,同一 boot 内递增) +[8..14] CP56Time2a 时标(7 B,IEC 60870-5-4 §5.1.6) +[15..18] epoch 秒(uint32 小端;时钟无效时置 0,供主机交叉校验) +[19..22] 值 value(uint32 小端,语义随类型) +[23..62] 保留,恒 0 +[63] 校验和 = sum8([0..62]),解析侧拒绝不匹配(撕裂/半写检测) +``` + +所有多字节字段小端。容量 1024 条,满则删除重建,**重建不重置 seq**(只有设备复位才重置,所以主机校验单调性要按"同一 boot 段"分段看)。 + +## 3. 写入路径 + +### 3.1 投递:任意任务只把条目丢给队列 + +``` +任意上下文 syslog_post(id, desc) + ├─ 内核未运行(app_main_init 期间)→ 静态待发数组(8 槽,满则丢弃并计数) + └─ 调度器已运行 → 消息队列(8 槽,满则丢弃并计数;日志永远不阻塞业务) +``` + +`syslog_entry_t` 内嵌定长缓冲 `char desc[SYSLOG_DESC_MAX + 1]` 并**拷贝**描述串,调用方返回后即可复用或销毁自己的缓冲。条目里不存指针。 + +两个投递口,按调用者的栈预算选: + +- `syslog_post()`:通用。队列满/启动前数组满时,就地打一条留痕日志——那两条分支各内联约 216 B 的行缓冲。 +- `syslog_post_quiet()`:静态最坏链吃紧的任务用(**protectTask、iec104Task 必须用它**)。同样丢弃,但只累加 `syslog_post_dropped()` 计数、不打任何日志,由 defaultTask 在 `syslog_drain()` 里按差值补一条留痕。收/丢判据两个口完全一致。 + +### 3.2 落盘:defaultTask 是唯一写者 + +``` +defaultTask(挂载成功之后): + 1) f_mkdir("/log")(幂等) + 2) 排空启动前数组 + 队列 → 按文件合并落盘(一批 ≤8 条,一次 + f_open(FA_OPEN_APPEND) → f_write(n×64) → f_close) + 3) 排空 SOE 队列 → /log/soe.log + 4) 执行挂起的"清除记录"请求 + 5) 主循环:osMessageQueueGet(阻塞, 10 s 超时) → 落盘;超时空转继续喂狗与 LED +``` + +**一次排空 = 每文件一次 open/write/close**。逐条写的话每条都要回写目录项、对目标扇区读-改-写,即每条日志 2~4 次 NAND 页读写;同一文件的一批(≤8 条 = 512 B 静态缓冲)合并成一次追加,掉电语义不变——批次仍在同一次 `syslog_drain()` 调用内写完并 `f_close`,而 `f_close` 内部 `sync_fs` → `disk_ioctl(CTRL_SYNC)` 会把 FTL 的写回缓存刷进 NAND。**批次不跨调用驻留 RAM**。 + +失败与回投的判据: + +- 卷忙(卷锁 try 取不到)→ 返回 -2,整批**原样回投队列**,静默顺延到下一轮。这是硬要求:defaultTask 是喂狗点,阻塞等锁等于停喂看门狗。 +- 写失败 / 容量不足 → 按需退回逐条慢路径("满了删除重建"只在逐条路径上实现,避免一批跨多轮重建)。 +- 启动前数组整批成功才删;队列条目落盘失败时回投,**丢一整个批次是设计上禁止的**。 +- **自愈重建只认介质/结构类错误码**(`FR_DISK_ERR` / `FR_INT_ERR` / `FR_NO_FILESYSTEM` / `FR_DENIED`):只有"删了这个文件真能修好"的才删。名字/参数类错误码(`FR_INVALID_NAME` / `FR_NO_PATH` / `FR_NO_FILE` / `FR_INVALID_OBJECT`)与 `FR_NOT_READY` / `FR_NOT_ENABLED` / `FR_TIMEOUT` 一律不删——同因下删了也照样失败,只会把历史日志一轮轮删光。判定入口是 `s_is_selfheal_code()`。 +- 追加写必须 `FA_WRITE | FA_OPEN_APPEND`(本 FatFs 移植的 `FA_OPEN_APPEND` 不含写位);`f_write` 必须校验写回字节数(簇分配失败时返回 `FR_OK` + 短写,没有错误码)。 +- 存储没就绪或挂载失败时,条目滞留 RAM,下次上电丢失,串口打告警——此时本就无法持久化。 + +### 3.3 留痕节流 + +避免"日志系统自己把日志刷爆": + +- 排空的整批回投只在**正常→故障**那一次报(落盘成功一轮复位),文案 `write fail, requeue N entries`。 +- 投递侧队列满、启动前数组满各有一个独立抑制位,只压留痕次数,不改变丢弃行为。 +- 节流之后不统计丢弃条数(那属于新增对外可见信息)。 + +### 3.4 卷锁 fail-open 边沿留痕 + +卷锁层只自增计数、绝不产日志——它唯一能落盘的通道自己也要取那把非递归锁,被卷忙挡住时既写不出也不许在失败分支里再取同一把锁(机理见 `App/fs/vol_lock.h`「可观测性」段)。 + +所以留痕由唯一写者在 `syslog_drain()` 尾部按**边沿**补投:判据是"计数比上一轮读到的变大了",不是"计数大于零"(后者是电平,每轮一条 = 每轮一次 NAND 写,诊断自己变成负载)。**一个 episode(= 一次上电)只投一条**,重新上电复位 latch 后故障复发才会再投。文本形如 `vollock failopen=3`,落 `selftest.log`。 + +三条约束:① 不在失败调用点就地打日志;② 不打 DBG 错级、不打 `[ERR]`——`[ERR]` 是"设备出故障"的唯一信号通道,把可观测性事件塞进去就同时不再是故障、也不再是禁令;③ 记录自带的时间戳是**被观测到的时刻**(粒度 = 一轮 `syslog_drain()`),不是发生时刻,因此不另造更"精确"的字段。 + +### 3.5 取证文件 `/log/wdev.log` + +看门狗停喂现场证据走独立通道 `sys_log_evidence()`:**变长行**、不走 64B 定长格式,写完立刻诱导一次同步把脏页刷进 NAND,防止复位丢现场。超过 8 KB 先删除重建,防长跑无界增长。它不在 `syslog_request_clear()` 的作用域内。 + +## 4. 读取与呈现 + +### 4.1 读接口 + +```c +uint32_t syslog_count(syslog_id_t id); /* = 文件大小 / 64 */ +int syslog_get_record(syslog_id_t id, uint32_t idx, + char *ts, size_t ts_len, + char *desc, size_t desc_len); /* 成功 0,越界/读失败/缓冲不足 -1 */ +int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch); +``` + +**容量契约**:`ts_len > SYSLOG_TS_LEN`、`desc_len > SYSLOG_DESC_MAX` 必须成立;不足一律返回 -1,既不静默截断、也不写调用方一个字节。描述在写入侧已截到 28 B,按最小容量给就一定装得下,读侧不需要第二道截断。UI 用 32 B 列缓冲,走允许路径。 + +### 4.2 读放大收口:8 条滑动窗口 + +MCU 端有 512 B 静态读窗口:`s_read_raw()` 先查窗口(同 id + 代数未变 + 落在区间内 → 零 I/O 直接返回),未命中才 `f_open → f_lseek(窗口首条×64) → f_read(≤8×64B) → f_close` 一次取回连续 8 条再切。写者每次落盘后把 `s_rd_gen[id]` 自增,读者比对填充时记录的代数,不匹配就重填。对齐 32 位在 ARM 上原子,跨任务(uiTask 填、defaultTask 增)无需临界区,最坏多读一轮、不会读错。 + +### 4.3 屏上怎么看到 + +菜单「记录查询」下挂九个叶子,其中七个是运行日志页、一个是 SOE 页: + +| 菜单叶子 | 数据源 | 每记录几行 | +|---|---|---| +| 运行记录 / 自检记录 / 事故记录 / 操作记录 / 保护告警 / 保护启动 / 遥控记录 | 对应 `/log/*.log` | 2 行(时间戳 + 描述) | +| SOE 记录 | `/log/soe.log` | 另一套(二进制解码) | + +七个文本页共用 `ui_w_records` 组件:页面模块只提供描述符与一个日志 ID,缓存与读盘都在组件内。每页 `rows_per_page = 8`(= 4 条记录/页),↑↓按行移动,页脚自动算"第 X 页,共 Y 页"。空日志时 `count_cb` 返回 0,页面以单条伪行 `无记录` 呈现(避免"共 0 页")。 + +UI 页面**不再 include `sys_log.h`**:日志 ID 在两端绑定处(`App/task/ui_task.c` 与模拟器)映射成不透明的 `UI_W_REC_LOG_*`,并有编译期断言把 `UI_W_REC_LOG_* == syslog_id_t` 逐项钉死。 + +这些记录页都是**内容页**——框架怎么建屏、怎么分页、怎么写一个新页,属 `docs/UI设计/内容页架构.md`;本节只说记录这一侧的口径。 + +### 4.4 清除记录 + +非写者上下文(uiTask 等)只能 `syslog_request_clear(id)` 置一个标志(零盘触点、零卷锁),真正的 `f_unlink` 在 defaultTask 的 `syslog_drain()` **尾部**执行。顺序是判据的一部分:排在条目排空之后,否则本轮刚排空的旧记录会被写回去,`syslog_count()` 不归零。 + +- 结果读 `syslog_clear_state(id)`:`NONE` / `OK` / `WAIT` / `FAIL`;事实复核读 `syslog_count()`。 +- 落地函数 `syslog_clear_apply(id)` 是 MCU 后端专属,模拟器不实现——共享代码误调会在链接期报错,这是故意的。 +- 卷锁一律 try,卷占用返回 -1 且不动原文件;幂等只认 `FR_NO_FILE`,`FR_NO_PATH` 如实返回 -1 且不做任何卷级动作(不补目录、不重格式化)。 +- 清除成功会往操作记录里落一条 `clear <文件>`;**但清 `oper.log` 自己时刻意不投**,否则"我清除了记录"这条会把刚删空的文件又写成 1 条。 + +## 5. 事件分类 + +### 5.1 上电还是重启 + +defaultTask 挂载成功、排空待发队列之后写一条运行记录,靠 `run.log` 末条的 epoch 判断: + +``` +读 run.log 末条记录的 epoch 字段 E: + 墙钟有效 且 (now - E) < 600 s → "装置重启记录"(近期刚运行过 = 软复位/看门狗) + 其余(无文件 / 首条 / 间隔 ≥10 min / 墙钟无效)→ "装置上电记录" +``` + +阈值 `SYSLOG_REBOOT_WINDOW_S = 600`。断电不到 10 分钟会被误判成重启——没有掉电检测硬件时这个歧义消不掉,取工程折中。串口同步打一条 `runlog boot entry: 装置上电记录 / 装置重启记录`,供 `/serial-boot-test` 判读。 + +### 5.2 自检故障:只记故障 + +全部通过不写记录;下面每条都是**一个检查点对应一条固定描述串**,串即落盘内容,也是屏上显示的那行: + +| 检查点 | 位置 | 描述串 | +|---|---|---| +| 看门狗复位 | app_main 启动早期读 `RCC_CSR` | `watchdog reset` | +| 墙钟/RTC 建立失败 | app_main:`sys_clock_init()` | `clock chip fault` | +| NAND 初始化失败 | app_main:`gd5f2gq5ue_init()` | `NAND fault` | +| ADC 转换器初始化失败 | app_main:`tpafe5160_init()` | `ADC fault` | +| ADC 通道映射失败 | app_main:`adc_init()` 返回 -1 | `ADC map fault` | +| ADC DMA 挂载失败 | app_main:`adc_init()` 其他负值 | `ADC dma fault` | +| 采样心跳看护 | adc_task | `adc sampling fault` / `adc code rail fault` | +| 文件系统故障 | default_task:挂载/重试耗尽、卷探测失败 | `filesystem fault` / `storage fault` | +| 存储容量耗尽 | default_task:卷探测报 `BSP_ERR_NO_SPACE` | `storage full` | +| 堆低水位 | default_task | `heap low water` | +| 定值区损坏 | default_task | `prot set corrupt` | +| 通讯链路异常 | net_task:bring_up 超时 | `comms fault` | +| 网络初始化/重建失败 | net_task | `net init fail` / `net reinit fail` | +| 配置文件引擎故障 | cfg_file(分配/模板/加载/保存/视图守卫/poll) | `cfg heap alloc fail` / `cfg template write fail` / `cfg load read fail` / `cfg load oversized` / `cfg save read fail` / `cfg save write fail` / `cfg save overflow` / `cfg save line too long` / `cfg save view stale` / `cfg poll view stale` / `cfg poll oversized` | +| 卷锁 fail-open | sys_log(边沿补投,§3.4) | `vollock failopen=N` | + +FatFS 挂载/建卷失败时存储本身不可写,这条只能走串口:`[SELFTEST] fs: FAIL`。 + +不做"已复归 / 持续监测 / 恢复记录"——那是保护元件语义,不属于自检。 + +## 6. 控制台日志(串口) + +与落盘日志是两条独立通道,共用"单写者"思想: + +- 生产者(任何任务,含 `printf`)只把**已格式化好的整行**写进环形缓冲 `dbg_console_write_line`;格式化在 `DBG_*` 宏内用库 `snprintf` 完成,不自研可变参数函数。 +- 写者 = defaultTask,`dbg_console_writer_attach()` 注册、主循环里 `dbg_console_drain()` 排空,是唯一的 `HAL_UART_Transmit` 调用点(行级原子)。 +- 异常/早期路径 `dbg_console_write_raw()`:拿不到整行独占就把原文转入环形缓冲延后发,与当场直发逐字节相同。 +- 成本:每条 `DBG_*` 约 90 B Flash + 216 B 栈;深调用链模块须预定义 `DBG_LINE_BUF_MAX`。 +- 模拟器**不编译** `console_log.c`,`DBG_*` 直接映射 `printf`——所以环形缓冲溢出/丢弃逻辑在模拟器上测不到。 + +## 7. 双端差异 + +| 项 | MCU | 模拟器 | +|---|---|---| +| 后端 | `sys_log_mcu.c`(NAND + FatFS) | `sim_syslog.c`(工作目录下七份 `sim_*.log`) | +| 投递 | 队列 + 启动前静态数组,会丢、会回投 | 直接写宿主文件,**无队列、无丢弃**,`syslog_post_dropped()` 恒 0 | +| `syslog_post_quiet()` | 与 `syslog_post()` 判据同、栈成本不同 | 等价于 `syslog_post()` | +| 清除记录 | 请求与落地分离,`WAIT` 态真实存在 | 调用内同步删完,`WAIT` 永不出现 | +| SOE / 控制台 | 有 | 不编译 | + +完整清单与"模拟器测不到什么"见 `docs/测试说明/专项验证/模拟器差距清单.md` §2.2。 + +## 8. 内存占用 + +| 项 | 大小 | 位置 | +|---|---|---| +| sys_log MCU:启动前待发数组 8 条 + 队列 8 条 + 状态 | 约 300 B 静态 + 约 150 B FreeRTOS 堆 | .bss / heap_4 | +| 读写窗口:写批 512 B + 读窗 512 B | 1 KB | .bss(常驻) | +| 记录页模块 | 0(描述符驱动,零 LVGL 对象,只有栈缓冲) | — | + +不新增任务、不动任务栈。平台总量台账 → `docs/内存管理/RAM预算台账.md`。 + +## 9. 异常与恢复 + +| 场景 | 行为 | +|---|---| +| 卷忙(FTP 传输占锁) | 本轮整批不落盘、条目回投队列,下一轮再试;业务任务完全不阻塞 | +| 写失败 / 容量不足 | 退回逐条路径;满则删除重建该文件并串口告警 | +| 宿主/介质给回损坏文件 | 仅对介质类错误码删除重建并重试一次;错误只报第一次 | +| 掉电在写批中途 | `floor(size/64)` 排除半条;已 `f_close` 的批次均已同步进 NAND | +| 存储未就绪 / 挂载失败 | 条目滞留 RAM(下次上电丢失),串口告警;存储自愈重试由 default_task 负责 | +| 墙钟无效 | 时间戳落 `1970-01-01 08:00:00` + epoch 0,上电/重启分类因此退化为"上电" | +| 记录的条目被静默丢弃 | 投递侧计数,defaultTask 按差值补一条留痕 | + +## 10. 自测要点 + +- 模拟器无头:`--logtest`(投递 N 条 → 读回断言 + 宿主文件字节级校验:64 B 定长、ISO 时间戳、NUL 填充)、`--keys` 导航到记录页做像素回归。 +- 真机串口标记:Required `App init OK` / `Network Init` / `net init: OK` / `runlog boot entry:`;Forbidden `FAIL` / `ERROR` / `HardFault`。 +- 主机侧单测覆盖队列回投、容量轮转、读窗口代数失效、清除请求与落地分离等只能在真机或纯逻辑层验证的判据(见 `test/host/sys_log_mcu/`)。 +- 人工目检:FTP 下载 `/log/*.log` 看文本可读性;记录页翻页。 diff --git a/docs/子系统设计/运行与自检记录设计说明.md b/docs/子系统设计/运行与自检记录设计说明.md deleted file mode 100644 index c8d2e5db..00000000 --- a/docs/子系统设计/运行与自检记录设计说明.md +++ /dev/null @@ -1,351 +0,0 @@ -# 运行/自检记录与待机显示设计说明 - -> **版本**:v1.4(2026-11-13:§5.2 待机屏从 6 路占位电流改为 UA/UB/UC/IA/IB/IC 六路实时值,§1 R2 按新口径改写、不再引 mockup,§9 对应开放问题销项删行;v1.3 为 §4.4 新增"卷锁 fail-open 边沿补投自检记录"一条,道G task-40;v1.2 为 §4.3 随七类记录更新,v1.1 的原始设计口径不变) -> **日期**:2026-09-14(v1.2 更新 2026-10-26,v1.4 更新 2026-11-13) -> **状态**:步骤 1–9 全部完成——双端构建 0/0、`--logtest` PASS、ui-verify 五门禁全绿;真机 /serial-boot-test 待上电回归(§8.2) -> **关联文档**:`项目架构总览.md` · `STM32F4-Base存储架构说明.md` · `系统墙钟时间维护说明.md` · `UI设计/内容页架构.md`(如存在)· `docs/UI设计/mockups/`(待机显示页面.jpg / 运行记录页.jpg / 自检记录页.jpg / 密码页面实际机器的示例图片.jpg) - -## 1. 背景与需求 - -| # | 需求 | 说明 | -|---|------|------| -| R1 | 密码错误提示 | 用户密钥页输错密码时提示"密码输入错误",并允许重新输入(现状:仅清空重输、无提示) | -| R2 | 待机显示页 | 长时间无操作时默认显示的界面:UA/UB/UC/IA/IB/IC 六路电量的实时值 + 底部时钟;**只有按确认键才进入菜单** | -| R3 | 运行记录页 | 记录装置上电时间、重启时间(参考 `mockups/运行记录页.jpg`:标题 + 逐条"时间戳 + 事件" + 页脚分页) | -| R4 | 自检记录页 | 记录开机自检结果,有问题才记录(参考 `mockups/自检记录页.jpg`) | -| R5 | FTP 可读存储 | 记录以**纯文本文件**落盘,FTP(根目录 = FatFS 卷根)可直接下载读取 | -| R6 | 先调研后设计 | 网络调研存储方式选型 → 写入本设计文档 → 按文档实施 | - -## 2. 现状分析 - -### 2.1 UI 框架(`Drivers/BSP/UI/`,MCU/模拟器共用) - -- **内容页 = 描述符驱动**:`content_page_def_t`(标题/几何/行数据源/4 钩子),框架 `ui_content_page.c` 统一建屏、切屏、焦点、按键分发与绘制;页面模块零 LVGL 对象。 -- **分页已内建**:`selectable=1` + `rows_per_page`(↑↓移选、窗口跟随)+ 页脚"第 X 页,共 Y 页"自动计算;动态行经 `refresh(row, name, value)` 逐行取数(栈缓冲 **32B/列**),行数经 `count_cb()`。 -- **菜单树已含目标叶子**:`ui_menu_tree.txt` "记录查询"下已有 **自检记录 = node 39、运行记录 = node 40**(当前为空白终点框,无内容页映射);无需改树。 -- **密码页** `ui_page_password.c`:3 位数字框 + ↑↓←→/ENTER;输错仅清空重输(R1 缺口)。 -- **按键流**:物理/远端键 → LVGL keypad → 默认输入组 → 焦点对象事件回调(菜单容器 `menu_key_cb` / 内容页容器 `cp_key_cb`)——空闲检测可在此统一挂钩。 -- **字体**:子集位图字体 `ui_font_cn_12`,字符集 `ui_cn_chars.txt`(当前 224 汉字/符号 + ASCII 0x20~0x7E,共 319 字形;2026-10-29 实测。122/217 是 2026-09-14 的历史快照,见 §6.1)。 - -### 2.2 存储与时间 - -| 项 | 现状 | -|---|------| -| 卷 | GD5F2GQ5UE SPI NAND **256MB**(2048 块 × 64 页 × 2KB),dhara FTL(磨损均衡/坏块管理),FatFS FAT32,`FF_USE_LFN=0` → **文件名必须 8.3** | -| FTP | lftpd 根目录 = 卷根 `/`;现有文件 `/config.ini`(8.3) | -| 并发 | `FF_FS_REENTRANT=1` + 系统锁(`ffsystem.c` osMutex):单次 `f_*` 调用受保护,**多调用序列(open→seek→write→close)需单写者纪律** | -| 墙钟 | `sys_clock_get_str(buf,20)` → `"YYYY-MM-DD HH:MM:SS"`;`sys_clock_get()` → Unix 秒(失败 = `UINT32_MAX`);SD2506 RTC + timeSyncTask TCP 校时 | -| 挂载 | defaultTask:`f_mount(forced=1)`,空卷自动 `f_mkfs` FAT32,成功后置 `SYS_EVT_STORAGE_READY` | - -### 2.3 现有自检检查点(v1 埋点位置) - -| 检查点 | 位置 | 失败表现 | -|---|------|---------| -| RTC 读取/墙钟建立 | `app_main_init()`:`sd2506_init()` + `sys_clock_init()`(FreeRTOS 启动前) | 时间戳无效,后续日志无意义时间 | -| NAND 初始化 | `app_main_init()`:`gd5f2gq5ue_init()` 返回非 `GD5F_OK` | 存储不可用 | -| ADC 初始化 | `app_main_init()`:`tpafe5160_init()` 超时 | 采样不可用 | -| FatFS 挂载/建卷 | defaultTask:`f_mount`/`f_mkfs` 失败(置位 STORAGE_READY 前) | FTP/配置/记录全部不可写 | -| 网络链路 | net_task `net_stack_bring_up()`:PHY 等待 ≤3s 后仍 DISCONNECT | 组网可用但无链路 | - -### 2.4 RAM/LVGL 池预算(依据 `docs/内存管理/RAM使用分析与优化方案.md`) - -- FreeRTOS 堆真实余量 ≈ **11~12KB**;LVGL 池 16KB 静态,菜单常驻占用 ≈5KB。 -- 本设计新增 RAM:**全部 < 1KB**(§7),不新增任务、不动任务栈。 - -## 3. 网络调研摘要(R6) - -| 主题 | 结论 | 来源 | -|---|------|------| -| 日志行格式与时间戳 | syslog(RFC 5424):一行一条记录;TIMESTAMP 采用 **ISO 8601/RFC 3339**(`YYYY-MM-DDTHH:MM:SS[.frac][Z]`,机器可读、字典序可排序);MSG 用 UTF-8。本设计取空格变体 `YYYY-MM-DD HH:MM:SS`(与既有 `sys_clock_get_str` 输出一致),另附 epoch 秒字段供程序化解析 | RFC 5424 §6.2.3/§6.4(datatracker.ietf.org/doc/html/rfc5424) | -| 写入模型 | Linux printk:消息先进 **RAM 环形缓冲**,由**专用线程**异步刷持久存储;热路径禁止同步 I/O。本设计对应"RAM 待发队列 + defaultTask 单写者落盘" | kernel.org/doc/html/latest/core-api/printk-basics.html | -| FatFs 追加语义 | `FA_OPEN_APPEND`(= FA_WRITE\|FA_SEEKEND):打开即定位 EOF,文件不存在则创建;配合 `f_lseek(fp, FILE_SIZE)` 可取文件大小。本设计每条记录一次 `f_open→f_write(64B)→f_close` | 工程内 `Lib/FatFs/ff.h:399`、`ff.c:3871`(与官方文档一致) | -| NAND 写入代价 | dhara FTL 已做磨损均衡;FAT32 下每次小追加约消耗 1 个簇(4KB)。本场景**写频率极低**(每上电 1~N 条、故障才写),簇开销与磨损可接受;以文件容量上限约束增长 | `docs/架构设计/STM32F4-Base存储架构说明.md` §6.3 + GD5F 驱动参数 | - -**选型结论**:纯文本、**定长 64B 记录、一行一条**(ISO 时间戳 + epoch + UTF-8 描述);追加写 + 容量上限截断;RAM 队列 + 单写者任务落盘。 -不选变长行(需扫描计数/解析,掉电半行难界定);不选二进制格式(违背 R5 FTP 直读)。 - -## 4. 总体设计 - -### 4.1 模块与文件清单 - -| 文件 | 端 | 说明 | -|---|---|------| -| `App/log/sys_log.h` | 共享 | 公共 API + 记录格式常量 + 行拆分内联函数(无 OS 依赖) | -| `App/log/sys_log_mcu.c` | MCU | FatFS 后端:待发队列(启动前静态数组 + FreeRTOS 消息队列)、defaultTask 落盘、容量截断、上电/重启分类 | -| `sim/sim_syslog.c` | 模拟器 | 宿主文件后端(CWD 下与七类一一对应的 `sim_*.log` 七份),直接追加,`time()` 时间戳;**没有队列与丢弃**,`syslog_post_quiet()` 在本端等价于 `syslog_post()`(两端差异登记在 `docs/测试说明/专项验证/模拟器差距清单.md` §2.2) | -| `Drivers/BSP/UI/ui_standby.h/.c` | 共享 | 空闲检测 + 待机屏(屏幕覆盖层:保存/还原前屏与焦点) | -| `Drivers/BSP/UI/ui_page_runlog.h/.c` | 共享 | 运行记录内容页描述符(动态行,数据源 = sys_log API) | -| `Drivers/BSP/UI/ui_page_selftest.h/.c` | 共享 | 自检记录内容页描述符(同上) | -| `App/app/app_main.c` | MCU | 埋点:RTC/NAND/ADC 失败 → `syslog_post(SYSLOG_SELFTEST, …)` | -| `Src/freertos.c`(defaultTask USER CODE) | MCU | 挂载失败埋点 + **上电/重启分类记录** + 队列排空落盘循环 | -| `App/task/net_task.c` | MCU | 埋点:链路超时 → `syslog_post(SYSLOG_SELFTEST, "通讯异常")` | -| `Drivers/BSP/UI/ui_page_password.c` | 共享 | R1:错误提示 + 重输 | -| `Drivers/BSP/UI/ui_content_page.h/.c`、`ui_menu.c` | 共享 | 新增 2 个内容页 ID/描述符表项/node 39·40 映射;键事件挂钩空闲通知 | -| `ui_cn_chars.txt` + `tools/font_gen/` | — | 补 10 字重生成字体(§6) | -| `MDK-ARM/*.uvprojx`、`simulator/CMakeLists.txt` | — | 新文件双端注册(new-source-file skill) | -| `simulator/main.c` | 模拟器 | `--logtest` / `--standby` / `--idle ` 无头自检与测试钩子 | - -### 4.2 日志记录格式(定长 64B,一行一条) - -``` -偏移 长度 内容 -[0..18] 19 "YYYY-MM-DD HH:MM:SS"(ISO 8601 空格变体;墙钟无效时 = "1970-01-01 08:00:00") -[19] 1 ' ' -[20..29] 10 epoch 秒 ASCII("%010u",机器解析/上电重启分类用;墙钟无效 = "0000000000") -[30] 1 ' ' -[31..58] 28 描述 UTF-8(≤28B = 9 个汉字),不足补 '\0' -[59..63] 5 '\0' 填充 -``` - -示例行:`2026-09-14 16:48:34 1789465714 装置上电记录`(后随 NUL 填充至 64B)。 - -**定长收益**:条数 = `filesize / 64`(O(1),免扫描);第 k 条偏移 = `k*64`(随机读,UI 翻页零解析成本);掉电半行被 `floor(size/64)` 自然排除。 -**epoch 10 位约束**:2026 年 Unix 秒已达 10 位(2001-09-23 起),字段按 `%010u` 定宽;uint32 上限 4294967295(2106 年)内不溢出。 -**描述 ≤28B 约束**:受框架动态行栈缓冲 `CP_ROW_BUF_LEN=32`(含 NUL)限制,不改框架。 - -### 4.3 文件与容量 - -**记录类型 = 日志 ID 本身,一类记录 = `/log` 下一个文件**(记录体内没有类型字段位, -理由与逐类判据、写入点清单见 `App/log/README.md`「五类记录与『记录类型 = 日志 ID』」—— -本节只登记"有哪些文件、各多大"这一层,判据表**不在这里抄第二份**)。 - -| 文件 | 记录类型(= `syslog_id_t`) | 上限 | 满时策略 | -|---|------|------|---------| -| `/log/run.log` | `SYSLOG_RUN` 上电/重启与运行事件 | **1024 条(64KB)** | 删除重建(整卷清空,串口告警);DTU 上电频率低,容量充裕 | -| `/log/selftest.log` | `SYSLOG_SELFTEST` 自检故障(有问题才记;来源含**卷锁 fail-open 边沿留痕**,见 §4.4 最后一条) | **512 条(32KB)** | 同上 | -| `/log/accid.log` | `SYSLOG_ACCIDENT` 事故(跳闸型动作) | **512 条(32KB)** | 同上 | -| `/log/alarm.log` | `SYSLOG_ALARM` 保护告警 | **1024 条(64KB)** | 同上 | -| `/log/start.log` | `SYSLOG_START` 保护启动(越限电平) | **1024 条(64KB)** | 同上 | -| `/log/oper.log` | `SYSLOG_OPER` 操作(改动了装置状态的运维动作) | **512 条(32KB)** | 同上 | -| `/log/yk.log` | `SYSLOG_YK` 遥控(104 命令的选择/执行/拒绝) | **256 条(16KB)** | 同上 | - -- 表内上限是**逻辑上限,不是预分配**:文件按追加写自然增长,一条不写就不占簇,到顶才 - 删除重建;七类全满的名义峰值 = 4864 条 × 64 B = **304 KB**(NAND 2 GB,且这些都只在 - 事件边沿写,不构成容量或磨损瓶颈)。 -- 告警/启动给到 1024 条:一次系统故障会连片触发多个元件,别让最能说明故障过程的两类 - 记录最先被截掉。 -- `id` 只准**尾部追加**:枚举序 = 两端 `s_paths[]`/`s_caps[]` 的下标,中间插值会让 - 某类记录安静地写进别的文件(不报错、屏上照看照翻),机械防线是 - `TC-SYSLOG-130`/`131`/`131b`(逐 id 验"哪类落哪个文件、各用各的上限")。 -- 另有两类**不属于本表**的文件各有各的属主与通道:`/log/wdev.log`(停喂取证, - `sys_log_evidence()`,§6)、`/log/soe.log`(二进制 SOE,64 B 定长但**另一套 - 布局与读者**,见本节末尾与 `App/log/README.md` soe 小节)。 -- 目录 `/log/` 首次落盘前 `f_mkdir`(已存在则忽略);文件名均**严格 8.3** - (`FF_USE_LFN=0`:主名 ≤8、扩展 ≤3、不以点开头),逐条机器核 - `TC-SYSLOG-081`/`130c`。 -- FTP 视角:卷根下 `LOG` 目录内上述记录文件均为**同一格式**的 64 B 定长文本记录, - 可直接下载后按固定偏移解析(R5)。 -- 记录**只增不改不删**;删除只有两条既有途径——满时重建,以及 UI「清除记录」 - (`syslog_request_clear(id)` 请求 / defaultTask `syslog_drain()` 尾部落地, - 语义与判据见 `App/log/README.md`,O3 已由此关闭)。 - -### 4.4 写路径(单写者,调研结论落地) - -**待发条目存副本而非指针(B 方案整改,2026-10-24,台架实证驱动)**:`syslog_entry_t` 原为 -`{ uint8_t id; const char *desc; }`——只存调用方指针,隐含「desc 必须活到落盘」的契约, -但该契约既没写进 `sys_log.h`、也没有任何机械检查。一次可控台架实验把它照了出来:临时在 -testTask 挂投递钩子,复用**同一块缓冲**连投 12 条带序号记录,并与一次 8 MB FTP STOR(占住 -卷锁)重叠——落盘的 12 条变成「1~5 + 第 12 条七份」,即所有滞留条目别名到该缓冲的最后一次 -写入。危险不止于内容错:卷忙整批回投让条目最长滞留到卷空闲,生产者若投栈缓冲,函数返回后 -就是悬垂读。现改为条目内嵌定长缓冲 `char desc[SYSLOG_DESC_MAX + 1u]`,`syslog_post` 投递时按 -`SYSLOG_DESC_MAX` 截断拷贝并补终止符,调用方缓冲区可立即复用;`sys_log.h` 的参数说明同步 -改写。RAM 代价(`scripts/ram_report.py` 同口径前后各跑一次):`ZI 131,500 → 131,852 B` -(+352 B,`RW_IRAM1` 57.4% → 57.7%)、`Code +60 B`、`RW` 不变;`RW_CCM` 静态不变,队列 -存储多要的 192 B 取自 FreeRTOS 堆池,烧录后串口水位行 `[STACK] heap free=15624 -min_ever=8464` 说明余量充足。回归由 **TC-SYSLOG-108**(投出去之后改写调用方缓冲,落盘必须 -仍是投递时的原文)与 **TC-SYSLOG-108b**(超长在条目内钳位)钉住。**为什么主机侧此前照不出 -来**:用例传的都是字符串字面量、静态存储期无限,正好绕开生命周期维度——要照出这类问题必须 -专门构造可变缓冲。 - -``` -任意上下文 syslog_post(id, desc) - ├─ FreeRTOS 未运行(app_main_init 期)→ 静态待发数组(8 槽,满则丢弃+计数) - └─ 调度器已运行 → osMessageQueueSend(8 槽,满则丢弃+计数;日志不得阻塞业务) - -defaultTask(唯一写者,挂载成功后): - 1) f_mkdir("/log")(幂等) - 2) 排空启动前数组 + 队列 → 按文件合并落盘(一批最多 8 条:一次 - f_open FA_OPEN_APPEND → f_write(n×64B) → f_close,见下方「日志读写放大收口」说明) - 3) 写"上电/重启"分类记录(§5.3,基于 run.log 末条 epoch) - 4) 主循环:osMessageQueueGet(阻塞, 10s 超时) → 落盘;超时空转继续 LED 闪烁 -``` - -- **日志读写放大收口(2026-10-15,写侧):一次排空合并成"每文件一次 open/write/close"**。旧实现每条记录一轮,而 `f_close` 要回写目录项、`f_write` 要对目标扇区做读-改-写 → **每条日志 2~4 次 NAND 页读写**。现在同一文件的一批(≤8 条 = 512 B 静态缓冲 `s_batch`)合并成一次追加写: - - **掉电语义不变**:批次仍在**同一次 `syslog_drain()` 调用内**写完并 `f_close`;`f_close` 内部 `sync_fs` → `disk_ioctl(CTRL_SYNC)` 会 `ftl_flush_cache()` 把 FTL 的单页写回缓存刷进 NAND(`Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` 的 `CTRL_SYNC` 分支)。落盘时机与旧实现每条 close 等价,**批次不跨调用驻留 RAM**:"写完即关"的路径关文件即落盘,残余只到"单次写事务进行中掉电/复位"为止(无空闲/掉电前周期 flush,2026-10-22 用户裁定暂不做)。 - - **条目不丢**:启动前数组按下标"看而不删",整批成功才删;队列条目在落盘失败(-2 卷忙 / -1 写失败 / -3 容量不足)时**回投队列**。 - ⚠ **2026-10-24 更正本条**:旧实现里 -2 只在**按条慢路径**被正确回投,**批量路径根本没有 -2 分支**(`done` 保持 `n` → 回投循环空转)→ 卷忙时一批(≤8 条)既不落盘也不回投、静默丢失,而本节长期写着"条目不丢"替它背书。现已补 `rc == -2` 分支整批回投(不计故障、不打日志、保序),由 `test/host/sys_log_mcu/` 的 TC-SYSLOG-035 组逐条钉住。**真机必验**:FTP 传输期间持续投日志,传输结束后条数 == 投递条数、顺序不乱。 - - **容量轮转仍只在按条慢路径做**:批量路径发现剩余容量装不下一批时返回 -3,退回逐条 `s_append()`("满了 unlink 重建"只保留一处实现,避免一批跨多轮重建)。 - - 自愈重试(L1 丢弃重建 + 重试一次)与错误去重(L2 只报第一次)判据与单条路径一致。 - - **自愈触发集分类(2026-10-24,代码侧唯一入口 `s_is_selfheal_code()`)**:只有"删了这个文件真能修好"的介质/结构类码才触发 unlink 重建——`FR_DISK_ERR` / `FR_INT_ERR` / `FR_NO_FILESYSTEM` / `FR_DENIED`;**名字/参数类严禁归入**(`FR_INVALID_NAME` / `FR_NO_PATH` / `FR_NO_FILE` / `FR_INVALID_OBJECT`,另显式排除 `FR_NOT_READY` / `FR_NOT_ENABLED` / `FR_TIMEOUT`)。同名同因下 unlink 必然同样失败,旧集合的真实后果是**每轮排空删一次自己的历史日志且永远修不好**,违反 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.1 第 10 条。`f_write` 之后的"失败/短写 → unlink 重试一次"**不接**本判定(能走到那里说明已打开成功;而簇分配失败是 `FR_OK` + 短写、根本没有码可判,必须无条件重试)。钉住:TC-SYSLOG-101 / 101b / 102 / 103;`App/log/soe_store.c` 侧同源但取**更保守子集**(TC-SOESTORE-022 / 022b)。 - - **卷锁 fail-open(零互斥那一态)边沿补投一条自检记录(2026-10-27 道G task-40)**:锁层只自增计数、 - 绝不产日志——唯一能落盘的通道(`/log/wdev.log` 等)自己也要取这把**非递归**卷锁,被卷忙 - 挡住时既写不出、也不许在失败分支里再取同一把锁(机理与四态语义的唯一出处是 - `App/fs/vol_lock.h` 的「可观测性」段,本文不复述)。因此留痕由唯一写者在 `syslog_drain()` - 尾部按**边沿**补投:判据是"计数比上一轮读到的大了",不是"计数大于零"(后者是电平,每轮一条 - = 每轮一次 NAND 写,诊断自己变成负载);**一个 episode(= 一次上电)只投一条**,重新上电 - 复位 latch 后故障复发仍会再投(否则第二次开机起永久静默)。文本形如 - `vollock failopen=3`(累计值就是证据,不带态号),落 `selftest.log`。 - ⚠ 三条约束(与 task-23「日志让路不计 `[ERR]`」同一根判据):① 不在失败调用点就地打日志; - ② 不打 DBG 错级、不打 `[ERR]`——`[ERR]` 是"设备出故障"的唯一信号通道(`/serial-boot-test` 的 Forbidden - 也依赖它),塞进可观测性事件就同时不再是故障、不再是禁令;③ 记录自带的时间戳是 - **被观测到的时刻**(粒度 = 一轮 `syslog_drain()`),不是发生时刻,所以本模块刻意不再另造 - `obs_first_ms`/`obs_last_ms` 字段去"看起来更精确"。主机判据:`TC-SYSLOG-137` 至 - `TC-SYSLOG-137d`(首投一条并带累计值 / 同 boot 不再投 / 计数不变不投 / 重新上电可再投), - 其中 `g_sys_err_count == 0` 就是"不打错级"的机器证据。 - - **留痕节流分两层、各自独立抑制位**:drain 侧"整批回投"只在正常→故障那一次报(`write fail, requeue %lu entries`,落盘成功一轮复位;旧文案写作 "drop N entries" 与实际行为相反——整批其实一条没丢);post 侧 `queue full, drop` / `preboot full, drop` 各一位(**丢弃语义不变,满则丢照旧**,只压留痕次数)。节流之后**没有丢弃计数**——要计数属新增对外可见信息,另列后续项。 -- 读路径与写路径并发安全:UI 页在 uiTask 经 `syslog_get_record()` 读记录(内部走 `s_read_raw()`,见 §4.5);读写两侧都取卷锁 try 语义,FatFS 系统锁保护单次调用。 -- **存储未就绪/挂载失败**:待发条目滞留 RAM(下次上电丢失),串口打印告警——存储故障时本就无法持久化,属预期行为。 - -### 4.5 读路径(UI / 模拟器) - -```c -uint32_t syslog_count(syslog_id_t id); /* = filesize/64 */ -int syslog_get_record(syslog_id_t id, uint32_t idx, char *ts, size_t ts_len, - char *desc, size_t desc_len); /* 越界 / 读失败 / 缓冲容量不足 均返回 -1 */ -``` - -- MCU:**8 条滑动读窗口**(日志读写放大收口,读侧)——`s_read_raw()` 先查窗口(同 id + 代数未变 + 落在区间内 → **零 I/O** 直接从 512 B 静态窗口返回);未命中才 `f_open(FA_READ) → f_lseek(窗口首条×64) → f_read(≤8×64B) → f_close` **一次取回连续 8 条**,再从中切出请求的那条。旧实现每读一条都要 open/lseek/read/close 一轮,UI 翻 20 条记录 = 20 次目录项/FAT 往返。 - - 失效机制:写者每次落盘后把 `s_rd_gen[id]` 自增,读者比对填充时记录的代数,不匹配就重填。对齐 32 位在 ARM 上原子,跨任务(uiTask 填、defaultTask 增)无需临界区;最坏情况多读一轮,不会读错。 - - **API 与调用方一律不变**(`syslog_get_record()` 签名照旧,`ui_page_runlog` / `ui_page_selftest` 零改动)。 -- **容量契约(2026-10-24 写死在 `App/log/sys_log.h`)**:`ts_len > SYSLOG_TS_LEN`、`desc_len > SYSLOG_DESC_MAX`,**不足一律 -1,既不静默截断也不写调用方一个字节**(描述在写入侧已截到 `SYSLOG_DESC_MAX`,按最小容量给就一定装得下,读侧不需要第二道截断)。旧实现里那句 `if (dl >= desc_len) dl = desc_len - 1` 由上面的守卫保证**永不可达**,已删除;它的存在还会误导调用方"给 28 字节缓冲能读到 27 字符"——实际是整条读不到。UI 侧现用 32 B 列缓冲,走允许路径、行为不变。钉住:TC-SYSLOG-057、057b(0x7A 哨兵逐字节回比)、098。 -- 模拟器:宿主文件同语义(UI 页双端零差异)。 -- UI 页每帧仅读当前窗口 ≤8 条记录(每次重绘 8×64B SPI 读,用户翻页触发,频率极低)。 - -## 5. 功能设计 - -### 5.1 R1 密码错误提示(`ui_page_password.c`) - -- ENTER 比对失败 → `s_err=1`:3 框清零、光标回首框,**数字框下方居中显示"密码输入错误"**(y≈104)。 -- 错误态下按 ↑↓←→:**先清除提示**再正常执行该键(即"让用户重新输入");再按 ENTER = 重新比对。 -- 比对成功路径不变(回调授权/关页)。布局常量与绘制沿用现有 culling 模式,零 LVGL 对象。 - -### 5.2 R2 待机显示页(`ui_standby.c/h`) - -**进入条件**:UI 初始化完成后,**60s(默认,可配)内无任何按键** → 切待机屏。 -- 空闲检测 = `lv_timer`(1s 周期,MCU/模拟器均经 `lv_timer_handler` 驱动,无 OS 依赖); -- 活动通知挂钩:`menu_key_cb`、`cp_key_cb` 每收到一个键调 `ui_standby_notify_activity()`(重置计时)。 - -**待机屏内容**(v1.1 起显示实时值,2026-11-13;全 ASCII 无需新字体字): - -``` -UA = 0.341 ← 6 行实时值 = ui_data 通道一 yc[](下标 = adc_semantic_t 语义序: -UB = 0.338 UA/UB/UC = 0..2,IA/IB/IC = 4..6;UN/IN 不在待机屏——用户点名 -UC = 0.339 六路,且 UN 本台架永久无数据,摆一排假通道不如不摆) -IA = 0.021 ← 示例为现状 ENG_PIN 引脚电压档:数字照显、不挂单位(挂单位 = -IB = 0.020 替用户完成没做的标定换算,同交流量页引脚档不造第二口径) -IC = 0.021 ← 升二次/一次值档后单位随 eng:UA/UB/UC 挂 V、IA/IB/IC 挂 A -────────────────────── ← 分隔线(y≈132) -YYYY-MM-DD HH:MM:SS ← 底部实时时钟(每秒重绘,lv_timer 驱动 invalidate) -``` - -- 空值/坏质口径与格式化的唯一真源 = 交流量页(`ui_page_ac.c`):无首帧或品质带 - `IV|NT|RAW|SB|BL|OV` → `--`;3 位小数定点格式化复用 `ui_w_editor_fmt_scaled`(不引 %f)。 -- 帧读取按 `ui_data_rt_seq()` 门控整帧拷(`ui_data.h` 头注);待机 1s 定时器整屏 - invalidate 即数值刷新节拍,未新增定时器。模拟器 `main.c` 自带固定仿真 rt 帧:待机截图 - (`--standby`)即双分支实证——UA/IA 显引脚档数字(0.812 / 0.046,不挂单位)、 - UB/UC/IB/IC 带 `Q_IV|Q_OV` 坏质显 `--`。 - -**键语义(用户定案)**:待机屏上**只有 ENTER(确认键)进入菜单**;其余按键忽略(同时重置空闲计时)。 -- 实现为**屏幕覆盖层**:进入时保存当前活动屏 + 组内焦点对象,加载待机屏并聚焦其容器;ENTER 时还原前屏与焦点。 -- 内容页打开期间超时 → 同样切待机(页面对象保留在底层,ENTER 返回后原样恢复,编辑态不丢)。 - -### 5.3 R3 运行记录(上电/重启分类) - -defaultTask 挂载成功、排空待发队列**之后**写一条: - -``` -读 run.log 末条记录的 epoch 字段 E: - 墙钟有效 且 (now - E) < 600s → "装置重启记录"(近期刚运行过 = 软复位/看门狗) - 其余(无文件/首条/间隔≥10min/墙钟无效)→ "装置上电记录" -``` - -- 阈值 `SYSLOG_REBOOT_WINDOW_S = 600`:断电 <10min 会被误判为重启——无掉电检测硬件时该歧义不可消除,取工程折中并在此声明。 -- 串口标记(供 /serial-boot-test):`runlog boot entry: 装置上电记录 / 装置重启记录`(`sys_log_mcu.c` `DBG_INFO`,与落盘描述串同源)。 - -### 5.4 R4 自检记录(v1 项表) - -| 检查点 | 触发时机 | 描述串(落盘 + UI 显示同源) | -|---|------|------| -| RTC/墙钟建立失败 | app_main_init | `时钟芯片异常` | -| NAND 初始化失败 | app_main_init | `NAND异常` | -| ADC 初始化超时 | app_main_init | `ADC异常` | -| FatFS 挂载/建卷失败 | defaultTask(仅串口,存储不可写) | (无法落盘,串口 `[SELFTEST] fs: FAIL`) | -| 网络链路 3s 未建立 | net_task bring_up 后 | `通讯异常` | - -- **只记故障**;全部通过不写记录(与 mockup"有问题进行记录"一致)。 -- v1 不做"已复归/动作/标志字"(mockup 中为保护继电器专有语义)——列入开放问题 O2。 -- UI 页空态:`count_cb` 返回 0 时以单条伪行 `无记录` 呈现(页脚"共 1 页",避免"共 0 页")。 - -## 6. UI 布局与字体 - -### 6.1 新增字符集(11 字)→ 重生成 `ui_font_cn_12.c` - -| 用途 | 新字 | -|---|------| -| 密码错误提示"**密码输入错误**" | 输、入、错、误 | -| 运行记录"装置**上电**记录 / 装置**重**启记录" | 上、电、重 | -| 自检故障名"时**钟芯片异常**"等(异/常为公共后缀) | 芯、片、异、常 | - -流程按 `font-regen` skill:改 `ui_cn_chars.txt` → extract_cn → make_font → diff。注意:**extract_cn 只扫 UI 源码**,故障描述串在非 UI 源码(sys_log_mcu.c/app_main.c/net_task.c)中,须手动补入字符集;其余文案(运行记录/自检记录/无记录/第 X 页共 Y 页)均已在字符集内;待机页全 ASCII。 - -### 6.2 页面几何(160×160,沿用框架常量体系) - -| 页 | 参数 | -|---|------| -| 运行记录 / 自检记录 | `selectable=1, rows_per_page=8, top_y=24, pitch=14, band_h=13`(同 AC/DC 约定;**偏离 mockup**:实机照片无选中带,但框架 selectable 页选中行恒黑字、band_h=0 时黑字黑底不可见,故保留标准白带),`left_x=7`;**每条记录 = 2 行**:偶数行 = 时间戳(`YYYY-MM-DD HH:MM:SS`,19 ASCII ≈114px 可容)、奇数行 = 描述(≤9 汉字);↑↓按行移动(8 行/页 = 4 条/页),页脚自动分页 | -| 待机屏 | 6 数据行 y=28 起 pitch=18(label x≈24 / `=` x≈72 / 值 x≈96);分隔线 y≈132;时钟 y≈140 居中 | -| 密码页错误提示 | "密码输入错误" 居中,y≈104(数字框行 y=78..92 之下) | - -## 7. 内存预算 - -| 项 | 大小 | 位置 | -|---|------|------| -| sys_log MCU:启动前待发数组 8×12B + 队列 8×12B + 状态 | ≈300B(静态)+ ≈150B(FreeRTOS 堆,消息队列) | .bss / heap_4 | -| ui_standby:待机屏 + 容器 LVGL 对象(常驻) | ≈300B | LVGL 池(16KB,当前占用 ≈5KB) | -| 记录页模块 | 0(描述符驱动,零 LVGL 对象;仅栈缓冲) | — | -| 字体 +10 字形 | ≈250B Flash | .rodata | - -**结论:不新增任务、不动任务栈、FreeRTOS 堆增量 <200B,LVGL 池余量充足。** - -## 8. 测试计划 - -### 8.1 模拟器(无头自动化) - -| 用例 | 手段 | -|---|------| -| 日志引擎闭环 `--logtest` | post N 条 → count/get 回读断言 + 宿主文件字节级校验(64B 定长、ISO 时间戳、NUL 填充),0=PASS(仿 `--cfgtest`) | -| 待机屏渲染 `--standby` | 无头注键后直接调测试钩子强制进入待机 → 截图比对(布局/时钟行);窗口模式 `--idle ` 可人工验证真实超时路径 | -| 记录页像素回归 | `--keys` 导航至 node 39/40 → 截图 + 文本断言(时间戳行/描述行/页脚分页),纳入 ui-verify 门禁 | -| 密码错误提示 | `--keys` 输错密码序列 → 截图断言"密码输入错误"可见;再按方向键 → 提示消失 | - -### 8.2 真机(/serial-boot-test 串口标记) - -Required:`App init OK`、`Network Init`、`net init: OK`、**`runlog boot entry:`**(装置上电记录 / 装置重启记录)、**`UI standby: ready (idle=60s)`**; -Forbidden:`FAIL`/`ERROR`/`HardFault`。 -人工目检:待机屏(静置 60s)、记录页翻页、FTP 下载 `/log/*.log` 文本可读性。 - -## 9. 风险与开放问题 - -| # | 项 | 策略 | -|---|----|------| -| O1 | 断电 <10min 的"上电/重启"歧义 | §5.3 阈值折中,文档声明;如需精确可后续加掉电标志(PVD) | -| O2 | 故障"已复归"/持续监测(mockup 语义) | v2:周期自检 + 活动故障状态机 + 恢复记录 | -| O3 | ~~"清除记录"菜单项~~ **已实施(2026-10-26 道F task-4)** | 数据面 = `syslog_request_clear(id)` + `syslog_clear_state(id)`,落地在 defaultTask(唯一写者)的 `syslog_drain()` 尾部;UI 侧页面归记录/维护页批次。细口径见 `App/log/README.md` | -| — | FAT32 小追加簇开销 | §3 调研结论:写频率极低可接受;容量上限约束增长 | -| — | UI 读与落盘并发 | 只读既有内容 + EOF 追加 + FatFS 系统锁,无互斥需求(§4.4) | - -## 10. 实施步骤(按序执行,每步双端构建 0/0) - -> ✅ **步骤 1–9 已于 2026-09-14 全部完成**:Keil `@build` 0 错 0 警 + 模拟器 0 警告;步骤 8 = `--logtest` PASS(期间修复 ts 19 字符 / epoch 10 位两处格式 bug)+ ui-verify 五门禁全绿(池基线 5440/5520B、pwc 参考更新)+ 记录页/待机屏/密码错误提示截图目检通过;步骤 9 = 文档同步。 - -| # | 步骤 | 产物 | -|---|------|------| -| 步骤 1 | 字体补字 + 重生成 | `ui_cn_chars.txt`、`ui_font_cn_12.c` | -| 步骤 2 | 日志引擎:`sys_log.h`(共享 API/格式)+ `sys_log_mcu.c` + `sim/sim_syslog.c` | 双端可 post/count/get | -| 步骤 3 | 自检埋点 + defaultTask 落盘循环 + 上电/重启分类 | app_main/freertos/net_task 接线 | -| 步骤 4 | 密码错误提示 | ui_page_password.c | -| 步骤 5 | 待机屏 `ui_standby`(空闲检测 + 覆盖层)+ 键挂钩 | ui_menu/ui_content_page 各一行通知 | -| 步骤 6 | 记录页 ×2 + 内容页框架接线(枚举/描述符表/node 映射) | ui_page_runlog/selftest、ui_content_page.h/.c、ui_menu.c | -| 步骤 7 | 双端注册新文件(uvprojx + CMakeLists)+ 构建 0/0 | new-source-file skill | -| 步骤 8 | 模拟器无头回归(--logtest/--standby/截图断言)+ 真机 /serial-boot-test | 测试通过记录 | -| 步骤 9 | 文档同步(项目架构总览、AGENTS.md、本文档状态) | docs | diff --git a/docs/架构设计/软件架构与运行时规格.md b/docs/架构设计/软件架构与运行时规格.md index 47bf4b55..9d2e2ccb 100644 --- a/docs/架构设计/软件架构与运行时规格.md +++ b/docs/架构设计/软件架构与运行时规格.md @@ -56,7 +56,7 @@ > 并在 ISR 内唤醒 `adcTask`——采集节拍与任务唤醒**不允许**各自计时。 > ⚠ **ADC1 必须在挂 DMA 的瞬间处于关闭态**:`app_main_init()` 步骤 4b-pre 的 `adc1_disable()` 不可删、不可后移, > 否则 DMA 乒乓缓冲整体旋转 2 格(表象与"映射表写错"一致)。证据见 -> [`docs/子系统设计/ADC采样节拍设计说明.md`](../子系统设计/ADC采样节拍设计说明.md) §4.5。 +> [`docs/子系统设计/ADC采样节拍设计.md`](../子系统设计/ADC采样节拍设计.md) §4.5。 ## 3. 就绪屏障(Event Flags) diff --git a/docs/架构设计/项目架构总览.md b/docs/架构设计/项目架构总览.md index 0f1efb72..1c6267aa 100644 --- a/docs/架构设计/项目架构总览.md +++ b/docs/架构设计/项目架构总览.md @@ -197,7 +197,7 @@ flowchart LR ``` - **配置文件**:通用 `/config.ini`(cfg_file 引擎,静态态单任务收敛于 defaultTask,外部变更 4s 沉降后自动重载),**多段注册制**:`[network]`(net_cfg)/ `[ftp]`(鉴权)/ `[adc_cal]`(标定系数与零点)/ `[run_par]`(运行参数 + 104 地址映射真源)/ `[comm]`(104 ca/端口);新模块加段=注册 `cfg_file` 段处理器,唯一落盘者不变。→ `docs/子系统设计/配置文件设计.md` -- **记录九件**:`sys_log` 引擎按 `App/log/sys_log_mcu.c` 的 `s_paths[]` 写**七本**定长 64B 文本记录——`/log/run.log`、`selftest.log`、`accid.log`、`alarm.log`、`start.log`、`oper.log`、`yk.log`;`soe_store` 同先例落 `/log/soe.log`(64B 定长二进制,容量满删除重建);`/log/wdev.log` 是**变长取证行**(掉电不丢,`App/fs/vol_lock` 侧投,不走 64B 定长格式)。defaultTask 是 `/log` 唯一写者(两条队列在 `syslog_drain` 尾部统一排空),FTP 可直下。→ `docs/子系统设计/运行与自检记录设计说明.md` +- **记录九件**:`sys_log` 引擎按 `App/log/sys_log_mcu.c` 的 `s_paths[]` 写**七本**定长 64B 文本记录——`/log/run.log`、`selftest.log`、`accid.log`、`alarm.log`、`start.log`、`oper.log`、`yk.log`;`soe_store` 同先例落 `/log/soe.log`(64B 定长二进制,容量满删除重建);`/log/wdev.log` 是**变长取证行**(掉电不丢,`App/fs/vol_lock` 侧投,不走 64B 定长格式)。defaultTask 是 `/log` 唯一写者(两条队列在 `syslog_drain` 尾部统一排空),FTP 可直下。→ `docs/子系统设计/运行与自检记录设计.md` - **录波段文件**:`/wave`(首 boot `mkdir`,8.3 短名 WAVE 前缀轮转、启动扫盘重建下标),段头 + 通道表 + 两处 CRC-16/MODBUS + 整段自检;defaultTask 游标**分次搬运**(每次 64B 批)不独占 FatFs,落盘成功回执才解冻录波环。→ `docs/子系统设计/故障录波设计说明.md` - **实时调试流** = 控制台单写者日志(**UART5**/RS232#1,4KB 环形缓冲 + defaultTask 唯一发送;2026-10-29 因屏板占用 USART1 自 UART5 定态),详见 `docs/子系统设计/控制台单写者日志设计说明.md`。 - NAND 坏块表(BBT)持久化在保留块池,不交给 dhara 管理。 @@ -388,13 +388,13 @@ flowchart LR | 采集与算法层 语义取数与有效性~保护判据(**设计与判据**) | `docs/算法/采集与算法层设计说明.md` | | 采集与算法层目录索引(文件清单 / 构建测试) | `App/algo/README.md` | | 保护功能复现规划(分层归属 / 功能项状态) | `docs/算法/继电保护功能复现规划.md` | -| ADC 采样节拍(TIM3 1kHz 同源驱动) | `docs/子系统设计/ADC采样节拍设计说明.md` | +| ADC 采样节拍(TIM3 1kHz 同源驱动) | `docs/子系统设计/ADC采样节拍设计.md` | | ADC 通道映射(网名/板级/通道号 13 路置换) | `docs/硬件平台/ADC通道映射说明.md` | | 故障录波(数据面/段格式/回放契约) | `docs/子系统设计/故障录波设计说明.md` | | 屏板链路(USART1 主出口 / 帧 A 帧 B / 实测证据) | `docs/子系统设计/屏板链路设计说明.md` | | IEC 60870-5-104 上行(默认 :2404,可经 `[comm]` 改) | `docs/子系统设计/IEC104上行设计说明.md` | | 配置文件引擎(/config.ini 多段注册制) | `docs/子系统设计/配置文件设计.md` | -| 运行与自检记录(64B 记录 + SOE) | `docs/子系统设计/运行与自检记录设计说明.md` | +| 运行与自检记录(64B 记录 + SOE) | `docs/子系统设计/运行与自检记录设计.md` | | 墙钟时间维护 | `docs/子系统设计/系统墙钟时间维护说明.md` | | 控制台单写者日志(UART5 调试流) | `docs/子系统设计/控制台单写者日志设计说明.md` | | 看门狗 / CubeMX 重生成存活方法论 | `docs/子系统设计/看门狗设计说明.md` §10 | diff --git a/docs/测试说明/单元测试/主机单测覆盖率口径.md b/docs/测试说明/单元测试/主机单测覆盖率口径.md index b242229a..be400098 100644 --- a/docs/测试说明/单元测试/主机单测覆盖率口径.md +++ b/docs/测试说明/单元测试/主机单测覆盖率口径.md @@ -423,8 +423,8 @@ uint16_t ch395f_link_change_count(void); (`net_msg_t` 是按值拷入队列的,改副本调用方看不到)。桩 = 新建假 CH395F。 该文件 80 次提交、15 次 fix/refactor,值;但**一次做完必翻车**(先求把状态机进分母,别求百分比)。 2. **`sys_log_mcu.c`(458) + `cfg_file_store_mcu.c`(129) + `soe_store.c`(123) = 710 行** —— 一套假 FatFs - 吃三块,性价比最高的第二批。判据现成:`docs/子系统设计/运行与自检记录设计说明.md` §4.4(写侧批量合并 + **掉电语义不变** + - 失败回投不丢条目 + 容量轮转只在慢路径)与 `docs/子系统设计/运行与自检记录设计说明.md` §4.5 + 吃三块,性价比最高的第二批。判据现成:`docs/子系统设计/运行与自检记录设计.md` §3.2(写侧批量合并 + **掉电语义不变** + + 失败回投不丢条目 + 容量轮转只在慢路径)与 `docs/子系统设计/运行与自检记录设计.md` §4.5 (读侧 8 条窗口 + 写者代数失效 + API 不变)。 这块的历史教训就是"日志读写放大"与"`mkdir` 失败静默不再重试"。 3. **`console_log.c`(265)** —— 单写者契约 K1/K2 已经写成表,照抄成用例最省力: @@ -499,7 +499,7 @@ uint16_t ch395f_link_change_count(void); | # | 位置 | 现象与影响 | 钉住它的用例 | |---|---|---|---| -| 1 | `App/log/sys_log_mcu.c:509~537` | **疑似数据丢失(建议按 P1 定级)**:批量路径 `done = n` 只有 `rc==-3`/`rc==-1` 两个分支会改它,**`rc==-2`(卷忙)没有分支** → 紧随其后的 `for (i = done; i < n; …)` 回投循环一次都不执行 → 最多 8 条日志既不落盘也不回投、不计故障、不打日志。同段 `:531` 注释与 `docs/子系统设计/运行与自检记录设计说明.md` §4.4 都写"条目不丢(旧实现 -2 丢那一条)"→ **现状比旧实现更糟**(旧丢 1 条、现一次丢 8 条)。对照:**逐条慢路径遇 `-2` 会正确回投尾部**,同一错误码在两条路径行为相反。触发面常见:卷锁 v1.2 之后 ftpTask 传文件期间 defaultTask 每 2 s 排空一次 | TC-SYSLOG-035 / 035b / 035c | +| 1 | `App/log/sys_log_mcu.c:509~537` | **疑似数据丢失(建议按 P1 定级)**:批量路径 `done = n` 只有 `rc==-3`/`rc==-1` 两个分支会改它,**`rc==-2`(卷忙)没有分支** → 紧随其后的 `for (i = done; i < n; …)` 回投循环一次都不执行 → 最多 8 条日志既不落盘也不回投、不计故障、不打日志。同段 `:531` 注释与 `docs/子系统设计/运行与自检记录设计.md` §3.2 都写"条目不丢(旧实现 -2 丢那一条)"→ **现状比旧实现更糟**(旧丢 1 条、现一次丢 8 条)。对照:**逐条慢路径遇 `-2` 会正确回投尾部**,同一错误码在两条路径行为相反。触发面常见:卷锁 v1.2 之后 ftpTask 传文件期间 defaultTask 每 2 s 排空一次 | TC-SYSLOG-035 / 035b / 035c | | 2 | `App/log/sys_log_mcu.c:527~529` | `rc==-1` 打 `DBG_ERROR("drop %lu entries …")`,但该分支 `done=0` → 整批其实**全部回投**、一条没丢 → 文案与行为相反,且持续写故障时每轮 drain 都重投重试一遍 | TC-SYSLOG-036 | | 3 | `App/log/sys_log_mcu.c:616` vs `627~628` | 守卫写成 `desc_len <= SYSLOG_DESC_MAX` 就 `-1`,而 `dl` 恒 ≤ 28 → 截断分支**永不可达**。无害(宁可拒绝不越界),但"传小缓冲能拿到截断内容"的直觉是错的 | TC-SYSLOG-098 / 057 | | 4 | `Drivers/BSP/NET/net_socket.c:2825` / `:2869` / `:3080` | **同一文件两套 IP 字节序约定,`inet_addr` 与 `ntoa`/`getsockname` 不互逆**:`net_inet_addr` 小端组装后又 `NET_HTONL` → `0xC0A80164`;`net_inet_ntoa` 交换后高段在前 → 只能正确还原 `getsockname` 那族(`0x6401A8C0`);`net_connect_locked:1571` 用 `>>24` 取首段 → 只配 `inet_addr` 那族。**为什么长期不被看见**:`ntoa` 自己 `:2865~2867` 那段 E13 修复注释里的例子「192.168.1.100 → 0xC0A80164 → "192.168.1.100"」取的是**另一族**的值(交换宏下实算 `"100.1.168.192"`)。前提已核清:`Drivers/BSP/NET/net_types.h`:56 是 `#ifdef __BIG_ENDIAN` 才恒等,STM32 与 x86/MSVC 都走交换分支 → **不存在"主机语义≠目标语义"**。当前无调用者跨族(`lftpd.c`:864 走 `getpeername→ntoa` 同族恰好正确),属埋在库 API 面上的地雷;统一必须跨 `inet_addr`/`ntoa`/`getsockname`/`connect` 解析/`lftpd.c`:548 一起过 | TC-NETSOCK-100b / 100c / 100d、107f、430h(钉的是**具体数值**,统一时会五条一起红并互相指认,不会被误读成单点回归) | @@ -704,7 +704,7 @@ uint16_t ch395f_link_change_count(void); 第 2 轮交付汇报里,「卷忙整批回投不丢条目」被列进上台架必验项。本轮把它跑成,并在跑的过程中改掉一个真隐患。 - **正/负双向实证(台架,全程非破坏性)**:临时钩子从 testTask 以 2.5 s 间隔投 12 条带序号记录(远低于 8 槽队列容量,避开投递侧「满则丢」),窗口中段起一次 8 MB FTP STOR 抢卷,回读 `LOG/SELFTEST.LOG` 按 64 B 定长解析——**修复在位 12/12 全落、序号严格递增**;把 `-2` 支路临时改成 `rc == -2 && 0u` 之后**只落 5/12、6~12 全丢**(两轮独立复现完全一致,且这一轮没有任何 `queue full` 留痕,说明丢的路径确实是「卷忙整批丢」而不是投递侧溢出)。**负向对照是必要的**:若卷其实没被占住,「零丢失」就是空结论。 -- **顺手照出的隐患**:待发条目只存 `const char *`,复用栈缓冲投递时整批别名到最后一次写入(实测落盘「1~5 + 12 七份」)。已改为条目内嵌 `desc[SYSLOG_DESC_MAX + 1u]` 并在投递时截断拷贝,头文件参数契约同步改写;代价 `+352 B ZI / +60 B Code`。理由与实测细节 → `docs/子系统设计/运行与自检记录设计说明.md` §4.4。 +- **顺手照出的隐患**:待发条目只存 `const char *`,复用栈缓冲投递时整批别名到最后一次写入(实测落盘「1~5 + 12 七份」)。已改为条目内嵌 `desc[SYSLOG_DESC_MAX + 1u]` 并在投递时截断拷贝,头文件参数契约同步改写;代价 `+352 B ZI / +60 B Code`。理由与实测细节 → `docs/子系统设计/运行与自检记录设计.md` §3.2。 - **两次测法自误(教训,别重复)**:① 先按 700 ms 快投递测,测到的 34 条丢失其实是投递侧队列满(设计如此),根本没走到被整改的支路——**测错了对象**;② 钩子投完后 `return` 使 testTask 退出,约 10 s 后被看门狗复位,把还压在 RAM 队列里的条目一起带走——**污染了自己的计数**。两者都不是固件问题,根因都是没有先验证测量手段本身有效。 - **假件护栏的一次正向记录**:条目由 8 B 变 32 B 后,`test/host/sys_log_mcu` 的假消息队列因 `MQ_MAXMSG`(原 24)拒收一切 `osMessageQueuePut`,从既有用例起整片失败——这种「尺寸变了就响」的断言比任何评审都可靠;该宏已抬到 40 并把现象写进注释。 - 本轮改动面:`App/log/sys_log_mcu.c`(条目结构 + 投递拷贝)、`App/log/sys_log.h`(参数契约)、`test/host/sys_log_mcu/sys_log_mcu_test.c`(新增 2 条断言 + `MQ_MAXMSG`)、`scripts/archive/id_gate/id_registry.json`(游标 `TC-SYSLOG-108`)。 diff --git a/docs/硬件平台/ADC通道映射说明.md b/docs/硬件平台/ADC通道映射说明.md index 5f98e1b9..9264e56a 100644 --- a/docs/硬件平台/ADC通道映射说明.md +++ b/docs/硬件平台/ADC通道映射说明.md @@ -118,7 +118,7 @@ STM32(U17)为 LQFP144 脚号。 | ADC1 上电自校准 | `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`,代码里按 RM 自建 `ADC1_CAL_BIT`;审计项 `adc1-calibration-call` / `adc1-calibration-impl` 兜底 | 缺这一步**不报错,但读数带失调**(实测校准后 STM32 侧六路同向移动 +10~+63 LSB,TPAFE 侧不动) | | ADC 时钟 | ADCCLK = 21 MHz(PCLK2/4)。CubeMX 里 DIV2 候选被 DB 约束 `FadcConstraint Max=36000000` 禁掉(APB2 84 MHz → 42 MHz 超限),`.ioc` 不保存 `ADC1.ClockPrescaler` 键,重生成只会回落到约束允许的值 | 不额外设防护 | | `adc_task` | `App/task/adc_task.c` 每拍调 `adc_input_poll()` = TPAFE `tpafe5160_snapshot()` 原子快照(8 通道)+ 消费一帧 STM32 ADC1 DMA 乒乓缓冲,**经 `adc_input_raw()` 按语义取数**;消费唤醒由 TIM3 ISR 置位 `ADC_WAKE_FLAG`;任务在 `Src/freertos.c` 的 `USER CODE` 区创建(640 words / **AboveNormal**,`Src/freertos.c:159 .stack_size = 640 * 4`) | 台架实测 `frames` 严格 1000 Hz 递增、`idle=0`、`adc1_rc=0`、`adc1_err=0` | -| TPAFE 采样触发 | `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 通道,并调 `adc_tick_irq_notify()` **唤醒消费者**(内含"消费者就绪"门) | 两片转换器由同一硬件事件同源对齐,连续波形 1 kHz;详见 `docs/子系统设计/ADC采样节拍设计说明.md` §4(DMA 乒乓搬运)与 §6(事件驱动唤醒) | +| TPAFE 采样触发 | `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 通道,并调 `adc_tick_irq_notify()` **唤醒消费者**(内含"消费者就绪"门) | 两片转换器由同一硬件事件同源对齐,连续波形 1 kHz;详见 `docs/子系统设计/ADC采样节拍设计.md` §4(DMA 乒乓搬运)与 §6(事件驱动唤醒) | | 前端偏置 | 每路 1k 串联 + 滤波 C 跨接差分输入;差分负端(`*_GND` 网)经 1k 拴 AGND,**不是对地分压**(拓扑图见 §7.1),**无中点偏置** | TPAFE 输入阻抗 **1 MΩ 恒定**(`../芯片手册/TPAFE5160.md` "Analog Input Impedance" 节),端子对 AGND 的直流通路 = 1k + 1 MΩ + 1k ≈ **1.002 MΩ**,不是 1 kΩ;两侧各 1 kΩ 是手册要求的**源阻抗匹配**,不是分压。STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若算 RMS/功率需硬件补偏置或另定算法口径 | | TPAFE 量程 | 板上 RANGE 接 GND → **±5 V 双极性量程**(芯片手册输入量程表:RANGE = 0 → −5 ~ +5 V),16 位二进制补码,**±5 V 满量程 = 10 V 跨度**,LSB = 10 V / 65536 ≈ **152.59 µV/码(@ 转换器引脚)**。RANGE = HIGH 的 ±10 V 档(跨度 20 V、LSB ≈ 305.18 µV)是芯片另一可选档,**本板未用**;305.18 只属该档的 LSB,**不是本板系数** | 用 `tpafe5160_to_voltage()` 换算;端到端口径见 §7.1 | | 取数契约 | 消费者**只能**用 **`tpafe5160_snapshot(dst, count)`**(临界区原子拷贝,返回 1=有新帧 / 0=无新帧 / -1=参数错,**仅任务上下文**)+ **`tpafe5160_frame_seq()`**(帧序号只增不清)+ **`tpafe5160_frstdata_err()`**(无跳变帧计数)。`tpafe5160_init()` 含**首帧自检**:一帧读下来 FRSTDATA 无跳变(芯片不驱动并行总线,板上 R24 4.7k 上拉会把它钉死为高)→ 返回 `-3` + `[ERR]` | `AGENTS.md` 已立为铁律;开机 `[ADC] Init OK` 只在自检帧通过后才打印 | diff --git a/docs/算法/继电保护功能复现规划.md b/docs/算法/继电保护功能复现规划.md index d282bcdb..3fe9d55d 100644 --- a/docs/算法/继电保护功能复现规划.md +++ b/docs/算法/继电保护功能复现规划.md @@ -238,7 +238,7 @@ | 功能名 | 关键参数 | 完成判据 | 状态 | | --- | --- | --- | --- | -| 周期 CONVST 重触发 | 1000 Hz(与保护节拍同源) | 示波器见 U3 `BUSY` 连续脉冲;`tpafe5160_frame_seq()` 持续递增 | ✅ 2026-10-10:**TIM3 更新事件(1 kHz)硬件触发**(ISR 内 `tpafe5160_start_conv_irq()`;方案 A,零引脚/零驱动改动。⚠ 节拍源**不能**是 TIM6/TIM7——F4 上 ADC1 收不到它们的 TRGO,见 `docs/子系统设计/ADC采样节拍设计说明.md` §3.2);台架 `/serial-boot-test` PASS——`seq` 每 10 s 精确 +10000 = 1000.0 Hz、`frst_err=0`(早期证据 `docs/测试说明/测试数据/boot_log_data/20261010_prod_adc_tim6_boot_log.txt`,节拍源当时为 TIM6) | +| 周期 CONVST 重触发 | 1000 Hz(与保护节拍同源) | 示波器见 U3 `BUSY` 连续脉冲;`tpafe5160_frame_seq()` 持续递增 | ✅ 2026-10-10:**TIM3 更新事件(1 kHz)硬件触发**(ISR 内 `tpafe5160_start_conv_irq()`;方案 A,零引脚/零驱动改动。⚠ 节拍源**不能**是 TIM6/TIM7——F4 上 ADC1 收不到它们的 TRGO,见 `docs/子系统设计/ADC采样节拍设计.md` §3.2);台架 `/serial-boot-test` PASS——`seq` 每 10 s 精确 +10000 = 1000.0 Hz、`frst_err=0`(早期证据 `docs/测试说明/测试数据/boot_log_data/20261010_prod_adc_tim6_boot_log.txt`,节拍源当时为 TIM6) | | TPAFE 快照消费 | 8 通道 / 帧 | `adc_task` 每拍调 `tpafe5160_snapshot()`,返回 1/0/-1 有处理 | ✅ ADC 采集链路 已实现并 `/serial-boot-test` 闭环(`adc_task.c`;台架 `idle=0`、`frst_err=0`) | | STM32 ADC1 启动与扫描 | 9 通道、144 cycles、**TIM3 TRGO 硬件触发 + DMA** | 全库出现 `HAL_ADC_Start_DMA`;9 通道码值随注入变化 | ✅ 2026-10-10(v2.0):`App/algo/adc_input.c` 的 `stm32_take()`——ADC1 由 TIM3 更新事件经 TRGO 触发一轮 9 通道,`HAL_ADC_Start_DMA(&hadc1, buf, 18)` + DMA2_Stream4 循环模式把码值自动搬进**乒乓双缓冲**,任务侧只消费刚完成的半区(**零忙等**,实测单拍 `adcp` 122 µs → **8 µs**);台架 `/serial-boot-test` PASS——`adc1_seq` 每拍 +1、`adc1_err=0`、`adc1_hw=0`。⚠ 「9 通道码值随注入变化」已由 2026-10-27 台架已知电压注入直接证实(六项判据全 PASS,实测记录 `docs/硬件平台/ADC通道映射说明.md` §8.1);多点标定与端子/子板变比确认仍待做 | | 通道语义映射 | 见 `docs/硬件平台/ADC通道映射说明.md`(**按语义取数,不写死下标**) | `UA→buf[6]`、`UN→buf[5]`、`IA→buf[2]`;STM32 侧 `UB→ADC_CHANNEL_14` | ✅ 2026-10-10:`App/algo/adc_input.c` 的 `k_adc_map[]` 语义表(`UA/UN/IA`→TPAFE `buf[6]/[5]/[2]`,`UB/UC/IB/IC/IN/TEMP`→ADC1 `CH14/3/6/1/2/5`)+ `adc_input_raw()` 按语义取数;开机**映射自检**(`adc_input_init()` 校验 STM32 映射通道必落在扫描序列内 → 挡住"映射到不扫描的通道"静默错)通过(`ADC input layer init: OK`)。台架首帧日志 8 路 + 温度全部读出:`UA=11429 UN=11454 IA=11452`(与 TIM3 轮次一致)、`UB=1018 UC=1044 IB=943 IC=1069 IN=1052 T=850`(互异、非饱和;**两次开机读数不同** → 确为实时转换而非常量) | diff --git a/docs/算法/采样有效性校验设计说明.md b/docs/算法/采样有效性校验设计说明.md index 32f6bee4..150da2b3 100644 --- a/docs/算法/采样有效性校验设计说明.md +++ b/docs/算法/采样有效性校验设计说明.md @@ -2,7 +2,7 @@ > **本文回答的问题**:`App/algo/adc_health.c` 这 200 行为什么长成现在这样——判据是怎么定下来的、 > 每个设计选择换到了什么、代价与盲区在哪里、改它之前必须一起动什么。 -> **阅读前提**:采样节拍与乒乓双缓冲 → `docs/子系统设计/ADC采样节拍设计说明.md`; +> **阅读前提**:采样节拍与乒乓双缓冲 → `docs/子系统设计/ADC采样节拍设计.md`; > 8 路电气量为何分属两片转换器 → `docs/硬件平台/ADC通道映射说明.md`。 > **不在本文范围**:接口签名与全层约定速览在 `docs/算法/采集与算法层设计说明.md`,本文不重复其接口表; > 需求与验收口径登记在 `docs/算法/继电保护功能复现规划.md` §3.1(功能项「采样有效性校验」)与同文档 §3.5(动作留痕自检与闭锁)。 @@ -92,7 +92,7 @@ 两条边界是刻意的,破坏它们的代价写在右边: 1. **ISR 侧只加计数,不在中断里打日志**——FRSTDATA 异常只累计,帧序号只自增。 - 中断里做重活会直接推后下一拍采样,见 `docs/子系统设计/ADC采样节拍设计说明.md` §5。 + 中断里做重活会直接推后下一拍采样,见 `docs/子系统设计/ADC采样节拍设计.md` §5。 2. **判定层不许出现 HAL / 日志 / 取数**——`test/host/adc_health/adc_health_test.c` 用 `#include` 原样纳入生产源文件、**零桩**跑全套。判定层里多一个 `DBG_ERROR`,主机侧就要为它写一份桩, 「离线穷举故障」这条唯一的验证路径随之失效(§11)。 @@ -249,7 +249,7 @@ else { 清电平位 } /* 只有 stall 类有 else 分支,SEQ_BACKWARD 没有 两条车道**同单位(帧点数)**,心跳里 `tgap` 与 `adc1_gap` 可以直接对比(2026-10-21 之前 固件的 `adc1_gap` 是"次数"口径,跨版本对比要留意:单拍超一帧时两者数值相同,只有 -一次跨过 ≥2 帧才会分出差值)。字段口径见 `docs/子系统设计/ADC采样节拍设计说明.md` §8。 +一次跨过 ≥2 帧才会分出差值)。字段口径见 `docs/子系统设计/ADC采样节拍设计.md` §8。 为什么刻意不告警、不锁存、不闭锁:消费者超拍是**常态**。同一批台架日志里, 启动日志突发期 `adc1_gap` 已累计 23 次,稳态每 10 s 心跳 +1~2 次(`tgap` 1 → 2)。 @@ -542,7 +542,7 @@ stateDiagram-v2 | 剧本 | 判定路径 | 结论 | |---|---|---| | 现场报「保护退出」 | 查 `[PROT] sampling FAULT` 的 `hf` → 回到 `flags` 上升沿行看 `stall_ms` | `hf=0x4` → 计数器被动过,查软件;`hf=0x1/0x2` → 采样侧真停,查节拍与转换器供电 | -| 心跳里 `idle` / `skip` 在涨但无 `health` 行 | 那不是本层的判据(本层只看序号) | 属 语义取数与有效性相位伪影或通道首次未就绪,见 `docs/子系统设计/ADC采样节拍设计说明.md` §8 | +| 心跳里 `idle` / `skip` 在涨但无 `health` 行 | 那不是本层的判据(本层只看序号) | 属 语义取数与有效性相位伪影或通道首次未就绪,见 `docs/子系统设计/ADC采样节拍设计.md` §8 | | 偶发一条 `health` + 一条 `recovered` | 一次瞬态静默(几十 ms 到几百 ms) | 查同窗口的日志突发、NAND 擦写、FTP 传输等长耗时窗口(`docs/子系统设计/看门狗设计说明.md` §4.3) | --- @@ -632,7 +632,7 @@ stateDiagram-v2 **边界与残余**:TIM3 停 → ADC1 无触发 → 无 DMA 请求 → `s_dma_seq` 也停(节拍源是唯一上游,别 以为「DMA 还在循环所以计数会涨」)。开机不误锁的前提是 TIM3 在 `app_main_init()` 就启动、远早于 -`osKernelStart()`(`docs/子系统设计/ADC采样节拍设计说明.md` §3.4),protectTask 首拍时计数器已在 +`osKernelStart()`(`docs/子系统设计/ADC采样节拍设计.md` §3.4),protectTask 首拍时计数器已在 前进。残余风险:`protectTask` 自己被饿死时无人判老,此时退回 IWDG(它在 `required_mask = 0x1F7` 内)。 --- @@ -640,7 +640,7 @@ stateDiagram-v2 ## 16. 相关文档 - `docs/算法/采集与算法层设计说明.md` §4 —— 本模块在全层中的位置与接口速览(本文不重复) -- `docs/子系统设计/ADC采样节拍设计说明.md` §8 —— 心跳字段全表与判读(`tgap` 所在行) +- `docs/子系统设计/ADC采样节拍设计.md` §8 —— 心跳字段全表与判读(`tgap` 所在行) - `docs/子系统设计/看门狗设计说明.md` §4.3 —— 长耗时窗口的三类处置(瞬态静默的候选来源) - `docs/子系统设计/看门狗设计说明.md` §11.4 —— `[PROF]` 探针字段与耗时基线(判「超拍」要看它) - `docs/硬件平台/ADC通道映射说明.md` —— 语义映射唯一权威口径(与「哪一侧静默」直接相关) @@ -648,4 +648,4 @@ stateDiagram-v2 - `docs/算法/继电保护功能复现规划.md` §3.5 —— 动作留痕自检与闭锁(本层供数的「自检失败闭锁」在此登记) - `docs/内存管理/RAM使用分析与优化方案.md` §2.3 与 §4.0.6 —— `adcTask` 栈配置、最坏链与余量台账 - `App/algo/README.md` —— 目录级文件清单与构建测试入口 -- `docs/子系统设计/ADC采样节拍设计说明.md` §3.4 —— TIM3 启动位置与顺序(兜底门开机不误锁的前提) +- `docs/子系统设计/ADC采样节拍设计.md` §3.4 —— TIM3 启动位置与顺序(兜底门开机不误锁的前提) diff --git a/docs/算法/采集与算法层设计说明.md b/docs/算法/采集与算法层设计说明.md index ef70c83d..5fd6e987 100644 --- a/docs/算法/采集与算法层设计说明.md +++ b/docs/算法/采集与算法层设计说明.md @@ -11,7 +11,7 @@ > **相关文档**: > 分层与功能项(语义取数与有效性~动作留痕)的唯一属主登记处:`docs/算法/继电保护功能复现规划.md` §2.1(分层)、§3(功能清单)、§4.2(精度)、§7(路线图) > `docs/硬件平台/ADC通道映射说明.md`(**语义映射唯一权威口径**) -> `docs/子系统设计/ADC采样节拍设计说明.md`(TIM3 1 kHz 同源节拍、ADC1+DMA 乒乓缓冲、`adcTask` 唤醒) +> `docs/子系统设计/ADC采样节拍设计.md`(TIM3 1 kHz 同源节拍、ADC1+DMA 乒乓缓冲、`adcTask` 唤醒) > `docs/算法/傅氏算法详解.md` §2.3(有效值口径怎么来的) > `docs/测试说明/单元测试/相量层单元测试说明.md`、`docs/测试说明/单元测试/ADC输入层单元测试说明.md`(主机单测细则) @@ -53,7 +53,7 @@ ## 2. 一拍的数据流与初始化落点 一拍 = TIM3 的一个 1 kHz 更新事件(节拍与消费者唤醒同源,见 -`docs/子系统设计/ADC采样节拍设计说明.md` §1)。 +`docs/子系统设计/ADC采样节拍设计.md` §1)。 ```mermaid flowchart TD @@ -98,7 +98,7 @@ flowchart TD | `adc_input_poll()` | 采一拍:`tpafe5160_snapshot()` 原子快照(8 通道)+ `stm32_take()` 消费**刚完成的那半区**(按完成计数奇偶推半区,零忙等) | **仅任务上下文**(快照是临界区);在 ISR 里调会破坏 `tpafe5160_snapshot()` 的"仅任务上下文"约定 | | `adc_input_raw(ch, &out)` | 按语义读原始码值(TPAFE 16 位补码 / STM32 `0..4095`);`-2` = 该来源尚无有效数据 | 消费侧取数**唯一入口**;须先成功 `poll()` | | `adc_input_desc(ch)` | 语义 → 物理通道描述(来源 + 下标 / 通道号),供自检注入与 UI 通道序 | 返回静态地址,**勿改** | -| `adc_input_stm32_rc()` / `_seq()` / `_errs()` / `_gaps()` / `_hw_errs()` | 最近一拍状态码 / DMA 整帧完成计数 / 停摆(连续 ≥2 拍无新帧)/ 丢帧**点数**(Σ 每次前进的超出部分,与 `tgap` 同单位)/ HAL 错误回调 | **四类成因不同,勿混读**:对应心跳字段 `adc1_rc` / `adc1_seq` / `adc1_err` / `adc1_gap` / `adc1_hw`(判读见 `docs/子系统设计/ADC采样节拍设计说明.md` §8) | +| `adc_input_stm32_rc()` / `_seq()` / `_errs()` / `_gaps()` / `_hw_errs()` | 最近一拍状态码 / DMA 整帧完成计数 / 停摆(连续 ≥2 拍无新帧)/ 丢帧**点数**(Σ 每次前进的超出部分,与 `tgap` 同单位)/ HAL 错误回调 | **四类成因不同,勿混读**:对应心跳字段 `adc1_rc` / `adc1_seq` / `adc1_err` / `adc1_gap` / `adc1_hw`(判读见 `docs/子系统设计/ADC采样节拍设计.md` §8) | **要点** @@ -107,7 +107,7 @@ flowchart TD **V0.2 改版只改这张表的内容**,本层 API 与上层语义不动。 - **STM32 侧是顺序扫描**(Rank1..8 = CH0..CH7、Rank9 = CH14,与 `MX_ADC1_Init()` 严格一致), 9 个通道**不是同一时刻**(一轮 ≈67 µs);由 TIM3 TRGO 硬件触发 + DMA 自动搬运( - `docs/子系统设计/ADC采样节拍设计说明.md` §4)。STM32 侧数据**固定滞后 1 拍(1 ms)**——与 TPAFE + `docs/子系统设计/ADC采样节拍设计.md` §4)。STM32 侧数据**固定滞后 1 拍(1 ms)**——与 TPAFE 侧本来就没有同刻性(双转换器无共同采样触发),无新增误差。 - **开机映射自检**挡住"映射到一个从不扫描的通道"这类静默错(启动日志 `ADC input layer init: OK`)。 - 本文件是本层**唯一**允许出现 HAL 调用的地方;新增模块若不需要外设,请保持 `sample_ring` / @@ -501,7 +501,7 @@ UA 无幅值落 IA → 0.0020 Hz。 | `prot_set.c` `s_val[2][40]` + 封存 / `uint64_t` 位图 / 闭锁 | 336 B | 项数 32 → 40(测频与遥测快照)后的量 | | `prot_elm.c` `s_rt[9]`(16 B / 实例) | 144 B | 实例 7 → 9(测频与遥测快照)后的量 | | `adc_input.c` `s_stm32_raw[16]` + `s_tpafe_raw[8]` | 48 B | — | -| `adc_input.c` `s_stm32_dma[2×9]`(`uint16_t`) | 36 B | 必须在 `RW_IRAM1`(DMA2 不可达 CCM)→ `docs/子系统设计/ADC采样节拍设计说明.md` §4.3 | +| `adc_input.c` `s_stm32_dma[2×9]`(`uint16_t`) | 36 B | 必须在 `RW_IRAM1`(DMA2 不可达 CCM)→ `docs/子系统设计/ADC采样节拍设计.md` §4.3 | | `adc_health.c` 内部状态 | ≈ 40 B | — | | `adc_cal.c` 三个观测计数(`volatile uint32_t`) | **12 B** | 只增不清零、不参与判定;定义与读法见 §10.1 | | 运行期栈 `phasor_run()` 的 `win[20]` | 80 B / 帧 | 抬高 `PHASOR_N` 直接抬栈 | @@ -892,7 +892,7 @@ typedef struct { - 「采样有效性校验」判定层深设计(判据推导 / 盲区 / 验证分工):`docs/算法/采样有效性校验设计说明.md` - 分层与功能项清单(语义取数与有效性~动作留痕的属主登记处):`docs/算法/继电保护功能复现规划.md` - 通道映射权威口径:`docs/硬件平台/ADC通道映射说明.md` -- 采样节拍与 DMA 搬运:`docs/子系统设计/ADC采样节拍设计说明.md` +- 采样节拍与 DMA 搬运:`docs/子系统设计/ADC采样节拍设计.md` - 傅氏算法与有效值口径:`docs/算法/傅氏算法详解.md` §2.3 - 主机单测细则:`docs/测试说明/单元测试/相量层单元测试说明.md`、`docs/测试说明/单元测试/ADC输入层单元测试说明.md` - RAM 与栈台账:`docs/内存管理/RAM使用分析与优化方案.md` §2.3 diff --git a/docs/问题记录/ADC_Trap_Records.md b/docs/问题记录/ADC_Trap_Records.md index ba54edcd..33d485f9 100644 --- a/docs/问题记录/ADC_Trap_Records.md +++ b/docs/问题记录/ADC_Trap_Records.md @@ -7,7 +7,7 @@ ADC1 采样链路(TIM3 1 kHz 节拍 → ADC1 九通道扫描 → DMA2_Stream4 > 编号口径:本域既有条目按 `TRAP-ADC-` 自 01 起连续编号,**只读引用**;2026-10-29 起**不再新增 TRAP 号**(`docs/文档规范.md` §7.1 一律停发),新踩坑在本文写语义行名。编号不复用、不重排。 > 状态图例:✅ 已修复定案 · ⏳ 待修。 > 设计侧口径(采样节拍、寄存器配置、铁律)不在本文,见 -> `docs/子系统设计/ADC采样节拍设计说明.md`;本文只记「踩过的坑」与「怎么判断踩到了」。 +> `docs/子系统设计/ADC采样节拍设计.md`;本文只记「踩过的坑」与「怎么判断踩到了」。 ## 索引 @@ -92,7 +92,7 @@ DMA 从序列中途开始搬运时,整个缓冲一起旋转,拷贝照样"恒 ⚠ **顺序不可调换**:`adc1_disable()` 必须严格早于 `adc_input_init()`。若 ADC 在挂 DMA 时 仍处于使能态,旋转会重新出现。该顺序已写进 -`docs/子系统设计/ADC采样节拍设计说明.md` 的设计铁律第 8 条,并由 CubeMX 存活审计项 +`docs/子系统设计/ADC采样节拍设计.md` 的设计铁律第 8 条,并由 CubeMX 存活审计项 `adc1-calibration-impl`(整段守护 `USER CODE BEGIN 1`)的基线漂移兜底。 **回归判据**: @@ -139,7 +139,7 @@ DMA 从序列中途开始搬运时,整个缓冲一起旋转,拷贝照样"恒 ## 相关文档 - 设计侧口径、寄存器配置、9 行证据表的原始记录与复现方法: - `docs/子系统设计/ADC采样节拍设计说明.md` §4.5(设计铁律第 8 条)。 + `docs/子系统设计/ADC采样节拍设计.md` §4.5(设计铁律第 8 条)。 - 通道映射与引脚对应:`docs/硬件平台/ADC通道映射说明.md`。 - 前端拓扑(③ 段变比 = 1:1,非分压):`docs/硬件平台/V0.0_网表详细分析报告.md` §4.7。 - 零点/线性/映射的现场验证步骤与判据:`docs/测试说明/专项验证/ADC前端注入验证流程.md`。 diff --git a/scripts/archive/id_gate/id_aliases.json b/scripts/archive/id_gate/id_aliases.json index be980917..cd792a82 100644 --- a/scripts/archive/id_gate/id_aliases.json +++ b/scripts/archive/id_gate/id_aliases.json @@ -3281,7 +3281,7 @@ "N-24": { "to": null, "status": "void", - "note": "登记簿 N-24 = 报告 N-16(日志读写放大:写侧合并 open/write/close + 读侧 8 条滑动窗口),2026-10-15 销项删行。结论去处:docs/子系统设计/运行与自检记录设计说明.md §4.4/§4.5 + App/log/sys_log_mcu.c 的 `s_format_rec` / `s_append_rec` / `s_append_many` / `s_read_raw` 注释" + "note": "登记簿 N-24 = 报告 N-16(日志读写放大:写侧合并 open/write/close + 读侧 8 条滑动窗口),2026-10-15 销项删行。结论去处:docs/子系统设计/运行与自检记录设计.md §3.2/§4.1 + App/log/sys_log_mcu.c 的 `s_format_rec` / `s_append_rec` / `s_append_many` / `s_read_raw` 注释" }, "N-26": { "to": null, diff --git a/simulator/sim/sim_syslog.c b/simulator/sim/sim_syslog.c index fabbda56..22b2c9d0 100644 --- a/simulator/sim/sim_syslog.c +++ b/simulator/sim/sim_syslog.c @@ -8,7 +8,7 @@ * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: - * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §4) + * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计.md §4) * v1.1 2026-10-26 王建锋 新增「记录删除」后端(道F task-4,与 sys_log_mcu.c * 同一份 sys_log.h 契约):syslog_request_clear() / * syslog_clear_state()。两端**时序有别、判据相同**: diff --git a/test/host/sys_log_mcu/run_unit_test.py b/test/host/sys_log_mcu/run_unit_test.py index c12c0b57..2e7a6fb8 100644 --- a/test/host/sys_log_mcu/run_unit_test.py +++ b/test/host/sys_log_mcu/run_unit_test.py @@ -44,7 +44,7 @@ r"""运行/自检记录落盘后端(sys_log_mcu)的主机侧纯逻辑单元 退出码:0=PASS / 1=测试失败 / 2=环境错误(找不到编译器 / 编译失败 / 无汇总行)。 -设计依据:docs/子系统设计/运行与自检记录设计说明.md §4.4/§4.5/§5.3、 +设计依据:docs/子系统设计/运行与自检记录设计.md §3.2/§4.1/§5.1、 docs/架构设计/STM32F4-Base存储架构说明.md §10.1 第 10/11 条、 docs/测试说明/单元测试/主机单测覆盖率口径.md §9 P2 第 2 项 创建日期:2026-10-24