STM32 看门狗简介
看门狗(Watchdog) 是一种常用于微控制器中的定时器,它可以在程序出现故障或卡死时重新启动系统,以确保系统能够恢复正常运行。在 STM32 系列中,通常有两种类型的看门狗:独立看门狗(IWDG) 和 窗口看门狗(WWDG)。
1. 独立看门狗(IWDG)
独立看门狗(IWDG)是 STM32 系列中一个非常常见的看门狗,它用于在 MCU 出现故障时复位系统。
总结:
这个框图描述了 STM32 的 独立看门狗(IWDG) 工作原理。IWDG 通过 LSI 时钟 和 重载寄存器 定时,保证系统在遇到死循环或其他异常时能进行复位,恢复正常工作。看门狗的定时是独立于主系统时钟的,因此在系统发生故障时,IWDG 仍能继续工作,确保系统的可靠性。
工作原理:IWDG 通过定期喂狗来重启 MCU。通过向看门狗寄存器写入一个特定的值来重置计数器,如果看门狗没有在预定的时间内被“喂食”,它会产生一个 复位信号,将 MCU 复位。
-
独立看门狗(IWDG)框图功能说明:
LSI(低速内部时钟):
- IWDG 使用 LSI(40 kHz) 作为定时器源。这个时钟源是独立于主系统时钟的,即使系统主时钟出现故障,IWDG 仍能工作,保证系统安全。
预分频器(IWDG_PR):
- IWDG_PR 是一个 8 位的预分频器,用来调整定时器的计数频率。通过调整预分频器,可以让 IWDG 计时器根据需要产生不同的超时周期。
状态寄存器(IWDG_SR):
- IWDG_SR 用于存储当前 IWDG 的状态,检测是否发生了复位或者其它状态信息。
重载寄存器(IWDG_RLR):
- IWDG_RLR 是 12 位的重载寄存器,它定义了 IWDG 定时器的周期(也就是定时器计时的上限)。如果在重载值设定的时间内没有进行“喂狗”操作,IWDG 会触发复位。
12 位速率计数器(IWDG Counter):
- IWDG 通过一个 12 位计数器来计算定时器超时,计数器会从重载寄存器加载的值开始计数,直到超时。
IWDG 控制寄存器(IWDG_KR):
- IWDG_KR 是一个键寄存器,通过写入特定的密钥来启动或复位 IWDG 功能。
复位功能:
- 如果 IWDG 计时器超时(即计时器未被“喂狗”),则触发 复位,以确保 MCU 被复位并重新启动。
主要特点:
- 硬件独立性:即使主系统时钟故障,看门狗仍能正常工作。
- 定时器:IWDG 内部使用 LSI(低速内部时钟) 作为定时源,具有独立于主系统时钟的特性。
- 复位功能:超时后,IWDG 会触发一个 硬件复位,系统重新启动。
使用方法:
- 启动 IWDG,设置预分频器和计数器值。
- 在应用程序中定期“喂狗”(重新加载计数器)。
- 如果在规定时间内没有喂狗,IWDG 会复位 MCU。
代码示例:
#include "stm32f10x.h"
// 启动 IWDG 并设置计数器
void IWDG_Init(void) {
// 使能 IWDG 外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_IWDG, ENABLE);
// 配置 IWDG 时钟,设置预分频器和计数器值
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 使能 IWDG 写访问
IWDG_SetPrescaler(IWDG_Prescaler_64); // 设置预分频器
IWDG_SetReload(0xFFF); // 设置重载值(最大值)
// 启动 IWDG
IWDG_Enable();
}
// 定期喂狗函数
void IWDG_Feed(void) {
IWDG_ReloadCounter(); // 喂狗,重装计数器
}
int main(void) {
SystemInit(); // 系统初始化
IWDG_Init(); // 初始化看门狗
while (1) {
// 主循环中定期喂狗
IWDG_Feed();
}
}
2. 窗口看门狗(WWDG)
窗口看门狗(WWDG)与独立看门狗类似,但是它具有一个“窗口”功能,用于增强系统的可靠性。
总结
工作原理:WWDG 会在设定的时间窗口内等待“喂狗”。如果在此窗口内没有喂狗,WWDG 将重置系统。不同的是,窗口看门狗要求喂狗操作在特定的时间窗口内完成,超过窗口时间将触发复位。
-
框图分析:
这个框图展示了 STM32 的 窗口看门狗(WWDG) 工作原理。窗口看门狗(WWDG)是一个定时器,常用于确保系统不会进入死循环或卡死状态。通过定期喂狗,可以避免系统复位。
主要功能组件分析:
PCLK1 时钟(来自 RCC 时钟控制器):
- WWDG 的时钟源是来自 PCLK1 时钟,该时钟可以是系统时钟的一部分,通过 RCC 时钟控制器 进行配置。
窗口看门狗预分频器(WDGTB):
- 用于生成用于看门狗计数器的时钟信号。通过预分频器调整时钟频率,确保看门狗的计时周期符合系统要求。
窗口看门狗计数器(CNT):
- 这是一个 6 位的计数器,每次递增。当计数器达到重载值时,会触发看门狗复位,除非在指定的“窗口”内进行了喂狗操作。
- 窗口机制:窗口看门狗不同于独立看门狗,窗口机制要求在设定的时间窗口内喂狗,否则会触发复位。
窗口看门狗配置寄存器(WWDG_CFR):
- 配置寄存器用来设定 重载值 和 窗口值,影响看门狗的计时行为。
- W6 ~ W0:这些位用于设置窗口值,以定义喂狗操作必须发生的窗口。
- 窗口看门狗 是一个可以在特定的时间窗口内进行喂狗操作的定时器。
- 如果在这个时间窗口内没有喂狗,系统将会触发复位,保证系统的可靠性。
- 该框图显示了 WWDG 的时钟、计数器、配置寄存器等各个模块是如何协同工作,来监控系统状态并确保其稳定运行的。
窗口看门狗控制寄存器(WWDG_CR):
- WDGA:启动窗口看门狗操作。写入此寄存器启用看门狗计数。
- T6 ~ T0:这些位设置计数器的初始值,并决定计数器的重载值。
比较器(Comparator):
- 用于比较 WWDG_CR 寄存器的值与 WWDG_CFR 寄存器的窗口值,来决定是否可以喂狗。
- 如果比较结果是 1,即在窗口内喂狗,那么系统将继续运行;如果比较结果是 0,则触发复位。
主要特点:
- 时间窗口:需要在规定的时间窗口内进行喂狗操作,否则会复位系统。
- 快速复位:通过控制喂狗的时间窗口,可以更严格地控制系统的稳定性。
使用方法:
- 启动 WWDG,设置计数器和窗口值。
- 在应用程序中定期“喂狗”,并确保在窗口内进行操作。
- 如果在窗口外喂狗,WWDG 会复位 MCU。
代码示例:
#include "stm32f10x.h"
// 启动 WWDG 并设置计数器和窗口值
void WWDG_Init(void) {
// 使能 WWDG 外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
// 设置窗口值、计数器和预分频器
WWDG_SetWindowValue(0x7F); // 设置窗口值
WWDG_SetCounter(0x7F); // 设置计数器值
WWDG_Enable(0x7F); // 启动 WWDG
}
// 定期喂狗函数
void WWDG_Feed(void) {
WWDG_SetCounter(0x7F); // 在窗口内喂狗
}
int main(void) {
SystemInit(); // 系统初始化
WWDG_Init(); // 初始化窗口看门狗
while (1) {
// 主循环中定期喂狗
WWDG_Feed();
}
}
总结
- 独立看门狗(IWDG):硬件独立、使用低速内部时钟,适用于系统保护,超时后会触发复位。
- 窗口看门狗(WWDG):具有更严格的时间窗口控制,只有在指定时间内喂狗,才能避免复位。
两种看门狗的使用方式和设置方法有所不同,IWDG 更加简单且适用于大多数场景,而 WWDG 提供了更高的灵活性和保护。
项目:对IDWG进行一个检测,进行“喂狗”检测IWDG防止其复位,通过OLDE屏来显示检测结果
操作:
1判断IWDG的复位状态,可通过OLED屏来观察
2开始IWDG模式(LSI会被自动开启时钟设置)调用函数:IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
3对键寄存器进行解除预分频值和重装载值的写保护
4设置预分频值和重装载值的参数(也就设置"喂狗"的时间) (设置1000ms的喂狗时间)
5调试喂狗的时间,在while循环里面延时一定时间定时喂狗观察设置的喂狗时间是否一致
#include "stm32f10x.h" // Device header
#include "Dog.h"
#include "oled.h"
#include "Delay.h"
void iwdg_Init()
{
OLED_Init();
OLED_ShowString (1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST)!=RESET )
{
OLED_ShowString (2,1,"IWDGRESET");
delay_ms (500);
OLED_ShowString (2,1," ");
delay_ms (100);
RCC_ClearFlag();
}
else{
OLED_ShowString (3,1,"RST");
delay_ms (500);
OLED_ShowString (3,1," ");
delay_ms (100);
RCC_ClearFlag();
}
//WWDG的初始化
//Tiwdg=TLSI*PR预分频系数*(RC+1)
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_16); //选择16分频
IWDG_SetReload(2499); //选择2499重装载值 总计算为1000ms
IWDG_ReloadCounter(); //先提前喂狗一次
IWDG_Enable();
while(1)
{
IWDG_ReloadCounter(); //每隔xxms喂狗一次
delay_ms (1100);
}
}
通过按键来进行喂狗操作:
#include "stm32f10x.h" // Device header
#include "Dog.h"
#include "oled.h"
#include "Delay.h"
#include "key.h"
void iwdg_Init()
{
key_Init();
OLED_Init();
OLED_ShowString (1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST)!=RESET )
{
OLED_ShowString (2,1,"IWDGRESET");
delay_ms (500);
OLED_ShowString (2,1," ");
delay_ms (100);
RCC_ClearFlag();
}
else{
OLED_ShowString (3,1,"RST");
delay_ms (500);
OLED_ShowString (3,1," ");
delay_ms (100);
RCC_ClearFlag();
}
//WWDG的初始化
//Tiwdg=TLSI*PR预分频系数*(RC+1)
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_16); //选择16分频
IWDG_SetReload(2499); //选择2499重装载值 总计算为1000ms
IWDG_ReloadCounter(); //先提前喂狗一次
IWDG_Enable();
while(1)
{
Get_keynum (); //如果一致按下按键,那么无法进行后面喂狗,就会复位
IWDG_ReloadCounter(); //每隔xxms喂狗一次
OLED_ShowString (4,1,"FEED");
delay_ms (200);
OLED_ShowString (4,1," ");
delay_ms (600);
}
#include "stm32f10x.h" // Device header
#include "key.h"
#include "Delay.h"
//按键模块,该模块建议独立文件
void key_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitTypeDef key_inItsturt;
key_inItsturt.GPIO_Mode = GPIO_Mode_IPU ;
key_inItsturt.GPIO_Pin =GPIO_Pin_1 ;
key_inItsturt.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &key_inItsturt);
}
uint8_t Get_keynum(void)
{
uint8_t keynum=0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 )==RESET)
{
delay_ms (20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 )==RESET);
delay_ms (20);
keynum=1;
}
return keynum;
}
IWDG(独立看门狗)的超时时间公式可以通过以下公式来计算:
超时时间=(重载值+1)×(预分频器值+1)LSI_频率\text{超时时间} = \frac{(重载值 + 1) \times (预分频器值 + 1)}{LSI\_频率}"
- 重载值(IWDG_RLR):设置IWDG定时器的计数器上限,通常为12位(0~4095),表示计时器的计数周期。
- 预分频器值(IWDG_PR):设置IWDG定时器的预分频器。该值可以是 4、8、16、32、64、128、256 中的一个。
- LSI 频率:IWDG 使用 LSI(低速内部时钟) 作为时钟源,其频率为 40 kHz(标准值),但可以根据实际情况略有不同。
公式中的每个部分:
- 重载值:设置定时器计数的最大值。
- 预分频器:决定了定时器计数的频率。
- LSI频率:独立时钟源频率,通常为 40 kHz。
举个例子:
假设:
- 重载值 = 0x0FFF (4095)
- 预分频器 = 0x03(预分频器为 64)
- LSI频率 = 40 kHz
则超时时间计算为:
WWDG(窗口看门狗)项目:
操作:
1调用APB窗口看门狗的时钟,
2初始化窗口看门狗,包括设置预分频参数,窗口时间WDGTB(T位6保证为1,后五位设置想要"喂狗的时间区段")
3使能 WWDG_Enable()窗口看门狗,同时也要保证WDGTB T位6为1(进行一次初始喂狗)
4在while循环里面进行按键喂狗操作以及测试喂狗的时间
代码案例:
void wwdg_Init()
{
key_Init();
OLED_Init();
OLED_ShowString (1,1,"WWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST )!=RESET )//判断WWDG复位情况
{
OLED_ShowString (2,1,"WWDGRESET");
delay_ms (500);
OLED_ShowString (2,1," ");
delay_ms (100);
RCC_ClearFlag();
}
else{
OLED_ShowString (3,1,"RST");
delay_ms (500);
OLED_ShowString (3,1," ");
delay_ms (100);
}
//初始化窗口看门狗
RCC_APB1PeriphClockCmd( RCC_APB1Periph_WWDG,ENABLE );
WWDG_SetPrescaler(WWDG_Prescaler_8);//2*2*2=8;
WWDG_SetWindowValue(0x40|21); //到窗口时间30ms
WWDG_Enable(0x40|54); //最晚一共50ms
while(1)
{
Get_keynum (); //如果一致按下按键,那么无法进行后面喂狗,就会复位
//每隔xxms喂狗一次
OLED_ShowString (4,1,"FEED");
delay_ms (20);
OLED_ShowString (4,1," ");
delay_ms (10);
WWDG_SetCounter(0x40|54);
}
}
void key_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitTypeDef key_inItsturt;
key_inItsturt.GPIO_Mode = GPIO_Mode_IPU ;
key_inItsturt.GPIO_Pin =GPIO_Pin_1 ;
key_inItsturt.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &key_inItsturt);
}
uint8_t Get_keynum(void)
{
uint8_t keynum=0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 )==RESET)
{
delay_ms (20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 )==RESET);
delay_ms (20);
keynum=1;
}
关于 窗口看门狗(WWDG) 的 时间范围公式:
窗口看门狗的超时时间由两个因素决定:
- 计数器值(CNT):6位计数器的值,范围从 0 到 63(即 0x00 到 0x3F)。
- 窗口值:控制在指定的时间窗口内喂狗的时限。
公式
窗口看门狗的超时时间 T_window 的计算公式如下:
Twindow=(预分频器值+1)×(计数器值+1)PCLK1 时钟T_{window} = \frac{(预分频器值 + 1) \times (计数器值 + 1)}{PCLK1 \, 时钟}
- 预分频器值(WDGTB):该值控制看门狗时钟的频率,常见值有 1, 2, 4, 8。具体选择哪个预分频器值取决于你的系统时钟(PCLK1)。
- 计数器值(CNT):6 位计数器,范围从 0 到 0x3F。
- PCLK1 时钟:这是窗口看门狗的时钟源,通常与系统时钟相同,可能是 72 MHz。
举个例子:
假设:
- PCLK1 时钟 = 72 MHz
- 预分频器值 = 4
- 计数器值 = 0x3F(最大值)
则 窗口看门狗时间范围 为:
Twindow=(4+1)×(63+1)72×106=5×6472×106≈0.0000444 秒=44.4 μsT_{window} = \frac{(4 + 1) \times (63 + 1)}{72 \times 10^6} = \frac{5 \times 64}{72 \times 10^6} \approx 0.0000444 \, \text{秒} = 44.4 \, \mu s