目录
一:看门狗
1:WDT
2:独立看门狗 (IWDG)
A:IWDG框图
B:IWDG_KR键寄存器
C:IWDG超时时间
D:HAl库的配置
3:窗口看门狗 (WWDG)
A:WWDG框图
B:WWDG工作特性
C:WWDG超时时间
D:HAL库配置
4:独立看门狗和窗口看门狗的区别
5:数据手册
二:案例
A:独立看门狗
B:窗口看门狗
一:看门狗
1:WDT
WDG(Watchdog)看门狗------本质就是自动复位
看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器--重置不是清零)操作时,看门狗硬件电路就自动产生复位信号
STM32内置两个看门狗
独立看门狗(IWDG):独立工作,对时间精度要求较低
窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用
2:独立看门狗 (IWDG)
A:IWDG框图
结构
它的结构和定时器是常相似的 , 只不过是,定时器溢出,产生中断 . 而看门狗定时器溢出,直接产生复位信号(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 : 标志电路运行的状态
B:IWDG_KR键寄存器
键寄存器本质上是控制寄存器,用于控制硬件电路的工作
在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值来代替控制寄存器写入一位的功能,以降低硬件电路受到干扰的概率
上面说的喂狗操作 : 实际就是在键寄存器里面写入 0xAAAA
C:IWDG超时时间
超时时间(溢出时间) :如果超超过了自己设置的时间,没有喂狗系统就会自动复位
复位条件: 计数器的值数到零时,就会复位
超时时间: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
时间的计算
以第一组为例
D:HAl库的配置
都在响应的iwdg文件里面(HAl库函数)
3:窗口看门狗 (WWDG)
A:WWDG框图
T[6:0]和w[6:0]的数字是我们自己可以设置的, 下面的0x40(64)和0x3F(63)已经固定好了。
T[6:0]: 递减计数器(第6位做位标志位) 11 1111= 127=0x7F(最大填写的数),又因为窗口下限值为63(0x3F),所以实际可设置的范围为6到127
W[6:0]:窗口值(看门狗配置寄存器,窗口上限值)1111 1111=225=0xff 注意窗口的值不能比计数器初始值还大
喂狗
窗口看门狗没有重装寄存器 ,喂狗操作 : 这个,我们直接在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,通过或门,也可以去申请复位-------这就是喂狗最早时间窗口的实现流程
B:WWDG工作特性
递减计数器T[6:0]的值小于0x40时,WWDG产生复位------------T[6:0]包含T6位
递减计数器T[6:0]在窗口W[6:0]外被重新装载时,WWDG产生复位------过早喂狗
递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位-----在溢出的前一刻发送, 也叫做死前中断----操作比如保存重要数据、关闭危险设备等等
过早喂狗不会产生中断,中断是递减计数器等于0X4F的时候才会产生的。所以如果要在系统发生错误的时候做出一些紧急的操作,我们需要避免过早喂狗
定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位
C:WWDG超时时间
窗口时间(喂狗的最早时间):
TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])
TPCLK1-----1/36000 000
超时时间(计数器减到零的时间):
TWWDG = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] + 1)---T[5:0]不包含T6位
其中:
TPCLK1 = 1 / FPCLK1
FPCLK1---36Mkz 36000 000HZ
TPCLK1-----1/36000 000
s
6位递减计数器(CNT)--------6位的计数器,最大值是2^6-1=63
D:HAL库配置
在WWDG的初始化函数中,初始化函数会自动调用Mspinit回调函数
4:独立看门狗和窗口看门狗的区别
IWDG和WWDG对比
5:数据手册
硬件看门狗:
如果用户在选择字节中启用了“硬件看门狗”功能,在系统上电复位后,看门狗会自动开始运行;如果在计数器计数结束前,若软件没有向键寄存器写入相应的值,则系统会产生复位。
窗口看门狗:
启动看门狗------------在系统复位后,看门狗总是处于关闭状态,设置WWDG_CR寄存器的WDGA位能够开启看门狗,随后它不能再被关闭,除非发生复位。
控制递减计数器-----------递减计数器处于自由运行状态,即使看门狗被禁止,递减计数器仍继续递减计数。当看门狗被启用时,TE位必须被设置,以防止立即产生一个复位。-------也就是我们在开启的时候 , 一定要首次给个重装值,并且T6位给1 . 以防止开的时候,立刻就复位了
二:案例
A:独立看门狗
设置溢出时间为1S, 如果在1S内没有及时喂狗系统就会复位;
#include "stm32f1xx_hal.h"IWDG_HandleTypeDef g_iwdg_handle;/*** @brief 独立看门狗初始化* @param psc :预分频器值* @param rlr:重装载值 * @retval 无*/
void Iwdg_Init(uint8_t psc,uint16_t rlr)
{g_iwdg_handle.Instance=IWDG; /*基地址*/g_iwdg_handle.Init.Prescaler=psc; /*预分频器值*/g_iwdg_handle.Init.Reload=rlr; /*重装载值*/HAL_IWDG_Init(&g_iwdg_handle);} /*** @brief 独立看门狗喂狗操作*/
void feed_dog(void)
{HAL_IWDG_Refresh(&g_iwdg_handle);}#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "exit.h"
#include "UART.h"
#include "OLED.h"
#include "iwdg.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */LED_Init(); /* LED初始化 */LED_Exit_Init();OLED_Init();Uart_Init(115200);OLED_ShowString(1, 1, "Please feed the");OLED_ShowString(2, 1, "in time!!!!!");Iwdg_Init(IWDG_PRESCALER_16,2500);/*设置溢出时间为为1S,所以分频系数为16,重装载值为2500*/while(1){delay_ms(1050);feed_dog();OLED_ShowString(1, 1, " ");OLED_ShowString(2, 1, " ");OLED_ShowString(1, 1, "Dog fed");}}
重装载值的计算(rlr):
公式在上面写啦.
B:窗口看门狗
#include "stm32f1xx_hal.h"
#include "led.h"
#include "delay.h"/*** @brief 窗口看门狗初始化* @param psc :预分频器值 * @param coun:计数器的值 T[6:0] * @param window_val:窗口的值 W[6:0] * @retval 无*/
void wwdg_feed_dog(void);
WWDG_HandleTypeDef Wwdg_Init_handle;void wwdg_Init(uint32_t psc,uint8_t coun,uint8_t window_val)
{Wwdg_Init_handle.Instance=WWDG;Wwdg_Init_handle.Init.Counter=coun; /*计数器的值*/Wwdg_Init_handle.Init.EWIMode=WWDG_EWI_ENABLE; /*使能中断*/Wwdg_Init_handle.Init.Prescaler=psc; /*预分频器值*/Wwdg_Init_handle.Init.Window=window_val;HAL_WWDG_Init(&Wwdg_Init_handle);
} /*回调函数:在初始化函数中都会自调用回调函数*/
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{__HAL_RCC_WWDG_CLK_ENABLE();//HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);HAL_NVIC_SetPriority(WWDG_IRQn,2,0);HAL_NVIC_EnableIRQ(WWDG_IRQn);
}/*中断函数*/
void WWDG_IRQHandler()
{HAL_WWDG_IRQHandler(&Wwdg_Init_handle);
}/*中断处理函数,发送中断时
*HAL_WWDG_IRQHandler函数自动调用这个函数
*
*/
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{ HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}/*喂狗*/
void wwdg_feed_dog(void)
{HAL_WWDG_Refresh(&Wwdg_Init_handle);
}#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */LED_Init(); /* LED初始化 */LED_Exit_Init();OLED_Init();wwdg_Init(WWDG_PRESCALER_8,0x7f, 0x5f);if(__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)!=RESET) /*1===>发生窗口看门狗复位*/{OLED_ShowString(1, 1,"Widnow dog Reset");__HAL_RCC_CLEAR_RESET_FLAGS(); /*清除中断标志位*/}else{OLED_ShowString(1, 1,"Outside dog Re");OLED_ShowString(2, 1,"set!!!!");}OLED_ShowString(3, 1,"Please feed");OLED_ShowString(4, 1,"the dog!!!!");while(1){delay_ms(25);wwdg_feed_dog();OLED_ShowString(3, 1," ");OLED_ShowString(4, 1,"feed dog....");HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6); }}
关于时间的计算:
窗口时间(喂狗的最早时间):
TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])
TPCLK1-----1/36000 000
就设置计数器值为7f,窗口寄存器为5f,分频数为8,然后可由前面的公式得到窗口上限时间Twwdg=4096×8×(0x7F-0x5F)/36MHz=29.12ms,
窗口下限时间Twwdg=4096×8×(0x7F-0x3F)/36MHz=58.25ms,
即喂狗的窗口区间为29.12~58.25ms。
关于寄存器:
if(__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)!=RESET) /*1===>发生窗口看门狗复位*/{OLED_ShowString(1, 1,"Widnow dog Reset");__HAL_RCC_CLEAR_RESET_FLAGS(); /*清除中断标志位*/}
在stm32f1xx hal_rcc.h文件里面
代码解释:
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg)
__weak void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
void HAL_WWDG_IRQHandler(WWDG_HandleTypeDef *hwwdg)
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg)
可以看到他们这些函数的句柄都一样,使用使用一个即可:
WWDG_HandleTypeDef Wwdg_Init_handle;
我们在主函数中调用WWDG初始化函数(HAL_WWDG_Init)时,初始化函数里面会自动调用回调函数。在递减计数器减到0X4F时候触发中断或者在非窗口期喂狗触发中断,我们写的中断函数中调用中断函数,中断函数里面自动调用回滚函数HAL_WWDG_EarlyWakeupCallback()。
过早喂狗不会产生中断,中断是递减计数器等于0X4F的时候才会产生的。所以如果要在系统发生错误的时候做出一些紧急的操作,我们需要避免过早喂狗