1.什么是看门狗
由单片机构成的微型计算机系统中,单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞而陷入死循环;或者因为用户配置代码出现错误,导致芯片无法正常工作。出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog)。简单说:看门狗的本质就是定时计数器,计数器使能之后一直在累加;而“喂狗”就是重新写入计数器的值,使得计数器重新累加;如果在一定时间内没有接收到“喂狗”信号(表示MCU已经停止工作),便进行处理器的自动复位重启(发送复位信号)。
CH32V103系列内置两个看门狗,提供了更高的安全性和时间的精确性以及使用的灵活性。两个看门狗设备(独立看门狗、窗口看门狗)可以用来检测和解决由外界电磁干扰、软件错误引起的故障。当计数器达到给定的超时值时,触发一个中断(仅适用于窗口看门狗)或者产生系统复位。
独立看门狗用来检测逻辑错误和外部环境干扰引起的软件故障。独立看门狗时钟源来自LSI,可独立于主程序运行,适用于对精度要求较低的场合。
窗口看门狗一般用来监测系统运行的软件故障,例如外部干扰、不可预见的逻辑错误等。它需要在一个特定的窗口时间(有上下限)内进行计数器刷新(喂狗),早于或者晚于这个窗口时间,看门狗电路都会产生系统复位信号。
2.相关的函数及说明:
序 号 | 函数名称 | 功能描述 |
1 | IWDG_ WriteAccessCmd | 使能或者失能操作键值锁 |
2 | IWDG_SetPrescaler | 设置IWDG预分频值 |
3 | IWDG_SetReload | 设置IWDG重装载值 |
4 | IWDG_ReloadCounter | 将IWDG重载寄存器的值重新装载到 IWDG 计数值 |
5 | IWDG_Enable | 使能IWDG |
6 | IWDG_GetFlagStatus | 检查指定的IWDG标志位 |
7 | WWDG_DeInit | 将WWDG外设寄存器配置为默认值 |
8 | WWDG_SetPrescaler | 设置 WWDG预分频值 |
9 | WWDG_SetWindowValue | 设置 WWDG窗口值 |
10 | WWDG_EnableIT | 使能 WWDG早期唤醒中断 |
11 | WWDG_SetCounter | 设置 WWDG计数器值 |
12 | WWDG_Enable | 使能WWDG并装入计数值 |
13 | WWDG_GetFlagStatus | 检查早期唤醒中断标志位是否置位 |
14 | WWDG_ClearFlag | 清除 WWDG 早期唤醒中断标志位 |
3.官方例程的学习:
/*
* Userpinmux.c
*
* Created on: 2023年7月5日
* Author: 86195
*/
#ifndef USER_USERPINMUX_C_
#define USER_USERPINMUX_C_
#include "Userpinmux.h"
#include "debug.h"
Void WWDG_IROHandler(void)__attribute((interrupt("WCH- Interrupt-fast")));
void WWDG_ IROHandler(void)
{
WWDG_Feed();//窗口看门狗喂狗
printf("wwDG_Feed\r\n");
WWDG_ClearFlag();//清除WWDG标志位
}
void WWDG_ NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;//配置WWDG中断向量
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//设置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//设置响应优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 WWDG中断向量
NVIC_Init(&NVIC_InitStructure);//初始化wwDG中断
}
void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv)
{
RCC_APBI PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//使能WWDG时钟
WWDG_SetCounter( tr );//更新WWDG计数器
WWDG_SetPrescaler( prv );//设置WWDG预分频值
WWDG_SetWindowValue( wr );//设置窗口值
WWDG_Enable(WWDG_CNT);//使能 WWDG计数
WWDG_ClearFlag();//清除标志
WWDG_NVIC_Config();//配置WWDG中断
WWDG_EnableIT();//使能WWDG中断
}
void WWDG_Feed(void)
{
WWDG_SetCounter( WWDG_CNT);//更新WWDG计数器
}
int main(void)
{
u8 wwdg_tr, wwdg_wr;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("WWDG Test\r\n");
WWDG_Config(0x7f, 0x5f, WWDG_Prescaler_8); /* 48M/8/4096 */
wwdg_wr = WWDG->CFGR & 0x7F;
while(1)
{
Delay_Ms(10);
printf("**********\r\n");
wwdg_tr = WWDG->CTLR & 0x7F;
if(wwdg_tr < wwdg_wr)
{
WWDG_Feed();
}
printf("##########\r\n");
}
}
4.对官方例程的修改:
/*
* Userpinmux.c
*
* Created on: 2023年7月5日
* Author: 86195
*/
#ifndef USER_USERPINMUX_C_
#define USER_USERPINMUX_C_
#include "Userpinmux.h"
#include "debug.h"
Void WWDG_IROHandler(void)__attribute((interrupt("WCH- Interrupt-fast")));
void WWDG_ IROHandler(void)
{
WWDG_Feed();//窗口看门狗喂狗
printf("wwDG_Feed\r\n");
WWDG_ClearFlag();//清除WWDG标志位
}
void WWDG_ NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;//配置WWDG中断向量
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//设置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//设置响应优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 WWDG中断向量
NVIC_Init(&NVIC_InitStructure);//初始化wwDG中断
}
void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv)
{
RCC_APBI PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//使能WWDG时钟
WWDG_SetCounter( tr );//更新WWDG计数器
WWDG_SetPrescaler( prv );//设置WWDG预分频值
WWDG_SetWindowValue( wr );//设置窗口值
WWDG_Enable(WWDG_CNT);//使能 WWDG计数
WWDG_ClearFlag();//清除标志
WWDG_NVIC_Config();//配置WWDG中断
WWDG_EnableIT();//使能WWDG中断
}
void WWDG_Feed(void)
{
WWDG_SetCounter( WWDG_CNT);//更新WWDG计数器
}
void EXTI15_10_IROHandler(void)_attribute__((interrupt("wCH- Interrupt-fast")));
void EXTI15_10_IROHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line15)! =RESET){
printf("EXTI15\r\n");
EXTI_ClearITPendingBit(EXTI_Line15);
while(1);//进入死循环
}
}
void EXTI15_10_INT_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ AFIO|RCC_APB2Periph_ GPIOA,ENABLE);
GPIO_InitStructure. GPIO Pin = GPIO_ Pin 15;
GPIO_InitStructure. GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSourcel);
EXTI_InitStructure. EXTI Line = EXTI_Line15;
EXTI_InitStructure. EXTI_Mode = EXTI_Mode_Interrupt;
EXTI InitStructure. EXTI_Trigger = EXTI Trigger Falling;
EXTI_InitStructure. EXTI _LineCmd = ENABLE,
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure. NVIC_IRQChannel = EXTI15_10_IROn;
NVIC_InitStructure. NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure, NVIC_IRQChannelSubPriority =0;
NVIC_InitStructure. NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
u8 wwdg_tr, wwdg_wr;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
EXTI15_10_INT_INIT();
printf("WWDG Test\r\n");
WWDG_Config(0x7f, 0x5f, WWDG_Prescaler_8); /* 48M/8/4096 */
wwdg_wr = WWDG->CFGR & 0x7F;
while(1)
{
}
}
需要注意: