merge: task/docfix-doc-truth-sync 合入 v2-on-ui

任务 docfix(1 个提交)经 task-worktree 回收:
- docs(真值): 勘查 13 条文档/代码不一致全部处置(10 改 3 核实)+ 补 3 处
This commit is contained in:
2026-10-30 14:22:07 +08:00
24 changed files with 61 additions and 55 deletions
+3 -3
View File
@@ -750,7 +750,7 @@ repos:
# P1 新增的 12 套不在任何提交钩子里。原先"再加一份全量插桩等于同一件事做两遍" # P1 新增的 12 套不在任何提交钩子里。原先"再加一份全量插桩等于同一件事做两遍"
# 这条理由到现在已经**反了**(现状是有一遍没做),所以必须挂;缺口此前如实登记在 # 这条理由到现在已经**反了**(现状是有一遍没做),所以必须挂;缺口此前如实登记在
# docs/测试说明/单元测试/主机单测覆盖率口径.md §8。 # docs/测试说明/单元测试/主机单测覆盖率口径.md §8。
# 为什么挂这一根而不是补 12 个 per-module 钩子:本钩子当时跑**全部 28 套**(现值 44 套,台账见 # 为什么挂这一根而不是补 12 个 per-module 钩子:本钩子当时跑**全部 28 套**(现值 47 套,台账见
# docs/门禁说明/host-unit-tests.md §6 第 4 条),并且守 §5 的 # docs/门禁说明/host-unit-tests.md §6 第 4 条),并且守 §5 的
# 回退台账(某套被删、或某文件的已覆盖行变少 → [REGRESS] 直接红)。这是 per-module # 回退台账(某套被删、或某文件的已覆盖行变少 → [REGRESS] 直接红)。这是 per-module
# 钩子做不到的——删掉一套测试,它那根钩子只会因为"没有文件要检查"而静默跳过。 # 钩子做不到的——删掉一套测试,它那根钩子只会因为"没有文件要检查"而静默跳过。
@@ -761,8 +761,8 @@ repos:
# 口径 / 分母 / 基线 / 盲区详解都在 docs/测试说明/单元测试/主机单测覆盖率口径.md,本处只留接线事实。 # 口径 / 分母 / 基线 / 盲区详解都在 docs/测试说明/单元测试/主机单测覆盖率口径.md,本处只留接线事实。
# 触发面**含门禁自身**(.pre-commit-config.yaml):不带自身就等于谁改坏这根闸的 files: # 触发面**含门禁自身**(.pre-commit-config.yaml):不带自身就等于谁改坏这根闸的 files:
# 模式,它就再也不跑且无人察觉(同 lv-conf-sync / bsp-err 的既有做法)。 # 模式,它就再也不跑且无人察觉(同 lv-conf-sync / bsp-err 的既有做法)。
# ⚠ stages: [manual](2026-10-28 用户裁定):本钩子跑光全部 44 套单测(41 套时实测 ~105 s,套件数 # ⚠ stages: [manual](2026-10-28 用户裁定):本钩子跑光全部 47 套单测(41 套时实测 ~105 s,套件数
# 涨了耗时就涨,44 套这轮未重测耗时; # 涨了耗时就涨,47 套这轮未重测耗时;
# 占一次带代码提交耗时的三分之二)→ 不压在提交期,由**远端 CI** 强制 # 占一次带代码提交耗时的三分之二)→ 不压在提交期,由**远端 CI** 强制
# (.gitea/workflows/ci.yml 的「手动阶段门禁(覆盖率总闸 + 全量 cppcheck)」步),本地等价入口 = # (.gitea/workflows/ci.yml 的「手动阶段门禁(覆盖率总闸 + 全量 cppcheck)」步),本地等价入口 =
# scripts/ci_local.py(manual 段);手工复跑: # scripts/ci_local.py(manual 段);手工复跑:
+5 -5
View File
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -194,7 +194,9 @@ int app_main_init(void) {
syslog_post(SYSLOG_SELFTEST, "clock chip fault"); syslog_post(SYSLOG_SELFTEST, "clock chip fault");
} }
/* 2) RS-485 初始化(UART4 + PD0 方向控制),初始化即自动开启接收 */ /* 2) RS-485 初始化(UART4 + 方向脚 = ST_DIR4 宏:V0.1 板实测 PA15,V0.0 曾是 PD0,
* 轮换记录 = docs/硬件平台/V0.1_网表详细分析报告.md 的 V0.0→V0.1 引脚迁移表),
* 初始化即自动开启接收 */
rs485_init(&g_rs485, &huart4, ST_DIR4_GPIO_Port, ST_DIR4_Pin); rs485_init(&g_rs485, &huart4, ST_DIR4_GPIO_Port, ST_DIR4_Pin);
DBG_INFO("RS485 init: OK (UART4, DE=PA15)"); DBG_INFO("RS485 init: OK (UART4, DE=PA15)");
+4 -3
View File
@@ -22,11 +22,12 @@ extern "C" {
* 由 MX_FREERTOS_Init 在 freertos.c 的 generated 区域自动声明并 osEventFlagsNew 创建) */ * 由 MX_FREERTOS_Init 在 freertos.c 的 generated 区域自动声明并 osEventFlagsNew 创建) */
extern osEventFlagsId_t sys_evtHandle; extern osEventFlagsId_t sys_evtHandle;
/* 各子系统"就绪"事件位(32 位上限内的 bit 0~30) */ /* 各子系统"就绪"事件位(32 位上限内的 bit 0~30)。**bit0 / bit1 在用,bit2 / bit3 是预留位**
* ——逐位的生产者与消费者对照表 = docs/架构设计/项目启动顺序管理.md §3.2 */
#define SYS_EVT_NET_READY (1u << 0) /* netTask:net_init + PHY 稳定完成 */ #define SYS_EVT_NET_READY (1u << 0) /* netTask:net_init + PHY 稳定完成 */
#define SYS_EVT_STORAGE_READY (1u << 1) /* defaultTask:FTL/FatFS 挂载完成 */ #define SYS_EVT_STORAGE_READY (1u << 1) /* defaultTask:FTL/FatFS 挂载完成 */
#define SYS_EVT_FTP_READY (1u << 2) /* ftpTask:lftpd_start 完成 */ #define SYS_EVT_FTP_READY (1u << 2) /* ftpTask:lftpd_start 完成后置位;暂无消费者(预留) */
#define SYS_EVT_TIMESYNC_READY (1u << 3) /* timeSyncTask:首次时间同步完成 */ #define SYS_EVT_TIMESYNC_READY (1u << 3) /* 预留:timeSyncTask 在跑但不置本位,生产者消费者都没有 */
#define SYS_EVT_NET_RECONFIG (1u << 4) /* UI:请求立即应用新网口配置(netTask 消费后重建网络) */ #define SYS_EVT_NET_RECONFIG (1u << 4) /* UI:请求立即应用新网口配置(netTask 消费后重建网络) */
#define SYS_EVT_CFG_SAVE (1u << 5) /* UI:网口配置保存请求(defaultTask = 配置引擎属主,消费并写盘) */ #define SYS_EVT_CFG_SAVE (1u << 5) /* UI:网口配置保存请求(defaultTask = 配置引擎属主,消费并写盘) */
#define SYS_EVT_CONSOLE_TX (1u << 6) /* 控制台单写者:生产者入环形缓冲后置位,defaultTask 消费并发送 #define SYS_EVT_CONSOLE_TX (1u << 6) /* 控制台单写者:生产者入环形缓冲后置位,defaultTask 消费并发送
+5 -5
View File
@@ -59,11 +59,11 @@
| 记录 | 日志 ID / 文件 | 容量上限 | 写入点 | 触发判据 | | 记录 | 日志 ID / 文件 | 容量上限 | 写入点 | 触发判据 |
|---|---|---|---|---| |---|---|---|---|---|
| 事故 | `SYSLOG_ACCIDENT` / `accid.log` | 512 | `App/task/protect_task.c:181` | `asserted` 上升沿**且**该元件跳闸投退项 = 1 | | 事故 | `SYSLOG_ACCIDENT` / `accid.log` | 512 | `App/task/protect_task.c:188` | `asserted` 上升沿**且**该元件跳闸投退项 = 1 |
| 保护告警 | `SYSLOG_ALARM` / `alarm.log` | 1024 | 同文件 `184` | `asserted` 上升沿**且**跳闸投退项 = 0 或该元件无此项 | | 保护告警 | `SYSLOG_ALARM` / `alarm.log` | 1024 | 同文件 `191` | `asserted` 上升沿**且**跳闸投退项 = 0 或该元件无此项 |
| 保护启动 | `SYSLOG_START` / `start.log` | 1024 | 同文件 `176` | `picked` 上升沿(越限电平,早于/等于动作) | | 保护启动 | `SYSLOG_START` / `start.log` | 1024 | 同文件 `183` | `picked` 上升沿(越限电平,早于/等于动作) |
| 操作 | `SYSLOG_OPER` / `oper.log` | 512 | `App/protocol/iec104/iec104_pts.c:574/602`、`App/task/default_task.c:242`、`App/log/sys_log_mcu.c:646`(宿主 `sim_syslog.c:245`) | 强制复归受理 / 软压板投退落地 / 定值**真落盘成功** / 清除记录成功 | | 操作 | `SYSLOG_OPER` / `oper.log` | 512 | `App/protocol/iec104/iec104_pts.c:582/610`、`App/task/default_task.c:266`、`App/log/sys_log_mcu.c:682`(宿主 `sim_syslog.c:245`) | 强制复归受理 / 软压板投退落地 / 定值**真落盘成功** / 清除记录成功 |
| 遥控 | `SYSLOG_YK` / `yk.log` | 256 | `App/protocol/iec104/iec104_pts.c:573/576/582/586/596/598/609` | 104 双点命令的选择段、执行段、非法 DPC、未知 IOA 各一条 | | 遥控 | `SYSLOG_YK` / `yk.log` | 256 | `App/protocol/iec104/iec104_pts.c:581/584/590/594/604/606/617` | 104 双点命令的选择段、执行段、非法 DPC、未知 IOA 各一条 |
- **"哪些元件算跳闸"只有一个真源**:定值表 `g_prot_set_meta` 里的 `PS_*_TRIP`(枚举注释 - **"哪些元件算跳闸"只有一个真源**:定值表 `g_prot_set_meta` 里的 `PS_*_TRIP`(枚举注释
即"0 告警 / 1 跳闸")+ 实例表 `k_pe[]` 的 `trip_ctl` 列。分类入口 即"0 告警 / 1 跳闸")+ 实例表 `k_pe[]` 的 `trip_ctl` 列。分类入口
+2 -1
View File
@@ -11,7 +11,8 @@
* 时基 = 调用方注入 now_ms(tick ms,回绕安全:差值法) * 时基 = 调用方注入 now_ms(tick ms,回绕安全:差值法)
* → 全栈(asdu+pts+link)主机侧纯逻辑单测可离线跑(分层约束)。 * → 全栈(asdu+pts+link)主机侧纯逻辑单测可离线跑(分层约束)。
* 参数(DL/T 634.5104 标准默认,§1.5 未另定):k=12 w=8 t1=15s t2=10s t3=20s。 * 参数(DL/T 634.5104 标准默认,§1.5 未另定):k=12 w=8 t1=15s t2=10s t3=20s。
* 端口:TCP **2404**(§1.5 B0 定义;本 DTU = 被控站/服务器,主站连入)。 * 端口:TCP **2404** = 工厂默认(§1.5 B0 定义 / IEC 60870-5-104 标准端口;
* 本 DTU = 被控站/服务器,主站连入)。真源与可改性见本文件下方 [comm] 段说明。
* 线程约定:单实例单任务上下文(板端 = 104 服务在 netTask 消息循环内驱动, * 线程约定:单实例单任务上下文(板端 = 104 服务在 netTask 消息循环内驱动,
* 见 iec104_serv 胶水层);非线程安全,禁止多任务共享一实例。 * 见 iec104_serv 胶水层);非线程安全,禁止多任务共享一实例。
* 失败语义:send_apdu 内**不得阻塞**;发送失败由胶水层断链(主站会重连), * 失败语义:send_apdu 内**不得阻塞**;发送失败由胶水层断链(主站会重连),
+3 -2
View File
@@ -235,8 +235,9 @@ static int ui_task_maint_reset_req(void) {
* 返回值:位图中置 1 的位数(纯 RAM 读,无锁) uint32_t * 返回值:位图中置 1 的位数(纯 RAM 读,无锁) uint32_t
* 限定条件:uiTask 绘制路径每帧可直调 * 限定条件:uiTask 绘制路径每帧可直调
* 函数说明:给个数而不是位图,是为了让 160x160 上一行放得下、也让"复归前 N>0 → * 函数说明:给个数而不是位图,是为了让 160x160 上一行放得下、也让"复归前 N>0 →
* 复归后 N=0"这个台架判据肉眼可读;判据前提(产品构建处于未标定闭锁, * 复归后 N=0"这个台架判据肉眼可读;判据前提(产品构建处于未标定闭锁——
* App/task/protect_task.c:98 停走判据 ⇒ 闭锁期 N 恒 0)已写进页面注释 * App/task/protect_task.c:268 判停,判定本体 = adc_cal_prot_blocked(),
* 闭锁期 N 恒 0)已写进页面注释
*/ */
static uint32_t ui_task_maint_active_count(void) { static uint32_t ui_task_maint_active_count(void) {
uint32_t mask = prot_elm_active_mask(); uint32_t mask = prot_elm_active_mask();
+1 -1
View File
@@ -84,7 +84,7 @@ main.c
| uiTask | 4096 words | BelowNormal | LVGL UI 界面 | | uiTask | 4096 words | BelowNormal | LVGL UI 界面 |
| timeSyncTask | 640 words | Normal | 墙钟时间同步 | | timeSyncTask | 640 words | Normal | 墙钟时间同步 |
| adcTask | 640 words | AboveNormal | TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA,纯消费者) | | adcTask | 640 words | AboveNormal | TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA,纯消费者) |
| iec104Task | 640 words | Normal | IEC 60870-5-104 上行(:2404 监听,遥信/遥测/定值区) | | iec104Task | 640 words | Normal | IEC 60870-5-104 上行(工厂默认 :2404 监听,遥信/遥测/定值区) |
| protectTask | 512 words | Normal | 保护判据 20ms 节拍(保护判据元件 + 采样故障闭锁,B5) | | protectTask | 512 words | Normal | 保护判据 20ms 节拍(保护判据元件 + 采样故障闭锁,B5) |
<!-- TASK_TABLE:END --> <!-- TASK_TABLE:END -->
+1 -1
View File
@@ -39,7 +39,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓
| 刷新周期 | 10 ms(`ui_port_step(10)` + `osDelay(10)`) | `ui_task.c` | | 刷新周期 | 10 ms(`ui_port_step(10)` + `osDelay(10)`) | `ui_task.c` |
| 推流端口 | 7003(TCP,与 DTU-RemoteLCD 客户端对齐) | `ui_net_sink.h` | | 推流端口 | 7003(TCP,与 DTU-RemoteLCD 客户端对齐) | `ui_net_sink.h` |
| 默认字体 | `ui_font_cn_12`(12px 1bpp,224 汉字/符号 + ASCII 0x20–0x7E,共 319 字形) | `Lib/lv_conf.h` | | 默认字体 | `ui_font_cn_12`(12px 1bpp,224 汉字/符号 + ASCII 0x20–0x7E,共 319 字形) | `Lib/lv_conf.h` |
| uiTask 栈 | 16384 words(BelowNormal) | `Src/freertos.c` | | uiTask 栈 | 4096 words = 16384 B(BelowNormal) | `Src/freertos.c:218`(`.stack_size = 4096 * 4`) |
### 1.4 硬约束(改 UI 必须遵守) ### 1.4 硬约束(改 UI 必须遵守)
+1 -1
View File
@@ -33,7 +33,7 @@
<rect x="428" y="557" width="30" height="30" rx="7" fill="#94a3b8"/> <rect x="428" y="557" width="30" height="30" rx="7" fill="#94a3b8"/>
<text x="443" y="577" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">3</text> <text x="443" y="577" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">3</text>
<text x="470" y="566" font-size="14" font-weight="700" fill="#94a3b8">bit3 · TIMESYNC_READY</text> <text x="470" y="566" font-size="14" font-weight="700" fill="#94a3b8">bit3 · TIMESYNC_READY</text>
<text x="470" y="588" font-size="12" fill="#64748b">预留(尚未接入调度)</text> <text x="470" y="588" font-size="12" fill="#64748b">预留(无生产者置位)</text>
<rect x="40" y="200" width="290" height="84" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.5"/> <rect x="40" y="200" width="290" height="84" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.5"/>
<text x="56" y="234" font-size="15" font-weight="700" fill="#0f172a">defaultTask</text> <text x="56" y="234" font-size="15" font-weight="700" fill="#0f172a">defaultTask</text>
<text x="56" y="258" font-size="12" fill="#64748b">卷挂载 + net_cfg_load(f_mount)</text> <text x="56" y="258" font-size="12" fill="#64748b">卷挂载 + net_cfg_load(f_mount)</text>

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

