STM32的獨立看門狗由內(nèi)部專門的40Khz低速時鐘驅(qū)動,即使主時鐘發(fā)生故障,它也仍然有效。這里需要注意獨立看門狗的時鐘是一個內(nèi)部RC時鐘,所以并不是準確的40Khz,而是在30~60Khz之間的一個可變化的時鐘,只是我們在估算的時候,以40Khz的頻率來計算,看門狗對時間的要求不是很精確,所以,時鐘有些偏差,都是可以接受的。
首先我們得講解一下看門狗的原理。我們總結(jié)一下:
單片機系統(tǒng)在外界的干擾下會出現(xiàn)程序跑飛的現(xiàn)象導致出現(xiàn)死循環(huán),看門狗電路就是為了避免這種情況的發(fā)生??撮T狗的作用就是在一定時間內(nèi)(通過定時計數(shù)器實現(xiàn))沒有接收喂狗信號(表示MCU已經(jīng)掛了),便實現(xiàn)處理器的自動復(fù)位重啟(發(fā)送復(fù)位信號)。
下面我們在了解幾個與獨立看門狗相關(guān)聯(lián)的寄存器之后講解怎么通過庫函數(shù)來實現(xiàn)配置。
首先是鍵值寄存器IWDG_KR,該寄存器的各位描述如圖所示:
在鍵值寄存器(IWDG_KR)中寫入 0xCCCC,開始啟用獨立看門狗;此時計數(shù)器開始從其復(fù)位值 0xFFF 遞減計數(shù)。當計數(shù)器計數(shù)到末尾 0x000 時,會產(chǎn)生一個復(fù)位信號(IWDG_RESET)。無論何時,只要鍵寄存器 IWDG_KR 中被寫入 0xAAAA, IWDG_RLR 中的值就會被重新加載到計數(shù)器中從而避免產(chǎn)生看門狗復(fù)位 。IWDG_PR 和 IWDG_RLR 寄存器具有寫保護功能。要修改這兩個寄存器的值,必須先向IWDG_KR 寄存器中寫入 0x5555。將其他值寫入這個寄存器將會打亂操作順序,寄存器將重新被保護。重裝載操作(即寫入 0xAAAA)也會啟動寫保護功能。
還有兩個寄存器,一個預(yù)分頻寄存器(IWDG_PR),該寄存器用來設(shè)置看門狗時鐘的分頻系數(shù)。另一個重裝載寄存器。該寄存器用來保存重裝載到計數(shù)器中的值。該寄存器也是一個 32位寄存器,但是只有低 12 位是有效的。
只要對以上三個寄存器進行相應(yīng)的設(shè)置,我們就可以啟動 STM32 的獨立看門狗,啟動過程可以按如下步驟實現(xiàn)(獨立看門狗相關(guān)的庫函數(shù)和定義分布在文件 stm32f10x_iwdg.h 和stm32f10x_iwdg.c 中) :
1)取消寄存器寫保護(向 IWDG_KR 寫入 0X5555)
通過這步,我們?nèi)∠?IWDG_PR 和 IWDG_RLR 的寫保護,使后面可以操作這兩個寄存器,設(shè)置 IWDG_PR 和 IWDG_RLR 的值。這在庫函數(shù)中的實現(xiàn)函數(shù)是:
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
這個函數(shù)非常簡單,顧名思義就是開啟/取消寫保護,也就是使能/失能寫權(quán)限。
2)設(shè)置獨立看門狗的預(yù)分頻系數(shù)和重裝載值
設(shè)置看門狗的分頻系數(shù)的函數(shù)是:
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //設(shè)置 IWDG 預(yù)分頻值
設(shè)置看門狗的重裝載值的函數(shù)是:
void IWDG_SetReload(uint16_t Reload); //設(shè)置 IWDG 重裝載值
設(shè)置好看門狗的分頻系數(shù) prer 和重裝載值就可以知道看門狗的喂狗時間 (也就是看門狗溢出時間) ,該時間的計算方式為:
Tout=((4×2^prer) ×rlr) /40
其中 Tout 為看門狗溢出時間(單位為 ms) ;prer 為看門狗時鐘預(yù)分頻值(IWDG_PR 值),范圍為 0~7;rlr 為看門狗的重裝載值(IWDG_RLR 的值) ;
比如我們設(shè)定 prer 值為 4, rlr 值為 625,那么就可以得到 Tout=64×625/40=1000ms,這樣,看門狗的溢出時間就是 1s,只要你在一秒鐘之內(nèi),有一次寫入 0XAAAA 到 IWDG_KR,就不會導致看門狗復(fù)位(當然寫入多次也是可以的)。這里需要提醒大家的是,看門狗的時鐘不是準確的 40Khz,所以在喂狗的時候,最好不要太晚了,否則,有可能發(fā)生看門狗復(fù)位。
3)重載計數(shù)值喂狗(向 IWDG_KR 寫入 0XAAAA)
庫函數(shù)里面重載計數(shù)值的函數(shù)是:
IWDG_ReloadCounter(); //按照 IWDG 重裝載寄存器的值重裝載 IWDG 計數(shù)器
通過這句,將使 STM32 重新加載 IWDG_RLR 的值到看門狗計數(shù)器里面。 即實現(xiàn)獨立看門狗的喂狗操作。
4) 啟動看門狗(向 IWDG_KR 寫入 0XCCCC)
庫函數(shù)里面啟動獨立看門狗的函數(shù)是:
IWDG_Enable(); //使能 IWDG
通過這句,來啟動 STM32 的看門狗。注意 IWDG 在一旦啟用,就不能再被關(guān)閉!想要關(guān)閉,只能重啟,并且重啟之后不能打開 IWDG,否則問題依舊,所以在這里提醒大家,如果不用 IWDG 的話,就不要去打開它,免得麻煩。
通過上面 4 個步驟,我們就可以啟動 STM32 的看門狗了,使能了看門狗,在程序里面就必須間隔一定時間喂狗,否則將導致程序復(fù)位。利用這一點,我們本章將通過一個 LED 燈來指示程序是否重啟,來驗證 STM32 的獨立看門狗。
在配置看門狗后, DS0 將常亮,如果 WK_UP 按鍵按下,就喂狗,只要 WK_UP 不停的按,看門狗就一直不會產(chǎn)生復(fù)位,保持 DS0 的常亮,一旦超過看門狗定溢出時間(Tout)還沒按,那么將會導致程序重啟,這將導致 DS0 熄滅一次。我們要加入固件庫看門狗支持文件 stm32f10x_iwdg.h 和stm32f10x_iwdg.c 文件。
wdg.c 里面的代碼如下:
#include “wdg.h”
//初始化獨立看門狗
//prer:分頻數(shù):0~7(只有低 3 位有效?。?/p>
//分頻因子=4*2^prer.但最大值只能是 256!
//rlr:重裝載寄存器值:低 11 位有效。
//時間計算(大概):Tout=((4*2^prer)*rlr)/40 (ms)。
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //①使能對寄存器 I 寫操作
IWDG_SetPrescaler(prer); //②設(shè)置 IWDG 預(yù)分頻值:設(shè)置 IWDG 預(yù)分頻值
IWDG_SetReload(rlr); //②設(shè)置 IWDG 重裝載值
IWDG_ReloadCounter(); //③按照 IWDG 重裝載寄存器的值重裝載 IWDG 計數(shù)器
IWDG_Enable(); //④使能 IWDG
}
//喂獨立看門狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}
該代碼就 2 個函數(shù),void IWDG_Init(u8 prer,u16 rlr)是獨立看門狗初始化函數(shù),就是按照上面介紹的步驟 1~4 來初始化獨立看門狗的。該函數(shù)有 2 個參數(shù),分別用來設(shè)置與預(yù)分頻數(shù)與重裝寄存器的值的。通過這兩個參數(shù),就可以大概知道看門狗復(fù)位的時間周期為多少了。其計算方式上面有詳細的介紹,這里不再多說了。
void IWDG_Feed(void)函數(shù),該函數(shù)用來喂狗,因為 STM32 的喂狗只需要向鍵值寄存器寫
入 0XAAAA 即可,也就是調(diào)用 IWDG_ReloadCounter()函數(shù),所以,我們這個函數(shù)也是簡單的很。
頭文件 wdg.h 的源碼如下大家可以看下,這里我們就不列出來了。
接下來我們看看主函數(shù) main 的代碼。
在主程序里面我們先初始化一下系統(tǒng)代碼,然后啟動按鍵輸入和看門狗,在看門狗開啟后馬山點亮 LED0(DS0) ,并進入死循環(huán)等待按鍵的輸入,一旦 WK_UP 有按鍵,則喂狗,否則等待 IWDG 復(fù)位的到來。這段代碼很容易理解,該部分代碼如下:
int main(void)
{
delay_init(); //延時函數(shù)初始化
NVIC_Configuration(); //設(shè)置 NVIC 中斷分組 2:2 位搶占優(yōu)先級,2 位響應(yīng)優(yōu)先級
uart_init(9600); //串口初始化波特率為 9600
LED_Init(); //初始化與 LED 連接的硬件接口
KEY_Init(); //按鍵初始化
delay_ms(500); //讓人看得到滅
IWDG_Init(4,625); //與分頻數(shù)為 64,重載值為 625,溢出時間為 1s
LED0=0; //點亮 LED0
while(1)
{
if(KEY_Scan(0)==KEY_UP)
{
IWDG_Feed(); //如果 WK_UP 按下,則喂狗
}
delay_ms(10);
};
}
評論