实现独立看门狗: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:
+26
-2
@@ -17,6 +17,7 @@
|
||||
#include "net_socket.h"
|
||||
#include "sys_clock.h"
|
||||
#include "sys_events.h"
|
||||
#include "task_watchdog.h"
|
||||
|
||||
#define DBG_TAG "[TIME_SYNC]"
|
||||
#include "dbg_log.h"
|
||||
@@ -36,6 +37,11 @@
|
||||
*/
|
||||
#define TIME_SYNC_TOTAL_BUDGET_MS 30000U
|
||||
|
||||
/* 看门狗分片等待:单次 net_recv_to 的等待片长上限。须显著小于看门狗抽样窗
|
||||
* (task_watchdog.c WD_POLL_PERIOD_MS=100ms)的容错窗口(8 窗 = 800ms),
|
||||
* 200ms 留足余量;语义上仅把一次长等待拆成多次短等待,总预算不变。 */
|
||||
#define TIME_SYNC_WAIT_SLICE_MS 200U
|
||||
|
||||
/*
|
||||
* 函数功能:创建并监听 TCP 时间同步服务端口
|
||||
* 入口参数:无
|
||||
@@ -49,6 +55,7 @@ static int time_sync_open_listener(void) {
|
||||
int retry;
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
task_wd_beat(TASK_WD_TIMESYNC); /* 每次重试前喂心跳(见下方 500ms/2000ms 延时) */
|
||||
int s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||
if (s < 0) {
|
||||
DBG_ERROR("time_sync: net_socket FAIL");
|
||||
@@ -65,6 +72,7 @@ static int time_sync_open_listener(void) {
|
||||
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
|
||||
net_close(s);
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
@@ -72,6 +80,7 @@ static int time_sync_open_listener(void) {
|
||||
if (net_listen(s) != 0) {
|
||||
DBG_ERROR("time_sync: listen FAIL");
|
||||
net_close(s);
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
@@ -99,7 +108,11 @@ static void time_sync_handle_conn(int conn_sock) {
|
||||
|
||||
while (got < (int)sizeof(buf)) {
|
||||
/* 单次预算 = 剩余总预算(L3 pending:等待期 netTask 零占用,调用方
|
||||
* 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役) */
|
||||
* 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役)
|
||||
* ⚠ 看门狗约束:本任务在 required_mask 内,而单次 net_recv_to 可睡满
|
||||
* 30s 预算 —— 等待期必须周期性喂心跳,否则"对端连上但不发时间包"这种
|
||||
* 完全正常的场景会被判为任务卡死(见设计说明 §4.4 长耗时白名单)。
|
||||
* 做法:把长等待切成 WD 窗口内的短片(片长 ≤200ms),每片先喂心跳。 */
|
||||
TickType_t elapsed = xTaskGetTickCount() - t0;
|
||||
uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(TIME_SYNC_TOTAL_BUDGET_MS)) ? 0U
|
||||
: (uint32_t)((TickType_t)TIME_SYNC_TOTAL_BUDGET_MS - elapsed);
|
||||
@@ -107,6 +120,10 @@ static void time_sync_handle_conn(int conn_sock) {
|
||||
DBG_INFO("time_sync: no TIME packet in 30s, drop conn");
|
||||
break; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
if (rem > TIME_SYNC_WAIT_SLICE_MS) {
|
||||
rem = TIME_SYNC_WAIT_SLICE_MS; /* 分片:等待期保持心跳节奏 */
|
||||
}
|
||||
int n = net_recv_to(conn_sock, buf + got, (int)(sizeof(buf) - got), rem);
|
||||
if (n > 0) {
|
||||
got += n;
|
||||
@@ -119,7 +136,7 @@ static void time_sync_handle_conn(int conn_sock) {
|
||||
break; /* 对端关闭(EOF) */
|
||||
}
|
||||
if (n == -2) {
|
||||
continue; /* 本次预算等满无数据 → 下轮 rem=0 退出 */
|
||||
continue; /* 本片预算等满无数据 → 下一片重算剩余预算 */
|
||||
}
|
||||
break; /* 真错误 */
|
||||
}
|
||||
@@ -157,12 +174,17 @@ void time_sync_task(void *argument) {
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* 心跳:本任务在 required_mask 内。以下每一处可能长于 100ms 抽样窗的
|
||||
* 阻塞点(监听重试 2s、accept 轮询 200ms/次、收尾 50ms)都就近补心跳。 */
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
|
||||
/* 记录本次监听对应的网络代次;代次变化(网络重建)则放弃旧监听重建 */
|
||||
uint32_t gen = net_get_generation();
|
||||
|
||||
int ls = time_sync_open_listener();
|
||||
if (ls < 0) {
|
||||
DBG_ERROR("time_sync: open listener FAIL, retry later");
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
@@ -176,6 +198,7 @@ void time_sync_task(void *argument) {
|
||||
net_close(ls); /* 网络重建:旧监听失效,回外层重建 */
|
||||
break;
|
||||
}
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
} while (conn < 0);
|
||||
@@ -188,6 +211,7 @@ void time_sync_task(void *argument) {
|
||||
time_sync_handle_conn(conn);
|
||||
/* conn(单连接模式下即 ls)已被 net_close 释放,下次循环重建监听 */
|
||||
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user