修复IWDG超时未配置与dhara resume回滚:看门狗误复位与复位后写入全部丢失,台架全量自检PASS

This commit is contained in:
2026-10-02 20:48:30 +08:00
parent d1a2eaab07
commit 930215f81a
33 changed files with 1875 additions and 94 deletions
+50 -16
View File
@@ -10,6 +10,10 @@
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
* v1.2 2026-10-02 王建锋 round 8b 消抖:等待片长 200ms→50ms,并把各处长
* 等待(500/2000/200ms)统一走 time_sync_delay_fed()
* 按 ≤50ms 分片、每片喂心跳——消除"心跳滑出两个连续
* 100ms 抽样窗"的节拍抖动(round 7 台架 snap=0x17)
*/
#include <string.h>
@@ -38,9 +42,31 @@
#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
* (task_watchdog.c WD_POLL_PERIOD_MS=100ms)——片长 ≤50ms 时每片前喂的心跳
* 必然落在同一个 100ms 抽样窗内,消除"心跳恰好滑过两个连续窗口"的边界抖动
* (round 7 台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,round 8b 消抖)。
* 语义上仅把一次长等待拆成多次短等待,总预算不变。 */
#define TIME_SYNC_WAIT_SLICE_MS 50U
/*
* 函数功能:可喂心跳的延时(把长等待切成 ≤50ms 短片,每片喂一次心跳)
* 入口参数:ms - 总延时(ms) uint32_t
* 返回值:无
* 限定条件:任务上下文调用
* 函数说明:看门狗抽样窗为 100ms(task_watchdog.c WD_POLL_PERIOD_MS)。把任意长
* 等待切成 ≤TIME_SYNC_WAIT_SLICE_MS 的短片、每片前喂心跳,使心跳必然
* 落在单个抽样窗内——语义等价于一次长延时,仅把"心跳节奏"钉在窗内
* (round 8b 消抖;不改变总延时预算)。
*/
static void time_sync_delay_fed(uint32_t ms) {
while (ms > 0U) {
uint32_t slice = (ms > TIME_SYNC_WAIT_SLICE_MS) ? TIME_SYNC_WAIT_SLICE_MS : ms;
task_wd_beat(TASK_WD_TIMESYNC);
vTaskDelay(pdMS_TO_TICKS(slice));
ms -= slice;
}
}
/*
* 函数功能:创建并监听 TCP 时间同步服务端口
@@ -59,7 +85,7 @@ static int time_sync_open_listener(void) {
int s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (s < 0) {
DBG_ERROR("time_sync: net_socket FAIL");
vTaskDelay(pdMS_TO_TICKS(500));
time_sync_delay_fed(500U);
continue;
}
@@ -72,16 +98,14 @@ 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));
time_sync_delay_fed(2000U);
continue;
}
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));
time_sync_delay_fed(2000U);
continue;
}
@@ -166,11 +190,24 @@ static void time_sync_handle_conn(int conn_sock) {
void time_sync_task(void *argument) {
(void)argument;
task_wd_bind(TASK_WD_TIMESYNC); /* FTL 心跳路由:登记本任务句柄(入口一次) */
/* 等网络栈真正就绪(取代拍脑袋的 8s 魔法延时),确保 netTask 已开始处理消息;
* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒) */
if (sys_evtHandle != NULL) {
(void)osEventFlagsWait(sys_evtHandle, SYS_EVT_NET_READY,
osFlagsWaitAny | osFlagsNoClear, osWaitForever);
/* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒)。
* 2026-10-02 台架修复:等待改 100ms 分片 + 每片喂心跳——IWDG 必需掩码含
* TS 位,原 osWaitForever 无心跳 → 启动初期(存储已就绪、网络未就绪)
* 掩码 800ms 不可达 → 停喂 → 复位循环(2026-10-02 台架根因之一)。
* 100ms 分片 = 抽样窗,纯任务睡眠零队列开销。 */
for (;;) {
uint32_t r = osEventFlagsWait(sys_evtHandle, SYS_EVT_NET_READY,
osFlagsWaitAny | osFlagsNoClear, 100u);
if ((r & osFlagsError) == 0u) {
break; /* NET_READY 已置位(状态位,未被消费) */
}
task_wd_beat(TASK_WD_TIMESYNC); /* 看门狗:分片喂心跳(屏障等待态) */
}
}
for (;;) {
@@ -184,8 +221,7 @@ void time_sync_task(void *argument) {
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));
time_sync_delay_fed(2000U);
continue;
}
@@ -198,8 +234,7 @@ void time_sync_task(void *argument) {
net_close(ls); /* 网络重建:旧监听失效,回外层重建 */
break;
}
task_wd_beat(TASK_WD_TIMESYNC);
vTaskDelay(pdMS_TO_TICKS(200));
time_sync_delay_fed(200U);
}
} while (conn < 0);
@@ -211,7 +246,6 @@ 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));
time_sync_delay_fed(50U);
}
}