Files
STM32F4-Base/App/util/task_watchdog.h
T
wandering1 73941da6b1 看门狗判定状态机增加主机侧单元测试(L0.5 层)
为 task_watchdog.c 的判定逻辑(位图取走清零 / 必需掩码 0x37 判定 /
miss_streak 连续 8 次停喂 / 100ms 抽样节流 / tick 回绕安全)补一层
主机侧纯逻辑单元测试,弥补此前只能靠真机或模拟器才能发现回归的空白。

架构(沿用项目既有"共享核心 + 平台后端"做法,参照 cfg_file_store):
- task_watchdog.h 末段新增"平台后端(内部)"声明 3 个钩子
  (wd_platform_feed / _start / _tick_ms),MCU 端以 __weak 提供默认实现,
  行为与重构前逐字一致(@build 0 错误 0 警告,Flash +20B)。
- 主机侧单测以同名强符号覆盖三钩子,即可离线注入/观测,无需碰 MCU 代码。
- 测试文件 #include 生产源文件本身,保证测的是真代码而非副本。
- 测试专用复位 task_wd_test_reset() 仅 TASK_WATCHDOG_UNIT_TEST 下编入,
  MCU 构建零代码零符号。

测试(test/host/watchdog/,MSVC 离线编译,纯标准库驱动):
- 39 项断言全过(退出码 0);注入故障可复现退出码 1。
- run_unit_test.py 直接拼 INCLUDE/LIB 调 cl.exe,绕开本机被禁的 reg.exe。
- stub/ 三桩头覆盖 main.h / iwdg.h / dbg_log.h。

门禁:pre-commit 新增 watchdog-unit-test 本地钩子(L0.5 层),仅被测文件
或其测试变动时触发;测试开发标准流程.md 新增 L0.5 层说明。

顺带改善:task_watchdog.c 的节流基线 s_last_tick 由函数内 static 提为文件
static(可测性 + 模块状态归一)。
2026-10-01 06:57:01 +08:00

126 lines
4.5 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.
/*
* 模块名称:Task Watchdog(IWDG + 任务心跳位图)
* 模块功能:独立看门狗与"全部任务存活"绑定的对外接口
* 适用平台:STM32F407ZGTx + FreeRTOS(CMSIS-RTOS V2)
* 作者:王建锋
* 创建日期:2026-09-29
* 修改记录:
* v1.0 2026-09-29 王建锋 创建
*
* 设计依据:docs/子系统设计/看门狗设计说明.md
*/
#ifndef TASK_WATCHDOG_H
#define TASK_WATCHDOG_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* 任务 ID(位序号 = 心跳位图上的位置,与必需掩码一一对应)
* 顺序一旦确定不要插空/改动,掩码是编译期常量
*/
typedef enum {
TASK_WD_DEFAULT = 0,
TASK_WD_NET = 1,
TASK_WD_FTP = 2,
TASK_WD_TEST = 3,
TASK_WD_UI = 4,
TASK_WD_TIMESYNC = 5,
TASK_WD_ID_MAX
} task_wd_id_t;
/*
* 函数功能:任务心跳置位(各任务在自己主循环里调用)
* 入口参数:id - 任务 ID
* 返回值:无
* 限定条件:任意上下文可调(单字原子写、无内核对象依赖)
* 函数说明:仅 1~2 条指令,无锁、无阻塞,可放在中断外任意位置。
*/
void task_wd_beat(task_wd_id_t id);
/*
* 函数功能:启动 IWDG(**延后启动**)
* 入口参数:无
* 返回值:无
* 限定条件:仅 defaultTask 在存储 bring-up 完成后调用一次
* 函数说明:MX_IWDG_Init() 只填参数不启动(原因见 Src/iwdg.c 的
* IWDG_DEFER_START 注释:HAL_IWDG_Init 内部会立即启动,而它被
* CubeMX 插在 app_main_init 之前,那段无喂狗点且耗时可达数秒)。
* 本函数才真正启动,使"启动段不喂狗、主循环段才喂"成立。
*/
void task_wd_start(void);
/*
* 函数功能:心跳抽样与喂狗判定
* 入口参数:无
* 返回值:无
* 限定条件:仅 defaultTask 主循环调用(唯一喂狗者)
* 函数说明:内部节流至 100ms 一次。全员到位才喂狗;连续 800ms 有任务
* 缺席则停止喂狗,由 IWDG 自然溢出复位(保留 IWDGRSTF 权威归因)。
*/
void task_wd_poll(void);
/* 观测接口(自检 / 串口报告用) */
uint32_t task_wd_last_mask(void); /* 上次抽样到的位图(0 位 = 缺席任务) */
uint32_t task_wd_miss_count(void); /* 累计未达标轮数 */
uint32_t task_wd_is_armed(void); /* 1 = IWDG 已启动 */
/*
* ============================ 平台后端(内部) ============================
* 把判定状态机中的硬件访问收敛到三个薄钩子,使"取走清零 / 掩码判定 / 缺失
* 累计 / 节流"成为纯逻辑,可在主机侧用桩替换后离线穷举验证
* (见 test/host/watchdog/ 与 docs/测试说明/看门狗单元测试说明.md)。
*
* 与 App/cfg/cfg_file_store.h 的"共享核心 + 平台后端"同款做法:
* - 三者由 task_watchdog.c 以 __weak 提供 MCU 默认实现(读写 IWDG 寄存器 /
* 调 HAL_GetTick),**行为与不抽钩子时逐字一致**;
* - 主机侧单测提供同名**强符号**覆盖 → 不碰 MCU 代码即可观测/注入;
* - MCU 构建零行为变化。
*
* ⚠ 非模块对外接口,业务代码**不要**直接调用(由 task_wd_* 间接使用)。
* ========================================================================
*/
/*
* 函数功能:重装 IWDG 计数器(喂狗)
* 返回值:无
* 函数说明:MCU 默认实现 = `IWDG->KR = 0x0000AAAAu;`
* 主机侧单测覆盖此符号以记录"何时被喂"。
*/
void wd_platform_feed(void);
/*
* 函数功能:启动 IWDG(不可逆,启动后只能靠复位停止)
* 返回值:无
* 函数说明:MCU 默认实现 = `IWDG->KR = 0x0000CCCCu;`
*/
void wd_platform_start(void);
/*
* 函数功能:读单调毫秒时基(用于 100ms 抽样节流)
* 返回值:自启动以来的毫秒计数(32 位回绕安全,用无符号差比较)
* 函数说明:MCU 默认实现 = `HAL_GetTick()`
* 主机侧单测覆盖此符号以驱动时间推进。
*/
uint32_t wd_platform_tick_ms(void);
/*
* 函数功能:复位判定状态机(**仅主机侧单元测试使用**)
* 返回值:无
* 函数说明:仅当定义 TASK_WATCHDOG_UNIT_TEST 时编入(MCU 构建无此符号)。
* 用于在用例之间清空位图/计数/节流基线,保证用例互不污染。
*/
#ifdef TASK_WATCHDOG_UNIT_TEST
void task_wd_test_reset(void);
#endif
#ifdef __cplusplus
}
#endif
#endif /* TASK_WATCHDOG_H */