記錄一下,方便以後翻閱~
主要內容:
1) 窗口看門狗概述;
2) 常用寄存器和庫函數配置;
3) 窗口看門狗實驗。
窗口看門狗實驗內容:
爲了對之前的知識進行總結複習,本人在教學案例的基礎上又“意淫”了一些附加要求,具體內容爲,啓動後,
獨立看門狗每次自動復位時,LED0先滅後亮,蜂鳴器不叫。按KEY2鍵可對獨立看門狗進行手動餵狗,按下時,LED0滅,蜂鳴器叫,指令內容通過串口傳至PC端,鬆開時,LED0亮,蜂鳴器不叫,長按KEY2時,LED0一直滅,蜂鳴器一直叫,但指令只傳一次;
窗口看門狗啓動提前喚醒中斷,每次因中斷自動餵狗時,LED1翻轉一次(即由0至1或由1至0),蜂鳴器不叫,若餵狗同時KEY1按下,指令內容通過串口傳至PC端。
官方資料:《STM32中文參考手冊V10》第18章——窗口看門狗
1. 窗口看門狗的概念
由於餵狗時間是一個有上下限的範圍內(窗口),通過設定相關寄存器,設定其上限時間(下限固定),餵狗的時間不能過早也不能過晚,所以稱之爲窗口看門狗;
獨立看門狗限制餵狗時間在0-x內,x由鍵寄存器的低16位決定。餵狗的時間不能過晚。
2. 窗口看門狗工作示意圖
2.1 由上圖所示,餵狗時間只能在刷新窗口間;
2.2 3Fh用二進制表示是0011 1111;
2.3 T6位至到達3Fh(0011 1111)前一時刻(0100 0000),當下一時刻到達3Fh時,變爲0011 1111,即第六位由1變爲0,執行復位。
3. 窗口看門狗框圖
3.1 窗口看門狗的時鐘來源於PCLK1,PCLK1最高頻率可達36MHz,PLCK1先除以4096,再進入看門狗預分頻器,最後計算出的值作爲窗口看門狗的時鐘頻率;;
3.2 控制寄存器WWDG_CR的第0~5位用來設置計數值,當第6位(即T6)由1變爲0時,變產生一個復位,第7位WDGA爲激活位,使能窗口看門狗;
3.3 通過比較器,當控制寄存器WWDG_CR的第0~6位(即T6:0)>配置寄存器WWDG_CFR的第0 ~6位(即W6:0),則比較結果爲1;
3.5 因此,上圖中,產生看門狗復位的條件有:
3.5.1 當滿足3.3條件,即(T6:0)>(W6:0),此時,對控制寄存器WWDG_CFR進行寫入,則經過或運算後,產生復位(因爲處於不允許刷新的區域,不能餵狗);
3.5.2 當計數器的數值從0x40減到0x3F時(即T6位由1跳變到0),經過非運算,再經過或運算後,產生復位。
3.6 如果啓動了看門狗並且允許中斷,當遞減計數器等於0x40時產生早期喚醒中斷(EWI),它可以用於餵狗以避免WWDG復位;
3.7 上窗口值W[6:0]必須大於下窗口值0x40。否則就無窗口了。
4. 窗口看門狗超時時間
根據上述公式,假設 Fpclk1爲最大值36MHz,那麼可以得到最小~最大超時時間表如下所示:
5. 窗口看門狗作用
對於一般的看門狗,程序可在它產生復位前的任意時刻刷新看門狗,但是,有可能程序跑亂了又跑回到正常的地方,或跑亂的程序正好執行了刷新看門狗操作,這樣的情況下一般的看門狗就檢測不出來了;
若使用窗口看門狗,可以根據程序正常執行的時間設置刷新看門狗的一個時間窗口,保證不會提前刷新看門狗也不會滯後刷新看門狗,這樣可以檢測出程序沒有按照正常的路徑運行非正常地跳過了某些程序段的情況。
6. 窗口看門狗常用寄存器
6.1 控制寄存器WWDG_CR
void WWDG_Enable(uint8_t Counter); //啓動並設置初始值//
void WWDG_SetCounter(uint8_t Counter); //餵狗//
6.2 配置寄存器WWDG_CFR
void WWDG_EnableIT(void); //使能提前喚醒中斷//
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler); // 設置分頻係數//
void WWDG_SetWindowValue(uint8_t WindowValue); //設置上窗口值//
6.3 控制寄存器WWDG_CR
FlagStatus WWDG_GetFlagStatus(void);
void WWDG_ClearFlag(void);
7. 窗口看門狗配置過程
7.1 使能看門狗時鐘:
RCC_APB1PeriphClockCmd();
7.2 設置分頻係數:
WWDG_SetPrescaler();
7.3 設置上窗口值:
WWDG_SetWindowValue();
7.4 開啓提前喚醒中斷並分組(可選):
WWDG_EnableIT();
NVIC_Init();
7.5 使能看門狗:
WWDG_Enable();
7.6 餵狗:
WWDG_SetCounter();
7.7 編寫中斷服務函數
WWDG_IRQHandler();
8. 實驗代碼部分解讀
跑馬燈、蜂鳴器、按鍵輸入、串口等相關代碼可參考以前的文章。
8.1 exti.c代碼解讀
將KEY2鍵作爲外部中斷,按下時,
#include "led.h"
#include "beep.h"
#include "key.h"
#include "exti.h"
#include "usart.h"
#include "sys.h"
//編寫EXTIx_Init初始化函數,包括中斷線配置和每個中斷線的優先級配置//
void EXTIx_Init(void)
{
//*申明一個結構體,名字爲EXTI_InitStructure,結構體原型由EXTI_InitTypeDef確定*//
EXTI_InitTypeDef EXTI_InitStructure;
//*申明一個結構體,名字爲NVIC_InitStructure,結構體原型由NVIC_InitTypeDef確定*//
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能複用功能時鐘AFIO//
//設置I/O口與中斷線的映射關係//
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); //PE2對應KEY2//
//PE2中斷線初始化配置,中斷模式,下降沿觸發,使能//
EXTI_InitStructure.EXTI_Line=EXTI_Line2; //GPIOx,Pin2對應EXTI2中斷線//
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式//
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //雙邊沿觸發//
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能//
//中斷線初始化,GPIOE,Pin2映射到EXTI2中斷線,中斷模式,下降沿觸發,使能//
EXTI_Init(&EXTI_InitStructure);
//對上述配置好的每個中斷進行優先級初始化//
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //選擇EXTI2外部中斷通道,即PE2,即KEY2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶佔優先級2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //響應優先級2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能//
NVIC_Init(&NVIC_InitStructure);
}
//編寫EXTI2中斷服務函數,對應KEY2按鍵,對獨立看門狗的相應工作//
void EXTI2_IRQHandler(void)
{
if(KEY2==0) //判斷KEY2按鍵是按下還是鬆開,按下則KEY2==0,if(1)
{
BEEP=1; //按下時蜂鳴器叫//
LED0=1; //按下時LED0滅//
printf("KEY2按下,對獨立看門狗進行手動餵狗(在main.c中執行),LED0滅,蜂鳴器叫\r\n");
}
else
{
BEEP=0; //鬆開時蜂鳴器不叫//
LED0=0; //鬆開時LED0亮//
IWDG_ReloadCounter();
printf("KEY2鬆開,對獨立看門狗進行手動餵狗,LED0亮,蜂鳴器不叫\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE0上的中斷標誌位
}
8.2 wwdg.c代碼解讀
包括IWDG_Init和WWDG_Init初始化函數,並編寫了窗口看門狗早期喚醒中斷服務函數。
#include "wdg.h"
#include "led.h"
#include "key.h"
#include "usart.h"
//編寫IWDG_Init初始化函數//
void IWDG_Init(u8 IWDG_PR,u16 IWDG_RLR)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//取消寄存器寫保護//
IWDG_SetPrescaler(IWDG_PR); //設置獨立看門狗的預分頻係數//
IWDG_SetReload(IWDG_RLR); //設置看門狗重裝載值//
IWDG_ReloadCounter(); //餵狗,否則將從0xFFF算起//
IWDG_Enable(); //使能獨立看門狗//
}
//定WWDG_CR控制寄存器第0~6位值,默認爲最大0111 1111//
u8 WWDG_CNT=0x7f;
//窗口看門狗計數頻率的計算公式爲:Fwwdg=PCLK1/(4096*2^fprer)//
//編寫WWDG_Init初始化函數,WWDG_TV(CR寄存器0~6位計數值),WWDG_WV(用來設置上窗口值)和WWDG_PR(用來設置預分頻係數)//
void WWDG_Init(u8 WWDG_TV,u8 WWDG_WV,u32 WWDG_PR)
{
//先WWDG時鐘使能//
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
WWDG_CNT=WWDG_TV&WWDG_CNT; //初始化WWDG_CNT,一般0x40≤WWDG_TV≤0x7F,則實際意思是將WWDG_TV賦值給WWDG_CNT//
WWDG_SetPrescaler(WWDG_PR); //設置WWDG預分頻值//
WWDG_SetWindowValue(WWDG_WV); //設置上窗口值,且0x40≤WWDG_WV≤0x7F//
WWDG_Enable(WWDG_CNT); //使能看門狗,並載入計數值,且0x40≤WWDG_CNT≤0x7F//
WWDG_ClearFlag(); //清除提前喚醒中斷標誌位
WWDG_NVIC_Init(); //初始化窗口看門狗中斷服務函數//
WWDG_EnableIT(); //開啓窗口看門狗中斷,即到計數到0x40時,發生一次中斷//
}
//重置WWDG計數器的值,即執行WWDG_Enable(cnt)函數//
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt); //使能看門狗,並載入計數值,且0x40≤cnt≤0x7F//
}
//編寫窗口看門狗中斷服務初始化函數//
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶佔優先級爲2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //響應優先級爲3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//編寫窗口看門狗中斷服務函數//
void WWDG_IRQHandler(void)
{
//餵狗,實際執行 WWDG_Enable(cnt)函數//
WWDG_SetCounter(WWDG_CNT); //當禁掉此句後,窗口看門狗將產生復位//
WWDG_ClearFlag(); //清除提前喚醒中斷標誌位//
LED1=!LED1; //LED狀態翻轉
if(KEY1==0) //判斷KEY1按鍵是否按下,按下則KEY2==0,if(1)
{
printf("對窗口看門狗進行手動餵狗,LED1閃爍\r\n");
}
}
8.3 main.c代碼解讀
主函數中,死循環裏寫了KEY2按下時,對獨立看門狗的餵狗方式。
#include "led.h"
#include "beep.h"
#include "key.h"
#include "wdg.h"
#include "exti.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
int main(void)
{
u8 key;
delay_init(); //延時函數初始化//
//設置中斷優先級分組爲組2:2位搶佔優先級,2位響應優先級//
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//usart1串口初始化,波特率爲115200//
LED_Init(); //LED端口初始化,LED0和LED0默認滅//
BEEP_Init(); //初始化蜂鳴器端口,不叫//
KEY_Init(); //按鍵初始化//
EXTIx_Init(); //外部中斷初始化//
uart_init(115200); //串口USART1初始化//
delay_ms(300);
LED0=0; //LED0點亮//
IWDG_Init(4,3125); //根據預分配值和重裝載值確定溢出時間,根據參數,溢出時間大約爲//
WWDG_Init(0X7F,0X4F,WWDG_Prescaler_8); //設置計數器值,上窗口值,分頻係數爲8//
//系統進入死循環後,LED0是亮的//
while(1)
{
key=KEY_Scan(1); //支持連按//
if(key)
{
switch(key)
{
case 3: //若KEY2按下,則對獨立看門狗進行餵狗//
IWDG_ReloadCounter();
break;
}
}
}
}
最後調試效果如下圖
9. 舊知識點
1)複習如何新建工程模板,可參考STM32學習心得二:新建工程模板;
2)複習基於庫函數的初始化函數的一般格式,可參考STM32學習心得三:GPIO實驗-基於庫函數;
3)複習寄存器地址,可參考STM32學習心得四:GPIO實驗-基於寄存器;
4)複習位操作,可參考STM32學習心得五:GPIO實驗-基於位操作;
5)複習寄存器地址名稱映射,可參考STM32學習心得六:相關C語言學習及寄存器地址名稱映射解讀;
6)複習時鐘系統框圖,可參考STM32學習心得七:STM32時鐘系統框圖解讀及相關函數;
7)複習延遲函數,可參考STM32學習心得九:Systick滴答定時器和延時函數解讀;
8)複習ST-LINK仿真器的參數配置,可參考STM32學習心得十:在Keil MDK軟件中配置ST-LINK仿真器;
9)複習ST-LINK調試方法,可參考STM32學習心得十一:ST-LINK調試原理+軟硬件仿真調試方法;
10)複習如何對GPIO進行復用,可參考STM32學習心得十二:端口複用和重映射;
11)複習中斷相關知識,可參考STM32學習心得十三:NVIC中斷優先級管理;
12)複習串口通信相關知識,可參考STM32學習心得十四:串口通信相關知識及配置方法;
13)複習外部中斷一般配置,可參考STM32學習心得十五:外部中斷實驗;
14)複習獨立看門狗相關知識,可參考STM32學習心得十六:獨立看門狗實驗。