看門狗與喂狗詳解(轉載只是為了查閱方便,若侵權,立刪)
“看門狗定時器”是這樣一種東西,從功能上說它可以讓微控制器在意外狀況下(比如軟體陷入死迴圈)重新回覆到系統上電狀態,以保證系統出問題的時候重啟一次。就跟我們現在用電腦一樣,宕機了你就按一下reset鍵重啟一次電腦,看門狗就是負責幹這個事兒的。它是52微控制器增加的一個功能,以前Intel 8031、……、AT 89C51時代微控制器片內都沒有“看門狗”功能,需要我們外擴看門狗晶片,比如X5045。
很多人初次接觸不太理解怎麼用,書上也講的含含糊糊,故意說的很複雜很玄妙(可能是現在寫書人的通病,生怕寫的簡單的別人覺得他沒水平)。其實要是說明白點:“看門狗”就是一個計數器,由於位數有限計數器能夠裝的數值是有限的(比如8位的最多裝256個數、16位的最多裝65536個數),從開啟“看門狗”那刻起,它就開始不停的數機器週期,數一個機器週期就計數器加1,加到計數器盛不下了(術語叫溢位)就就產生一個復位訊號,重啟系統
註解:這裡順便說一下,一般教材上叫“看門狗定時器”,其實定時器原理還是計數器,只是計的是時鐘週期,所以我為了初學者好理解叫統一叫“計數器”,這裡闡明一下。
明白了上面的原理,我們在設計程式時,先根據看門狗計數器的位數和系統的時鐘週期算一下計滿數需要的時間,就是說在這個時間內“看門狗”計數器是不會裝滿的,然後在這個時間內告訴它重新開始計數,就是把計數器清零,這個過程叫“喂狗”,這樣隔一段時間喂一次狗,只要程式正常執行他就永遠計不滿,一旦出現死迴圈之類的故障,沒有及時來清零計數器,就會導致裝滿了溢位,他就重啟系統,這就是看門狗的看門原理,其實想想傻傻的、笨笨的。
舉個例子說:8051 微控制器選用12MHz晶振,一個時鐘週期為1us,如果“看門狗計數器”是16位的,最大計數65536個,那麼從0開始計到65535需要約65ms,所以我們可以在程式的50ms左右清零一次計數器(“喂狗”),讓他重新從0開始計,再過50ms,再清,……,這樣下去只要程式正常執行,計數器永遠不會計滿,也就永遠不會被“看門狗”復位。當然這個喂狗的時間是大家自己選的,只要不超過65ms,你選多少都可以,一般不要喂得太勤,這樣微控制器執行時間浪費了,比如你1ms喂一次就太勤了,也不要說那我65ms喂一次,這樣太邊緣,這樣抗干擾能力就下降了,最好是留一定的餘量,這個就是設計者自己掌握了,我一般是讓計到90%左右就清一次。
每種微控制器的“看門狗”實現方法不盡相同,但是原理都一樣,而且“看門狗”都是啟動了之後就不能被關閉,只能系統復位(重新斷電在上電)才能關閉。設定“看門狗”的一般步驟如下:
1. 設定“看門狗”相關暫存器, 啟動“看門狗”;
2. 隔一段時間清零一次,“喂狗”;
3. 如果程式正常,一直執行;如果程式出錯,沒有按時“喂狗”,“看門狗”就在溢位的時候復位系統。
值得提一下:
由於現在AT89S52應用比較廣泛,所以我先說說ATMEL的看門狗;再說說本次試驗用的STC89C52RC的看門狗;注意兩個不一樣!!!
AT89S52微控制器看門狗定時器是14位的,最大計數214=16384個數,每計16384個時鐘週期就溢位一次。也就是說如果使用12M晶振的話,至少應該在16.384ms內喂一次狗。
STC89C5X系列微控制器由於採用了“預分頻技術”,它的溢位時間是=(N*Prescale*32768)/晶振頻率(不要問我為什麼,他們就是這麼設計的,我們就這麼用就行)。
- 其中N是微控制器的時鐘週期,STC89C5X系列微控制器提供6時鐘週期和12時鐘週期兩種時鐘週期,可以在燒寫程式時修改;
- Prescale是預分頻數,通過設定【看門狗控制暫存器】可以設定為2、4、8、16、32、64、128、256;怎麼設定演示程式中有介紹;
- 晶振頻率就是系統選用的晶振。
所以如果同樣選擇12MHz晶振,使用傳統的12時鐘週期,它最小的溢位時間是(12*2*32768)/(12*106)=65.536ms,最大溢位時間是(12*256*32768)/(12*106)≈8.38s。如果選擇256分頻,也就是說只要在8.38秒之內喂一次狗就可以了。戲謔的說:這隻狗比較抗餓,J~~
對於我們使用者來說,看門狗的時間是越長越好,這樣可以節省更多的微控制器資源,尤其是對時間要求精準的系統,如果執行過程中我們不停地“喂狗”,那麼是比較浪費時間的。所以STC89C5X系列微控制器的看門狗更有優勢一些。當然這個也是個人的選擇,如果對時間要求的不苛刻的話,勤喂幾次狗也沒關係。
下面我就以STC89C52RC微控制器為例說說典型的51微控制器的看門狗程式如何寫,關於STC89C52RC微控制器的“看門狗”定義請看STC89C51RC-RD微控制器使用說明。以下程式在Keil 2和Keil 3下調時通過,下載在本校的實驗板上達到預期效果。STC89C52RC/54RD+/58RD+/516RD+微控制器上測試正常執行。
如果沒有我們的實驗板,請按照下面的硬體原理圖自己在最小系統上搭建一個實驗環境也很容易。
圖1. STC89C52最小微控制器系統+兩個指示燈
圖2. 序列口介面(用於下載程式和測試本次試驗)
view sourceprint?
001 |
/*************************************************************************** |
002 |
程式功能:本程式演示STC51微控制器看門狗程式 |
003 |
程式設計:燕山大學 魯信瓊 |
004 |
晶振選擇:11.0592MHz, 如果晶振不匹配,請修改延時函式引數 |
005 |
承接51微控制器、PIC微控制器程式、VB/VC++上位機程式、電子產品軟硬體設計開發工作 |
006 |
EMail: xqlu(at)ysu.net.cn QQ: 9790335 |
007 |
|
008 |
由於現在AT89S52很流行,所以我先說說ATMEL的看門狗;再說說本次試驗用的STC89C52RC的看門狗;注意兩個不一樣!!! |
009 |
|
010 |
★下面是關於ATMEL-51微控制器看門狗的描述 |
011 |
【看門狗計數器】(watchdog timer)是一個14位的計數器,它以機器週期(晶振頻率/12)增加,當計數值計滿(16383/0x3FFF)了就使微控制器軟復位; |
012 |
當啟動了【看門狗計數器】之後,我們需要在它計數沒有滿之前復位計數器強制它不能夠溢位,這個過程稱作喂狗。 |
013 |
|
014 |
"看門狗"原理: |
015 |
1. 系統上電並不啟動看門狗計數器,通過設定【看門狗重置暫存器(WDTRST SFR)】啟動【看門狗計數器】,一般設定是給WDTRST寫入0x1E和0xE1啟動; |
016 |
2. 【看門狗計數器】一旦啟動不可停止,除非是硬體RST或者看門狗的軟復位才能使其停止; |
017 |
3. 設計程式在適當的時間喂狗一次,使其不能計滿,程式就能不間斷執行; |
018 |
4. 如果程式中出現死迴圈或者執行某一步超時,看門狗計數器就會計滿溢位,(這個時候我們認為程式沒有按照預定計劃執行--程式跑飛),則復位系統。 |
019 |
|
020 |
★下面是關於STC89C5XX-51微控制器看門狗的描述 |
021 |
WDT_CONTR位置0xE1; [-] [-] [EN_WDT] [CLR_WDT] [IDLE_WDT] [PS2] [PS1] [PS0] |
022 |
EN_WDT: 看門狗允許位,置1啟動看門狗,看門狗不能自動啟動,需要設定該位後啟動,一旦啟動不能關閉(只能系統重新上電和看門狗復位可以關閉) |
023 |
CLR_WDT: 看門狗計數器清零位,置1清零看門狗計數器,當計數器開始重新計數,硬體清零該位。 |
024 |
IDLE_WDT: 微控制器IDLE模式看門狗允許位,當IDLE_WDT=1時,微控制器在IDLE模式(空閒模式)依然啟用看門狗 |
025 |
PS2~PS0: 看門狗定時器預分頻器,下表中Prescale表示預分頻數 |
026 |
PS2 PS1 PS0 Prescale |
027 |
0 0 0 2 |
028 |
0 0 1 4 |
029 |
0 1 0 8 |
030 |
0 1 1 16 |
031 |
1 0 0 32 |
032 |
1 0 1 64 |
033 |
1 1 0 128 |
034 |
1 1 1 256 |
035 |
|
036 |
看門狗溢位時間:(N*Prescale*32768)/晶振頻率,其中N表示指令週期數N=12表示12時鐘週期模式;N=6表示6時鐘週期模式 |
037 |
|
038 |
關於實驗的注意事項: |
039 |
1. 本次試驗使用的是11.0592MHz晶振,設定WDT_CONTR=(0011 0100)B,32預分頻,微控制器使用12指令週期模式。 |
040 |
計算看門狗溢位時間:[12*32*32768/(11059200)]≈1s。 |
041 |
2. 本次試驗的硬體電路很簡單,就是最小系統上增加兩個LED燈,原理圖見正文,使用者可以很容易實現。 |
042 |
***************************************************************************/ |
043 |
#include |
044 |
sfr WDT_CONTR=0xE1; //定義特殊功能暫存器:STC微控制器看門狗控制暫存器 |
045 |
#define uchar unsigned char |
046 |
#define true 1 |
047 |
#define false 0 |
048 |
#define WEIGOU WDT_CONTR=0x34 //看門狗啟動設定和“喂狗”操作 |
049 |
sbit LED=P1^6; //訊號燈,系統正常工作就一閃一閃的 |
050 |
sbit LED_busy=P1^7; //工作燈,上電滅一會兒(約800ms),然後正常工作的時候一直亮著;用於指示系統是否重啟 |
051 |
uchar timer0_ctr,i; |
052 |
const uchar str[]= "I love MCU!" ; //定義一句話,讓他從串列埠輸出,只有系統重啟的時候才輸出一次,所以也是用於驗證看門狗有沒有重啟系統 |
053 |
|
054 |
/*************************************************************************/ |
055 |
//延時函式,11.0592MHz晶振下延時約xms毫秒 |
056 |
void delay_ms(unsigned xms) |
057 |
{ |
058 |
unsigned x,y; |
059 |
for (x=xms; x>0; x--) |
060 |
for (y=110; y>0; y--); |
061 |
} |
062 |
/*************************************************************************/ |
063 |
|
064 |
/*************************************************************************/ |
065 |
//主程式初始化函式 |
066 |
void InitMain() |
067 |
{ |
068 |
//初始化時兩盞燈都熄滅 |
069 |
LED=1; |
070 |
LED_busy=1; |
071 |
|
072 |
TMOD=0x21; //定時器0工作在方式1,作為16位定時器;定時器1工作在方式2,作為序列口波特率發生器 |
073 |
TH0=0x4C; //定時器0裝初值:每隔50ms溢位一次 |
074 |
TL0=0x00; |
075 |
IE=0x82; //IE=(1000 0010)B, 使能定時器0中斷 |
076 |
TR0=1; //啟動定時器0 |
077 |
} |
078 |
/*************************************************************************/ |
079 |
|
080 |
/*************************************************************************/ |
081 |
//序列口初始化程式 |
082 |
void InitCOM() |
083 |
{ |
084 |
SCON=0x50; //SCON=(0101 0000)B,波特率不加倍,允許接收 |
085 |
TH1=0xFD; //設定波特率=9600bps |
086 |
TL1=TH1; |
087 |
TR1=1; //啟動定時器1 |
088 |
} |
089 |
/*************************************************************************/ |
090 |
|
091 |
/*************************************************************************/ |
092 |
//定時器0中斷服務程式程式,控制訊號燈閃爍。如果系統正常執行,訊號燈1.5秒閃一次 |
093 |
void Timer0_isr() interrupt 1 |
094 |
{ |
095 |
TH0=0x4C; |
096 |
TL0=0x00; |
097 |
timer0_ctr++; |
098 |
|
099 |
if (timer0_ctr>=30) |
100 |
{ |
101 |
TR0=0; //定時器0暫停,否則再次來中斷會沖斷程式 |
102 |
timer0_ctr=0; |
103 |
LED=0; |
104 |
delay_ms(100); |
105 |
LED=1; |
106 |
TR0=1; //定時器0重新啟動 |
107 |
} |
108 |
} |
109 |
/*************************************************************************/ |
110 |
|
111 |
void main() |
112 |
{ |
113 |
WEIGOU; //上來第一步設定看門狗定時器,並且啟動 |
114 |
InitMain(); |
115 |
InitCOM(); |
116 |
|
117 |
//開機通過串列埠傳送一次“I love MCU!”,使用串列埠除錯助手可以檢視 |
118 |
//由於在while大迴圈外邊,所以只要系統不重新啟動,則上電後只會傳送一次,用於判斷系統是否重啟 |
119 |
i=0; |
120 |
while (str[i]!= '' ) |
121 |
{ |
122 |
SBUF=str[i]; |
123 |
while (TI==0); |
124 |
TI=0; |
125 |
i++; |
126 |
} |
127 |
|
128 |
//while大迴圈 |
129 |
while ( true ) |
130 |
{ |
131 |
//約每隔800ms喂一次狗,可以通過調整這裡的喂狗時間來驗證看門狗是否有效 |
132 |
//我們設定的看門狗約1秒。所以可以用800和2000分別做一次試驗,看是否會被看門狗復位 |
133 |
delay_ms(2000); |
134 |
LED_busy=0; //第一次上電約延時800ms工作燈點亮,如果系統不重啟,他將一直亮著,用於指示系統是否重啟 |
135 |
WEIGOU; |
136 |
} |
137 |
} |