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));
    }
}
posted @ 2025-03-17 20:37  星光映梦  阅读(91)  评论(0)    收藏  举报