adc_input: 失败拍清 s_stm32_valid,杜绝超时沿用上一拍旧值(单测 70->79)

缺陷(真机实测发现):本层只有一份 s_stm32_raw[],有效标志此前只在成功时
置 1、失败时从不清零 -> 超时拍消费侧照样取数、照样压入采样环(且超时前
几个 Rank 已写本拍新值、后面几个是上一拍旧值 = 半新半旧混合帧),直接污
染 20 点 DFT 相量窗;adc_input_poll() 返回值只由 TPAFE 决定,s_frames_skip
也抓不到它。

修复:stm32_scan() 两条失败支路(HAL_ADC_Start 失败 / 轮询超时)都清
s_stm32_valid -> 本拍 adc_input_raw() 对 STM32 语义通道返 -2 -> adc_task
走既有 skip 整帧跳过路径;下一拍扫描成功自动置回 1(自愈)。

测试:新增 TC-AI-070..078(70 -> 79 用例)。旧用例 TC-AI-034/039b 都是
init 后立刻失败(valid 本就是 0),照不出成功过之后再失败这条路径,故新
用例先成功一拍进入有效态再注入失败。**反向验证已做**:把支路改回旧行为 ->
TC-AI-039b/073/074 立即 FAIL(76/79),证明用例有效。

验证:主机单测 79/79 PASS;@build 0 错 0 警(Code 256868 -> 256872);
@test PASS(本轮无超时:adc1_err=0、skip=0、idle=0、frames=20001);
18 钩子全绿。文档同步 4 处(停摆分析 4.4 / ADC采样节拍 10.4 / ADC输入层
单测说明 / test-host README)。

