目录
一:看门狗
1:WDG
2:独立看门狗 (IWDG)
A:IWDG框图
B:IWDG_KR键寄存器
C:IWDG超时时间
3:窗口看门狗 (WWDG)
A:WWDG框图
B:WWDG工作特性
C:WWDG超时时间
4:独立看门狗和窗口看门狗的区别
5:数据手册
二:案例
A:独立看门狗
1:连接图
2:步骤
3:函数介绍
3:代码
B:窗口看门狗
1:连接图
2: 步骤
3:代码
WDG(Watchdog)看门狗------本质就是自动复位
看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器--重置不是清零)操作时,看门狗硬件电路就自动产生复位信号
STM32内置两个看门狗
独立看门狗(IWDG):独立工作,对时间精度要求较低
窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用
结构
它的结构和定时器是常相似的 , 只不过是,定时器溢出,产生中断 . 而看门狗定时器溢出,直接产生复位信号(IWDG复位) . 喂狗操作,其实也就是重置这个计数器(12位 递减计数器). 这是一个递减计数器,减到0之后就复位 .
程序正常运行时,为了避免复位 , 就得在这个计数器(12位 递减计数器)减到0之前 , 及时地把计数值加大点-----这个操作,就是喂狗 . 如果你程序卡死了, 没有及时加大这个计数器 , 那减到0之后,就自动复位了
独立看门狗运行逻辑
开始 : A : 输入时钟,是LSI,内部低速时钟(时钟频率为40KHz)--------------- B: 时钟进入预分频器进行分频(8位) ,所以它最大只能进行256分频 ; 上面这个预分频j寄存器IWDG PR, 可以配置分频系数, 这个PR和定时器的PSC (//预分频器PSC) 是一个意思 -----------------C:时钟动递减计数器 , 每来一个时钟,自减一个数(12位, max=2^12-1=4095) . 然后,当自减到0之后-------------D: 产生IWDG复位 ;
正常运行时,为了避免复位 . 我们可以提前在重装载寄存器写一值, IWDG_ RLR,和定时器的ARR(自动重装载寄存器ARR 计数目标值)是一样的 . 那当我们预先写好值之后, 在运行过程中我们在这个键寄存器里,写一个特定数据 . 控制电路,进行喂狗 这时重装值,就会复制到当前的计数器中, 这样计数器就会回到重装值,重新自减运行了
状态奇存器IWDG_SR : 标志电路运行的状态
键寄存器本质上是控制寄存器,用于控制硬件电路的工作
在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值来代替控制寄存器写入一位的功能,以降低硬件电路受到干扰的概率
上面说的喂狗操作 : 实际就是在键寄存器里面写入 0xAAAA
超时时间:TIWDG = TLSI × PR预分频系数 × (RL + 1)
其中:TLSI = 1 / FLSI
RL重装值-----重装载寄存器IWDG_RLR写入的值
PR预分频系数------相当于定时器的PSC (//预分频器PSC)
RL------相当于定时器的ARR(自动重装载寄存器ARR 计数目标值)是一样的
FLSI----是LSI内部低速时钟 40KHz=40 000HZ
TLSI= 1/40 000=0.000 025s = 0.025ms
时间的计算
以第一组为例
喂狗
窗口看门狗没有重装寄存器 ,喂狗操作 : 这个,我们直接在CNT写入数据就行了 , 想写多少就写多少
窗口看门狗运行逻辑
开始 : A :这个时钟源是PCLK1(36MHz)---------B: 先经过一个预分频器进行分频 , 这个和独立看门狗的预分频器,定时器的预分频器相同---------C: 分频之后的时钟,驱动这个计数器进行计数(6位递减计数器)...........
6位递减计数器
从图上看,这里写了T6~T0, 总共是7个位 . 但是下面却写的是6位递减计数器---------因为这个计数器只有T5~T0, 这6位是有效的计数值. 最高位T6,这里用来当作溢出标志位:
T6位等于1时,表示计数器没溢出
T6位等于0时,表示计数器溢出
不过对于硬件电路来说 , T6位其实也是计数器的一部分 , 只不过是T6位被单独拎出来 , 当作标志位了而已. EG: 当看门狗寄存器把数据从1111 111 减到了1000 000(16进制表示:0X40) . 此时,如果把T6位也当做计数器的一部分, 那计数器的值实际上才减一半. 但是,如果我们把T6位剥离出去,当作溢出标志位, 低6位,当作计数器. 那此时的状态就是,标志位为1, 计数器为00 0000,已经减到0了. 再减一次变为011 1111 ,这时最高位T6,由1变为0---溢出--产生复位信号
结论:
T6位看作是计数器的一部分, 那就是整个计数器,值减到0x40(1000 000)之后溢出;
如果你把T6位当成溢出标志位,低6位,当作计数器, 那就是,低6位的计数值减到0之后溢
WDGA
WDGA-------是窗口看门狗的激活位 ; WDGA写入1,启用窗口看门狗
喂狗最早时间窗口的实现流程
由这部分电路组成
需要计算一个最早界限的计数值 , 写入到这里的W6~W0中 . 这些值,写入之后是固定不变的 . 一旦我们执行写入CR操作时(写看门狗控制寄存器) ,这个与门开关打开,
写入CR,其实就是写入计数器,也就是喂狗. 在喂狗时,比较器开始工作 . 一旦它比较,我们当前的计数器T6:0>窗口值W6:0, 比较结果就=1 .这个1,通过或门,也可以去申请复位-------这就是喂狗最早时间窗口的实现流程
递减计数器T[6:0]的值小于0x40时,WWDG产生复位------------T[6:0]包含T6位
递减计数器T[6:0]在窗口W[6:0]外被重新装载时,WWDG产生复位------过早喂狗
递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位-----在溢出的前一刻发送, 也叫做死前中断----操作比如保存重要数据、关闭危险设备等等
定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位
超时时间(喂狗的最晚时间):
TWWDG = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] + 1)---T[5:0]不包含T6位
窗口时间(喂狗的最早时间):
TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])
其中:
TPCLK1 = 1 / FPCLK1
FPCLK1---36Mkz 36000 000HZ
TPCLK1-----1/36000 000
6位递减计数器(CNT)--------6位的计数器,最大值是2^6-1=63
IWDG和WWDG对比
硬件看门狗:
如果用户在选择字节中启用了“硬件看门狗”功能,在系统上电复位后,看门狗会自动开始运行;如果在计数器计数结束前,若软件没有向键寄存器写入相应的值,则系统会产生复位。
窗口看门狗:
启动看门狗------------在系统复位后,看门狗总是处于关闭状态,设置WWDG_CR寄存器的WDGA位能够开启看门狗,随后它不能再被关闭,除非发生复位。
控制递减计数器-----------递减计数器处于自由运行状态,即使看门狗被禁止,递减计数器仍继续递减计数。当看门狗被启用时,TE位必须被设置,以防止立即产生一个复位。-------也就是我们在开启的时候 , 一定要首次给个重装值,并且T6位给1 . 以防止开的时候,立刻就复位了
1:开启时钟LSI,内部低速时钟(时钟频率为40KHz)------(如果独立看门狗已经由硬件选项或软件启动,LSI振荡器将被强制在打开状态,并且不能被关闭。在LSI振荡器稳定后,时钟供应给IWDG。)---所以开启时钟的代码不需要我们书写
2: 解除写保护-----IWDG_KR键寄存器
3 : 写入预分频和重装值
4 : 启动独立看门狗----IWDG_KR键寄存器
在stm32f10x_iwdg.h文件里面的独立看门狗函数
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);
void IWDG_SetReload(uint16_t Reload);
void IWDG_ReloadCounter(void);
void IWDG_Enable(void);
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);
IWDG_WriteAccessCmd : 写使能保护
/** * @brief Enables or disables write access to IWDG_PR and IWDG_RLR registers. * @param IWDG_WriteAccess: new state of write access to IWDG_PR and IWDG_RLR registers. * This parameter can be one of the following values: * @arg IWDG_WriteAccess_Enable: Enable write access to IWDG_PR and IWDG_RLR registers * @arg IWDG_WriteAccess_Disable: Disable write access to IWDG_PR and IWDG_RLR registers * @retval None */ void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess) { /* Check the parameters */ assert_param(IS_IWDG_WRITE_ACCESS(IWDG_WriteAccess)); IWDG->KR = IWDG_WriteAccess; }
在IWDG_KR键寄存器里面写入 使能还是不使能
#define IWDG_WriteAccess_Enable ((uint16_t)0x5555) #define IWDG_WriteAccess_Disable ((uint16_t)0x0000)
IWDG_SetPrescaler : 设置预分频系数----在预分频寄存器IWDG_PR里面写入
IWDG_SetReload : 设置重装值----在重装载寄存器IWDG_RLR里面写入
IWDG_ReloadCounter : 喂狗
/** * @brief Reloads IWDG counter with value defined in the reload register * (write access to IWDG_PR and IWDG_RLR registers disabled). * @param None * @retval None */ void IWDG_ReloadCounter(void) { IWDG->KR = KR_KEY_Reload; }
在IWDG_KR键寄存器写入
#define KR_KEY_Reload ((uint16_t)0xAAAA) #define KR_KEY_Enable ((uint16_t)0xCCCC)
IWDG_Enable : 启动独立看
/** * @brief Enables IWDG (write access to IWDG_PR and IWDG_RLR registers disabled). * @param None * @retval None */ void IWDG_Enable(void) { IWDG->KR = KR_KEY_Enable; }
/* KR register bit mask */ #define KR_KEY_Reload ((uint16_t)0xAAAA) #define KR_KEY_Enable ((uint16_t)0xCCCC)
IWDG_GetFlagStatus : 查看中断标志位
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1, 1, "IWDG TEST:");
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST)==SET)
{
/*独立看门狗复位现象
RCC_FLAG_IWDGRST---看门狗复位标志位
*/
OLED_ShowString(2, 1, "IWDG TEST:");
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag();
}
else
{
/*32自带的复位按键现象*/
OLED_ShowString(4, 1, "RTC:");
Delay_ms(500);
OLED_ShowString(4, 1, " ");
Delay_ms(100);
}
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//解除写保护
IWDG_SetPrescaler(IWDG_Prescaler_16); //写入预分频器的值
IWDG_SetReload(2499);//1000=0.025*16*(重装载寄存器+1)最长喂狗时间
IWDG_ReloadCounter();//喂狗
IWDG_Enable();//启动看门狗就是在键寄存器里面写入0XCCCC
//也就是0X5555之外的值,也就是开启写保护 , 所以我们不写写开启写保护了;
while (1)
{
Key_GetNum();
IWDG_ReloadCounter();//喂狗
Delay_ms(800);
}
}
1 : 开启窗口看门狗时钟---PCLK1(36MHz)
2: 配置寄存器--比如,预分频,和窗口值
3: 写入控制奇存器CR
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1, 1, "WWDG TEST:");
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST)==SET)
{
/*独立看门狗复位现象
RCC_FLAG_WWDGRST---窗口门狗复位标志位
*/
OLED_ShowString(2, 1, "WWDG TEST:");
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag();
}
else
{
/*32自带的复位按键现象*/
OLED_ShowString(4, 1, "RTC:");
Delay_ms(500);
OLED_ShowString(4, 1, " ");
Delay_ms(100);
}
//1: 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
//2: 配置寄存器
WWDG_SetPrescaler(WWDG_Prescaler_8); //预分频器的值
//WWDG_SetWindowValue()
WWDG_Enable(0x40 | 53);//把T6位当作标志位来计算的0x40--T6位置1
/*
超时时间(喂狗的最晚时间)
TWWDG = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] + 1)---T[5:0]不包含T6位
超时时间50ms=1/36000 KHZ * 4096 *8 *(T[5:0] + 1)
*/
WWDG_SetWindowValue(0x40 | 21); //30ms
/*
窗口时间(喂狗的最早时间):
TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])
30=1/36000KHZ*4096*8*(53-W[5:0])
*/
while (1)
{
Key_GetNum();
Delay_ms(38);
WWDG_SetCounter(0x40 | 53);
/*
窗口看门狗没有重装寄存器 ,喂狗操作
这个,我们直接在CNT写入数据就行了 想写多少就写多少
*/
}
}