#include "wdt_task/wdt_task.h" #if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D #include "iwdg.h" /* [CCU601E_D] STM32 IWDG 独立看门狗 */ #endif #include "sys_drv_init.h" #include "publicdata/public_define.h" #include "mylog/mylog.h" #include "publicdata/publicdata.h" #define WDT_TASK_CNT ((uint8_t)MY_TASK_NUM) #define WDT_TIMEOUT_SECONDS (300U) #define WDT_RESET_ENABLE 0U /* 1: 超时后触发系统复位; 0: 仅钳位计数 */ static uint16_t g_wdt_evt_flag[WDT_TASK_CNT] = {0U}; #define WDT_LOG(fmt, ...) \ MYLOG_MSG(TASK_ID_WATCHDOG,fmt, ##__VA_ARGS__) /** * @brief 获取指定任务的看门狗计数值。 * * 计数值含义:距离该任务最近一次调用 `v_wdt_setFlag()` 的秒数。 * 当任务ID非法时返回0。 * * @param id 任务ID(对应 `TASK_ID` 枚举值)。 * @return uint16_t 对应任务当前计数值。 */ uint16_t u16_get_wdt_cnt(uint8_t id) { if (id < WDT_TASK_CNT) { return g_wdt_evt_flag[id]; } return 0U; } /** * @brief 清零指定任务的看门狗计数(任务喂狗)。 * * 该函数通常由各业务任务在其主循环中周期调用,用于表明任务仍在正常运行。 * 传入ID越界时函数静默返回,不执行任何写操作。 * * @param task_id 任务ID(`TASK_ID` 枚举)。 */ void v_wdt_setFlag(TASK_ID task_id) { uint8_t id = (uint8_t)task_id; if (id < WDT_TASK_CNT) { g_wdt_evt_flag[id] = 0U; } } /** * @brief 每秒对所有任务看门狗计数加1。 * * 基于 `xTaskGetTickCount()` 做1秒节拍判断,避免依赖RTC/系统秒计时接口。 * 计数采用饱和累加,最大不超过 `0xFFFF`。 */ static void v_wdt_add_timer(void) { static TickType_t last_tick = 0U; TickType_t now_tick = xTaskGetTickCount(); if ((now_tick - last_tick) >= pdMS_TO_TICKS(1000U)) { last_tick = now_tick; for (uint8_t i = 0U; i < WDT_TASK_CNT; ++i) { if (g_wdt_evt_flag[i] < 0xFFFFU) { ++g_wdt_evt_flag[i]; } } } } void v_wdt_dog_toggle() { WATCKDOG_TOGGLE(); //看门狗翻转 #if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D v_iwdg_refresh(); /* [CCU601E_D] STM32 内部看门狗喂狗 */ #endif } /** * @brief 执行任务运行状态检查。 * * 检查流程: * 1. 先调用 `v_wdt_add_timer()` 更新各任务计数; * 2. 跳过 lwIP 内部任务(`EthIf`、`TCPIP`); * 3. 当 Shell 未使能时跳过 `MyShell`; * 4. 若任务计数超过超时阈值,当前阶段先钳位到阈值,不直接复位系统。 */ static void v_wdt_task_runing_cheak(void) { v_wdt_add_timer(); /* EthIf/TCPIP 为 lwIP 内部线程创建,不参与本任务计数判断 */ for (uint8_t i = 0U; i < WDT_TASK_CNT; ++i) { if ((i == (uint8_t)TASK_ID_EthIf) || (i == (uint8_t)TASK_ID_TCPIP)) { continue; } if (g_wdt_evt_flag[i] > WDT_TIMEOUT_SECONDS) { #if (WDT_RESET_ENABLE) /* 超时触发系统复位:打印故障任务名后复位 */ const char *task_name = (i < (uint8_t)MY_TASK_NUM) ? my_task_data[i].task_name : "UNKNOWN"; printf("\r\n!!! WDT RESET: Task[%s](ID=%u) timeout > %us, system reset !!!\r\n", task_name, (unsigned int)i, (unsigned int)WDT_TIMEOUT_SECONDS); /* 短暂延时让串口数据发送完成 */ for (volatile uint32_t delay = 0U; delay < 100000U; delay++) {} NVIC_SystemReset(); #else /* 仅钳位计数,不复位(调试阶段使用) */ g_wdt_evt_flag[i] = WDT_TIMEOUT_SECONDS; #endif } } } /** * @brief 看门狗任务主循环。 * * 任务职责: * - 对自身任务喂狗; * - 周期扫描其它任务运行计数; * - 按半周期翻转外部看门狗引脚,维持硬件喂狗节奏。 * * @param argument 任务参数,当前未使用。 */ void v_wdt_task(void *argument) { (void)argument; uint16_t sleep_ms = MY_GET_SLEEP_TIME(TASK_ID_WATCHDOG); /* 将完整周期拆成两次半周期喂狗;当周期过小时兜底为1ms,避免出现0ms延时。 */ uint16_t half_sleep_ms = (sleep_ms > 1U) ? (sleep_ms / 2U) : 1U; for (;;) { v_wdt_setFlag(TASK_ID_WATCHDOG); v_wdt_task_runing_cheak(); v_wdt_dog_toggle(); mSleep(half_sleep_ms); v_wdt_dog_toggle(); mSleep(half_sleep_ms); } }