883c9dd0cc
B1(ADC 采集链路)落地,消化 P1-1: - adc_task.c:空桩 -> 真实消费者。每 1ms 触发一次 CONVST, 用 tpafe5160_snapshot() 原子取数(AGENTS 铁律)+ 帧统计 + 每 10s 心跳日志。只取数,不做保护运算。 - Src/freertos.c:手工创建 adcTask(512 words / Normal,落 USER CODE 保护区,同 timeSyncTask 先例)。 - task_watchdog:新增 TASK_WD_ADC;required_mask 产品 0x37 -> 0x77、ENABLE_HW_LAYER_TESTS 0x35 -> 0x75。 - default_task.c:栈水位探针增列 adcTask(extern + 表项)。 - 脚本:stack_report.py 增 ROOTS/RUNTIME_ALIAS;gen_task_table 增 adcTask(README/AGENTS 任务表已 --write)。 - 单测:watchdog_test.c beat_all/掩码断言同步(39 断言全绿)。 - 文档:待处理内容/看门狗设计/RAM 方案/继保规划 同步。 验证:@build 0 错 0 警;stack_report 静态最坏链 504B(余 75%); pre-commit 全绿。剩 @test 实机闭环。
249 lines
12 KiB
C
249 lines
12 KiB
C
/*
|
||
* 模块名称: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_ADC = 6, /* adcTask:1ms 采样节拍消费者(B1,2026-10-10) */
|
||
TASK_WD_ID_MAX
|
||
} task_wd_id_t;
|
||
|
||
/*
|
||
* 函数功能:任务心跳置位(各任务在自己主循环里调用)
|
||
* 入口参数:id - 任务 ID
|
||
* 返回值:无
|
||
* 限定条件:任意上下文可调(单字原子写、无内核对象依赖)
|
||
* 函数说明:仅 1~2 条指令,无锁、无阻塞,可放在中断外任意位置。
|
||
*/
|
||
void task_wd_beat(task_wd_id_t id);
|
||
|
||
/*
|
||
* 函数功能:把当前任务句柄登记进 FTL 心跳路由表(任务入口调用一次)
|
||
* 入口参数:id - 任务 ID
|
||
* 返回值:无
|
||
* 限定条件:任务上下文调用(内核启动后)
|
||
* 函数说明:nand_ftl_wd_beat() 强符号(本模块)按此表把 FTL 长耗时操作
|
||
* 的心跳路由到调用者任务的心跳位(见 gd5f2gq5ue.h 钩子说明)。
|
||
* testTask 刻意不登记:它不在必需掩码内(抽样为精确匹配),
|
||
* 其上下文置位会引入多余位反而误杀。
|
||
*/
|
||
void task_wd_bind(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 已启动 */
|
||
|
||
/*
|
||
* ==================== round 8 取证:复位不丢的"进段打点" ====================
|
||
* 背景(2026-10-02 台架 round 7~8 取证,完整链条见
|
||
* docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录):误复位时判定分支(STALL)从未
|
||
* 执行,当时判为**喂狗点 task_wd_poll 自身停摆**(round 8b 已更正:真因是 IWDG 超时
|
||
* 被静默配成复位默认值),而非某个被监视任务饿死。RAM 态随复位消失、串口又可能来不及打,
|
||
* 故需要一条**复位后仍可读**的"defaultTask 主循环最后走到哪一段"轨迹。
|
||
*
|
||
* 实现:轨迹槽放在 .noinit(scatter 的 UNINIT 执行区,见
|
||
* MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct 的 UNINIT_CCM)——IWDG/引脚复位
|
||
* 不改变 SRAM 内容,故复位后仍能读出上一轮的最后进段。槽布局:
|
||
* [0]=magic(校验是否为本模块写过)[1]=段号 [2]=进段时刻(ms) [3]=循环序号
|
||
* round 8b 追加"时间源存活性"槽(同样在 .noinit):
|
||
* cur = [magic, uwTick, 本轮 TIM7 计数, 0](TIM7 ISR 每 1ms 更新)
|
||
* prev = [magic, uwTick, 上一轮 TIM7 计数, 0](main() 在 HAL_Init 之前快照)
|
||
* 注意:**仅 MCU 构建存在**(TASK_WATCHDOG_UNIT_TEST 下不编入,主机单测不受影响)。
|
||
* ==========================================================================
|
||
*/
|
||
|
||
/* defaultTask 主循环"进段"编号(task_wd_seg 的入参;复位后从 PREVRUN 行读出) */
|
||
typedef enum {
|
||
TASK_WD_SEG_IDLE = 0, /* 未进入任何段(初值) */
|
||
TASK_WD_SEG_POLL = 1, /* task_wd_poll(喂狗判定) */
|
||
TASK_WD_SEG_EVTWAIT = 2, /* osEventFlagsWait(50ms 兜底等待) */
|
||
TASK_WD_SEG_DRAIN = 3, /* dbg_console_drain(控制台排空) */
|
||
TASK_WD_SEG_CFGSAVE = 4, /* net_cfg_save_run(配置保存) */
|
||
TASK_WD_SEG_CFGPOLL = 5, /* cfg_file_poll(2s 配置轮询) */
|
||
TASK_WD_SEG_LED = 6, /* LED 心跳 */
|
||
TASK_WD_SEG_SYSLOG = 7, /* syslog_drain(运行期日志落盘) */
|
||
TASK_WD_SEG_STACK = 8 /* stack_probe_poll(栈水位观测) */
|
||
} task_wd_seg_t;
|
||
|
||
/*
|
||
* 函数功能:记录"即将进入的主循环段"(round 8 取证;**仅 MCU 构建**)
|
||
* 入口参数:seg - 段号(task_wd_seg_t)
|
||
* 返回值:无
|
||
* 限定条件:defaultTask 主循环调用(任意上下文亦可,纯 SRAM 写)
|
||
* 函数说明:4 字写(magic/段/时刻/序号),进 .noinit 槽;复位不丢。
|
||
*/
|
||
void task_wd_seg(uint32_t seg);
|
||
|
||
/*
|
||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(round 8 取证;**仅 MCU 构建**)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:启动早期调用(内核启动前亦可,裸 UART 直发)
|
||
* 函数说明:magic 命中才打一行 `PREVRUN seg=<段> seq=<序号> t=<ms>`(裸 UART,
|
||
* 不依赖环形缓冲写者),随后清 magic(一次复位只报一次)。
|
||
* round 8b:紧接着打一行 `TICKRUN uw= n= duw=`——uw 为 TIM7 在**上一轮**
|
||
* 最后一次记下的 HAL tick、n 为上一轮 TIM7 中断总数、duw = uw - t
|
||
* (相对进段时刻的增量),据此判定 TIM7 是否在冻结同刻停摆。
|
||
*/
|
||
void task_wd_seg_report(void);
|
||
|
||
/*
|
||
* 函数功能:开机快照上一轮的"时间源存活性"(round 8b 取证;**仅 MCU 构建**)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:**必须在 HAL_Init() 之前调用**(main() 的 USER CODE BEGIN 1)
|
||
* 函数说明:TIM7 在 HAL_Init 里即开始 1ms 中断,而开机上报要到 app_main_init
|
||
* (≈117ms)才跑;本函数把上一轮的终值先搬到 prev 槽再清空 cur,
|
||
* 否则终值会被本轮开机瞬间的 tick 覆盖(2026-10-02 台架实测踩坑)。
|
||
*/
|
||
void task_wd_tick_snapshot(void);
|
||
|
||
/*
|
||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(round 8b 取证;**仅 MCU 构建**)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:TIM7 中断上下文调用(`HAL_TIM_PeriodElapsedCallback` 内,每 1ms)
|
||
* 函数说明:更新 cur 槽 = [magic, uwTick, 本轮 TIM7 中断计数, 0](纯 SRAM 写,无阻塞)。
|
||
* 与 task_wd_seg 的"进段打点"配合,复位后由 task_wd_seg_report() 上报。
|
||
* 背景(2026-10-02 台架 round 8b 取证,见 docs/子系统设计/看门狗设计说明.md
|
||
* 2026-10-02 变更记录):HAL_UART_Transmit 的超时判据
|
||
* 依赖 uwTick,时间源停摆即死等——必须先把"TIM7 是否停摆"钉死再定向修复。
|
||
* ⚠ **本函数内严禁调用任何 FreeRTOS/CMSIS-RTOS API**:TIM7 在 HAL_Init
|
||
* 阶段即开始中断(远早于 xPortStartScheduler),此时 ISR 版内核 API 会触发
|
||
* port.c 的优先级断言并把系统卡死在 TIM7 ISR 内(2026-10-02 台架实测根因)。
|
||
*/
|
||
void task_wd_tick_probe(void);
|
||
|
||
/*
|
||
* ============================ 平台后端(内部) ============================
|
||
* 把判定状态机中的硬件访问收敛到三个薄钩子,使"取走清零 / 掩码判定 / 缺失
|
||
* 累计 / 节流"成为纯逻辑,可在主机侧用桩替换后离线穷举验证
|
||
* (见 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 默认实现 = 解锁 PR/RLR → 写 WD_IWDG_PRESCALER/WD_IWDG_RELOAD
|
||
* (/64 + 1249 → 1701~2498ms,等 SR.PVU/RVU 同步)→ `IWDG->KR = 0xCCCC`。
|
||
* ⚠ 配置**必须**在此完成:`Src/iwdg.c` 为实现延后启动,把生成的
|
||
* `HAL_IWDG_Init(&hiwdg)` 经宏改名成空实现(见该文件 `USER CODE BEGIN 0`),
|
||
* 而 `HAL_IWDG_Init()` 是 HAL 里唯一把 `hiwdg.Init.*` 写进寄存器的路径;
|
||
* 缺了这一步 PR/RLR 停在复位默认值(PR=0/RLR=4095 ≈ 400ms),
|
||
* 超时比设计短约 5 倍 → 800ms 容忍分支成死代码、易 IWDG 误复位。
|
||
* 主机侧单测以强符号覆盖本钩子(不碰寄存器),故其行为不受影响。
|
||
*/
|
||
void wd_platform_start(void);
|
||
|
||
/*
|
||
* 函数功能:读单调毫秒时基(用于 100ms 抽样节流)
|
||
* 返回值:自启动以来的毫秒计数(32 位回绕安全,用无符号差比较)
|
||
* 函数说明:MCU 默认实现 = `HAL_GetTick()`
|
||
* 主机侧单测覆盖此符号以驱动时间推进。
|
||
*/
|
||
uint32_t wd_platform_tick_ms(void);
|
||
|
||
/*
|
||
* 函数功能:lftpd 看门狗心跳钩子(**本模块提供强符号实现**)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任意上下文(IWDG 启动前只置位图,不影响判定)
|
||
* 函数说明:驱动层(lftpd.c / lftpd_inet.c 的节拍点)只调用弱钩子,本模块以
|
||
* 强符号覆盖并路由到 TASK_WD_FTP 心跳位——驱动层不向上依赖应用层
|
||
* (分层铁律)。声明于 lftpd.h 供驱动侧引用,此处为"本模块提供"侧的声明。
|
||
*/
|
||
void lftpd_task_wd_beat(void);
|
||
|
||
/*
|
||
* 函数功能:NAND FTL 直接喂狗钩子(**本模块提供强符号实现**)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任意上下文(IWDG 未启动时为空操作)
|
||
* 函数说明:FTL 长耗时路径(GC/块擦除等待)在调用者任务上下文**直接写 IWDG->KR**,
|
||
* 覆盖"喂狗点(defaultTask 主循环)自身被长 I/O 阻塞"的误复位形态。
|
||
* 声明于 gd5f2gq5ue.h 供驱动侧引用,此处为"本模块提供"侧的声明。
|
||
*/
|
||
void nand_ftl_wd_feed(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 */
|