9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
145 lines
4.4 KiB
C
145 lines
4.4 KiB
C
#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);
|
||
}
|
||
}
|