Files
STM32F4-Base/test/board/sd2506_test_task.c
T
wandering1 029437be89 台架执行《实机测试实施指南》步骤 3~7 并闭环
步骤 3/4(IWDG V2/V4/V5 + 堆低水位 S11):
- V2 任务饿死 stall 后 ≤2.5s IWDG 复位;V4 关中断死循环 ~4.9s 稳定复位;
  V5 30min 120×FTP 全 PASS、零 IWDG 复位。堆告警全程未触发(min_ever=4728B)。
- task_watchdog.c:stall 证据写盘会经 nand_ftl_wd_feed 喂狗 → 增 s_stall_evidenced
  每次 stall 仅取一次证据,避免"取证据"本身阻止复位。
- default_task.c:堆低水位告警(HEAP_LOW_WATER_B=3072)。

步骤 5(GD5F 错误通道 S9):gd5f2gq5ue.c/.h + nand_ftl.c 错误码统一为 int32_t 并
全量检查返回值;套件回归 16/16 PASSED。
- test/run_all_suites.py:--forbidden 改为按套件覆盖(GD5F 用 "HardFault;assert")——
  TC-GD5F-704 故意触发 [ERR] 回落轮询,统一默认值会误判 FAIL。

步骤 6(网络稳定性):7.1 正常路径 ✅(V5 证据);7.2 N-2 丢弃可观测性代码已具备、
49s 静默在 V5 未复现(降级低风险观察项);7.3 N-3 待 PC peer。

步骤 7(工程卫生):
- S5:新增 test-cppcheck 钩子(仅 warning,排除模拟器桩)。
- S4:新增 scripts/check_test_traceability.py(默认仅告警)。
- 待核实项 TEST_SUITE_SELECTED 实机核实 = 死代码(无使用点);RTC 套件实际正常执行,
  真缺口 = 缺套件级汇总行 → sd2506_test_task.c 补 === RTC Tests: N/M PASSED === 并
  修正 Phase 级汇总累计问题;test_config.h 补齐 RTC 并注释"当前无引用"。

验证:产品 @build 0/0;pre-commit --all-files 17/17 Passed;
run_all_suites.py --suites RTC → PASS。
文档:实机测试实施指南 / SD2506_Test_Guide / 待处理内容 / 测试与架构改进建议 同步。
2026-10-04 00:35:06 +08:00

