看门狗定时器
概述
看门狗定时器(Watchdog Timer,WDT)是一种硬件或软件机制,用于检测系统故障并在系统挂起时自动复位。
看门狗原理
| Text Only |
|---|
| 启动看门狗 -> 定期喂狗 -> 系统正常 -> 继续运行
│
▼ (超时未喂狗)
系统复位
|
看门狗类型
| 类型 |
说明 |
特点 |
| 独立看门狗 |
独立时钟源 |
不受系统时钟影响 |
| 窗口看门狗 |
必须在窗口内喂狗 |
更严格的监控 |
| 软看门狗 |
软件实现 |
灵活但依赖系统 |
STM32看门狗编程
独立看门狗(IWDG)
| C |
|---|
| #include "stm32f4xx_hal.h"
IWDG_HandleTypeDef hiwdg;
void IWDG_Init(void)
{
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 0xFFF; // 约32秒超时
// 超时时间 = (Reload + 1) * Prescaler / LSI频率
// 例:(4096 * 256) / 32000 ≈ 32.7秒
HAL_IWDG_Init(&hiwdg);
}
// 喂狗
void feed_watchdog(void)
{
HAL_IWDG_Refresh(&hiwdg);
}
// 在主循环中定期喂狗
void main(void)
{
IWDG_Init();
while (1) {
// 执行任务
// 任务完成后喂狗
feed_watchdog();
HAL_Delay(100);
}
}
|
窗口看门狗(WWDG)
| C |
|---|
| WWDG_HandleTypeDef hwwdg;
void WWDG_Init(void)
{
hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
hwwdg.Init.Window = 0x50; // 窗口值
hwwdg.Init.Counter = 0x7F; // 计数器值
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能早期唤醒中断
HAL_WWDG_Init(&hwwdg);
}
// 窗口看门狗必须在窗口内喂狗
// 不能太早(计数器 > Window)也不能太晚(计数器 = 0)
// 早期唤醒中断(计数器到达0x40)
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
// 在这里喂狗
HAL_WWDG_Refresh(hwwdg, 0x7F);
}
|
Linux看门狗编程
使用watchdog设备
| Bash |
|---|
| # 查看看门狗设备
ls /dev/watchdog*
# 查看看门狗信息
cat /sys/class/watchdog/watchdog0/identity
cat /sys/class/watchdog/watchdog0/timeleft
cat /sys/class/watchdog/watchdog0/timeout
|
C语言编程
| C |
|---|
| #include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
int watchdog_fd;
void watchdog_init(void)
{
watchdog_fd = open("/dev/watchdog", O_WRONLY);
// 设置超时时间
int timeout = 30;
ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &timeout);
// 获取超时时间
ioctl(watchdog_fd, WDIOC_GETTIMEOUT, &timeout);
}
// 喂狗
void watchdog_feed(void)
{
write(watchdog_fd, "\0", 1);
// 或
ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
}
// 关闭看门狗(需要支持)
void watchdog_disable(void)
{
write(watchdog_fd, "V", 1);
close(watchdog_fd);
}
// 使用示例
int main(void)
{
watchdog_init();
while (1) {
// 执行任务
watchdog_feed();
sleep(10);
}
watchdog_disable();
return 0;
}
|
Magic Close
| C |
|---|
| // 写入"V"字符后关闭,看门狗停止
write(watchdog_fd, "V", 1);
close(watchdog_fd);
// 如果直接关闭,看门狗继续运行
close(watchdog_fd); // 风险:系统可能意外复位
|
看门狗守护进程
systemd watchdog
| Bash |
|---|
| # /etc/systemd/system/my-service.service
[Unit]
Description=My Service
[Service]
Type=notify
WatchdogSec=30s
ExecStart=/usr/bin/my-service
Restart=on-failure
[Install]
WantedBy=multi-user.target
|
| C |
|---|
| #include <systemd/sd-daemon.h>
void main(void)
{
// 通知systemd服务已启动
sd_notify(0, "READY=1");
while (1) {
// 执行任务
// 通知systemd服务正常
sd_notify(0, "WATCHDOG=1");
sleep(10);
}
}
|
FreeRTOS看门狗任务
| C |
|---|
| #include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx_hal.h"
#define WATCHDOG_TIMEOUT_MS 5000
#define FEED_INTERVAL_MS 1000
static TaskHandle_t watchdog_task_handle;
void watchdog_task(void *pvParameters)
{
IWDG_HandleTypeDef hiwdg;
// 初始化看门狗
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 0xFFF;
HAL_IWDG_Init(&hiwdg);
while (1) {
// 检查各任务状态
if (all_tasks_healthy()) {
HAL_IWDG_Refresh(&hiwdg);
} else {
// 有任务异常,不喂狗,触发复位
vTaskSuspend(NULL);
}
vTaskDelay(pdMS_TO_TICKS(FEED_INTERVAL_MS));
}
}
void create_watchdog_task(void)
{
xTaskCreate(watchdog_task, "Watchdog", 128, NULL, 1, &watchdog_task_handle);
}
|
看门狗监控多个任务
| C |
|---|
| #define NUM_TASKS 3
typedef struct {
TaskHandle_t handle;
uint32_t last_feed;
uint32_t timeout_ms;
bool healthy;
} task_monitor_t;
static task_monitor_t task_monitors[NUM_TASKS];
// 任务注册监控
void register_task(int index, TaskHandle_t handle, uint32_t timeout_ms)
{
task_monitors[index].handle = handle;
task_monitors[index].timeout_ms = timeout_ms;
task_monitors[index].healthy = true;
task_monitors[index].last_feed = xTaskGetTickCount();
}
// 任务报告存活
void task_alive(int index)
{
task_monitors[index].last_feed = xTaskGetTickCount();
}
// 检查所有任务
bool check_all_tasks(void)
{
uint32_t now = xTaskGetTickCount();
for (int i = 0; i < NUM_TASKS; i++) {
uint32_t elapsed = (now - task_monitors[i].last_feed) * portTICK_PERIOD_MS;
if (elapsed > task_monitors[i].timeout_ms) {
task_monitors[i].healthy = false;
return false; // 有任务超时
}
}
return true;
}
void watchdog_task(void *pvParameters)
{
while (1) {
if (check_all_tasks()) {
HAL_IWDG_Refresh(&hiwdg);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
|
看门狗超时计算
STM32 IWDG
| Text Only |
|---|
| LSI频率 = 32kHz(典型值)
超时时间 = (Reload + 1) × Prescaler / LSI
例:Prescaler = 256, Reload = 4095
超时 = 4096 × 256 / 32000 = 32.768秒
|
常用配置
| C |
|---|
| // 1秒超时
hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
hiwdg.Init.Reload = 999; // 1000 × 32 / 32000 = 1秒
// 5秒超时
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 624; // 625 × 256 / 32000 = 5秒
// 最大超时(约32秒)
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 0xFFF;
|
看门狗设计建议
- 超时时间:设置为最长任务执行时间的2-3倍
- 喂狗位置:在主循环或任务末尾喂狗
- 任务监控:监控所有关键任务的健康状态
- 复位记录:复位后检查复位原因,判断是否为看门狗复位
- 禁止意外关闭:生产环境禁用看门狗关闭功能
复位原因检测
| C |
|---|
| void check_reset_reason(void)
{
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) {
printf("系统由独立看门狗复位\n");
__HAL_RCC_CLEAR_RESET_FLAGS();
}
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) {
printf("系统由窗口看门狗复位\n");
__HAL_RCC_CLEAR_RESET_FLAGS();
}
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) {
printf("系统上电复位\n");
}
if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) {
printf("软件复位\n");
}
}
|
参考资料