第35章 WWDG—窗口看門狗
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx中文參考手冊》WWDG章節。
學習本章時,配合《STM32F4xx中文參考手冊》WWDG章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。
35.1 WWDG簡介
STM32有兩個看門狗,一個是獨立看門狗,一個是窗口看門狗。我們知道獨立看門狗的工作原理就是一個遞減計數器不斷的往下遞減計數,當減到0之前如果沒有喂狗的話,產生復位。窗口看門狗跟獨立看門狗一樣,也是一個遞減計數器不斷的往下遞減計數,當減到一個固定值0X40時還不喂狗的話,產生復位,這個值叫窗口的下限,是固定的值,不能改變。這個是跟獨立看門狗類似的地方,不同的地方是窗口看門狗的計數器的值在減到某一個數之前喂狗的話也會產生復位,這個值叫窗口的上限,上限值由用戶獨立設置。窗口看門狗計數器的值必須在上窗口和下窗口之間才可以喂狗,這就是窗口看門狗中窗口兩個字的含義
圖 351 IWDG與WWDG區別
RLR是重裝載寄存器,用來設置獨立看門狗的計數器的值。TR是窗口看門狗的計數器的值,由用戶獨立設置,WR是窗口看門狗的上窗口值,由用戶獨立設置。
35.2 WWDG功能框圖剖析
圖 352 窗口看門狗功能框圖
1. ①窗口看門狗時鍾
窗口看門狗時鍾來自PCLK1,PCLK1最大是45M,由RCC時鍾控制器開啟。
2. ②計數器時鍾
計數器時鍾由CK計時器時鍾經過預分頻器分頻得到,分頻系數由配置寄存器CFR的位8:7 WDGTB[1:0]配置,可以是[0,1,2,3],其中CK計時器時鍾=PCLK1/4096,除以4096是手冊規定的,沒有為什么。所以計數器的時鍾CNT_CK=PCLK1/4096/(2^WDGTB),
這就可以算出計數器減一個數的時間T= 1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB)。
3. ③計數器
窗口看門狗的計數器是一個遞減計數器,共有7位,其值存在控制寄存器CR的位6:0,即T[6:0],當7個位全部為1時是0X7F,這個是最大值,當遞減到T6位變成0時,即從0X40變為0X3F時候,會產生看門狗復位。這個值0X40是看門狗能夠遞減到的最小值,所以計數器的值只能是:0X40~0X7F之間,實際上用來計數的是T[5:0]。當遞減計數器遞減到0X40的時候,還不會馬上產生復位,如果使能了提前喚醒中斷:CFR位9 EWI 置1,則產生提前喚醒中斷,如果真進入了這個中斷的話,就說明程序肯定是出問題了,
那么在中斷服務程序里面我們就需要做最重要的工作,比如保存重要數據,或者報警等,這個中斷我們也叫它死前中斷。
4. ④窗口值
我們知道窗口看門狗必須在計數器的值在一個范圍內才可以喂狗,其中下窗口的值是固定的0X40,上窗口的值可以改變,具體的由配置寄存器CFR的位6:0 W[6:0]設置。其值必須大於0X40,如果小魚或者等於0X40就是失去了窗口的價值,而且也不能大於計數器的值,所以必須得小於0X7F。那窗口值具體要設置成多大?這個得根據我們需要監控的程序的運行時間來決定。如果我們要監控的程序段A運行的時間為Ta,當執行完這段程序之后就要進行喂狗,如果在窗口時間內沒有喂狗的話,那程序就肯定是出問題了。一般計數器的值TR設置成最大0X7F,窗口值為WR,計數器減一個數的時間為T,那么時間:(TR-WR)*T應該稍微大於Ta即可,這樣就能做到剛執行完程序段A之后喂狗,起到監控的作用,這樣也就可以算出WR的值是多少。
5. ⑤計算看門狗超時時間
圖 353 窗口看門狗時序圖
這個圖來自數據手冊,從圖我們知道看門狗超時時間:Twwdg = Tpclk1 x 4096 x 2^wdgtb x (T[5:0] + 1) ms,當PCLK1 = 30MHZ時,WDGTB取不同的值時有最小和最大的超時時間,那這個最小和最大的超時時間該怎么理解,又是怎么算出來的? 講起來有點繞,這里我稍微講解下WDGTB=0時是怎么算的。遞減計數器有7位T[6:0] ,當位6變為0的時候就會產生復位,實際上有效的計數位是T[5:0],而且T6必須先設置為1。如果T[5:0]=0時,遞減計數器再減一次,就產生復位了,那這減一的時間就等於計數器的周期=1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB) = 1/30 * 4096 *2^0 = 136.53us,這個就是最短的超時時間。如果T[5:0]全部裝滿為1,即63,當他減到0X40變成0X3F時,所需的時間就是最大的超時時間=113.7*2^5=136.53*64=8.74ms。同理,當WDGTB等於1/2/3時,代入公式即可。
35.3 怎么用WWDG
WWDG一般被用來監測,由外部干擾或不可預見的邏輯條件造成的應用程序背離正常的運行序列而產生的軟件故障。比如一個程序段正常運行的時間是50ms,在運行完這個段程序之后緊接着進行喂狗,如果在規定的時間窗口內還沒有喂狗,那就說明我們監控的程序出故障了,跑飛了,那么就會產生系統復位,讓程序重新運行。
35.4 WWDG喂狗實驗
35.4.1 硬件設計
4、WWDG一個
5、LED兩個
WWDG屬於單片機內部資源,不需要外部電路,需要兩個LED來指示程序的運行狀態。
35.4.2 軟件設計
我們編寫兩個 WWDG驅動文件,bsp_wwdg.h 和bsp_wwdg.c,用來存放WWDG的初始化配置函數。
1. 代碼分析
這里只講解核心的部分代碼,有些變量的設置,頭文件的包含等並沒有涉及到,完整的代碼請參考本章配套的工程。
WWDG配置函數
代碼 351 WWDG配置函數
1 /* WWDG 配置函數
2 * tr :遞減計時器的值,取值范圍為:0x7f~0x40
3 * wr :窗口值,取值范圍為:0x7f~0x40
4 * prv:預分頻器值,取值可以是
5 * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1/4096)/1
6 * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1/4096)/2
7 * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1/4096)/4
8 * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1/4096)/8
9 */
10 void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv)
11 {
12 // 開啟 WWDG 時鍾
13 RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
14
15 // 設置遞減計數器的值
16 WWDG_SetCounter( tr );
17
18 // 設置預分頻器的值
19 WWDG_SetPrescaler( prv );
20
21 // 設置上窗口值
22 WWDG_SetWindowValue( wr );
23
24 // 設置計數器的值,使能WWDG
25 WWDG_Enable(WWDG_CNT);
26
27 // 清除提前喚醒中斷標志位
28 WWDG_ClearFlag();
29 // 配置WWDG中斷優先級
30 WWDG_NVIC_Config();
31 // 開WWDG 中斷
32 WWDG_EnableIT();
33 }
WWDG配置函數有三個形參,tr是計數器的值,一般我們設置成最大0X7F,wr是上窗口的值,這個我們要根據監控的程序的運行時間來設置,但是值必須在0X40和計數器的值之間,prv用來設置預分頻的值,取值可以是:
代碼 352 形參 prv 取值
1 /*
2 * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1/4096)/1
3 * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1/4096)/2
4 * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1/4096)/4
5 * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1/4096)/8
6 */
這些宏在stm32f10x_wwdg.h中定義,宏展開是32位的16進制數,具體作用是設置配置寄存器CFR的位8:7 WDGTB[1:0],獲得各種分頻系數。
WWDG中斷優先級函數
1 // WWDG 中斷優先級初始化
2 static void WWDG_NVIC_Config(void)
3 {
4 NVIC_InitTypeDef NVIC_InitStructure;
5
6 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
7 NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
8 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
9 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
10 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
11 NVIC_Init(&NVIC_InitStructure);
12 }
在遞減計數器減到0X40的時候,我們開啟了提前喚醒中斷,這個中斷我們稱它為死前中斷或者叫遺囑中斷,在中斷函數里面我們應該出來最重要的事情,而且必須得快,因為遞減計數器再減一次,就會產生系統復位。
提前喚醒中斷復位程序
代碼 353 提前喚醒中斷服務程序
1 // WWDG 中斷復服務程序,如果發生了此中斷,表示程序已經出現了故障,
2 // 這是一個死前中斷。在此中斷服務程序中應該干最重要的事,
3 // 比如保存重要的數據等,這個時間具體有多長,要
4 // 由WDGTB的值決定:
5 // WDGTB:0 113us
6 // WDGTB:1 227us
7 // WDGTB:2 455us
8 // WDGTB:3 910us
9 void WWDG_IRQHandler(void)
10 {
11 // 清除中斷標志位
12 WWDG_ClearFlag();
13
14 //LED2亮,點亮LED只是示意性的操作,
15 //真正使用的時候,這里應該是做最重要的事情
16 LED2(ON);
17 }
喂狗函數
代碼 354 喂狗函數
1 // 喂狗
2 void WWDG_Feed(void)
3 {
4 // 喂狗,刷新遞減計數器的值,設置成最大WDG_CNT=0X7F
5 WWDG_SetCounter( WWDG_CNT );
6 }
喂狗就是重新刷新遞減計數器的值防止系統復位,喂狗一般是在主函數中喂。
主函數
代碼 355 主函數
1 int main(void)
2 {
3 uint8_t wwdg_tr, wwdg_wr;
4
5 /* LED 端口初始化 */
6 LED_GPIO_Config();
7
8 // BLUE 藍色燈亮
9 LED3(ON);
10 Delay(0XFFFFFF);
11
12 // WWDG配置
13
14 /* WWDG 配置函數
15 * tr :遞減計時器的值,取值范圍為:0x7f~0x40,超出范圍會直接復位
16 * wr :窗口值,取值范圍為:0x7f~0x40
17 * prv:預分頻器值,取值可以是
18 * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(45MHz)/4096)/1
19 約10968Hz 91us
20 * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(45MHz)/4096)/2
21 約5484Hz 182us
22 * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(45MHz)/4096)/4
23 約2742Hz 364us
24 * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(45MHz)/4096)/8
25 約1371Hz 728us
26 *
27 * 例:tr = 127(0x7f,tr的最大值)
28 * wr = 80(0x50, 0x40為最小wr最小值)
29 * prv = WWDG_Prescaler_8
30 * 窗口時間為728 * (127-80) = 34.2ms < 刷新窗口 < ~728 * 64 = 46.6ms
31 * 也就是說調用WWDG_Config進行這樣的配置,若在之后的34.2ms前喂狗,
32 * 系統會復位,在46.6ms后沒有喂狗,系統也會復位。
33 * 需要在刷新窗口的時間內喂狗,系統才不會復位。
34 */
35 // 初始化WWDG:配置計數器初始值,配置上窗口值,啟動WWDG,使能提前喚醒中斷
36 WWDG_Config(127,80,WWDG_Prescaler_8);
37
38 // 窗口值我們在初始化的時候設置成0X5F,這個值不會改變
39 wwdg_wr = WWDG->CFR & 0X7F;
40
41 while (1)
42 {
43 // BLUE 藍色燈
44 LED3(OFF);
45 //-----------------------------------------------------
46 // 這部分應該寫需要被WWDG監控的程序,這段程序運行的時間
47 // 決定了窗口值應該設置成多大。
48 //-----------------------------------------------------
49
50 // 計時器值,初始化成最大0X7F,當開啟WWDG時候,這個值會不斷減小
51 // 當計數器的值大於窗口值時喂狗的話,會復位,當計數器減少到0X40
52 // 還沒有喂狗的話就非常非常危險了,計數器再減一次到了0X3F時就復位
53 // 所以要當計數器的值在窗口值和0X40之間的時候喂狗,其中0X40是固定的。
54 wwdg_tr = WWDG->CR & 0X7F;
55
56 if ( wwdg_tr < wwdg_wr )
57 {
58 // 喂狗,重新設置計數器的值為最大0X7F
59 WWDG_Feed();
60 }
61 }
62 }
主函數中我們把WWDG的計數器的值設置 為0X7F,上窗口值設置為0X5F,分頻系數為8分頻,則計數器減1的時間約為728us。在while死循環中,我們不斷讀取計數器的值,當計數器的值減小到小於上窗口值的時候,我們喂狗,讓計數器重新計數。
在while死循環中,一般是我們需要監控的程序,這部分代碼的運行時間,決定了上窗口值應該設置為多少,當監控的程序運行完畢之后,我們需要執行喂狗程序,比起獨立看門狗,這個喂狗的窗口時間是非常短的,對時間要求很精確。如果沒有在這個窗口時間內喂狗的話,那就說明程序出故障了,會產生提前喚醒中斷,最后系統復位。
35.4.3 下載驗證
把編譯好的程序下載到開發板,LED3被點亮,一段時間之后熄滅,之后LED3一直就沒有被點亮過,說明系統沒有產生復位,如果產生復位的話LED3會再被點亮一次。中斷服務程序中的LED也沒被點亮過,說明喂狗正常。