实现独立看门狗:CubeMX 配置 IWDG + 任务心跳位图,恢复三处被覆盖定制

经 CubeMX 图形界面启用 IWDG(Prescaler=64 / Reload=1249,标称 2498ms /
最坏 1701ms),并落地心跳位图判定、各任务埋点、RCC_CSR 复位原因上报与
configASSERT 改造。重生成覆盖的既有定制已全部恢复。

P0(开机死循环):HAL_IWDG_Init() 内部立即启动看门狗,而 CubeMX 把
MX_IWDG_Init() 插在 app_main_init() 之前——该段 NAND/RTC/ADC 初始化耗时
数秒无喂狗点,按 2.5s 超时必然复位。改 Src/iwdg.c 为只填参数不启动,
启动移交 task_wd_start(),由 defaultTask 在存储 bring-up 完成后调用。

恢复的三处定制(重生成覆盖):
- Src/stm32f4xx_it.c:HardFault_Handler 崩溃现场采集被打回空 while(1)
- Inc/FreeRTOSConfig.h:栈溢出检测与堆失败钩子两个宏被整块删除
- Src/freertos.c:三处任务栈实测记录中文注释丢失(数值未丢)

新增:
- App/util/task_watchdog.{c,h}:心跳位图 + 抽样判定 + IWDG 延后启动
- time_sync.c 长等待切片:net_recv_to 单次可睡满 30s 预算,切成 200ms 片
- Src/main.c 复位原因锁存(读后即清 RMVF,防残留误判)

埋点:defaultTask(唯一喂狗者)/ netTask(含 PHY 等待循环)/ uiTask /
timeSyncTask。ftpTask 首期剔除掩码,待二期改 lftpd。

门禁:build 0 错误 0 警告;pre-commit 10 钩子全通过;Flash +2956B / RAM +40B。
This commit is contained in:
2026-09-29 07:22:04 +08:00
parent e18e9f2cfd
commit 7feeea6c56
24 changed files with 1761 additions and 361 deletions
+13
View File
@@ -30,6 +30,7 @@
#include "sys_log.h"
#include "sys_events.h"
#include "dbg_log.h"
#include "task_watchdog.h"
/* 任务句柄与属性块(CubeMX 生成全局,定义于 Src/freertos.c):
* 栈水位观测需 6 任务句柄 + 属性块 stack_size(配置栈大小的唯一定义处) */
@@ -149,7 +150,19 @@ void default_task_func(void *argument) {
* 并让脏指针无法再进入 HAL_UART_Transmit 的缓冲遍历。 */
dbg_console_writer_attach();
/* 启动看门狗(延后启动):此处位于全部长耗时初始化之后——存储 bring-up 深链
* (mount/mkfs/FAT 探针/syslog 挂载/配置加载)已完成,监视一开始即处于稳态。
* 这正是"启动段不喂狗、主循环段才喂"的落点(见 docs/子系统设计/看门狗设计说明.md
* §4.5 决策 C):MX_IWDG_Init() 只填参数不启动,启动延后到此时。
* 代价(显式声明):从复位到此处约数秒的窗口无看门狗保护,该段死机表现为
* "无任何输出 + LED 不闪",可人工辨识。 */
task_wd_start();
for (;;) {
/* 心跳抽样与喂狗判定(内部节流至 100ms;全员到位才喂,否则累计停喂) */
task_wd_poll();
/* 本任务自己的心跳 */
task_wd_beat(TASK_WD_DEFAULT);
/* 等待"保存网络配置"或"控制台待发":任一即醒;无事件 50ms 后返回
* (日志时延 = 事件唤醒,实测无丢失;50ms 兜底仅防事件位异常) */
if (sys_evtHandle != NULL) {
+12
View File
@@ -28,6 +28,7 @@
#include "sys_events.h"
#include "sys_log.h"
#include "test_config.h" /* 测试模式宏(ENABLE_HW_LAYER_TESTS);产品构建全未定义,无作用 */
#include "task_watchdog.h"
#define DBG_TAG "[NET_TASK]"
#include "dbg_log.h"
@@ -112,6 +113,10 @@ int net_stack_bring_up(void) {
if (phy != CH395F_PHY_DISCONN) {
break;
}
/* 心跳埋点:本循环单次最长约 1.1s(含 PHY 仍未就绪的 60 次迭代里
* 每次 50ms 延时叠加),远超看门狗 100ms 抽样窗——必须在延时段补心跳,
* 否则 PHY 未插网线时本任务会被误判为卡死(见设计说明 §4.4 长耗时白名单) */
task_wd_beat(TASK_WD_NET);
osDelay(50);
}
DBG_INFO("PHY: %s",
@@ -166,6 +171,9 @@ void net_task_func(void const *arg) {
#if defined(ENABLE_HW_LAYER_TESTS)
DBG_INFO("netTask parked: HW-layer test mode, CH395F owned by testTask");
for (;;) {
/* 该模式下本任务已被 task_wd_required_mask() 剔除,喂心跳无副作用;
* 保留埋点以免掩码配置变更时意外误杀 */
task_wd_beat(TASK_WD_NET);
osDelay(1000);
}
#endif
@@ -196,6 +204,10 @@ void net_task_func(void const *arg) {
/* ==================== 主轮询循环 ==================== */
for (;;) {
/* 心跳:本任务在监视掩码内(ENABLE_HW_LAYER_TESTS 下由 task_wd_required_mask
* 剔除,本埋点无副作用) */
task_wd_beat(TASK_WD_NET);
/* UI 保存网口配置后请求立即应用:在此重建网络(新 IP/掩码/网关/MAC)。
* 重建完成后代次号已递增,各 TCP 服务据此重建监听;期间连接会断开(预期)。 */
if (sys_evtHandle != NULL
+7
View File
@@ -21,6 +21,7 @@
#include "sys_clock.h"
#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */
#include "net_latency_test.h" /* M3 探针:ui_loop(生产循环实测周期) */
#include "task_watchdog.h"
/*
* 函数功能:待机屏底部时钟提供回调(平台差异:MCU = sys_clock_get_str)
@@ -63,6 +64,9 @@ static void ui_task_wait_net_ready(void) {
void ui_task_func(void *argument) {
(void)argument;
/* 等待网络就绪期间不可能喂狗:本任务此时尚未进入主循环,而就绪位由 netTask 置位。
* 该段若长期不成立,看门狗会复位——这是期望行为(网络 bring-up 卡死须暴露)。
* 注意本任务在 required_mask 内,主循环跑起来后须保证每 100ms 窗口内至少喂一次。 */
DBG_INFO("UI task: started, wait net ready");
ui_task_wait_net_ready();
DBG_INFO("UI task: net ready, init port");
@@ -108,6 +112,9 @@ void ui_task_func(void *argument) {
#endif
ui_port_step(10);
ui_net_sink_poll();
/* 心跳:ui_port_step 内含 LVGL 渲染,单轮耗时可能逼近 10ms 量级;
* 100ms 抽样窗内可容纳多轮,此埋点足够 */
task_wd_beat(TASK_WD_UI);
osDelay(10);
}
}