275 lines
12 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.
/******************************************************************************
* 模块名称:SD2506 RTC Test Suite
* 模块功能:SD2506API-G RTC 驱动功能自测(黑盒,仅用公共 API)。
*
* 历史背景:本驱动曾出现"时间写不进"问题,根因是 sd2506.h 中寄存器宏写成
* "0x0FH"(H 后缀)。KEIL/ARMCC 不识别 H 整数后缀,将其编译为 0,
* 导致 SD2506_REG_CTR1 实际等于 0(秒寄存器),sd2506_write_enable()
* 把 WRTC 写到了错误寄存器,写保护永不解除。修正为 "0x0FU" 后恢复。
* 详见 docs/问题记录/SD2506_Trap_Records.md。
*
* 当前测试:Phase 1 基本读写(init / 读 / 写 / 回读一致 / 走时),
* 结束后自动恢复板子原始时间。
* 入口:sd2506_test_run()(由 start_test_task 经 TEST_SUITE_RTC 调度)
* 创建日期:2026-08-28
******************************************************************************/
#include <string.h>
#include <stdint.h>
#include "sd2506_test.h"
#include "test_config.h"
#include "cmsis_os.h"
#include "sd2506.h"
#define DBG_TAG "[RTC_TEST]"
#include "dbg_log.h"
#ifdef TEST_SUITE_RTC
/* 全局测试统计(宏自动管理) */
sd2506_test_stats_t g_sd2506_test_stats;
/* ===================== Phase 1:基本读写 ===================== */
#ifdef ENABLE_RTC_BASIC_TESTS
static void sd2506_phase_basic(void)
{
DBG_INFO("=== SD2506 Phase 1: basic read/write (public API) ===");
/* TC-RTC-001: init + 读取当前时间 */
int ret = sd2506_init();
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
sd2506_time_t r;
ret = sd2506_get_time(&r);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
DBG_INFO(" current RTC: %04u-%02u-%02u %02u:%02u:%02u wk=%u",
(unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day,
(unsigned int)r.hour, (unsigned int)r.minute,
(unsigned int)r.second, (unsigned int)r.week);
}
/* TC-RTC-002: 写固定时间,立即回读比对(破坏性:改设备 RTC 时间,套件结束会复原)。
* 若 FAIL -> 先查 sd2506.h 寄存器宏后缀是否为 H(曾因 0x0FH 被 KEIL 编译为 0
* 导致 SD2506_REG_CTR1 指向秒寄存器、WRTC 写错地址)。详见 docs/问题记录/SD2506_Trap_Records.md。 */
#ifdef ENABLE_DESTRUCTIVE_TESTS
{
sd2506_time_t t;
int match;
t.year = 2026; t.month = 8; t.day = 28; t.hour = 12; t.minute = 0; t.second = 0; t.week = 4;
ret = sd2506_set_time(&t);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_time() ret=%d (expect 0)", ret);
ret = sd2506_get_time(&r);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
DBG_INFO(" set : %04u-%02u-%02u %02u:%02u:%02u",
(unsigned int)t.year, (unsigned int)t.month, (unsigned int)t.day,
(unsigned int)t.hour, (unsigned int)t.minute, (unsigned int)t.second);
DBG_INFO(" readback: %04u-%02u-%02u %02u:%02u:%02u",
(unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day,
(unsigned int)r.hour, (unsigned int)r.minute, (unsigned int)r.second);
}
match = (r.year == t.year && r.month == t.month && r.day == t.day &&
r.hour == t.hour && r.minute == t.minute &&
(r.second == t.second || r.second == ((t.second + 1U) % 60U)));
SD2506_TEST_CHECK(match, "set==readback (in-session write takes effect)");
}
#else
DBG_INFO("TC-RTC-002 skipped: destructive tests disabled (RTC time write)");
#endif
/* TC-RTC-003: 走时。写后等 3s,应前进约 3 秒 */
if (ret == SD2506_OK) {
osDelay(3000);
sd2506_time_t r2;
int ret2 = sd2506_get_time(&r2);
if (ret2 == SD2506_OK) {
int adv = (r2.second + 60 * (r2.minute + 60 * (r2.hour + 24 * r2.day)))
- (r.second + 60 * (r.minute + 60 * (r.hour + 24 * r.day)));
if (adv < 0) adv += 60 * 60 * 24 * 32;
DBG_INFO(" 3s later: %04u-%02u-%02u %02u:%02u:%02u (advanced %d s)",
(unsigned int)r2.year, (unsigned int)r2.month, (unsigned int)r2.day,
(unsigned int)r2.hour, (unsigned int)r2.minute, (unsigned int)r2.second, adv);
SD2506_TEST_CHECK(adv >= 2 && adv <= 5, "RTC ticking forward (~3s)");
}
}
SD2506_TEST_REPORT("Phase 1 basic");
}
#endif /* ENABLE_RTC_BASIC_TESTS */
/* ===================== Phase 2:全功能 API 覆盖 ===================== */
#ifdef ENABLE_RTC_API_TESTS
static void sd2506_phase_api(void)
{
DBG_INFO("=== SD2506 Phase 2: full API coverage ===");
/* TC-RTC-010: BCD<->DEC 互转(纯算法,确定性,无需硬件交互) */
int conv_ok = 1;
uint8_t samples[][2] = {
{0, 0x00U}, {1, 0x01U}, {9, 0x09U}, {10, 0x10U}, {23, 0x23U},
{59, 0x59U}, {60, 0x60U}, {99, 0x99U}, {42, 0x42U}, {88, 0x88U}
};
for (uint32_t i = 0U; i < (sizeof(samples) / sizeof(samples[0])); i++) {
uint8_t dec = samples[i][0];
uint8_t bcd = samples[i][1];
if (sd2506_dec_to_bcd(dec) != bcd) {
conv_ok = 0;
DBG_ERROR(" dec_to_bcd(%u) != 0x%02X", (unsigned int)dec, (unsigned int)bcd);
}
if (sd2506_bcd_to_dec(bcd) != dec) {
conv_ok = 0;
DBG_ERROR(" bcd_to_dec(0x%02X) != %u", (unsigned int)bcd, (unsigned int)dec);
}
}
SD2506_TEST_CHECK(conv_ok, "bcd<->dec round-trip (10 samples)");
/* TC-RTC-011: 内部温度读取 */
int8_t temp = 0;
int ret = sd2506_get_temperature(&temp);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_temperature() ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
DBG_INFO(" temperature: %d C", (int)temp);
SD2506_TEST_CHECK(temp >= -40 && temp <= 85,
"temperature in range [-40,85]: %d", (int)temp);
}
/* TC-RTC-012: 电池电压(毫伏) */
uint16_t volt = 0U;
ret = sd2506_get_battery_voltage(&volt);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_battery_voltage() ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
DBG_INFO(" battery: %u mV", (unsigned int)volt);
SD2506_TEST_CHECK(volt > 2000U && volt < 5000U,
"battery voltage plausible: %u mV", (unsigned int)volt);
}
/* TC-RTC-013: 芯片 8 字节 ID */
uint8_t id[8] = {0};
ret = sd2506_get_id(id);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_id() ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
DBG_INFO(" id: %02X%02X%02X%02X%02X%02X%02X%02X",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]);
}
/* TC-RTC-014: 用户 SRAM 读写回环(addr=0, len=8;破坏性:写芯片 SRAM) */
#ifdef ENABLE_DESTRUCTIVE_TESTS
{
uint8_t wbuf[8] = {0xA1U, 0xB2U, 0xC3U, 0xD4U, 0xE5U, 0xF6U, 0x07U, 0x18U};
uint8_t rbuf[8] = {0};
ret = sd2506_write_sram(0U, wbuf, 8U);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_write_sram() ret=%d (expect 0)", ret);
if (ret == SD2506_OK) {
ret = sd2506_read_sram(0U, rbuf, 8U);
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_read_sram() ret=%d (expect 0)", ret);
int sram_ok = (memcmp(wbuf, rbuf, 8U) == 0);
SD2506_TEST_CHECK(sram_ok, "SRAM write==readback");
}
}
#else
DBG_INFO("TC-RTC-014 skipped: destructive tests disabled (RTC SRAM write)");
#endif
/* TC-RTC-015: 报警设置 + 清除(破坏性:写报警寄存器;验证 API 通路,不影响时间寄存器) */
#ifdef ENABLE_DESTRUCTIVE_TESTS
{
sd2506_time_t at;
at.year = 2026; at.month = 8; at.day = 28; at.hour = 12; at.minute = 0; at.second = 30; at.week = 4;
ret = sd2506_set_alarm(&at, (uint8_t)(SD2506_AL_EN_EAS | SD2506_AL_EN_EAMN));
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_alarm() ret=%d (expect 0)", ret);
ret = sd2506_clear_alarm();
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_clear_alarm() ret=%d (expect 0)", ret);
}
#else
DBG_INFO("TC-RTC-015 skipped: destructive tests disabled (RTC alarm write)");
#endif
SD2506_TEST_REPORT("Phase 2 API");
}
#endif /* ENABLE_RTC_API_TESTS */
/* ===================== 套件入口 ===================== */
void sd2506_test_run(void)
{
DBG_INFO("SD2506 test suite started");
#ifdef ENABLE_DESTRUCTIVE_TESTS
DBG_INFO("mode: DESTRUCTIVE (RTC time / SRAM / alarm writes enabled)");
#else
DBG_INFO("mode: READ-ONLY (destructive cases skipped; set "
"ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)");
#endif
int ret = sd2506_init();
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
/* 信息:读取 CTR1 默认状态(确认 WRTC 初始/可写能力,纯诊断) */
uint8_t c1 = 0x00U;
if (sd2506_read_ctr1(&c1) == SD2506_OK) {
DBG_INFO(" CTR1=0x%02X (WRTC2=CTR1.2=%u WRTC3=CTR1.7=%u)",
(unsigned int)c1,
(unsigned int)((c1 >> 2) & 1U),
(unsigned int)((c1 >> 7) & 1U));
}
#ifdef ENABLE_DESTRUCTIVE_TESTS
/* 记录原始时间,测试结束恢复,避免把板子时间留在测试值 */
sd2506_time_t orig;
int have_orig = (sd2506_get_time(&orig) == SD2506_OK);
if (!have_orig) {
DBG_INFO(" WARN: cannot read original time; restore step will be skipped");
}
#endif
/* 套件级累计:每个 Phase 进入前自清零,阶段结束后把本阶段结果累加进来。
* 目的有二:
* 1) 各 Phase 的 `=== Phase N ... ===` 汇总行只反映"本阶段"的断言数,
* 不再出现"Phase 2 显示的是含 Phase 1 的累计值"这种易误读输出;
* 2) 末尾输出套件级 `=== RTC Tests: N/M PASSED (failed=0) ===` 汇总行——
* 它是 run_all_suites.py --require-suite-summary 的判定标记
* (见 test_common.h / docs/测试说明/测试开发标准流程.md §3.2)。 */
uint16_t suite_total = 0U;
uint16_t suite_passed = 0U;
uint16_t suite_failed = 0U;
#ifdef ENABLE_RTC_BASIC_TESTS
memset(&g_sd2506_test_stats, 0, sizeof(g_sd2506_test_stats));
sd2506_phase_basic();
suite_total += g_sd2506_test_stats.total;
suite_passed += g_sd2506_test_stats.passed;
suite_failed += g_sd2506_test_stats.failed;
#endif
#ifdef ENABLE_RTC_API_TESTS
memset(&g_sd2506_test_stats, 0, sizeof(g_sd2506_test_stats));
sd2506_phase_api();
suite_total += g_sd2506_test_stats.total;
suite_passed += g_sd2506_test_stats.passed;
suite_failed += g_sd2506_test_stats.failed;
#endif
#ifdef ENABLE_DESTRUCTIVE_TESTS
/* 恢复原始时间(用公共 API,写保护逻辑已由 sd2506.c 正确处理) */
if (have_orig) {
int r = sd2506_set_time(&orig);
DBG_INFO(" restored original time (ret=%d)", r);
}
#endif
/* 套件级汇总行:名称固定 "RTC Tests",与 run_all_suites.py 的
* --require-suite-summary 约定一致。复用公共报告宏,保证格式统一。 */
g_sd2506_test_stats.total = suite_total;
g_sd2506_test_stats.passed = suite_passed;
g_sd2506_test_stats.failed = suite_failed;
SD2506_TEST_REPORT("RTC Tests");
DBG_INFO("SD2506 test suite done");
for (;;) {
osDelay(1000);
}
}
#endif /* TEST_SUITE_RTC */