f754d59218
分层:流程与规范/ 单元测试/ 板级测试/ 专项验证/ 测试工具/ 测试数据/, 顶层只留新增的目录导航 README.md(归层判据 + 硬约束 + 本次处置记录)。 - 搬迁 28 个路径:测试数据三个目录 58 个文件为纯改名(R100,内容逐字节不变) - 全库引用同步 124 个文件:AGENTS / docs 索引 / skills / 门禁注释 / scripts - 门禁面:scripts/check_test_traceability.py 的 glob 改 rglob (分层后不递归扫描=追溯门禁直接瞎掉) - scripts/rename_ref.py:收尾的 ℹ 提示在 GBK 代码页抛 UnicodeEncodeError, 让进程以退出码 1 收场而改写其实已落盘(RC 与真实结果相反);改为把 stdout/stderr reconfigure 成 UTF-8,与 check_doc_links.py 等同法 - docs/文档规范.md §1.1 补「子目录文件过多时再分一层」;§3.6 补 rename_ref 搬整目录的两个实测坑:① 会改写目录内部文件的内容 ② 内部文件的 ../ 出链 不补层级(本次 23 个被误改的证据文件已按 HEAD 原始字节逐字节还原) - docs/README.md §6 改为与六个子目录一一对应,并补登原先漏登记的 「启动日志原子性复验流程」一行 - .cubemx_survival_baseline.json:App/util/task_watchdog.c 的注释路径也随分层更新, 该文件是存活审计的 watchdog-module 漂移锚点,按脚本自己的提示跑 --write 重建基线 (实测重建后仅 watchdog-module 一条哈希变化) - 铁律 4 例外(本次在任务树里改了单写者文件):AGENTS.md 与 .pre-commit-config.yaml 只被 rename_ref 改写路径字面量(后者全在注释行), 任务表块与钩子声明一字未动 验证:check_doc_links.py --sections-strict 覆盖全部 88 个改动 .md 全绿; check_test_traceability.py 输出与主树逐行一致;196 个文本文件无 BOM、无新增 CR、无 U+FFFD、无行尾空白;陈旧引用面由主树 25 处降到 6 处(无新增断链)。
269 lines
18 KiB
Markdown
269 lines
18 KiB
Markdown
# ADC 输入层单元测试说明(主机侧)
|
||
|
||
> **模块**:ADC Input Layer(语义取数与预处理,`App/algo/adc_input.c`)
|
||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证语义通道映射、
|
||
> STM32 ADC1 的 DMA 乒乓帧消费(含半区选择)、TPAFE 快照消费与取数守卫,零硬件、零烧录、
|
||
> 毫秒级、不占 MCU 的 RAM。
|
||
> **状态**:✅ 已落地(**103** 用例,`/W4` 0 告警,已挂 pre-commit 钩子)
|
||
> **日期**:2026-10-10
|
||
> **关联文档**:
|
||
> [`../../../AGENTS.md`](../../../AGENTS.md)(平台约束 · CubeMX 生成区铁律)、
|
||
> [`测试开发标准流程.md`](../流程与规范/测试开发标准流程.md)(测试分层与门禁总览)、
|
||
> [`配置文件单元测试说明.md`](配置文件单元测试说明.md)(同层"强符号桩"范本)、
|
||
> [`相量层单元测试说明.md`](相量层单元测试说明.md)(同批落地的 全周傅氏相量姊妹篇)、
|
||
> [`../../硬件平台/ADC通道映射说明.md`](../../硬件平台/ADC通道映射说明.md)(**语义映射权威口径**)、
|
||
> [`../../算法/继电保护功能复现规划.md`](../../算法/继电保护功能复现规划.md)(「STM32 ADC1 启动与扫描」/「通道语义映射」设计依据)、
|
||
> 快照语义约定的历史审计(属主文档已于 2026-10-15 删除)
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 为什么单独做主机侧单测
|
||
|
||
`adc_input.c` 把两片转换器的原始码值按**语义**统一暴露,其正确性的核心是三者一致:
|
||
|
||
| 半边 | 内容 | 可测性 |
|
||
|---|---|---|
|
||
| **纯逻辑**(被测) | ① 语义通道映射表(「通道语义映射」):`UA/UB/UC/UN/IA/IB/IC/IN/TEMP` → `(来源, 下标)`;② STM32 ADC1 帧消费(「STM32 ADC1 启动与扫描」,v2.0 DMA 模型):**Rank 顺序 ↔ ADC1 通道号 ↔ 语义** + **乒乓半区选择**;③ 取数守卫与 poll 契约;④ 状态码与四类故障计数(`seq`/`gap`/`err`/`hw`) | 纯查表 + 状态机,与硬件无关 → **可在 PC 上位穷举** |
|
||
| **硬件副作用** | `HAL_ADC_Start_DMA()`(挂载)与被测代码自己实现的三个 HAL 弱回调 `HAL_ADC_Conv[Half]CpltCallback` / `HAL_ADC_ErrorCallback`;`tpafe5160_snapshot()`(临界区) | 挂载是**外部符号** → 主机侧给同名强符号桩;三个回调由**被测代码**定义 → 测试直接调用它们注入"DMA 完成" |
|
||
|
||
板端套件(`test/board/`)**无法每改必跑地验证本层的核心判据**:
|
||
|
||
- **通道映射**:台架只在 2026-10-27 一次性验过映射(万用表注入已知电压 + 逐路短接 AGND,8 路各自塌到
|
||
本路,实测记录 → `docs/硬件平台/ADC通道映射说明.md` §8.1),**不占 CI、不能每改必跑**;而映射表一旦
|
||
写错(本项目 V0.1 有 13/17 路被置换),读到的是**另一只脚**或**恒为陈旧值**,且**不会有任何报错**
|
||
——本层最危险的失效模式,必须由主机侧逐 Rank 钉死。
|
||
- **HAL 故障路径**:`HAL_ADC_Start_DMA()` 挂载失败、DMA 完成计数不前进(节拍源停摆)、
|
||
超拍丢帧、HAL 错误回调在真机上极难稳定复现,且**读错乒乓半区**这种缺陷在板端只会表现为
|
||
"读数偶尔怪"、无法定位。
|
||
|
||
主机侧的做法:给**每个 Rank 注入互不相同的码值**(第 i 个 Rank 写成 `100+i`),把
|
||
"Rank 顺序 ↔ ADC1 通道号 ↔ 语义"整条链路**逐通道钉死**;"DMA 完成"由测试直接调用被测代码
|
||
实现的半传输/传输完成回调注入(`dma_beat()`),故障路径用可注入桩一次覆盖。
|
||
|
||
> ⚠ 本单测**只覆盖「STM32 ADC1 启动与扫描」与「通道语义映射」**。「通道标定(一次系数 + 零漂)」本层不做,故取到的是**未标定原始码值**;
|
||
> 「周期 CONVST 重触发」与「TPAFE 快照消费」由 `adc_task` + TPAFE 驱动承担,板端套件覆盖。
|
||
|
||
### 1.2 被测对象与桩(**不改生产代码**)
|
||
|
||
`adc_input.c` 的全部平台依赖都是**外部符号**,故测试单元只需提供**同名强符号桩**,
|
||
**生产代码零改动**(无任何 `#ifdef` 测试钩子)。
|
||
|
||
```
|
||
test/host/adc_input/adc_input_test.c
|
||
├── #include "adc.h" → stub/adc.h (HAL_ADC_* 原型 + hadc1 + ADC_CHANNEL_0..15)
|
||
├── #include "tpafe5160.h" → stub/tpafe5160.h (仅 tpafe5160_snapshot 原型)
|
||
└── #include "adc_input.c" ← 生产源文件(App/algo/),原样纳入
|
||
```
|
||
|
||
> **`adc_input.h` 用真实头**:它只依赖 `<stdint.h>`,不拖 HAL,故接口契约原汁。
|
||
> **`adc.h` / `tpafe5160.h` 必须用桩**:真实 `Inc/adc.h` 经 `main.h` 拖入整套 HAL;
|
||
> 真实 `Drivers/BSP/TPAFE5160/tpafe5160.h` 拖入 GPIO 宏——主机侧无法编译。
|
||
> **桩目录必须排在 `App/algo` 之前**(`include_dirs` 顺序敏感,见 `run_unit_test.py`)。
|
||
|
||
| 平台接口 | MCU 端 | 主机侧桩 |
|
||
|---|---|---|
|
||
| `hadc1` / `hadc_other` | `MX_ADC1_Init()` 配置的 ADC1 句柄 | 两个占位对象:被测代码只**比较指针**(三个 HAL 回调的 `hadc == &hadc1` 守卫),从不读字段;`hadc_other` 专门用来证伪守卫 |
|
||
| `HAL_ADC_Start_DMA` | 挂载循环 DMA,长度 2×9(此后每拍由硬件自走) | 记录 `s_start_dma_calls` / `s_dma_buf` / `s_dma_len`,返回 `s_start_dma_rc`(可注入 `HAL_ERROR`) |
|
||
| `HAL_ADC_ConvHalfCpltCallback` / `HAL_ADC_ConvCpltCallback` / `HAL_ADC_ErrorCallback` | **由被测代码实现**(覆盖 HAL 弱符号),ISR 上下文 | 测试用 `dma_beat(base)` 直接调用:按 `seq & 1` 决定往哪个半区填 `base+i`,再触发对应回调——与消费侧的奇偶推导**互相证伪** |
|
||
| `tpafe5160_snapshot` | 临界区原子快照最新一帧 | 返回 `s_snapshot_rc`(1/0/-1);`rc==1` 时从 `s_fake_tpafe[]` 拷贝;记录 `count` 与 `dst` |
|
||
|
||
桩里 `ADC_CHANNEL_0..15` 的**宏值等于通道号**(F4 HAL 即如此),故 `(uint8_t)ADC_CHANNEL_14 == 14`
|
||
与真机一致——这是"映射判据"能成立的前提。
|
||
|
||
### 1.3 测试环境
|
||
|
||
| 依赖 | 版本 / 说明 |
|
||
|---|---|
|
||
| 编译器 | 本机 MSVC(VS2019/2022/2026 任一),由 `run_unit_test.py` 自动探测 |
|
||
| Python | 仓库 `.venv` 或任意 python3(**纯标准库**) |
|
||
| 硬件 | 无(不需要开发板 / 串口 / ST-LINK / 信号源) |
|
||
|
||
工具链探测 / `INCLUDE`-`LIB` 拼接 / 编译运行 / 退出码判定全部委托通用 harness
|
||
[`../../../test/host/common/unit_test_harness.py`](../../../test/host/common/unit_test_harness.py),
|
||
本模块 `run_unit_test.py` 只**声明** `TestBuild`(源文件 / 桩目录 / 汇总行标记)。
|
||
|
||
### 1.4 运行方式
|
||
|
||
```powershell
|
||
# 仓库根目录(统一用 .venv)
|
||
& '.venv\Scripts\python.exe' test/host/adc_input/run_unit_test.py
|
||
```
|
||
|
||
退出码:`0`=PASS / `1`=测试失败 / `2`=环境错误(找不到编译器 / 编译失败 / 无汇总行)。
|
||
|
||
### 1.5 回归接入
|
||
|
||
| 项 | 说明 |
|
||
|---|---|
|
||
| **触发范围** | `App/algo/adc_input.c` / `adc_input.h`,或 `test/host/adc_input/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||
| **pre-commit 钩子** | `adc-input-unit-test`(本地钩子,`.pre-commit-config.yaml` §7e) |
|
||
| **汇总行** | `=== ADC Input Tests: 70/70 PASSED (failed=0) ===` |
|
||
| **与板端测试的关系** | 互补:本单测保证**映射 / 契约 / 故障路径**正确;板端保证 **HAL 配置(Rank 顺序、采样时间、引脚)** 与真机一致 |
|
||
|
||
---
|
||
|
||
## 2. 测试用例
|
||
|
||
> 命名 `TC-AI-NNN`(AI = Adc Input;主机侧单测自定前缀,与板端套件缩写表互不影响)。
|
||
> 全部在 `adc_input_test.c` 内,主机侧运行。被测源文件被 `#include` 纳入同一编译单元,
|
||
> 故**静态函数(`map_check` / `stm32_take`)亦可直接调用**(白盒)。
|
||
|
||
### 2.1 初始化与复位(`adc_input_init`)
|
||
|
||
| 用例 | 断言 | 验证的设计点 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-001** | 前置:注入 `HAL_ADC_Start` 失败 → poll 后失败计数 = 1 | 统计可见性(前置构造) |
|
||
| **TC-ADCIN-002** | 前置:注入 TPAFE 有效帧 → poll 后 `UA` 可取数且值正确 | 取数通路(前置构造) |
|
||
| **TC-ADCIN-003** | `adc_input_init()` 返回 0 | 映射表自检通过 |
|
||
| **TC-ADCIN-004** | init 后 `adc_input_stm32_rc() == ADC_INPUT_RC_NOT_RUN` | 状态码复位 |
|
||
| **TC-ADCIN-005** | init 后 `adc_input_stm32_errs() == 0` | 失败计数归零 |
|
||
| **TC-ADCIN-006** | init 后 TPAFE / STM32 **两侧** `adc_input_raw()` 均返回 -2 | 有效标志被清(不会取到上一轮陈旧值) |
|
||
|
||
### 2.2 语义映射表(`adc_input_desc`)
|
||
|
||
| 用例 | 断言(语义 → 来源 / 下标) | 权威口径 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-010** | `UA` → TPAFE `buf[6]` | `docs/硬件平台/ADC通道映射说明.md` |
|
||
| **TC-ADCIN-011** | `UB` → STM32 `CH14` | 同上 |
|
||
| **TC-ADCIN-012** | `UC` → STM32 `CH3` | 同上 |
|
||
| **TC-ADCIN-013** | `UN` → TPAFE `buf[5]` | 同上 |
|
||
| **TC-ADCIN-014** | `IA` → TPAFE `buf[2]` | 同上 |
|
||
| **TC-ADCIN-015** | `IB` → STM32 `CH6` | 同上 |
|
||
| **TC-ADCIN-016** | `IC` → STM32 `CH1` | 同上 |
|
||
| **TC-ADCIN-017** | `IN` → STM32 `CH2` | 同上 |
|
||
| **TC-ADCIN-018** | `TEMP` → STM32 `CH5`(V_ZL2) | 同上 |
|
||
| **TC-ADCIN-019** | `adc_input_desc(ADC_SEM_COUNT)` → `NULL` | 越界防御 |
|
||
| **TC-ADCIN-01a** | `adc_input_desc((adc_semantic_t)-1)` → `NULL` | 无符号比较兜底 |
|
||
|
||
### 2.3 映射表自检(`map_check`,静态)
|
||
|
||
| 用例 | 断言 | 验证的设计点 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-020** | `map_check() == 0` | 真实映射表自洽:TPAFE 下标 < 8;每个 STM32 映射通道都出现在扫描 Rank 内 |
|
||
|
||
> 反向路径(表不自洽 → -1)**结构上不可达**,见 §4.1。
|
||
|
||
### 2.4 STM32 ADC1 帧消费(`stm32_take`,静态;v2.0 DMA 模型)
|
||
|
||
| 用例 | 断言 | 验证的设计点 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-021** | 有新帧 → 返回 0 | 正常路径 |
|
||
| **TC-ADCIN-022** | `adc_input_stm32_rc() == RC_OK` | 状态码 |
|
||
| **TC-ADCIN-023** | 成功不计入失败计数 | 统计语义 |
|
||
| **TC-ADCIN-024** | `HAL_ADC_Start_DMA` 只在 `init()` 挂**一次** | 外设生命周期(每拍不重启) |
|
||
| **TC-ADCIN-025** | DMA 完成计数 `seq` = 1 | 计数只由 HAL 回调推进 |
|
||
| **TC-ADCIN-026** | ⭐ **Rank → 通道号映射**:`UB` = CH14 / rank9 = 108 | **本层核心判据**(板端无法验) |
|
||
| **TC-ADCIN-027** | `UC` = CH3 / rank4 = 103 | 同上 |
|
||
| **TC-ADCIN-028** | `IB` = CH6 / rank7 = 106 | 同上 |
|
||
| **TC-ADCIN-029** | `IC` = CH1 / rank2 = 101 | 同上 |
|
||
| **TC-ADCIN-02a** | `IN` = CH2 / rank3 = 102 | 同上 |
|
||
| **TC-ADCIN-02b** | `TEMP` = CH5 / rank6 = 105 | 同上 |
|
||
| **TC-ADCIN-02c** | 取帧后 `UA`(TPAFE)仍为 -2 | 两来源互相独立 |
|
||
| **TC-ADCIN-080..085** | ⭐ **乒乓双缓冲**:往"正在被 DMA 写的另一半区"填 900/800 系干扰值,`stm32_take()` 仍必须读到**刚完成那一半**(108 / 208) | **撕裂帧防线**(读错半区 = 半新半旧混合帧) |
|
||
| **TC-ADCIN-086..089** | 两档超拍:连投 2 帧不消费 → 取到最新帧(408)且 `gaps()` = 1;再连投 3 帧不消费(一次跨 2 帧)→ `gaps()` 累计到 3,把单位钉成**丢掉的帧点数**(2026-10-21 由「次数」改来,与判定层 `tgap` 同单位);`errs()` 始终 = 0 | **丢帧与停摆分开计数**(消费者超拍 ≠ 节拍源停);两档并列才分得出「次数 vs 点数」 |
|
||
| **TC-ADCIN-090..09A** | `HAL_ADC_ErrorCallback(&hadc1)` ×2 → `hw_errs()` = 2;用**别的句柄**调三个回调 → `seq` / `hw_errs` 均不动 | 三个 HAL 弱回调的 `hadc == &hadc1` **句柄守卫** |
|
||
| **TC-ADCIN-030..034** | `HAL_ADC_Start_DMA` 返回 `HAL_ERROR` → `init()` 返回 **-2**、状态码 `RC_START`、计数 +1、STM32 侧无有效数据 | 挂载失败(一次性,非每拍) |
|
||
| **TC-ADCIN-035..039** | 无任何 DMA 完成 → `RC_STALL`、**单拍不计故障**、`seq` 不前进、STM32 侧无有效数据 | 无新帧路径 + 相位伪影不误报 |
|
||
| **TC-ADCIN-03a..03e** | 连续两拍无新帧 → `errs()` = 1;第三拍 → 2;恢复拍成功且 `gaps()` 仍 0 | 停摆判据 = **连续 ≥2 拍**;跨次单调累计 |
|
||
| **TC-ADCIN-070..078** | ⭐ **停摆拍不得沿用上一拍旧值**:先成功一拍让 STM32 侧进入有效态(`UB`=108),再注入无新帧拍 → `adc_input_raw()` 必须返 **-2**;恢复拍重新可取数 | **陈旧帧**(2026-10-10 真机实测发现的缺陷,见下注) |
|
||
|
||
> ⚠ **为什么 TC-ADCIN-034 照不出这条缺陷**:它是"`adc_input_init()` 后**立刻**失败",此时
|
||
> `s_stm32_valid` 本来就是 0,断言 -2 恒成立。真正的缺陷路径是**成功过之后再失败**:
|
||
> 本层只有一份 `s_stm32_raw[]`,而有效标志此前**只在成功时置 1、失败时从不清零** →
|
||
> 消费侧照样取数、照样压入采样环(`adc_input_poll()` 返回值只由 TPAFE 决定,
|
||
> `s_frames_skip` 抓不到),直接污染 20 点 DFT 窗。
|
||
> 修复 = 失败支路清 `s_stm32_valid`(`App/algo/adc_input.c`)。
|
||
> **这组用例在轮询版与 DMA 版都保留**:触发条件从"轮询超时"变成"本拍无新帧",
|
||
> **同一条缺陷、同一个防线**。
|
||
> **反向验证已做(v2.0)**:把停摆支路改回 `valid=1` → TC-ADCIN-039 / 073 / 074 / 074B 四条立即
|
||
> FAIL(89/93 时点);把半区奇偶取反 → 11 条 FAIL;`STALL_RUN_MAX` 改 1 → 6 条 FAIL;
|
||
> gap 阈值改 `>2` → 1 条 FAIL → 证明这些用例真的咬得住。
|
||
|
||
### 2.5 取数与参数守卫(`adc_input_raw`)
|
||
|
||
| 用例 | 断言 | 验证的设计点 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-040** | `out == NULL` → -1 | 参数防御 |
|
||
| **TC-ADCIN-041** | `ch == ADC_SEM_COUNT` → -1 | 越界防御 |
|
||
| **TC-ADCIN-042** | `ch == (adc_semantic_t)-1` → -1 | 负数兜底 |
|
||
| **TC-ADCIN-043** | 参数错时**不写** `*out` | 无副作用 |
|
||
| **TC-ADCIN-044** | TPAFE 无有效帧 → -2 | 有效标志语义 |
|
||
| **TC-ADCIN-045** | STM32 无有效扫描 → -2 | 有效标志语义 |
|
||
| **TC-ADCIN-049** | `UA` → `buf[6]` 的值(注入 3006) | 按**下标**取数(非按枚举序) |
|
||
| **TC-ADCIN-04a** | `UN` → `buf[5]` 的值(注入 2005) | 同上 |
|
||
| **TC-ADCIN-04b** | `IA` → `buf[2]` 的值(注入 **-1234**) | 16 位补码负值原样保留(不截断 / 不取绝对值) |
|
||
|
||
### 2.6 采集一拍(`adc_input_poll`)
|
||
|
||
| 用例 | 断言 | 验证的设计点 |
|
||
|---|---|---|
|
||
| **TC-ADCIN-046** | 快照返回 1 → `poll()` 返回 1 | 返回码透传 |
|
||
| **TC-ADCIN-047** | 快照被请求 **8** 通道 | 与 `ADC_INPUT_TPAFE_CH_NUM` 一致 |
|
||
| **TC-ADCIN-048** | 快照目的缓冲非空 | 不传 NULL 给驱动 |
|
||
| **TC-ADCIN-050** | 快照返回 0(无新帧)→ `poll()` 返回 0 | 无新帧语义 |
|
||
| **TC-ADCIN-051** | 无新帧时 TPAFE 侧无效 | 不置有效标志 |
|
||
| **TC-ADCIN-052** | 同拍 STM32 扫描**照常执行**(`rc == RC_OK`) | 两侧独立 |
|
||
| **TC-ADCIN-053** | 快照返回 -1(参数错)→ `poll()` 返回 -1 | 错误透传 |
|
||
| **TC-ADCIN-054** | 参数错时 TPAFE 侧无效 | 不置有效标志 |
|
||
| **TC-ADCIN-055** | 已有有效帧后再"无新帧" → **保持**上次有效值 | 有效标志只置不清 |
|
||
| **TC-ADCIN-056** | STM32 失败**不影响** `poll()` 返回值(仍为 1) | 两侧解耦 |
|
||
| **TC-ADCIN-057** | STM32 失败不影响 TPAFE 取数 | 同上 |
|
||
| **TC-ADCIN-058** | STM32 失败另由 `adc_input_stm32_rc()` 暴露 | 故障通道分离 |
|
||
|
||
### 2.7 状态码与失败计数观测
|
||
|
||
| 用例 | 断言 |
|
||
|---|---|
|
||
| **TC-ADCIN-060** | init 后未取帧 → `RC_NOT_RUN(-3)` |
|
||
| **TC-ADCIN-061** | init 后各计数 = 0 |
|
||
| **TC-ADCIN-062** | 一次成功后 `rc == OK` 且 `errs` 不变 |
|
||
| **TC-ADCIN-063** | 连续两拍无新帧 → `errs == 1`(单拍伪影不计) |
|
||
| **TC-ADCIN-064** | `rc` = **最近一次**(`RC_STALL`) |
|
||
| **TC-ADCIN-065** | 恢复成功后 `rc == OK` |
|
||
| **TC-ADCIN-066** | `errs` **单调不回退**(不因成功回落) |
|
||
| **TC-ADCIN-067** | `seq` 只由 DMA 完成回调推进(消费动作不改它) |
|
||
|
||
**当前结果**:**103/103 PASSED**(`failed=0`)。
|
||
|
||
---
|
||
|
||
## 3. 如何扩展用例
|
||
|
||
1. 在 `adc_input_test.c` 加一个 `static void tc_xxx(void)`;
|
||
2. 用例开头调 `fakes_reset()`(把全部桩复位到"健康"初值 + 复位观测计数);
|
||
3. 用 `s_start_rc` / `s_poll_fail_at` / `s_poll_vals[]` 注入 HAL 行为;
|
||
用 `s_snapshot_rc` / `s_fake_tpafe[]` 注入 TPAFE 帧;
|
||
4. `CHECK(cond, "描述")` 断言;在 `main()` 里注册(并同步更新本文档 §2)。
|
||
|
||
> ⚠ `fakes_reset()` 必须在新用例开头调用:桩计数器(`s_poll_calls` / `s_rank_idx` /
|
||
> `s_start_calls` / `s_stop_calls` / `s_snapshot_calls`)跨用例共享,不复位会互相污染。
|
||
>
|
||
> ⚠ 模块静态态(`s_stm32_raw` / `s_tpafe_raw` / `s_*_valid` / `s_stm32_rc` / `s_stm32_errs`)
|
||
> 由 `adc_input_init()` 复位;**每个用例开头都应调一次 `adc_input_init()`**(等价"刚上电")。
|
||
|
||
---
|
||
|
||
## 4. 已知边界与设计取舍
|
||
|
||
### 4.1 `map_check()` 反向路径结构上不可达
|
||
|
||
`map_check()` 的失败分支要求"映射表指向 TPAFE 下标 ≥8,或 STM32 通道号不在扫描 Rank 内"。
|
||
但 `k_adc_map[]` 与 `k_stm32_rank_ch[]` 都是**编译期 `static const`**,没有任何运行时输入
|
||
能使其不自洽(曾尝试用 `#define` 重定向表名,会与生产文件内的定义撞成重复定义)。
|
||
因此本单测**只覆盖正向路径**(`map_check() == 0`);反向路径的等效保障是
|
||
`adc_input_init()` 的返回值——它在开机时自检并留痕(见 `app_main.c`)。
|
||
|
||
### 4.2 本层不做标定(「通道标定(一次系数 + 零漂)」待办)
|
||
|
||
`adc_input_raw()` 返回的是**未标定原始码值**(TPAFE = 16 位补码,STM32 = 0..4095)。
|
||
一次系数 / 零漂标定属 **「通道标定(一次系数 + 零漂)」,尚未实现**,故本单测不对"物理量换算"做任何断言。
|
||
|
||
### 4.3 不覆盖 V0.1 的硬件缺陷(双转换器无共同采样触发)
|
||
|
||
V0.1 上 8 路电气量分属两片转换器且**无共同采样触发**(登记簿条目「双转换器无共同采样触发」),本层只做**逐通道独立监视**。
|
||
"跨转换器相量合成"(线 / 负序 / 零序)不得在本层或 全周傅氏相量上做——属「线/负序/零序合成」且 V0.1 受限
|
||
(见 `docs/算法/继电保护功能复现规划.md` §1.6)。
|