Files
STM32F4-Base/test/host/adc_input/adc_input_test.c
T
wandering1 696a1a49a9 主机单测:新增 ADC 输入层(L1)用例 70 项并登记 L1/L2 单测钩子
新增 App/algo/adc_input.c 的主机侧单测(L1),补齐 L1/L2 算法层
"每个函数都可离线验证"的最后一处空白。

- test/host/adc_input/{adc_input_test.c,run_unit_test.py,stub/}:
  70 个用例,覆盖 6 个公开函数 + map_check + stm32_scan。
  关键判据 = "通道号 <-> Rank 顺序 <-> 语义"三者映射:给每个 Rank
  注入互不相同的码值,逐通道钉死映射(板端无信号源,无法验此项)。
  另钉死 HAL 启动失败 / 轮询超时 / 参数错 / 无有效数据等分支。
- .pre-commit-config.yaml:登记 phasor-unit-test(7d)与
  adc-input-unit-test(7e)两个主机侧单测钩子。

主机侧全套:adc_input 70/70、phasor 52/52,均 0 失败、无 /W4 告警。
2026-10-10 17:46:45 +08:00

464 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:ADC 输入层(L1)主机侧单元测试
* 模块功能:在 PC 上对 App/algo/adc_input.c 做离线验证(6 个公开函数 + 2 个静态函数)。
*
* 被测对象:真实源文件 ../../../App/algo/adc_input.c(以 #include 原样纳入,
* 保证测的是生产代码而非副本)。平台依赖由本文件提供的**可注入桩**实现:
* - adc.h -> HAL_ADC_Start / PollForConversion / GetValue / Stop
* (可注入 启动失败 / 第 N 次轮询超时 / 逐 Rank 返回值)
* - tpafe5160.h-> tpafe5160_snapshot(可注入 新帧/无新帧/参数错 + 帧内容)
*
* 为什么要主机侧单测(而不是只用板端套件):
* 1. 本层核心是"**通道号 <-> Rank 顺序 <-> 语义**"三者的映射正确性。板端**无信号源**
* (见 docs/硬件平台/ADC通道映射说明.md §8),无法注入已知电压去验证"读到的是
* 哪只脚";主机侧可直接给每个 Rank 注入互不相同的码值,从而**逐通道钉死映射**
* (这是唯一能在离线端证伪"通道宏写错 -> 读到另一只脚"的手段)。
* 2. HAL 启动失败 / 转换超时 / 参数错等分支在真机上极难稳定复现,主机侧一次钉死。
* 3. 不占 MCU 的 RAM,毫秒级完成。
*
* 覆盖点(对应 docs/算法/继电保护功能复现规划.md §2.1 的 L1-3 / L1-4):
* —— adc_input_init(TC-AI-001..006)——
* TC-AI-001 poll 失败后失败计数可见(前置状态构造)
* TC-AI-002 poll 成功后 TPAFE 侧可取数(前置状态构造)
* TC-AI-003 init 返回 0(映射表自检通过)
* TC-AI-004 init 后 STM32 状态码 = NOT_RUN
* TC-AI-005 init 后失败计数归零
* TC-AI-006 init 后 TPAFE / STM32 两侧均无有效数据(有效标志被清)
* —— adc_input_desc(TC-AI-010..01A)——
* TC-AI-010..018 九个语义通道 -> (来源, 下标) 与权威映射表逐项一致
* TC-AI-019/01A 越界(ADC_SEM_COUNT)与负数语义 -> NULL
* —— map_check(TC-AI-020)——
* TC-AI-020 真实映射表自洽(反向路径结构上不可达,见文件末说明)
* —— stm32_scan(TC-AI-021..03A)——
* 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)
* TC-AI-02C stm32_scan 不置 TPAFE 有效标志(两来源独立)
* TC-AI-030..034 HAL_ADC_Start 失败:返回 -1 / 状态码 RC_START / 计数 +1 / 不调 Stop
* TC-AI-035..039 轮询超时:返回 -2 / 状态码 RC_TIMEOUT / 计数 +1 / 调 Stop 收尾 / 立即退出
* TC-AI-03A 失败计数跨次单调累计
* —— adc_input_raw(TC-AI-040..04B)——
* TC-AI-040..043 参数守卫:out=NULL / 越界 / 负数 -> -1,且不写 out
* TC-AI-044/045 无有效数据 -> -2(两来源分别)
* TC-AI-046..048 poll 快照契约:返回码透传 / 请求 8 通道 / 目的缓冲非空
* TC-AI-049..04B TPAFE 按**下标**取数(UA=6 / UN=5 / IA=2)且补码负值原样保留
* —— adc_input_poll(TC-AI-050..058)——
* TC-AI-050..052 无新帧(0):返回 0 / TPAFE 无效 / STM32 侧照常扫描
* TC-AI-053/054 参数错(-1):返回 -1 / TPAFE 无效
* TC-AI-055 已有有效帧后再"无新帧"**不清零**(保持上次有效值)
* TC-AI-056..058 STM32 失败**不阻断** TPAFE、**不影响** poll 返回值,另由状态码暴露
* —— adc_input_stm32_rc / _errs(TC-AI-060..066)——
* TC-AI-060/061 init 后 = NOT_RUN / 计数 0
* TC-AI-062 成功: rc=OK 且 errs 不变
* TC-AI-063/064 两次失败累计 = 2,rc = 最近一次(TIMEOUT)
* TC-AI-065/066 恢复成功 rc=OK,errs 单调不回退
*
* 通过:全部 TC PASS(main 返回 0)。
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
*
* 创建日期:2026-10-10
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/* ===================== 平台桩(先纳入,覆盖真实头) ===================== */
#include "adc.h"
#include "tpafe5160.h"
/* ===================== HAL ADC1 桩 =====================
* 被测代码经 HAL 只做四件事:启动、逐 Rank 轮询、读 DR、停止。桩把它们变成可注入:
* s_start_rc HAL_ADC_Start 返回值(注入启动失败)
* s_poll_fail_at PollForConversion 第几次(0 基)失败;-1 = 永不失败(注入超时)
* s_poll_vals[i] 第 i 个 Rank 读到的码值(默认 100+i,用于钉死 Rank->通道映射)
* s_poll_calls / s_rank_idx 轮询次数 / 取数次数(观测:超时后是否立即退出)
*/
static HAL_StatusTypeDef s_start_rc = HAL_OK;
static int s_poll_fail_at = -1;
static uint32_t s_poll_vals[16];
static int s_poll_calls = 0;
static int s_rank_idx = 0;
static int s_start_calls = 0;
static int s_stop_calls = 0;
/* 被测代码把 &hadc1 透传给 HAL 桩;桩不读其字段 */
ADC_HandleTypeDef hadc1;
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc) {
(void)hadc;
s_start_calls++;
s_poll_calls = 0;
s_rank_idx = 0;
return s_start_rc;
}
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc, uint32_t Timeout) {
(void)hadc;
(void)Timeout;
{
int idx = s_poll_calls;
s_poll_calls++;
if (s_poll_fail_at >= 0 && idx == s_poll_fail_at) {
return HAL_ERROR;
}
}
return HAL_OK;
}
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef *hadc) {
uint32_t v;
(void)hadc;
v = (s_rank_idx < 16) ? s_poll_vals[s_rank_idx] : 0u;
s_rank_idx++;
return v;
}
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc) {
(void)hadc;
s_stop_calls++;
return HAL_OK;
}
/* ===================== TPAFE5160 快照桩 ===================== */
static int16_t s_fake_tpafe[8];
static int s_snapshot_rc = 0;
static int s_snapshot_calls = 0;
static uint8_t s_snapshot_count = 0u;
static int16_t *s_snapshot_dst = NULL;
int tpafe5160_snapshot(int16_t *dst, uint8_t count) {
uint8_t i;
s_snapshot_calls++;
s_snapshot_count = count;
s_snapshot_dst = dst;
if (s_snapshot_rc == 1) {
for (i = 0u; (i < count) && (i < 8u); i++) {
dst[i] = s_fake_tpafe[i];
}
}
return s_snapshot_rc;
}
/* ===================== 断言 ===================== */
static int s_total = 0, s_passed = 0, s_failed = 0;
#define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
} while (0)
/* ===================== 被测代码(原样纳入生产源) ===================== */
#include "adc_input.c"
/* ===================== 工具 ===================== */
/*
* 函数功能:把全部桩复位到"健康"初值
* 入口参数:无
* 返 回 值:无
* 限定条件:无
* 函数说明:默认 Start 成功、轮询永不超时、第 i 个 Rank 读到 100+i(互不相同,
* 以便逐通道钉死 Rank->通道号映射)、TPAFE 快照无新帧。
*/
static void fakes_reset(void) {
uint8_t i;
s_start_rc = HAL_OK;
s_poll_fail_at = -1;
s_poll_calls = 0;
s_rank_idx = 0;
s_start_calls = 0;
s_stop_calls = 0;
for (i = 0u; i < 16u; i++) {
s_poll_vals[i] = 100u + (uint32_t)i;
}
s_snapshot_rc = 0;
s_snapshot_calls = 0;
s_snapshot_count = 0u;
s_snapshot_dst = NULL;
for (i = 0u; i < 8u; i++) {
s_fake_tpafe[i] = 0;
}
}
/* ===================== TC-AI-001..006:init ===================== */
static void tc_init(void) {
int32_t v = -999;
fakes_reset();
/* 前置:制造"STM32 失败一次 + TPAFE 有效一帧"的状态,再验 init 是否清干净 */
s_snapshot_rc = 1;
s_fake_tpafe[6] = 4321;
s_start_rc = HAL_ERROR;
(void)adc_input_poll();
CHECK(adc_input_stm32_errs() == 1u, "TC-AI-001 poll 失败后 STM32 失败计数 = 1");
CHECK((adc_input_raw(ADC_SEM_UA, &v) == 0) && (v == 4321),
"TC-AI-002 poll 后 UA 可取数且值正确");
CHECK(adc_input_init() == 0, "TC-AI-003 adc_input_init 返回 0(映射自检通过)");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_NOT_RUN,
"TC-AI-004 init 后 STM32 状态码 = NOT_RUN");
CHECK(adc_input_stm32_errs() == 0u, "TC-AI-005 init 后 STM32 失败计数归零");
CHECK((adc_input_raw(ADC_SEM_UA, &v) == -2) && (adc_input_raw(ADC_SEM_UB, &v) == -2),
"TC-AI-006 init 后 TPAFE / STM32 两侧均无有效数据");
}
/* ===================== TC-AI-010..01A:desc 映射表 ===================== */
static void tc_desc(void) {
const adc_channel_desc_t *d;
d = adc_input_desc(ADC_SEM_UA);
CHECK((d != NULL) && (d->source == ADC_SRC_TPAFE) && (d->index == 6u),
"TC-AI-010 UA -> TPAFE buf[6]");
d = adc_input_desc(ADC_SEM_UB);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 14u),
"TC-AI-011 UB -> STM32 CH14");
d = adc_input_desc(ADC_SEM_UC);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 3u),
"TC-AI-012 UC -> STM32 CH3");
d = adc_input_desc(ADC_SEM_UN);
CHECK((d != NULL) && (d->source == ADC_SRC_TPAFE) && (d->index == 5u),
"TC-AI-013 UN -> TPAFE buf[5]");
d = adc_input_desc(ADC_SEM_IA);
CHECK((d != NULL) && (d->source == ADC_SRC_TPAFE) && (d->index == 2u),
"TC-AI-014 IA -> TPAFE buf[2]");
d = adc_input_desc(ADC_SEM_IB);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 6u),
"TC-AI-015 IB -> STM32 CH6");
d = adc_input_desc(ADC_SEM_IC);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 1u),
"TC-AI-016 IC -> STM32 CH1");
d = adc_input_desc(ADC_SEM_IN);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 2u),
"TC-AI-017 IN -> STM32 CH2");
d = adc_input_desc(ADC_SEM_TEMP);
CHECK((d != NULL) && (d->source == ADC_SRC_STM32) && (d->index == 5u),
"TC-AI-018 TEMP -> STM32 CH5");
CHECK(adc_input_desc(ADC_SEM_COUNT) == NULL, "TC-AI-019 越界(ADC_SEM_COUNT) -> NULL");
CHECK(adc_input_desc((adc_semantic_t)(-1)) == NULL, "TC-AI-01A 负数语义 -> NULL");
}
/* ===================== TC-AI-020:map_check ===================== */
static void tc_map_check(void) {
/* 真实映射表自洽:TPAFE 下标 < 8 且每个 STM32 通道号都出现在扫描 Rank 内。
* 反向路径(表不自洽 -> -1)需在运行时改表,而 k_adc_map / k_stm32_rank_ch 均为
* 编译期 static const、无运行时输入可使其不自洽 -> 结构上不可达(见文件末说明)。 */
CHECK(map_check() == 0, "TC-AI-020 映射表自洽(TPAFE 下标与 STM32 Rank 均在范围内)");
}
/* ===================== TC-AI-021..02C:stm32_scan 成功路径 ===================== */
static void tc_scan_ok(void) {
int32_t v = 0;
fakes_reset();
(void)adc_input_init();
CHECK(stm32_scan() == ADC_INPUT_RC_OK, "TC-AI-021 stm32_scan 全通道成功 -> 0");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_OK, "TC-AI-022 状态码 = RC_OK");
CHECK(adc_input_stm32_errs() == 0u, "TC-AI-023 成功不计入失败");
CHECK((s_start_calls == 1) && (s_stop_calls == 1), "TC-AI-024 Start / Stop 各一次");
CHECK((s_poll_calls == (int)ADC_INPUT_STM32_CH_NUM) && (s_rank_idx == (int)ADC_INPUT_STM32_CH_NUM),
"TC-AI-025 轮询与取数各 9 次(Rank1..9)");
/* Rank -> 通道号映射:第 i 个 Rank 读到 100+i。
* k_stm32_rank_ch = [CH0..CH7, CH14],故 CH3->103 / CH5->105 / CH6->106 / CH14->108。 */
CHECK((adc_input_raw(ADC_SEM_UB, &v) == 0) && (v == 108), "TC-AI-026 UB 取到 CH14(rank9)=108");
CHECK((adc_input_raw(ADC_SEM_UC, &v) == 0) && (v == 103), "TC-AI-027 UC 取到 CH3(rank4)=103");
CHECK((adc_input_raw(ADC_SEM_IB, &v) == 0) && (v == 106), "TC-AI-028 IB 取到 CH6(rank7)=106");
CHECK((adc_input_raw(ADC_SEM_IC, &v) == 0) && (v == 101), "TC-AI-029 IC 取到 CH1(rank2)=101");
CHECK((adc_input_raw(ADC_SEM_IN, &v) == 0) && (v == 102), "TC-AI-02A IN 取到 CH2(rank3)=102");
CHECK((adc_input_raw(ADC_SEM_TEMP, &v) == 0) && (v == 105), "TC-AI-02B TEMP 取到 CH5(rank6)=105");
CHECK(adc_input_raw(ADC_SEM_UA, &v) == -2, "TC-AI-02C stm32_scan 不置 TPAFE 有效标志");
}
/* ===================== TC-AI-030..034:stm32_scan 启动失败 ===================== */
static void tc_scan_start_fail(void) {
int32_t v = 0;
fakes_reset();
(void)adc_input_init();
s_start_rc = HAL_ERROR;
CHECK(stm32_scan() == ADC_INPUT_RC_START, "TC-AI-030 HAL_ADC_Start 失败 -> -1");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_START, "TC-AI-031 状态码 = RC_START");
CHECK(adc_input_stm32_errs() == 1u, "TC-AI-032 失败计数 +1");
CHECK(s_stop_calls == 0, "TC-AI-033 启动失败不调用 Stop");
CHECK(adc_input_raw(ADC_SEM_UB, &v) == -2, "TC-AI-034 启动失败后 STM32 侧仍无有效数据");
}
/* ===================== TC-AI-035..03A:stm32_scan 轮询超时 ===================== */
static void tc_scan_timeout(void) {
int32_t v = 0;
fakes_reset();
(void)adc_input_init();
s_poll_fail_at = 4; /* 第 5 个 Rank 轮询超时 */
CHECK(stm32_scan() == ADC_INPUT_RC_TIMEOUT, "TC-AI-035 轮询超时 -> -2");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_TIMEOUT, "TC-AI-036 状态码 = RC_TIMEOUT");
CHECK(adc_input_stm32_errs() == 1u, "TC-AI-037 超时计入失败");
CHECK(s_stop_calls == 1, "TC-AI-038 超时前已 Start -> 调 Stop 收尾");
CHECK(s_poll_calls == 5, "TC-AI-039 超时后立即退出(只轮询 5 次)");
CHECK(adc_input_raw(ADC_SEM_UB, &v) == -2, "TC-AI-039b 超时后 STM32 侧无有效数据");
s_poll_fail_at = 0; /* 再失败一次 */
(void)stm32_scan();
CHECK(adc_input_stm32_errs() == 2u, "TC-AI-03A 失败计数跨次单调累计 = 2");
}
/* ===================== TC-AI-040..045:raw 参数守卫与无数据 ===================== */
static void tc_raw_guards(void) {
int32_t v = 0;
fakes_reset();
(void)adc_input_init();
CHECK(adc_input_raw(ADC_SEM_UA, NULL) == -1, "TC-AI-040 out=NULL -> -1");
CHECK(adc_input_raw(ADC_SEM_COUNT, &v) == -1, "TC-AI-041 ch 越界 -> -1");
CHECK(adc_input_raw((adc_semantic_t)(-1), &v) == -1, "TC-AI-042 ch 负数 -> -1");
CHECK(v == 0, "TC-AI-043 参数错时不写 out");
CHECK(adc_input_raw(ADC_SEM_UA, &v) == -2, "TC-AI-044 TPAFE 无有效帧 -> -2");
CHECK(adc_input_raw(ADC_SEM_UB, &v) == -2, "TC-AI-045 STM32 无有效扫描 -> -2");
}
/* ===================== TC-AI-046..04B:raw TPAFE 取数与补码 ===================== */
static void tc_raw_tpafe(void) {
int32_t v = 0;
fakes_reset();
(void)adc_input_init();
/* 注入 8 路 TPAFE 码值(互不相同),UA 应取 buf[6] / UN 取 buf[5] / IA 取 buf[2] */
s_fake_tpafe[0] = 1000;
s_fake_tpafe[1] = 1001;
s_fake_tpafe[2] = -1234;
s_fake_tpafe[3] = 1003;
s_fake_tpafe[4] = 1004;
s_fake_tpafe[5] = 2005;
s_fake_tpafe[6] = 3006;
s_fake_tpafe[7] = 1007;
s_snapshot_rc = 1;
CHECK(adc_input_poll() == 1, "TC-AI-046 poll 透传 TPAFE 快照码 1");
CHECK(s_snapshot_count == (uint8_t)ADC_INPUT_TPAFE_CH_NUM, "TC-AI-047 快照请求 8 通道");
CHECK(s_snapshot_dst != NULL, "TC-AI-048 快照目的缓冲非空");
CHECK((adc_input_raw(ADC_SEM_UA, &v) == 0) && (v == 3006), "TC-AI-049 UA -> buf[6]=3006");
CHECK((adc_input_raw(ADC_SEM_UN, &v) == 0) && (v == 2005), "TC-AI-04A UN -> buf[5]=2005");
CHECK((adc_input_raw(ADC_SEM_IA, &v) == 0) && (v == -1234),
"TC-AI-04B IA -> buf[2]=-1234(补码负值原样保留)");
}
/* ===================== TC-AI-050..058:poll 契约 ===================== */
static void tc_poll(void) {
int32_t v = 0;
/* --- 无新帧(0):返回 0,TPAFE 无效,STM32 侧照常扫描 --- */
fakes_reset();
(void)adc_input_init();
s_snapshot_rc = 0;
CHECK(adc_input_poll() == 0, "TC-AI-050 快照无新帧 -> poll 返回 0");
CHECK(adc_input_raw(ADC_SEM_UA, &v) == -2, "TC-AI-051 无新帧时 TPAFE 侧无效");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_OK, "TC-AI-052 同拍 STM32 扫描照常执行");
/* --- 参数错(-1):返回 -1,TPAFE 无效 --- */
fakes_reset();
(void)adc_input_init();
s_snapshot_rc = -1;
CHECK(adc_input_poll() == -1, "TC-AI-053 快照参数错 -> poll 返回 -1");
CHECK(adc_input_raw(ADC_SEM_UA, &v) == -2, "TC-AI-054 参数错时 TPAFE 侧无效");
/* --- 已有有效帧后再"无新帧":保持上次有效值(不清零) --- */
fakes_reset();
(void)adc_input_init();
s_fake_tpafe[6] = 777;
s_snapshot_rc = 1;
(void)adc_input_poll();
s_snapshot_rc = 0;
(void)adc_input_poll();
CHECK((adc_input_raw(ADC_SEM_UA, &v) == 0) && (v == 777),
"TC-AI-055 已有有效帧后无新帧不清零");
/* --- STM32 失败不阻断 TPAFE、不影响 poll 返回值 --- */
fakes_reset();
(void)adc_input_init();
s_fake_tpafe[2] = 55;
s_snapshot_rc = 1;
s_start_rc = HAL_ERROR;
CHECK(adc_input_poll() == 1, "TC-AI-056 STM32 失败不影响 poll 返回值(仍为 1)");
CHECK((adc_input_raw(ADC_SEM_IA, &v) == 0) && (v == 55),
"TC-AI-057 STM32 失败不影响 TPAFE 取数");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_START, "TC-AI-058 STM32 失败另由状态码暴露");
}
/* ===================== TC-AI-060..066:rc / errs 观测 ===================== */
static void tc_rc_errs(void) {
fakes_reset();
(void)adc_input_init();
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_NOT_RUN, "TC-AI-060 init 后未扫描 = NOT_RUN(-3)");
CHECK(adc_input_stm32_errs() == 0u, "TC-AI-061 init 后失败计数 = 0");
(void)stm32_scan(); /* 成功 */
CHECK((adc_input_stm32_rc() == ADC_INPUT_RC_OK) && (adc_input_stm32_errs() == 0u),
"TC-AI-062 成功: rc=OK 且 errs 不变");
s_start_rc = HAL_ERROR;
(void)stm32_scan(); /* 失败 1 */
s_start_rc = HAL_OK;
s_poll_fail_at = 0;
(void)stm32_scan(); /* 失败 2 */
CHECK(adc_input_stm32_errs() == 2u, "TC-AI-063 两次失败累计 = 2");
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_TIMEOUT, "TC-AI-064 rc = 最近一次(TIMEOUT)");
s_poll_fail_at = -1;
(void)stm32_scan(); /* 恢复成功 */
CHECK(adc_input_stm32_rc() == ADC_INPUT_RC_OK, "TC-AI-065 恢复成功后 rc=OK");
CHECK(adc_input_stm32_errs() == 2u, "TC-AI-066 errs 单调递增(不因成功回落)");
}
/* ===================== main ===================== */
int main(void) {
printf("=== ADC Input Tests ===\n");
printf("-- init / reset --\n");
tc_init();
printf("-- semantic map desc --\n");
tc_desc();
printf("-- map self-check --\n");
tc_map_check();
printf("-- stm32 scan: success + rank/channel mapping --\n");
tc_scan_ok();
printf("-- stm32 scan: HAL_ADC_Start fail --\n");
tc_scan_start_fail();
printf("-- stm32 scan: poll timeout --\n");
tc_scan_timeout();
printf("-- raw: guards / no-data --\n");
tc_raw_guards();
printf("-- raw: tpafe index + two's complement --\n");
tc_raw_tpafe();
printf("-- poll contract --\n");
tc_poll();
printf("-- rc / errs observability --\n");
tc_rc_errs();
printf("\n=== ADC Input Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}