窗口看门狗WWDG
IWDG与WWDG的异同点
WWDG的中断相较于IWDG的优势
当我们需要在程序跑飞时保存一些珍贵的数据,IWDG真的是无能为力,IWDG的功能就是“当检测到程序跑飞立刻复位单片机,就相当于危险发生时不管你什么重要的数据,保住单片机的命要紧”;与IWDG不同的WWDG是“当检测到程序跑飞时,我们不急着作复位的决定,我们先进入中断服务函数进行一些必要的操作,比如保存数据……等关键操作,然后在紧急复位抢救单片机的生命”。
IWDG与WWDG的检测时钟
IWDG用的是LSI内部低速时钟,LSI本身不是特别的精准而且LSI是外部时钟源,如果单片机的内部时钟源故障或因外部干扰发生紊乱,我们可以采用外部时钟源LSI来对这个单片机来个复位操作,拯救单片机;
WWDG用的是APB1内部高速时钟源比较精确,该时钟源是由APB1总线时钟4096分频而来,如果单片机内部时钟源废了,那么WWDG的时钟就失效了,WWDG自己都乱套了,当然顾不了单片机了。
IWDG与WWDG的触发复位条件不同
IWDG的计数方式
WWDG的计数方式
IWDG与WWDG的检测力度不同
WWDG相比于IWDG有更小的“喂狗”时间范围,当我们的程序跑飞了之后,我们也不知道具体那里跑飞了,可能是时钟源跑飞了。当时钟源跑飞了,可能会使得递减计数器的值发生改变但是仍在“喂狗”时间范围之内,此时不足以触发复位。针对于这种情况,我们缩小了“喂狗的时间范围“,这样可以尽可能地减小” 递减计数器的值发生改变但是仍在“喂狗”时间范围之内“发生的可能性。因此,相比于IWDG,WWDG对程序的监控有更强的监测力度。
WWDG的工作原理
我们应该格外关注的是:如何才可以触发复位操作?
在WDGA被激活的情况下,T6位为0(T6在②或门的接口是个非门),即递减计数器的值低于窗口阈值下限
|
当我们喂狗时,递减计数器的值大于窗口阈值上限
|
WWDG注意事项
上窗口值W[6:0]必须大于下窗口值0x40,否则就无窗口了
|
窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后
|
WWDG寄存器简介
CR控制寄存器
注:T6-T0是用来存储喂狗时的计数器值的。
CFR窗口配置寄存器
注:WDGTB是用来设置预分频系数的,W是用来设置窗口阈值上限的,窗口阈值下限的取值范围为[0x7F,0x3F),窗口阈值下限已经被确定了无需人工定义(窗口阈值下限=0x40=64)。
SR状态寄存器
WWDG超时时间计算
注:计数器重装值必须大于窗口阈值上限!
WWDG的库函数
WWDG库函数(含中断服务函数)的使用
声明位置
| 库函数名
| 功能以及配置顺序
|
WWDG.c
| RCC_APB1PeriphClockCmd()
| 使能WWDG外围设备时钟
|
WWDG.c
| WWDG_SetPrescaler()
| 设置预分频系数
|
WWDG.c
| WWDG_Enable
| 使能WWDG并且加载计数器值
|
WWDG.c
| WWDG_SetWindowValue()
| 设置WWDG的窗口上限阈值
|
WWDG.c
| NVIC_Init()
| NVIC嵌入式中断向量优先级配置
|
Main.c
| NVIC_PriorityGroupConfig()
| 中断优先级分组
|
Main.c
| WWDG_SetCounter()
| 喂狗/重新向计数器加载初始计数值
|
注:我们要在main函数中声明“NVIC_PriorityGroupConfig()”,因为中断优先级分组代表了全部中断优先级的分组不单独属于某一个中断向量。
代码示例
Main.c
#include "stm32f10x.h"
#include "led.h"
#include "wwdg.h"
#include "delay.h"
#include "key.h"
int main()
{
delay_init(); // 由于delay函数中包含时钟,因此我们要初始化delay函数
delay_ms(500); // 延迟500ms
LED_InitConfig(); // LED0初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组
WWDG_InitConfig(); // WWDG初始化
KEY_InitConfig(); // KEY0初始化
while(1)
{
if(KEY0 == 0)
{
WWDG_SetCounter(0x5F);
}
}
}
void WWDG_IRQHandler()
{
if(WWDG_GetFlagStatus() == SET) // 读取中断标志位被设置与否
{
LED0 = !LED0;
}
WWDG_ClearFlag(); // 软件清除WWDG中断标志位
}
Led.c
#include "led.h"
#include "stm32f10x.h"
void LED_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置GPIO引脚属性
GPIO_SetBits(GPIOB, GPIO_Pin_5); // 初始化GPIO引脚输出电平
}
Led.h
#ifndef _LED_H
#define _LED_H
#include "sys.h"
void LED_InitConfig();
#define LED0 PBout(5)
#endif
Key.c
#include "key.h"
#include "stm32f10x.h"
void KEY_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能KEY0的外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure); // 配置GPIO的属性
}
Key.h
#ifndef _KEY_H
#define _KEY_H
void KEY_InitConfig();
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
#endif
Wwdg.c
#include "wwdg.h"
#include "stm32f10x.h"
void WWDG_InitConfig()
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // 使能WWDG的外设时钟(只要是有内部时钟驱动都得使能外设时钟)
WWDG_SetPrescaler(WWDG_Prescaler_8); // 设置分频系数
WWDG_SetWindowValue(0x5F); // 设置窗口阈值上限
WWDG_Enable(0x5F); // 使能WWDG并且设置计数器初值
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级
WWDG_EnableIT(); // 使能WWDG中断
}
Wwdg.h
#ifndef _WWDG_H
#define _WWDG_H
void WWDG_InitConfig();
#endif
运行结果
行为
| 现象
|
过早的按下KEY0进行喂狗
| Reset并且LED0亮灭交替
|
过晚按下KEY0进行喂狗
| Reset并且LED0亮灭交替
|
在恰当时候就是窗口时间内喂狗
| LED0持续亮
|
窗口看门狗WWDG
IWDG与WWDG的异同点
WWDG的中断相较于IWDG的优势
当我们需要在程序跑飞时保存一些珍贵的数据,IWDG真的是无能为力,IWDG的功能就是“当检测到程序跑飞立刻复位单片机,就相当于危险发生时不管你什么重要的数据,保住单片机的命要紧”;与IWDG不同的WWDG是“当检测到程序跑飞时,我们不急着作复位的决定,我们先进入中断服务函数进行一些必要的操作,比如保存数据……等关键操作,然后在紧急复位抢救单片机的生命”。
IWDG与WWDG的检测时钟
IWDG用的是LSI内部低速时钟,LSI本身不是特别的精准而且LSI是外部时钟源,如果单片机的内部时钟源故障或因外部干扰发生紊乱,我们可以采用外部时钟源LSI来对这个单片机来个复位操作,拯救单片机;
WWDG用的是APB1内部高速时钟源比较精确,该时钟源是由APB1总线时钟4096分频而来,如果单片机内部时钟源废了,那么WWDG的时钟就失效了,WWDG自己都乱套了,当然顾不了单片机了。
IWDG与WWDG的触发复位条件不同
IWDG的计数方式
WWDG的计数方式
IWDG与WWDG的检测力度不同
WWDG相比于IWDG有更小的“喂狗”时间范围,当我们的程序跑飞了之后,我们也不知道具体那里跑飞了,可能是时钟源跑飞了。当时钟源跑飞了,可能会使得递减计数器的值发生改变但是仍在“喂狗”时间范围之内,此时不足以触发复位。针对于这种情况,我们缩小了“喂狗的时间范围“,这样可以尽可能地减小” 递减计数器的值发生改变但是仍在“喂狗”时间范围之内“发生的可能性。因此,相比于IWDG,WWDG对程序的监控有更强的监测力度。
WWDG的工作原理
我们应该格外关注的是:如何才可以触发复位操作?
在WDGA被激活的情况下,T6位为0(T6在②或门的接口是个非门),即递减计数器的值低于窗口阈值下限
|
当我们喂狗时,递减计数器的值大于窗口阈值上限
|
WWDG注意事项
上窗口值W[6:0]必须大于下窗口值0x40,否则就无窗口了
|
窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后
|
WWDG寄存器简介
CR控制寄存器
注:T6-T0是用来存储喂狗时的计数器值的。
CFR窗口配置寄存器
注:WDGTB是用来设置预分频系数的,W是用来设置窗口阈值上限的,窗口阈值下限的取值范围为[0x7F,0x3F),窗口阈值下限已经被确定了无需人工定义(窗口阈值下限=0x40=64)。
SR状态寄存器
WWDG超时时间计算
注:计数器重装值必须大于窗口阈值上限!
WWDG的库函数
WWDG库函数(含中断服务函数)的使用
声明位置
| 库函数名
| 功能以及配置顺序
|
WWDG.c
| RCC_APB1PeriphClockCmd()
| 使能WWDG外围设备时钟
|
WWDG.c
| WWDG_SetPrescaler()
| 设置预分频系数
|
WWDG.c
| WWDG_Enable
| 使能WWDG并且加载计数器值
|
WWDG.c
| WWDG_SetWindowValue()
| 设置WWDG的窗口上限阈值
|
WWDG.c
| NVIC_Init()
| NVIC嵌入式中断向量优先级配置
|
Main.c
| NVIC_PriorityGroupConfig()
| 中断优先级分组
|
Main.c
| WWDG_SetCounter()
| 喂狗/重新向计数器加载初始计数值
|
注:我们要在main函数中声明“NVIC_PriorityGroupConfig()”,因为中断优先级分组代表了全部中断优先级的分组不单独属于某一个中断向量。
代码示例
Main.c
#include "stm32f10x.h"
#include "led.h"
#include "wwdg.h"
#include "delay.h"
#include "key.h"
int main()
{
delay_init(); // 由于delay函数中包含时钟,因此我们要初始化delay函数
delay_ms(500); // 延迟500ms
LED_InitConfig(); // LED0初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组
WWDG_InitConfig(); // WWDG初始化
KEY_InitConfig(); // KEY0初始化
while(1)
{
if(KEY0 == 0)
{
WWDG_SetCounter(0x5F);
}
}
}
void WWDG_IRQHandler()
{
if(WWDG_GetFlagStatus() == SET) // 读取中断标志位被设置与否
{
LED0 = !LED0;
}
WWDG_ClearFlag(); // 软件清除WWDG中断标志位
}
Led.c
#include "led.h"
#include "stm32f10x.h"
void LED_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置GPIO引脚属性
GPIO_SetBits(GPIOB, GPIO_Pin_5); // 初始化GPIO引脚输出电平
}
Led.h
#ifndef _LED_H
#define _LED_H
#include "sys.h"
void LED_InitConfig();
#define LED0 PBout(5)
#endif
Key.c
#include "key.h"
#include "stm32f10x.h"
void KEY_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能KEY0的外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure); // 配置GPIO的属性
}
Key.h
#ifndef _KEY_H
#define _KEY_H
void KEY_InitConfig();
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
#endif
Wwdg.c
#include "wwdg.h"
#include "stm32f10x.h"
void WWDG_InitConfig()
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // 使能WWDG的外设时钟(只要是有内部时钟驱动都得使能外设时钟)
WWDG_SetPrescaler(WWDG_Prescaler_8); // 设置分频系数
WWDG_SetWindowValue(0x5F); // 设置窗口阈值上限
WWDG_Enable(0x5F); // 使能WWDG并且设置计数器初值
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级
WWDG_EnableIT(); // 使能WWDG中断
}
Wwdg.h
#ifndef _WWDG_H
#define _WWDG_H
void WWDG_InitConfig();
#endif
运行结果
行为
| 现象
|
过早的按下KEY0进行喂狗
| Reset并且LED0亮灭交替
|
过晚按下KEY0进行喂狗
| Reset并且LED0亮灭交替
|
在恰当时候就是窗口时间内喂狗
| LED0持续亮
|
举报