遗留:2 ms 忙等本身未消(硬化 2 = DMA,方案 B2 待 CubeMX 配置)。
This commit is contained in:
2026-10-10 23:08:01 +08:00
parent 9231a3c22c
commit dd8210867a
7 changed files with 89 additions and 13 deletions
+1 -1
View File
@@ -46,7 +46,7 @@
| `cfg_file/stub/` | 桩头文件(`dbg_log.h`),覆盖 MCU 版以在 PC 上编译 |
| `phasor/run_unit_test.py` | **相量层(L2)单测**:离线编译真实 `App/algo/phasor.c` + `sample_ring.c`(**无需任何桩**,纯算法),57 用例覆盖傅氏系数表 / 幅值·相位 / 谐波抑制与提取 / 频偏 / 采样环 / 双区原子交换 |
| `phasor/phasor_test.c` | 测试单元本体(`#include` 两个生产源码 + 合成任意波形) |
| `adc_input/run_unit_test.py` | **ADC 输入层(L1)单测**:离线编译真实 `App/algo/adc_input.c`,70 用例给每个 Rank 注入互异码值**逐通道钉死映射**,并覆盖 HAL 启动失败 / 转换超时 / 参数错等真机难复现分支 |
| `adc_input/run_unit_test.py` | **ADC 输入层(L1)单测**:离线编译真实 `App/algo/adc_input.c`,79 用例给每个 Rank 注入互异码值**逐通道钉死映射**,并覆盖 HAL 启动失败 / 转换超时 / **失败拍陈旧帧** / 参数错等真机难复现分支 |
| `adc_input/adc_input_test.c` | 测试单元本体(`#include` 生产源码 + HAL / TPAFE 可注入桩) |
| `adc_input/stub/` | 桩头文件(`adc.h` / `tpafe5160.h`) |
| `vol_lock/run_unit_test.py` | **卷锁单测**:离线编译真实 `App/fs/vol_lock.c`,33 断言点覆盖创建 / 获取 / 释放契约(**fail-open 不变量**:创建失败仍返回 1 且零阻塞)/ 幂等 / 超时透传 |
+42 -1
View File
@@ -29,7 +29,7 @@
* TC-AI-019/01A 越界(ADC_SEM_COUNT)与负数语义 -> NULL
* —— map_check(TC-AI-020)——
* TC-AI-020 真实映射表自洽(反向路径结构上不可达,见文件末说明)
* —— stm32_scan(TC-AI-021..03A)——
* —— stm32_scan(TC-AI-021..078)——
* TC-AI-021..025 全通道成功:返回 0 / 状态码 / 不计失败 / Start-Stop 各一次 / 轮询 9 次
* TC-AI-026..02B **Rank -> 通道号映射**:UB->CH14(rank9) / UC->CH3(rank4) /
* IB->CH6(rank7) / IC->CH1(rank2) / IN->CH2(rank3) / TEMP->CH5(rank6)
@@ -37,6 +37,11 @@
* TC-AI-030..034 HAL_ADC_Start 失败:返回 -1 / 状态码 RC_START / 计数 +1 / 不调 Stop
* TC-AI-035..039 轮询超时:返回 -2 / 状态码 RC_TIMEOUT / 计数 +1 / 调 Stop 收尾 / 立即退出
* TC-AI-03A 失败计数跨次单调累计
* TC-AI-070..078 **失败拍不得沿用上一拍旧值**(2026-10-10 补,真机实测发现的
* 正确性缺陷):先成功一拍让 STM32 侧进入有效态,再注入超时拍 /
* Start 失败拍 -> adc_input_raw() 必须返 -2(而非上一拍的值);
* 恢复拍重新置有效。TC-AI-034/039b 只覆盖"冷启动即失败",
* 照不出"成功过之后失败 → 静默沿用旧值",故单独立一组。
* —— adc_input_raw(TC-AI-040..04B)——
* TC-AI-040..043 参数守卫:out=NULL / 越界 / 负数 -> -1,且不写 out
* TC-AI-044/045 无有效数据 -> -2(两来源分别)
@@ -318,6 +323,40 @@ static void tc_scan_timeout(void) {
CHECK(adc_input_stm32_errs() == 2u, "TC-AI-03A 失败计数跨次单调累计 = 2");
}
/* ============ TC-AI-070..077:失败拍不得沿用上一拍旧值(陈旧帧) ============
* 背景(2026-10-10 真机实测发现):本层只有一份 s_stm32_raw[],超时拍里"超时前
* 几个 Rank 已写本拍新值、后面几个还是上一拍旧值",而 s_stm32_valid 此前只在
* 成功时置 1、失败时**从不清零** → 消费侧照样取数、照样压入采样环,
* s_frames_skip 抓不到。TC-AI-034/039b 都是"init 后立刻失败"(valid 本就是 0),
* 因此照不出这条路径——必须先成功一拍。 */
static void tc_scan_stale(void) {
int32_t v = 0;
/* --- 超时支路:成功拍 -> 超时拍 -> 恢复拍 --- */
fakes_reset();
(void)adc_input_init();
CHECK(stm32_scan() == ADC_INPUT_RC_OK, "TC-AI-070 前置:首拍成功");
CHECK((adc_input_raw(ADC_SEM_UB, &v) == 0) && (v == 108), "TC-AI-071 前置:UB 取到 108(有效态)");
s_poll_fail_at = 4; /* 第 5 个 Rank 超时(前 4 个已写新值) */
CHECK(stm32_scan() == ADC_INPUT_RC_TIMEOUT, "TC-AI-072 第二拍轮询超时");
CHECK(adc_input_raw(ADC_SEM_UB, &v) == -2, "TC-AI-073 超时拍不得沿用上一拍值 -> -2");
CHECK(adc_input_raw(ADC_SEM_UC, &v) == -2, "TC-AI-074 超时拍连本拍已写入的 Rank 也不得出数");
s_poll_fail_at = -1;
CHECK(stm32_scan() == ADC_INPUT_RC_OK, "TC-AI-075 第三拍恢复成功");
CHECK((adc_input_raw(ADC_SEM_UB, &v) == 0) && (v == 108), "TC-AI-076 恢复拍重新可取数(自愈)");
/* --- 启动失败支路:同样不得沿用 --- */
fakes_reset();
(void)adc_input_init();
(void)stm32_scan(); /* 先成功一拍 */
s_start_rc = HAL_ERROR;
CHECK(stm32_scan() == ADC_INPUT_RC_START, "TC-AI-077 Start 失败拍");
CHECK(adc_input_raw(ADC_SEM_UB, &v) == -2, "TC-AI-078 Start 失败拍不得沿用上一拍值 -> -2");
}
/* ===================== TC-AI-040..045:raw 参数守卫与无数据 ===================== */
static void tc_raw_guards(void) {
@@ -448,6 +487,8 @@ int main(void) {
tc_scan_start_fail();
printf("-- stm32 scan: poll timeout --\n");
tc_scan_timeout();
printf("-- stm32 scan: stale frame on failure beat --\n");
tc_scan_stale();
printf("-- raw: guards / no-data --\n");
tc_raw_guards();
printf("-- raw: tpafe index + two's complement --\n");