+1 -1
View File
@@ -67,7 +67,7 @@
<rect x="887" y="580" width="186" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/> <rect x="887" y="580" width="186" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
<text x="980" y="603" font-size="13" fill="#1e293b" text-anchor="middle">nand_ftl(dhara FTL)</text> <text x="980" y="603" font-size="13" fill="#1e293b" text-anchor="middle">nand_ftl(dhara FTL)</text>
<rect x="1083" y="580" width="164" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/> <rect x="1083" y="580" width="164" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
<text x="1165" y="603" font-size="13" fill="#1e293b" text-anchor="middle">LVGL v9.2(图形库)</text> <text x="1165" y="603" font-size="13" fill="#1e293b" text-anchor="middle">LVGL v9.3(图形库)</text>
<rect x="1258" y="580" width="177" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/> <rect x="1258" y="580" width="177" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
<text x="1347" y="603" font-size="13" fill="#1e293b" text-anchor="middle">sys_clock(墙钟基准)</text> <text x="1347" y="603" font-size="13" fill="#1e293b" text-anchor="middle">sys_clock(墙钟基准)</text>
<rect x="44" y="628" width="219" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/> <rect x="44" y="628" width="219" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+1 -1
View File
@@ -32,7 +32,7 @@ git commit
└─ manual(pre-commit --hook-stage manual;不随 git commit 跑) └─ manual(pre-commit --hook-stage manual;不随 git commit 跑)
├─ cppcheck 全量 C 静态分析(自有代码) ├─ cppcheck 全量 C 静态分析(自有代码)
├─ test-cppcheck test/ 侧静态分析 ├─ test-cppcheck test/ 侧静态分析
└─ test-coverage 主机单测覆盖率总闸(44 套 + 回退台账) └─ test-coverage 主机单测覆盖率总闸(47 套 + 回退台账)
``` ```
**阶段**:`pre-commit` 阶段拦截代码问题、`commit-msg` 阶段拦截格式问题,任一失败 → commit 中止;另有 `manual` 阶段(`stages: [manual]` 的慢钩子)**不随 `git commit` 跑**,由远端 CI 与 `scripts/ci_local.py` 的 manual 段承担(见下)。一次 `git commit` 实跑 **48** 根 = `pre-commit` 阶段 47 根 + `gitlint` 1 根。 **阶段**:`pre-commit` 阶段拦截代码问题、`commit-msg` 阶段拦截格式问题,任一失败 → commit 中止;另有 `manual` 阶段(`stages: [manual]` 的慢钩子)**不随 `git commit` 跑**,由远端 CI 与 `scripts/ci_local.py` 的 manual 段承担(见下)。一次 `git commit` 实跑 **48** 根 = `pre-commit` 阶段 47 根 + `gitlint` 1 根。
+1 -1
View File
@@ -237,7 +237,7 @@ M4 的六条退出判据(逐条对齐现状,别整格打勾):
保护判据拿"ADC 引脚电压"跟"二次值定值"比必然误动作,所以 `protect_task` 多一个闭锁源: 保护判据拿"ADC 引脚电压"跟"二次值定值"比必然误动作,所以 `protect_task` 多一个闭锁源:
**UA / UN / IA 的 `VALID` 未全置位 → 判据停走**(实现已搬进 `App/algo/adc_cal.c` 的 **UA / UN / IA 的 `VALID` 未全置位 → 判据停走**(实现已搬进 `App/algo/adc_cal.c` 的
`adc_cal_prot_blocked()`,`App/task/protect_task.c:245` 消费并打 INFO + 记 SOE)。 `adc_cal_prot_blocked()`,`App/task/protect_task.c:268-273` 消费并打 INFO + 记 SOE)。
- 与"自检失败 / 采样故障闭锁"同构(`PROT_HF_LOCK_MASK` 那一套),**不新增机制**。 - 与"自检失败 / 采样故障闭锁"同构(`PROT_HF_LOCK_MASK` 那一套),**不新增机制**。
- 现有台架 `/serial-boot-test` 的期望会因此变化——**这是有意的**,任务卡里同步更新判据。 - 现有台架 `/serial-boot-test` 的期望会因此变化——**这是有意的**,任务卡里同步更新判据。
+8 -7
View File
@@ -24,7 +24,7 @@
| ① 业务层 | `Drivers/BSP/NETCFG/` | 顶层共享配置(被 UI 与基础服务共用) | `net_cfg`(`/config.ini` `[network]` 段) | | ① 业务层 | `Drivers/BSP/NETCFG/` | 顶层共享配置(被 UI 与基础服务共用) | `net_cfg`(`/config.ini` `[network]` 段) |
| ② UI 层 | `Drivers/BSP/UI/` | LVGL 160×160 单色界面(与 PC 模拟器、屏板、远程显示**共用同一份显存**) | `ui_port` `ui_menu` `ui_menu_view` `ui_menu_tab`(生成物) `ui_content_page` `ui_page_registry`(生成物) `ui_page_*`(20+ 页插件) `ui_data`(数据供给发布 API) `ui_net_sink` `ui_standby` `ui_font_cn_12` `ui_deco` | | ② UI 层 | `Drivers/BSP/UI/` | LVGL 160×160 单色界面(与 PC 模拟器、屏板、远程显示**共用同一份显存**) | `ui_port` `ui_menu` `ui_menu_view` `ui_menu_tab`(生成物) `ui_content_page` `ui_page_registry`(生成物) `ui_page_*`(20+ 页插件) `ui_data`(数据供给发布 API) `ui_net_sink` `ui_standby` `ui_font_cn_12` `ui_deco` |
| ③ 基础服务层 | `App/`、`Drivers/BSP/NET/lftpd/` | 应用侧服务与编排 | `app_main` `sys_events` `ui_data_feed` `task/*`(9 任务) `algo/*`(语义取数与有效性~保护判据 + 遥测快照 + 录波数据面 + 屏板灯位映射) `protocol/iec104` `sys_log`·`soe_*`·`console_log` `cfg_file`·`prot_set_store` `vol_lock` `time_sync` `lftpd` | | ③ 基础服务层 | `App/`、`Drivers/BSP/NET/lftpd/` | 应用侧服务与编排 | `app_main` `sys_events` `ui_data_feed` `task/*`(9 任务) `algo/*`(语义取数与有效性~保护判据 + 遥测快照 + 录波数据面 + 屏板灯位映射) `protocol/iec104` `sys_log`·`soe_*`·`console_log` `cfg_file`·`prot_set_store` `vol_lock` `time_sync` `lftpd` |
| ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `net_events` `net_listener` `net_pending` `FatFs` `nand_ftl(dhara)` `LVGL v9.2` `sys_clock` `task_watchdog` | | ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `net_events` `net_listener` `net_pending` `FatFs` `nand_ftl(dhara)` `LVGL v9.3` `sys_clock` `task_watchdog` |
| ⑤ 驱动层 | `Drivers/BSP/{CH395F,GD5F2GQ5UE,TPAFE5160,SD2506,RS485,ScreenLink}/` | 芯片寄存器 / 时序 / 中断 | `ch395f` `gd5f2gq5ue` `tpafe5160` `sd2506` `rs485` `sb_link`·`sb_proto` | | ⑤ 驱动层 | `Drivers/BSP/{CH395F,GD5F2GQ5UE,TPAFE5160,SD2506,RS485,ScreenLink}/` | 芯片寄存器 / 时序 / 中断 | `ch395f` `gd5f2gq5ue` `tpafe5160` `sd2506` `rs485` `sb_link`·`sb_proto` |
| ⑥ 内核 / HAL | `Middlewares/`、`Drivers/STM32F4xx_HAL_Driver/`、`Drivers/CMSIS/` | 任务调度、外设 HAL、启动 | FreeRTOS V10.3.1 · STM32 HAL · CMSIS | | ⑥ 内核 / HAL | `Middlewares/`、`Drivers/STM32F4xx_HAL_Driver/`、`Drivers/CMSIS/` | 任务调度、外设 HAL、启动 | FreeRTOS V10.3.1 · STM32 HAL · CMSIS |
| ⑦ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART4 / UART5 / USART1 / ADC1 / TIM3 / DMA / GPIO | | ⑦ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART4 / UART5 / USART1 / ADC1 / TIM3 / DMA / GPIO |
@@ -59,7 +59,7 @@
| 任务 | 栈 (字节) | 优先级 | 职责 | 入口 | | 任务 | 栈 (字节) | 优先级 | 职责 | 入口 |
|---|---|---|---|---| |---|---|---|---|---|
| `defaultTask` | 5120 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 卷健康探针/自愈 → `net_cfg_load`(配置引擎属主)→ 置 `STORAGE_READY`(= 存储+配置就绪)→ **`/log` 唯一写者**(`syslog_drain` 尾部统一排空 syslog / SOE 两条队列 → run/selftest/soe 三本日志 + 控制台排空)+ **录波落盘道**(已封存段游标分次搬 `/wave`,轮转号启动扫盘重建)+ 配置保存执行/2s poll + LED 闪烁 + **栈水位观测**(`stack_probe_poll`) | `StartDefaultTask` | | `defaultTask` | 5120 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 卷健康探针/自愈 → `net_cfg_load`(配置引擎属主)→ 置 `STORAGE_READY`(= 存储+配置就绪)→ **`/log` 唯一写者**(`syslog_drain` 尾部统一排空 syslog / SOE 两条队列 → syslog 七本 + `soe.log` + 控制台排空)+ **录波落盘道**(已封存段游标分次搬 `/wave`,轮转号启动扫盘重建)+ 配置保存执行/2s poll + LED 闪烁 + **栈水位观测**(`stack_probe_poll`) | `StartDefaultTask` |
| `netTask` | 3840 | Normal | 等存储(配置已由 defaultTask 就绪,本任务只读 RAM 副本)→ `net_stack_bring_up()`(不触碰配置文件引擎)→ 置 `NET_READY` → 循环 `net_poll` + `net_process_messages`;消费 `SYS_EVT_NET_RECONFIG` → 按网络代次号重建监听(CH395F 硬件层测试模式下入口自挂起) | `StartNetTask` | | `netTask` | 3840 | Normal | 等存储(配置已由 defaultTask 就绪,本任务只读 RAM 副本)→ `net_stack_bring_up()`(不触碰配置文件引擎)→ 置 `NET_READY` → 循环 `net_poll` + `net_process_messages`;消费 `SYS_EVT_NET_RECONFIG` → 按网络代次号重建监听(CH395F 硬件层测试模式下入口自挂起) | `StartNetTask` |
| `ftpTask` | 4096 | Normal | 等网络**且**存储(`WaitAll`)→ `lftpd_start("/", 21)` → 单客户端会话循环(断开后经 `net_listener` 重建监听);录波段文件的取回通道 | `StartFtpTask` | | `ftpTask` | 4096 | Normal | 等网络**且**存储(`WaitAll`)→ `lftpd_start("/", 21)` → 单客户端会话循环(断开后经 `net_listener` 重建监听);录波段文件的取回通道 | `StartFtpTask` |
| `testTask` | 8192 | BelowNormal | 测试套件入口(`test/board/test_config.h` 选择)。**产品构建不创建**:CubeMX 生成区无条件建后,`MX_FREERTOS_Init()` 在未选套件时(`!TEST_SUITE_SELECTED`)立即 `vTaskDelete` 回收其 8KB CCM 栈(2026-10-04) | `start_test_task` | | `testTask` | 8192 | BelowNormal | 测试套件入口(`test/board/test_config.h` 选择)。**产品构建不创建**:CubeMX 生成区无条件建后,`MX_FREERTOS_Init()` 在未选套件时(`!TEST_SUITE_SELECTED`)立即 `vTaskDelete` 回收其 8KB CCM 栈(2026-10-04) | `start_test_task` |
@@ -189,7 +189,7 @@ flowchart LR
``` ```
- **配置文件**:通用 `/config.ini`(cfg_file 引擎,静态态单任务收敛于 defaultTask,外部变更 4s 沉降后自动重载),**多段注册制**:`[network]`(net_cfg)/ `[ftp]`(鉴权)/ `[adc_cal]`(标定系数与零点)/ `[run_par]`(运行参数 + 104 地址映射真源)/ `[comm]`(104 ca/端口);新模块加段=注册 `cfg_file` 段处理器,唯一落盘者不变。旧版 NCFG 二进制(`/cfg/netcfg.bin`)已不再读取(2026-10-10 删迁移),存在则忽略。→ `docs/子系统设计/配置文件设计说明.md` - **配置文件**:通用 `/config.ini`(cfg_file 引擎,静态态单任务收敛于 defaultTask,外部变更 4s 沉降后自动重载),**多段注册制**:`[network]`(net_cfg)/ `[ftp]`(鉴权)/ `[adc_cal]`(标定系数与零点)/ `[run_par]`(运行参数 + 104 地址映射真源)/ `[comm]`(104 ca/端口);新模块加段=注册 `cfg_file` 段处理器,唯一落盘者不变。旧版 NCFG 二进制(`/cfg/netcfg.bin`)已不再读取(2026-10-10 删迁移),存在则忽略。→ `docs/子系统设计/配置文件设计说明.md`
- **记录三本**:`sys_log` 引擎写定长 64B 文本记录到 `/log/run.log` 与 `/log/selftest.log`;`soe_store` 同先例落 `/log/soe.log`(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` - **录波段文件**:`/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`。 - **实时调试流** = 控制台单写者日志(**UART5**/RS232#1,4KB 环形缓冲 + defaultTask 唯一发送;2026-10-29 因屏板占用 USART1 自 UART5 定态),详见 `docs/子系统设计/控制台单写者日志设计说明.md`。
- NAND 坏块表(BBT)持久化在保留块池,不交给 dhara 管理。 - NAND 坏块表(BBT)持久化在保留块池,不交给 dhara 管理。
@@ -327,14 +327,15 @@ STM32F4-Base/
│ ├── UI/ LVGL UI(与模拟器共用):ui_port / ui_menu* / ui_content_page / ui_page_*(20+ 页)/ │ ├── UI/ LVGL UI(与模拟器共用):ui_port / ui_menu* / ui_content_page / ui_page_*(20+ 页)/
│ │ ui_page_registry(生成)/ ui_data / ui_net_sink / ui_standby / ui_font_cn_12 / ui_pwd_def.h │ │ ui_page_registry(生成)/ ui_data / ui_net_sink / ui_standby / ui_font_cn_12 / ui_pwd_def.h
│ └── dbg_log.h/dbg_cfg.h/app_assert.h/bsp_err.h 分级日志与 BSP 契约 │ └── dbg_log.h/dbg_cfg.h/app_assert.h/bsp_err.h 分级日志与 BSP 契约
├── Lib/ 第三方:LVGL v9.2、FatFs、dhara + lv_conf.h(MCU 端) ├── Lib/ 第三方:LVGL v9.3、FatFs、dhara + lv_conf.h(MCU 端)
├── Middlewares/ FreeRTOS V10.3.1(ST 官方中间件) ├── Middlewares/ FreeRTOS V10.3.1(ST 官方中间件)
├── Drivers/STM32F4xx_HAL_Driver/ + Drivers/CMSIS/ ST HAL / CMSIS(裁剪版) ├── Drivers/STM32F4xx_HAL_Driver/ + Drivers/CMSIS/ ST HAL / CMSIS(裁剪版)
├── MDK-ARM/ Keil 工程(uvprojx + scatter + build/download 脚本) ├── MDK-ARM/ Keil 工程(uvprojx + scatter + build/download 脚本)
├── simulator/ PC 模拟器(CMake + SDL2,共用 UI 源码 + tests/ref 像素黄金参考回归) ├── simulator/ PC 模拟器(CMake + SDL2,共用 UI 源码 + tests/ref 像素黄金参考回归)
├── test/ 测试套件(test_config.h 互斥选择,产品构建空转) ├── test/ 测试套件(test_config.h 互斥选择,产品构建空转)
│ ├── board/ 板端套件(.c/.h:CH395F / 存储 / RTC / ADC / RS485 / UI) │ ├── board/ 板端套件:CH395F(硬件层 + NET 层 Phase 6~11 + 链路时延探针)/ GD5F /
│ └── host/ 主机侧单测与脚本(44 套带 run_unit_test.py,覆盖率总闸全量真跑) │ │ 存储性能 / SD2506 RTC(逐文件清单 = `test/board/README.md`;无 ADC / RS485 / UI 板端套件)
│ └── host/ 主机侧单测与脚本(47 套带 run_unit_test.py,覆盖率总闸全量真跑)
├── ui_menu_tree.txt 菜单树声明源(生成器输入,仓根) ├── ui_menu_tree.txt 菜单树声明源(生成器输入,仓根)
├── ui_cn_chars.txt 中文字库输入(生成器输入,仓根) ├── ui_cn_chars.txt 中文字库输入(生成器输入,仓根)
├── tools/ font_gen / menu_gen / arch_diagram(文档配图生成器)/ refgraph(引用图)/ ├── tools/ font_gen / menu_gen / arch_diagram(文档配图生成器)/ refgraph(引用图)/
@@ -383,7 +384,7 @@ flowchart LR
| ADC 通道映射(网名/板级/通道号 13 路置换) | `docs/硬件平台/ADC通道映射说明.md` | | ADC 通道映射(网名/板级/通道号 13 路置换) | `docs/硬件平台/ADC通道映射说明.md` |
| 故障录波(数据面/段格式/回放契约) | `docs/子系统设计/故障录波设计说明.md` | | 故障录波(数据面/段格式/回放契约) | `docs/子系统设计/故障录波设计说明.md` |
| 屏板链路(USART1 主出口 / 帧 A 帧 B / 实测证据) | `docs/子系统设计/屏板链路设计说明.md` | | 屏板链路(USART1 主出口 / 帧 A 帧 B / 实测证据) | `docs/子系统设计/屏板链路设计说明.md` |
| IEC 60870-5-104 上行(:2404) | `docs/子系统设计/IEC104上行设计说明.md` | | IEC 60870-5-104 上行(默认 :2404,可经 `[comm]` 改) | `docs/子系统设计/IEC104上行设计说明.md` |
| 配置文件引擎(/config.ini 多段注册制) | `docs/子系统设计/配置文件设计说明.md` | | 配置文件引擎(/config.ini 多段注册制) | `docs/子系统设计/配置文件设计说明.md` |
| 运行与自检记录(64B 记录 + SOE) | `docs/子系统设计/运行与自检记录设计说明.md` | | 运行与自检记录(64B 记录 + SOE) | `docs/子系统设计/运行与自检记录设计说明.md` |
| 墙钟时间维护 | `docs/子系统设计/系统墙钟时间维护说明.md` | | 墙钟时间维护 | `docs/子系统设计/系统墙钟时间维护说明.md` |
@@ -56,7 +56,7 @@
## 3. 怎么跑 ## 3. 怎么跑
```powershell ```powershell
# 全量:44 套单测逐套"编译(带 PDB)→ 插桩运行 → 解析 Cobertura",最后并集 + 比基线 # 全量:47 套单测逐套"编译(带 PDB)→ 插桩运行 → 解析 Cobertura",最后并集 + 比基线
& '.venv\Scripts\python.exe' scripts/check_test_coverage.py & '.venv\Scripts\python.exe' scripts/check_test_coverage.py
# 只跑一套(调试用;子集会跳过"在测文件消失"类回退判定,见 §5) # 只跑一套(调试用;子集会跳过"在测文件消失"类回退判定,见 §5)
@@ -78,7 +78,7 @@
编译失败、XML 缺失)/ `3` 用法错误。全量实测约 **67 s**(33 套 × 编译 + 插桩运行; 编译失败、XML 缺失)/ `3` 用法错误。全量实测约 **67 s**(33 套 × 编译 + 插桩运行;
2026-10-25 实测;16 套时约 25 s、28 套时约 45 s,套件数涨了耗时就涨,别拿旧数当预算)。最新一次实测: 2026-10-25 实测;16 套时约 25 s、28 套时约 45 s,套件数涨了耗时就涨,别拿旧数当预算)。最新一次实测:
2026-10-28 = 41 套 **~105 s**(该钩子同日移入 `stages: [manual]`,由远端 CI 与 `ci_local.py` 的 manual 段强制); 2026-10-28 = 41 套 **~105 s**(该钩子同日移入 `stages: [manual]`,由远端 CI 与 `ci_local.py` 的 manual 段强制);
套件数现值 **44 套**(2026-10-29 实测,台账见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条),**44 套这一轮的总闸耗时未重测**, 套件数现值 **47 套**(2026-10-30 实测,台账见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条),**47 套这一轮的总闸耗时未重测**,
上面那三个耗时都是当时值,别当现值预算。 上面那三个耗时都是当时值,别当现值预算。
套件数的现值与逐个来历见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条。 套件数的现值与逐个来历见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条。
@@ -156,7 +156,7 @@
宁可红,也不给可疑的数字。 宁可红,也不给可疑的数字。
6. **只覆盖跑到的进程树。** 一次 `collect` 只看一次运行;没跑到的套件不进分母 6. **只覆盖跑到的进程树。** 一次 `collect` 只看一次运行;没跑到的套件不进分母
(全量跑时以"模块未跑到"判 REGRESS),子集运行明确降级(§5)。 (全量跑时以"模块未跑到"判 REGRESS),子集运行明确降级(§5)。
7. **头文件里的代码归 `.h`。** 统计面含 `.h`(静态内联与被展开的行落在头文件上);当前 44 套 7. **头文件里的代码归 `.h`。** 统计面含 `.h`(静态内联与被展开的行落在头文件上);当前 47 套
几乎没触及 `.h`,表里看不到它们——不要以为头文件"天生 100%"。 几乎没触及 `.h`,表里看不到它们——不要以为头文件"天生 100%"。
8. **依赖 `Microsoft.CodeCoverage.Console.exe`。** 探测顺序:`DTU_MS_COVERAGE`(设了就必须 8. **依赖 `Microsoft.CodeCoverage.Console.exe`。** 探测顺序:`DTU_MS_COVERAGE`(设了就必须
有效、**不静默回退**)→ 各 VS 安装根的 `Common7\IDE\Extensions\Microsoft\CodeCoverage.Console` → 有效、**不静默回退**)→ 各 VS 安装根的 `Common7\IDE\Extensions\Microsoft\CodeCoverage.Console` →
+4 -4
View File
@@ -21,11 +21,11 @@
| **温度** | V_ZL2 | 温度 | 温度检测模拟采样 | | **温度** | V_ZL2 | 温度 | 温度检测模拟采样 |
| 备用 | AIN5 / AIN6 / AIN7 / AIN12 / AIN13 / AIN14 / AIN15 / V_ZL1 | — | **未使用**(悬空或不用),软件侧一律标 `RESERVED`,不参与采集与上报 | | 备用 | AIN5 / AIN6 / AIN7 / AIN12 / AIN13 / AIN14 / AIN15 / V_ZL1 | — | **未使用**(悬空或不用),软件侧一律标 `RESERVED`,不参与采集与上报 |
**设计口径(重要,2026-10-10 更正)**:本产品的**电能计量在子板(计量模块)完成**,主板 ADC 只做**监视 / 辅助测量**。但**"8 路电气量被分到两片转换器上"(UA/UN/IA 走 TPAFE5160,UB/UC/IB/IC/IN 走 STM32 12 位 ADC)是 V0.1 的硬件缺陷**——建档时(2026-10-09)写的"有意分配、不要按'8 路电气量应全部走 TPAFE'去修正"**说法作废**:该分配**就是需要修正的对象**,已列入下一版硬件(V0.2)改版项(登记簿 **N-18**)。 **设计口径(重要,2026-10-10 更正)**:本产品的**电能计量在子板(计量模块)完成**,主板 ADC 只做**监视 / 辅助测量**。但**"8 路电气量被分到两片转换器上"(UA/UN/IA 走 TPAFE5160,UB/UC/IB/IC/IN 走 STM32 12 位 ADC)是 V0.1 的硬件缺陷**——建档时(2026-10-09)写的"有意分配、不要按'8 路电气量应全部走 TPAFE'去修正"**说法作废**:该分配**就是需要修正的对象**,已列入下一版硬件(V0.2)改版项(登记簿条目「双转换器无共同采样触发」)。
**缺陷实质(为什么必须改)**: **缺陷实质(为什么必须改)**:
1. **跨转换器通道没有同一时刻性**:TPAFE5160 是 8 通道**同步**采样,STM32 ADC 是**顺序**扫描(9 通道一轮 ≈ 67 µs),且两片转换器**没有共同采样触发**(CONVST 只打 TPAFE,ADC1 为软件触发)。于是 UA↔UB、IA↔IB 这类同族量之间**相位关系不确定**,主板侧任何功率 / 功率因数 / 相角 / 对称分量类运算都不成立。 1. **跨转换器通道没有同一时刻性**:TPAFE5160 是 8 通道**同步**采样,STM32 ADC 是**顺序**扫描(9 通道一轮 ≈ 67 µs),且两片转换器**没有共同采样触发**(CONVST 只打 TPAFE;ADC1 由 TIM3 TRGO 触发——见 §7 与 `Src/adc.c:53`,但 TIM3 与 TPAFE 的 CONVST 之间没有硬件耦合,仍不构成「共同采样触发」)。于是 UA↔UB、IA↔IB 这类同族量之间**相位关系不确定**,主板侧任何功率 / 功率因数 / 相角 / 对称分量类运算都不成立。
2. **精度与量程不一致**:TPAFE 16 位 ±5 V **双极性**(LSB ≈ 152.6 µV,满量程 = 10 V 跨度,口径见 §7.1)vs STM32 12 位 0~3.3 V **单极性**(前端无中点偏置,交流量负半周削顶;LSB ≈ 806 µV)。同为相电压,UA 与 UB/UC 满量程码值差 16 倍、可用象限也不同——同一套算法无法统一处理 8 路电气量。 2. **精度与量程不一致**:TPAFE 16 位 ±5 V **双极性**(LSB ≈ 152.6 µV,满量程 = 10 V 跨度,口径见 §7.1)vs STM32 12 位 0~3.3 V **单极性**(前端无中点偏置,交流量负半周削顶;LSB ≈ 806 µV)。同为相电压,UA 与 UB/UC 满量程码值差 16 倍、可用象限也不同——同一套算法无法统一处理 8 路电气量。
3. **语义与硬件归属交叉**:UA 在 TPAFE、UB 在 STM32,固件映射表 / UI 通道序 / 自检注入都必须逐通道查表绕开这个交叉(本文件 §4、§5 两张表的存在本身就是代价)。 3. **语义与硬件归属交叉**:UA 在 TPAFE、UB 在 STM32,固件映射表 / UI 通道序 / 自检注入都必须逐通道查表绕开这个交叉(本文件 §4、§5 两张表的存在本身就是代价)。
@@ -128,11 +128,11 @@ 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) | | **通道归属(硬件缺陷)** | 8 路电气量分属两片转换器:UA/UN/IA → TPAFE,UB/UC/IB/IC/IN → STM32 ADC | **V0.1 硬件缺陷**(非有意分配,理由见 §1)→ V0.1 上禁止跨转换器做相位/功率/功率因数/矢量类运算;**V0.2 改版要求 8 路电气量全部走 TPAFE**(登记簿条目「双转换器无共同采样触发」) |
| 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 | | 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`)→ 属 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 校准。缺这一步不报错,只是**读数带失调** | | 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 时钟 | 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;语义取数与有效性层 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**) | 数据路径已通(台架 `/serial-boot-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`(640 words / **AboveNormal**,`Src/freertos.c:159 .stack_size = 640 * 4`) | 数据路径已通(台架 `/serial-boot-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(事件驱动唤醒) | | 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/功率需硬件补偏置或明确算法口径 | | 前端偏置 | 每路 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 | | 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 |
@@ -63,7 +63,7 @@
| V7 | **VCAP1/VCAP2(U17.71/106)旁路电容 C94/C79 值未标注**:数据手册要求 VCAP1/VCAP2 各接 1~4.7µF 低 ESR 陶瓷电容至 VSS(内部 1.2V LDO 稳定裕量) | 实测 C94/C79 容值(若为 100nF 则低于手册建议,168MHz 满负荷下 1.2V 轨纹量/稳定性需留意);值补录 BOM | | V7 | **VCAP1/VCAP2(U17.71/106)旁路电容 C94/C79 值未标注**:数据手册要求 VCAP1/VCAP2 各接 1~4.7µF 低 ESR 陶瓷电容至 VSS(内部 1.2V LDO 稳定裕量) | 实测 C94/C79 容值(若为 100nF 则低于手册建议,168MHz 满负荷下 1.2V 轨纹量/稳定性需留意);值补录 BOM |
| V8 | **V15/J3/F3 子板 LED 3 线落在 PC13/PC14/PC15**(脚 7/8/9):备份域电源开关供电,手册 Note 3 限**灌流 ≤3mA、≤2MHz、不可作电流源** | 核子板 LED 驱动电流需求;超 3mA 则 V0.2 移足驱力脚或子板加三极管扩展 | | V8 | **V15/J3/F3 子板 LED 3 线落在 PC13/PC14/PC15**(脚 7/8/9):备份域电源开关供电,手册 Note 3 限**灌流 ≤3mA、≤2MHz、不可作电流源** | 核子板 LED 驱动电流需求;超 3mA 则 V0.2 移足驱力脚或子板加三极管扩展 |
| V9 | **V0.1 主板 SDIO 固定引脚组全被占用**:PC6/PC7/PC8/PC9/PA8(TPAFE 控制)+ PA9(USART1)+ PC10/PC11/PC12/PD2(UART4/UART5)= SDIO 全部 10 脚(F407 SDIO 无重映射) | 架构约束备案:V0.1 主板**无 SDIO 能力**;当前设计不受影响(代码未启用 SDIO,FatFs 卷在 SPI NAND 上) | | V9 | **V0.1 主板 SDIO 固定引脚组全被占用**:PC6/PC7/PC8/PC9/PA8(TPAFE 控制)+ PA9(USART1)+ PC10/PC11/PC12/PD2(UART4/UART5)= SDIO 全部 10 脚(F407 SDIO 无重映射) | 架构约束备案:V0.1 主板**无 SDIO 能力**;当前设计不受影响(代码未启用 SDIO,FatFs 卷在 SPI NAND 上) |
| **V10** | **8 路电气量分属两片转换器(V0.1 硬件缺陷,V0.2 改版项)**:UA/UN/IA 走 TPAFE5160(16 位、同步、±5 V 双极性),UB/UC/IB/IC/IN 走 STM32 ADC(12 位、顺序扫描、0~3.3 V 单极性)→ 跨转换器通道**无同一时刻性**(相位关系不确定)、精度与量程不一致。详见 §6-3 与 [`ADC通道映射说明.md`](ADC通道映射说明.md) §1(2026-10-10 定性更正,登记簿 **N-18**) | **V0.2 改版**:8 路电气量 UA/UB/UC/UN/IA/IB/IC/IN **全部接 TPAFE5160**,与 TPAFE 上 5 路备用路(AIN5/13/7/15/12)对调串联电阻在转换器一侧的目的地——元件数量与料号不变,属走线级改动。V0.1 现板:主板侧只做逐通道监视,**不做**跨转换器的相位/功率/功率因数/矢量运算 | | **V10** | **8 路电气量分属两片转换器(V0.1 硬件缺陷,V0.2 改版项)**:UA/UN/IA 走 TPAFE5160(16 位、同步、±5 V 双极性),UB/UC/IB/IC/IN 走 STM32 ADC(12 位、顺序扫描、0~3.3 V 单极性)→ 跨转换器通道**无同一时刻性**(相位关系不确定)、精度与量程不一致。详见 §6-3 与 [`ADC通道映射说明.md`](ADC通道映射说明.md) §1(2026-10-10 定性更正,登记簿条目「双转换器无共同采样触发」) | **V0.2 改版**:8 路电气量 UA/UB/UC/UN/IA/IB/IC/IN **全部接 TPAFE5160**,与 TPAFE 上 5 路备用路(AIN5/13/7/15/12)对调串联电阻在转换器一侧的目的地——元件数量与料号不变,属走线级改动。V0.1 现板:主板侧只做逐通道监视,**不做**跨转换器的相位/功率/功率因数/矢量运算 |
## 2. 元件级变化 ## 2. 元件级变化
@@ -214,7 +214,7 @@ V0.0 §2.2/§4.7 的 6 路 MCU 控制 LED(实板位号 LED5~LED10,阴极经
1. **TPAFE 8 路 ↔ STM32 ADC 9 路的"归属"也变了**:V0.1 中 TPAFE(U3)采 U5 的 B6/A6/B5/A5/B4/A4/A3/B3,STM32 ADC 采 C13/C9/C10/C14/C12/C11/A8/B7——两族通道数(8+9)不变,成员重分。 1. **TPAFE 8 路 ↔ STM32 ADC 9 路的"归属"也变了**:V0.1 中 TPAFE(U3)采 U5 的 B6/A6/B5/A5/B4/A4/A3/B3,STM32 ADC 采 C13/C9/C10/C14/C12/C11/A8/B7——两族通道数(8+9)不变,成员重分。
2. 网名 AINn 与 ADCn 编号**不再一一对应**(如 AIN1→ADC7),任何按"AINn=ADCn"假设的代码/文档/测试都会静默采错通道——这正是 V0.0 §1.6 警示的"静默死亡"型风险,务必以本表建立映射。 2. 网名 AINn 与 ADCn 编号**不再一一对应**(如 AIN1→ADC7),任何按"AINn=ADCn"假设的代码/文档/测试都会静默采错通道——这正是 V0.0 §1.6 警示的"静默死亡"型风险,务必以本表建立映射。
3. 置换疑似按 U5 连接器物理位置重排走线(TPAFE 8 路恰为 B6→B3 降序)。**2026-10-10 更正**:置换本身(AINn↔ADCn 编号打乱)可视为走线安排,但**"8 路电气量分到 TPAFE 与 STM32 两侧"属 V0.1 硬件缺陷**——2026-10-09 曾按"计量在子板、主板只做监视"把它确认为"有意设计",该定性**作废**。缺陷实质:TPAFE 8 通道同步采样、STM32 ADC 顺序扫描且两者无共同采样触发 → 跨转换器通道**无同一时刻性 / 相位关系不确定**;16 位 ±5 V 双极性 vs 12 位 0~3.3 V 单极性 → 同族量精度与量程不一致。**下一版硬件(V0.2)需把 8 路电气量 UA/UB/UC/UN/IA/IB/IC/IN 全部改接 TPAFE5160**(与 TPAFE 上 5 路备用路对调串联电阻目的地,BOM 不变),登记簿 **N-18**。语义分配(UA/UB/UC/UN、IA/IB/IC/IN、V_ZL2=温度检测,其余 8 路备用)与 17 路端到端映射已固化为 [`docs/硬件平台/ADC通道映射说明.md`](ADC通道映射说明.md)(缺陷实质与改版要求见其 §1);固件通道映射表、UI 通道显示、自检逐通道注入验证(V0.0 §5.1-6)按该表执行。 3. 置换疑似按 U5 连接器物理位置重排走线(TPAFE 8 路恰为 B6→B3 降序)。**2026-10-10 更正**:置换本身(AINn↔ADCn 编号打乱)可视为走线安排,但**"8 路电气量分到 TPAFE 与 STM32 两侧"属 V0.1 硬件缺陷**——2026-10-09 曾按"计量在子板、主板只做监视"把它确认为"有意设计",该定性**作废**。缺陷实质:TPAFE 8 通道同步采样、STM32 ADC 顺序扫描且两者无共同采样触发 → 跨转换器通道**无同一时刻性 / 相位关系不确定**;16 位 ±5 V 双极性 vs 12 位 0~3.3 V 单极性 → 同族量精度与量程不一致。**下一版硬件(V0.2)需把 8 路电气量 UA/UB/UC/UN/IA/IB/IC/IN 全部改接 TPAFE5160**(与 TPAFE 上 5 路备用路对调串联电阻目的地,BOM 不变),登记簿条目「双转换器无共同采样触发」。语义分配(UA/UB/UC/UN、IA/IB/IC/IN、V_ZL2=温度检测,其余 8 路备用)与 17 路端到端映射已固化为 [`docs/硬件平台/ADC通道映射说明.md`](ADC通道映射说明.md)(缺陷实质与改版要求见其 §1);固件通道映射表、UI 通道显示、自检逐通道注入验证(V0.0 §5.1-6)按该表执行。
## 7. 电源/地网络成员变化(拓扑不变) ## 7. 电源/地网络成员变化(拓扑不变)
+1 -1
View File
@@ -85,7 +85,7 @@
| [`setview-check.md`](setview-check.md) | `setview-check` | 定值查看行表与 `prot_set` 枚举序 / `g_prot_set_meta` 上下限是否同一份口径 | `tools/setview_check/**`、`ui_w_setview.*` | 已写 | | [`setview-check.md`](setview-check.md) | `setview-check` | 定值查看行表与 `prot_set` 枚举序 / `g_prot_set_meta` 上下限是否同一份口径 | `tools/setview_check/**`、`ui_w_setview.*` | 已写 |
| (专篇待补,档位与限度正文在 [`../UI设计/UI_自动化测试流程.md`](../UI设计/UI_自动化测试流程.md) §7.3) | `uinav-selftest` | 板端 UI 导航器认屏快检(黄金参考帧分类;完整闭环档=合入前/上板前手工跑,快检不互替) | `scripts/board_ui_nav.py`、`simulator/tests/ref/*.bin` | 指针 | | (专篇待补,档位与限度正文在 [`../UI设计/UI_自动化测试流程.md`](../UI设计/UI_自动化测试流程.md) §7.3) | `uinav-selftest` | 板端 UI 导航器认屏快检(黄金参考帧分类;完整闭环档=合入前/上板前手工跑,快检不互替) | `scripts/board_ui_nav.py`、`simulator/tests/ref/*.bin` | 指针 |
| [`host-unit-tests.md`](host-unit-tests.md) | 22 个 `*-unit-test` | 主机侧单测(同一机制 22 个实例):逻辑层在无硬件的 PC 上编译并跑断言 | 各自被测模块与其测试目录 | 已写 | | [`host-unit-tests.md`](host-unit-tests.md) | 22 个 `*-unit-test` | 主机侧单测(同一机制 22 个实例):逻辑层在无硬件的 PC 上编译并跑断言 | 各自被测模块与其测试目录 | 已写 |
| 详解在 [`../测试说明/单元测试/主机单测覆盖率口径.md`](../测试说明/单元测试/主机单测覆盖率口径.md)(口径 / 分母 / 基线 / 盲区都在那篇,本目录不重复) | `test-coverage` | **覆盖率总闸**:跑全部 44 套主机单测 + 已覆盖行只增不减的回退台账 | `App/`、`Drivers/BSP/`、`test/host/`、脚本与基线 | 已写(在 `docs/测试说明/`;`stages: [manual]`) | | 详解在 [`../测试说明/单元测试/主机单测覆盖率口径.md`](../测试说明/单元测试/主机单测覆盖率口径.md)(口径 / 分母 / 基线 / 盲区都在那篇,本目录不重复) | `test-coverage` | **覆盖率总闸**:跑全部 47 套主机单测 + 已覆盖行只增不减的回退台账 | `App/`、`Drivers/BSP/`、`test/host/`、脚本与基线 | 已写(在 `docs/测试说明/`;`stages: [manual]`) |
| [`trailing-whitespace.md`](trailing-whitespace.md) | `trailing-whitespace` | 行尾空白(改写型;CubeMX/Keil 生成文件豁免) | 全部 | 已写 | | [`trailing-whitespace.md`](trailing-whitespace.md) | `trailing-whitespace` | 行尾空白(改写型;CubeMX/Keil 生成文件豁免) | 全部 | 已写 |
| [`end-of-file-fixer.md`](end-of-file-fixer.md) | `end-of-file-fixer` | 文件末尾必须恰好一个换行(改写型;同豁免) | 全部 | 已写 | | [`end-of-file-fixer.md`](end-of-file-fixer.md) | `end-of-file-fixer` | 文件末尾必须恰好一个换行(改写型;同豁免) | 全部 | 已写 |
| [`check-added-large-files.md`](check-added-large-files.md) | `check-added-large-files` | 别把大文件(日志/镜像/二进制)误提进仓库 | 全部 | 已写 | | [`check-added-large-files.md`](check-added-large-files.md) | `check-added-large-files` | 别把大文件(日志/镜像/二进制)误提进仓库 | 全部 | 已写 |
+7 -7
View File
@@ -91,19 +91,19 @@
清点(2026-10-28 复点、2026-10-29 录波三套入库后再复点:下列数字已按复点值更新;上一版写「16 个模块有专属钩子、另有 12 套、总闸跑 28 套」——三个数都随 2026-10-25 的 清点(2026-10-28 复点、2026-10-29 录波三套入库后再复点:下列数字已按复点值更新;上一版写「16 个模块有专属钩子、另有 12 套、总闸跑 28 套」——三个数都随 2026-10-25 的
六根新钩子与后续新增套件过期了,正是本目录反复在防的"文档与机器面不同码"): 六根新钩子与后续新增套件过期了,正是本目录反复在防的"文档与机器面不同码"):
- **22** 根专属钩子 = 有独立 `files:` 触发面、需要**秒级归因**的那个子集; - **22** 根专属钩子 = 有独立 `files:` 触发面、需要**秒级归因**的那个子集;
- `test/host/` 下带 `run_unit_test.py` 的套件实测 **44** 套(46 个目录,除 `common` 与 - `test/host/` 下带 `run_unit_test.py` 的套件实测 **47** 套(48 个目录,除 `common` 与
`__pycache__`;两个数分别来自 `check_test_coverage.py --list` 与 `ls -d test/host/*/`,2026-10-29 实测) `__pycache__`;两个数分别来自 `check_test_coverage.py --list`(47 行)与 `ls -d test/host/*/`(48 个目录),2026-10-30 复点)
⇒ **没有专属钩子的是 22 套**:`boot_stats`、`cfg_file_store_mcu`、`console_log`、 ⇒ **没有专属钩子的是 25 套**:`boot_stats`、`cfg_file_store_mcu`、`console_log`、
`ftp_cfg`、`iec104_serv`、`lftpd`、`lftpd_inet`、`lftpd_io`、`net_cfg`、`net_events`、 `ftp_cfg`、`iec104_serv`、`lftpd`、`lftpd_inet`、`lftpd_io`、`net_cfg`、`net_events`、
`net_listener`、`net_pending`、`net_select`、`net_socket`、`prot_set_store`、`soe_store`、 `net_listener`、`net_pending`、`net_select`、`net_socket`、`prot_set_store`、`sb_led_map`、`sb_link`、`sb_proto`、`soe_store`、
`sys_log_mcu`、`task_prof`、`ui_sink`、`wave_ring`、`wave_store`、`wave_trig`(末三套 2026-10-29 随 `sys_log_mcu`、`task_prof`、`ui_sink`、`wave_ring`、`wave_store`、`wave_trig`(末三套 2026-10-29 随
故障录波入库,按同一条 Lead 裁定不补专属钩子;22 = 44 套 − 22 根专属钩子,逐点数与两处机器源互证); 故障录波入库,屏板三套(`sb_proto`/`sb_link` 2026-10-29、`sb_led_map` 2026-10-30)随屏板链路入库,同一条 Lead 裁定不补专属钩子;25 = 47 套 − 22 根专属钩子,逐点数与两处机器源互证);
- 这 22 套每次 CI 与 `scripts/ci_local.py` 都由 `test-coverage` **总闸**真跑(该钩子带 `stages: [manual]`: - 这 22 套每次 CI 与 `scripts/ci_local.py` 都由 `test-coverage` **总闸**真跑(该钩子带 `stages: [manual]`:
`git commit` 阶段不跑它,这一根与 `cppcheck` / `test-cppcheck` 同属 CI 与 `ci_local.py` 的 manual 段); `git commit` 阶段不跑它,这一根与 `cppcheck` / `test-cppcheck` 同属 CI 与 `ci_local.py` 的 manual 段);
发现规则是 发现规则是
`HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的主机侧纯逻辑单测同一口径), `HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的主机侧纯逻辑单测同一口径),
退出码语义写死「0 = 全部单测通过且覆盖率相对基线没回退 / 1 = 有单测失败或覆盖率回退」⇒ 退出码语义写死「0 = 全部单测通过且覆盖率相对基线没回退 / 1 = 有单测失败或覆盖率回退」⇒
**失败照样拦(CI 与 `ci_local.py` 的 manual 段)**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(44 套全量比单根慢得多); **失败照样拦(CI 与 `ci_local.py` 的 manual 段)**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(47 套全量比单根慢得多);
- 要不要给某套补第 23 根,判据现成:pre-commit 4.x 没有 `always: true`,**该模块有没有自己的 - 要不要给某套补第 23 根,判据现成:pre-commit 4.x 没有 `always: true`,**该模块有没有自己的
`files:` 触发面**决定补了会不会真跑;只为凑数加一根,等于把「快归因面」和「全量把关」两件事混成 `files:` 触发面**决定补了会不会真跑;只为凑数加一根,等于把「快归因面」和「全量把关」两件事混成
一件,还要让钩子计数再改一次; 一件,还要让钩子计数再改一次;
@@ -113,7 +113,7 @@
实跑数」三处同源同步的计数点——本仓 2026-10-26 到 27 一天之内就为 40 / 50 / 51 这三个数不同源 实跑数」三处同源同步的计数点——本仓 2026-10-26 到 27 一天之内就为 40 / 50 / 51 这三个数不同源
返工过两轮。**要读「这套跑没跑」,读总闸输出,不要来数钩子。** 两个方向的空头账一起堵住: 返工过两轮。**要读「这套跑没跑」,读总闸输出,不要来数钩子。** 两个方向的空头账一起堵住:
① 别把没接线的套件算进钩子账(原写「23 个 / 含 ui-sink」正是这么错的;2026-10-27 复核的准数是 ① 别把没接线的套件算进钩子账(原写「23 个 / 含 ui-sink」正是这么错的;2026-10-27 复核的准数是
22 根 / 40 套 / 18 套无专属钩子(三个数都是当时值)——第 41 套 `boot_stats` 当日 11:29 才入库,此后两数当时是 41 / 19,现值 44 / 22 以本条上面的实测行为准);② 也别把总闸的绿读成「每套都被单独归因过」——它只保证 22 根 / 40 套 / 18 套无专属钩子(三个数都是当时值)——第 41 套 `boot_stats` 当日 11:29 才入库,此后两数当时是 41 / 19,现值 47 / 25 以本条上面的实测行为准);② 也别把总闸的绿读成「每套都被单独归因过」——它只保证
「全跑过 + 覆盖率没回退」。将来确实要给某套补专属钩子,先过上一条判据,并在**同一笔**里改 「全跑过 + 覆盖率没回退」。将来确实要给某套补专属钩子,先过上一条判据,并在**同一笔**里改
AGENTS 与门禁索引的计数(口径 = YAML 解析条目数,见 `docs/门禁说明/README.md` §2 首条)。 AGENTS 与门禁索引的计数(口径 = YAML 解析条目数,见 `docs/门禁说明/README.md` §2 首条)。
- 总闸的触发面是 `App/`、`Drivers/BSP/`、`test/host/`、覆盖率脚本与基线,**外加门禁自身** - 总闸的触发面是 `App/`、`Drivers/BSP/`、`test/host/`、覆盖率脚本与基线,**外加门禁自身**
+1 -1
View File
@@ -44,7 +44,7 @@
| `ci_local.py` | **本地跑与 CI 等价的门禁**(静态门禁 pre-commit 47 根 + **manual 段**(`cppcheck` / `test-cppcheck` / `test-coverage` 三根逐根 `--hook-stage manual`,默认跑)+ 提交信息门禁 + **体积门禁** + 主机侧纯逻辑单测):自动选用装了 pre-commit 的解释器;无 `.map` 时自适应跳过 `mem-numbers` / `mem-budget`(⚠ 那是 SKIP 不是通过,缺口由体积门禁兜住);退出码 0/1/2 与 CI 对齐。CI 定义见 `../.gitea/workflows/ci.yml`,覆盖边界见 `docs/测试说明/流程与规范/测试开发标准流程.md` §9 | | `ci_local.py` | **本地跑与 CI 等价的门禁**(静态门禁 pre-commit 47 根 + **manual 段**(`cppcheck` / `test-cppcheck` / `test-coverage` 三根逐根 `--hook-stage manual`,默认跑)+ 提交信息门禁 + **体积门禁** + 主机侧纯逻辑单测):自动选用装了 pre-commit 的解释器;无 `.map` 时自适应跳过 `mem-numbers` / `mem-budget`(⚠ 那是 SKIP 不是通过,缺口由体积门禁兜住);退出码 0/1/2 与 CI 对齐。CI 定义见 `../.gitea/workflows/ci.yml`,覆盖边界见 `docs/测试说明/流程与规范/测试开发标准流程.md` §9 |
| `check_size_gate.py` | **体积门禁的强制路径**:`.map` 只在本地有,钩子无它时自适应 SKIP,于是"没测=通过"。本脚本用**已提交的同码凭证** `scripts/size_attestation.json` 把这条判据变成无需 Keil 也能跑:`--stamp` 只在"`.map` 存在、比全部受管源码新、且未超 92%"时才盖章(已挂在 `ram_baseline.py --update` 里,无新增人工步骤);`--local` 供 ci_local 的体积门禁步;`--ci` 供 runner(同码性 + 按凭证数字复核红线,主干 push 拦红、其余事件 `--warn-only`);`--show` 排障。**口径与边界的唯一正文在本脚本模块 docstring** | | `check_size_gate.py` | **体积门禁的强制路径**:`.map` 只在本地有,钩子无它时自适应 SKIP,于是"没测=通过"。本脚本用**已提交的同码凭证** `scripts/size_attestation.json` 把这条判据变成无需 Keil 也能跑:`--stamp` 只在"`.map` 存在、比全部受管源码新、且未超 92%"时才盖章(已挂在 `ram_baseline.py --update` 里,无新增人工步骤);`--local` 供 ci_local 的体积门禁步;`--ci` 供 runner(同码性 + 按凭证数字复核红线,主干 push 拦红、其余事件 `--warn-only`);`--show` 排障。**口径与边界的唯一正文在本脚本模块 docstring** |
| `size_attestation.json` | (被 `check_size_gate.py` 读写)体积同码凭证:受管源码指纹(`git ls-files` 范围内逐文件 sha256 聚合)+ 测量数字与占比 + `.map` 出处。改固件源码后不重测 → 指纹对不上 → 体积门禁与 CI 同时拦红 | | `size_attestation.json` | (被 `check_size_gate.py` 读写)体积同码凭证:受管源码指纹(`git ls-files` 范围内逐文件 sha256 聚合)+ 测量数字与占比 + `.map` 出处。改固件源码后不重测 → 指纹对不上 → 体积门禁与 CI 同时拦红 |
| `check_test_coverage.py` | 主机单测**覆盖率**(2026-10-23):mscoverage 动态插桩跑 `test/host/**` 全部 44 套,统计自研生产源码行/函数覆盖(并集)+ 与 `test_coverage_baseline.json` 比回退;`--module/--update-baseline/--md/--json/--list`;挂在 `test-coverage` 钩子上(`stages: [manual]`,**`git commit` 不跑**),入口 = 远端 CI「手动阶段门禁」步 / `scripts/ci_local.py` 的 manual 段(`--coverage` 旗标保留只为兼容旧命令;口径见 `docs/测试说明/单元测试/主机单测覆盖率口径.md`) | | `check_test_coverage.py` | 主机单测**覆盖率**(2026-10-23):mscoverage 动态插桩跑 `test/host/**` 全部 47 套,统计自研生产源码行/函数覆盖(并集)+ 与 `test_coverage_baseline.json` 比回退;`--module/--update-baseline/--md/--json/--list`;挂在 `test-coverage` 钩子上(`stages: [manual]`,**`git commit` 不跑**),入口 = 远端 CI「手动阶段门禁」步 / `scripts/ci_local.py` 的 manual 段(`--coverage` 旗标保留只为兼容旧命令;口径见 `docs/测试说明/单元测试/主机单测覆盖率口径.md`) |
| `ci_gitlint_range.ps1` | CI 侧**提交信息门禁**:逐条校验本次 push/PR 引入的提交(`--all-files` 跑不到 `commit-msg` stage,故必须单独守) | | `ci_gitlint_range.ps1` | CI 侧**提交信息门禁**:逐条校验本次 push/PR 引入的提交(`--all-files` 跑不到 `commit-msg` stage,故必须单独守) |
| `ci_find_python.ps1` | CI 侧 Python 探测(候选顺序须与 `ci_local.py` 同序同步,否则 CI 与本地判定会分叉) | | `ci_find_python.ps1` | CI 侧 Python 探测(候选顺序须与 `ci_local.py` 同序同步,否则 CI 与本地判定会分叉) |
| `ci_runner_bootstrap.ps1` | **本机 runner 运维脚本**(体检 `-Status` / 干跑 `-DryRun` / 部署 `-SetEnv -StartNow`):装"等 Gitea 就绪 + 崩溃自拉起"的看门狗 + 开机计划任务 + 机器级 `DTU_*` | | `ci_runner_bootstrap.ps1` | **本机 runner 运维脚本**(体检 `-Status` / 干跑 `-DryRun` / 部署 `-SetEnv -StartNow`):装"等 Gitea 就绪 + 崩溃自拉起"的看门狗 + 开机计划任务 + 机器级 `DTU_*` |
+1 -1
View File
@@ -76,7 +76,7 @@ ROLE = {
"uiTask": "LVGL UI 界面", "uiTask": "LVGL UI 界面",
"timeSyncTask": "墙钟时间同步", "timeSyncTask": "墙钟时间同步",
"adcTask": "TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA,纯消费者)", "adcTask": "TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA,纯消费者)",
"iec104Task": "IEC 60870-5-104 上行(:2404 监听,遥信/遥测/定值区)", "iec104Task": "IEC 60870-5-104 上行(工厂默认 :2404 监听,遥信/遥测/定值区)",
"protectTask": "保护判据 20ms 节拍(保护判据元件 + 采样故障闭锁,B5)", "protectTask": "保护判据 20ms 节拍(保护判据元件 + 采样故障闭锁,B5)",
} }
+1 -1
View File
@@ -193,7 +193,7 @@
* (s_entry_fill / s_put / syslog_post_quiet / syslog_post_dropped)与 26 行 * (s_entry_fill / s_put / syslog_post_quiet / syslog_post_dropped)与 26 行
* 可执行代码**全部**有断言跑到:覆盖行 +26、函数 +4,未覆盖仍是同一批 3 行老账 * 可执行代码**全部**有断言跑到:覆盖行 +26、函数 +4,未覆盖仍是同一批 3 行老账
* (一条死分支 + sys_log_evidence 里 snprintf<=0),**一条没涨**。 * (一条死分支 + sys_log_evidence 里 snprintf<=0),**一条没涨**。
* ⚠ 全量门禁(44 套并集 + 基线只增不减)由提交前那一次统一跑,本行只是自量记录。 * ⚠ 全量门禁(47 套并集 + 基线只增不减)由提交前那一次统一跑,本行只是自量记录。
* L 族变异实测(2026-10-26,三处同时注入后逐字节还原): * L 族变异实测(2026-10-26,三处同时注入后逐字节还原):
* ① s_paths[] 里 accid 与 alarm 两格**对调**(就是"表序错"那个静默错法); * ① s_paths[] 里 accid 与 alarm 两格**对调**(就是"表序错"那个静默错法);
* ② 给 quiet 的丢弃分支补一条 `DBG_ERROR`(= 悄悄退化成 syslog_post); * ② 给 quiet 的丢弃分支补一条 `DBG_ERROR`(= 悄悄退化成 syslog_post);
+1 -1
View File
@@ -64,7 +64,7 @@ L_SVC = dict(
L_MW = dict( L_MW = dict(
title="④ 中间件层", sub="通用库 / 协议栈", color="#16a34a", bg="#f0fdf4", title="④ 中间件层", sub="通用库 / 协议栈", color="#16a34a", bg="#f0fdf4",
chips=["net_socket / net_select / net_events / net_listener / net_pending(BSD API + 阻塞模型)", chips=["net_socket / net_select / net_events / net_listener / net_pending(BSD API + 阻塞模型)",
"FatFs(文件系统)", "nand_ftl(dhara FTL)", "LVGL v9.2(图形库)", "FatFs(文件系统)", "nand_ftl(dhara FTL)", "LVGL v9.3(图形库)",
"sys_clock(墙钟基准)", "task_watchdog(IWDG 判定)"], "sys_clock(墙钟基准)", "task_watchdog(IWDG 判定)"],
) )
L_DRV = dict( L_DRV = dict(
+1 -1
View File
@@ -60,7 +60,7 @@ BITS = [
dict(bit="bit2", flag="FTP_READY", color="#d97706", bg="#fffbeb", dict(bit="bit2", flag="FTP_READY", color="#d97706", bg="#fffbeb",
note="ftpTask 置位 · 预留消费者"), note="ftpTask 置位 · 预留消费者"),
dict(bit="bit3", flag="TIMESYNC_READY", color="#94a3b8", bg="#f8fafc", dict(bit="bit3", flag="TIMESYNC_READY", color="#94a3b8", bg="#f8fafc",
note="预留(尚未接入调度)"), note="预留(无生产者置位)"),
] ]
# 生产者(左栏):row = 其置位的 bit 行 # 生产者(左栏):row = 其置位的 bit 行