09. 模拟看门狗
一、看门狗简介
MCU 可能工作在一些复杂环境,可能受到某些电磁干扰出现程序跑飞,导致死循环无法继续执行工作,看门狗的作用就是为了避免这种情况。看门狗的本质也是一个定时器,在程序启动后,需要在一定的时间内再给它一个信号,俗称“喂狗”。如果没有按时“喂狗”,说明系统或软件出现了不可预知的问题(比如软件卡在某个循环或逾期事件中),这时看门狗就向系统发送个复位信号,使整个系统重启,重新进入正常的工作状态。看门狗有助于检测、处理系统或软件的错误行为。
ESP32 S3中有三个数字看门狗定时器、1 个模拟看门狗定时器和一个 XTAL32K 看门狗定时器,他们在各自有特定条件运行。
二、实验例程
这里,我们在【components】文件夹下的【peripheral】文件夹下的【inc】文件夹(用来存放头文件)新建一个 bsp_wdg.h
文件,在【components】文件夹下的【peripheral】文件夹下的【src】文件夹(用来存放源文件)新建一个 bsp_wdg.c
文件。
#ifndef __BSP_WDG_H__
#define __BSP_WDG_H__
#include "esp_timer.h"
#include "esp_system.h"
void bsp_wdg_init(void);
void bsp_wdg_start(uint64_t period);
void bsp_wdg_feed(void);
#endif // !__BSP_WDG_H__
#include "bsp_wdg.h"
static esp_timer_handle_t g_wdg_esp_timer_handle;
static uint64_t g_max_wdg_feed_time;
static void wdg_callback(void *arg);
/**
* @brief 看门狗初始化
*
* @param handle 系统定时器句柄
*/
void bsp_wdg_init(void)
{
esp_timer_create_args_t esp_timer_create_args = {0};
esp_timer_create_args.callback = wdg_callback;
esp_timer_create_args.arg = NULL;
esp_timer_create(&esp_timer_create_args, &g_wdg_esp_timer_handle);
}
/**
* @brief 启动看门狗
*
* @param period 喂狗的超时时间,单位是微秒
*/
void bsp_wdg_start(uint64_t period)
{
g_max_wdg_feed_time = period;
esp_timer_start_periodic(g_wdg_esp_timer_handle, period); // 每周期内触发一次
}
/**
* @brief 喂狗
*
*/
void bsp_wdg_feed(void)
{
// 重新启动当前运行的计时器,用以模拟喂狗过程
esp_timer_restart(g_wdg_esp_timer_handle, g_max_wdg_feed_time);
}
/**
* @brief 系统定时器回调函数
*
* @param arg 传入系统定时器回调函数的参数
*/
static void wdg_callback(void *arg)
{
// 若没有及时进行喂狗,那么芯片将一直进行复位
esp_restart();
}
我们在 bsp_wdg_init()
看门狗初始化函数中调用了看门狗回调函数,用以模拟当定时器溢出时产生的复位现象。同时,我们在 bsp_wdg_feed()
喂狗函数中调用了 esp_timer_restart()
重新启动当前运行的计时器,用以模拟喂狗过程。如果我们在最大喂狗时间内没有进行喂狗,则系统定时器回进入回调函数中,在回调函数中,我们会调用 esp_restart()
函数重启 ESP32 芯片,模拟看门狗复位。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "bsp_wdg.h"
#include "led.h"
#include "key.h"
// app_main()函数是ESP32的入口函数,它是FreRTOS的一个任务,任务优先级是1
// main()函数是C语言入口函数,它会在编译过程中插入到二进制文件中的
void app_main(void)
{
led_init(GPIO_NUM_1);
key_init(GPIO_NUM_0, 0);
bsp_wdg_init();
bsp_wdg_start(3000000);
printf("程序正在执行。\r\n");
while (1)
{
if (key_scan(GPIO_NUM_0, 0))
{
bsp_wdg_feed();
}
// 将一个任务延迟给定的滴答数,IDF中提供pdMS_TO_TICKS可以将指定的ms转换为对应的tick数
vTaskDelay(pdMS_TO_TICKS(10));
}
}