引言
之前講過了獨立看門狗,可以避免程序跑飛。這一節介紹的是窗口看門狗,他們雖然都是看門狗,但是也有許多的差別。例如窗口看門狗使用的時鍾是系統時鍾,而獨立看門狗則使用的是獨立的RC時鍾。關於兩個看門狗之間更多的不同,可以參考下面這張圖片:
窗口看門狗介紹
窗口,顧名思義,就像窗口比較器一樣,都會有一個上限值,和一個下限值。獨立看門狗只需要在計數器溢出之前,對其進行復位即可。而窗口看門狗不同,喂狗的時間點不能太早,也不能太遲。這樣做有什么好處呢?
如果我們使用的是獨立看門狗,當程序跑飛的時候,可能會機緣巧合之下又回到了正軌,假如在計數器溢出之前回到正軌,或者在錯誤的環節中正好又執行了喂狗的操作,那我們就沒有辦法發現出錯的那一部分,那我們就會默認沒有出錯。而如果我們使用了窗口看門狗,我們可以讓其在一段時間后才能喂狗,這樣一來,程序若沒有按照正確的路徑運行,我們就能夠及時發現。
窗口看門狗相對於獨立看門狗另一個重要的區別是窗口看門狗擁有一個中斷。這個中斷並不是用來日常喂狗的,而是在出現突發狀況時,能夠讓其保存一些重要數據。類似讓單片機寫一份“遺囑”,有一個緊急處理的機會。如果窗口看門狗的中斷只是用來日常喂狗的話,就和獨立看門狗沒什么區別了,甚至還會埋下隱患。
窗口看門狗的配置過程如下:
- 使能WWDG時鍾
- 設置窗口值和分頻數
- 開啟WWDG中斷並分組
- 設置計數器初始值
- 使能看門狗
- 編寫中斷服務函數
窗口看門狗的上窗口值是由使用者確定的,而下窗口值是固定的0x40。當計數器在上窗口之外被刷新或者低於下窗口,計數器都會產生復位。見下圖圖示:W[6:0]這七位是用來存儲上窗口值的寄存器的低七位,T[6:0]是計數器。
假設:
- Twwdg為超時時間(單位為ms)
- Fpclk1為APB1時鍾的頻率(單位為KHz)
- prer為預分頻系數
- T[5:0]為計數器的低六位
則窗口看門狗的超時公式為:
編碼
main.c
int main(void){
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
LED0 = 0;
delay_ms(300);
WWDG_Init(0x7f, 0x5f, WWDG_Prescaler_8); //窗口看門狗初始化
while(1){
LED0 = 1;
}
}
wdg.h
#ifndef __WDG_H
#define __WDG_H
#include "sys.h"
void WWDG_Init(u8 tr,u8 wr,u32 fprer); //看門狗初始化
void WWDG_Set_Counter(u8 cnt); //喂狗函數
void WWDG_NVIC_Init(void); //看門狗中斷優先級設置
#endif
wdg.c
#include "wdg.h"
#include "led.h"
u8 WWDG_CNT = 0x7f; //默認計數器的值為最大
void WWDG_Init(u8 tr, u8 wr, u32 fprer){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //打開WWDG時鍾
WWDG_CNT = tr & WWDG_CNT; //初始化計數器的值
WWDG_SetPrescaler(fprer); //設置預分頻系數
WWDG_SetWindowValue(wr); //設置窗口值
WWDG_NVIC_Init(); //WWDG中斷分組初始化
WWDG_Enable(WWDG_CNT); //使能看門狗,並設置計數器初值
WWDG_ClearFlag(); //清除提前喚醒中斷標志位
WWDG_EnableIT(); //開啟WWDG中斷
}
void WWDG_Set_Counter(u8 cnt){ //喂狗函數
WWDG_Enable(cnt);
}
void WWDG_NVIC_Init(){ //設置看門狗中斷優先級
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void WWDG_IRQHandler(void){ //看門狗中斷服務函數
WWDG_SetCounter(WWDG_CNT);
WWDG_ClearFlag();
LED1 = !LED1;
}
至此,我們可以得到的效果就是LED0亮起300ms,然后熄滅。而LED1不斷的閃爍。