以單片機為核心的微型計算機系統中,單片機經常會受到來自外界電磁場的干擾。
造成程序跑飛,只是程序的正常運行狀態被打斷而進入死循環,從而使單片機控制的系統無法正常工作。看門狗就是一種專門用于檢測單片機程序運行狀態的硬件結構。
stm32也是如此。
stm32 的獨立看門狗由內部專門的40Khz低速時鐘驅動,即使主時鐘發生故障時,它也仍然有效。這里需要注意的是獨立看門狗的時鐘是一個內部時鐘,所以不是準確的40Khz,而是在30~60Khz之間的一個可變化的時鐘,看門狗的時鐘對時間的要求不是很精確,所以時鐘有偏差可以接受。
本例直接操作寄存器實現驗證獨立看門狗的復位功能,設定一個800ms的喂狗時間,在主函數中實現LED閃爍,如果設定一個1s的延時,則觸發獨立看門狗復位,LED常亮。
庫函數實現當外部中斷發生(按下PA0按鍵),長時間不喂狗,引發獨立看門狗復位時,向外用串口輸出復位提示。
直接操作寄存器
使用獨立看門狗,需要了解一下寄存器:
鍵值寄存器:(IWDG_KR)
低16位有效的寄存器,只寫寄存器,讀出值恒為0x0000.
軟件必須以一定的間隔寫入0xAAAA,否則,當計數器為0時,看門狗會產生復位。
寫入0x5555表示允許訪問IWDG_PR和IWDG_RLR寄存器。
寫入0xCCCC,啟動看門狗工作。
預分頻寄存器:(IWDG_PR)
第三位有效寄存器,用于設置看門狗的分頻系數,最低為4,最高位256.
通過設置PR[2:0]:位來選擇計數器時鐘的預分頻因子。要改變預分頻因子,IWDG_SR寄存器的PVU位必須為0。
000: 預分頻因子=4 100: 預分頻因子=64
001: 預分頻因子=8 101: 預分頻因子=128
010: 預分頻因子=16 110: 預分頻因子=256
011: 預分頻因子=32 111: 預分頻因子=256
重裝載寄存器:(IWDG_RLR)
低12位有效,RL[11:0]。用于定義看門狗計數器的重裝載值。
每當向IWDG_KR寄存器寫入0xAAAA時,重裝載值會被傳送到計數器中。隨后計數器從這個值開始遞減計數。看門狗超時周期可通過此重裝載值和時鐘預分頻值來計算。 只有當IWDG_SR寄存器中的RVU位為0時,才能對此寄存器進行修改。
狀態寄存器:(IWDG_SR)
只有低兩位有效。都由硬件置’1’和 清’0’。
RVU[1]: 看門狗計數器重裝載值更新
PVU[0]: 看門狗預分頻值更新
代碼如下: (system.h 和 stm32f10x_it.h 等相關代碼參照 stm32 直接操作寄存器開發環境配置)
User/main.c
01 #include <stm32f10x_lib.h>
02 #include "system.h"
03 #include "wdg.h"
04
05 #define LED1 PAout(4)
06 #define LED2 PAout(5)
07
08 void Gpio_Init(void);
09
10 int main(void)
11 {
12
13 Rcc_Init(9); //系統時鐘設置
14
15 Gpio_Init();
16
17 Iwdg_Init(3,1000); //設定為800ms內喂狗
18
19 while(1){
20
21 LED1 = !LED1;
22
23 delay(100000); //延時100ms后喂狗,LED閃爍
24
25 //delay(1000000); //延時1000ms,引發獨立看門狗復位,LED不閃爍
26
27 Iwdg_Feed(); //喂狗
28
29 }
30
31 }
32
33
34 void Gpio_Init(void)
35 {
36 RCC->APB2ENR|=1<<2; //使能PORTA時鐘
37
38 GPIOA->CRL&=0x0000FFFF; // PA0~3設置為浮空輸入,PA4~7設置為推挽輸出
39 GPIOA->CRL|=0x33334444;
40
41 }
Library/wdg.c (此文件包含了獨立看門狗和窗口看門狗的驅動函數)
01 #include <stm32f10x_lib.h>
02 #include "wdg.h"
03
04 /********************************************
05 *
06 *本文件包含窗口看門狗和獨立看門口的相關函數
07 *
08 *********************************************/
09
10 u8 Wwdg_Cnt = 0x7F; //計數器值,默認為最大值127
11
12 //獨立看門狗初始化
13 //參數說明:
14 // pre:分頻數(0~7),相應分頻因子為4*(2^pre)
15 // rlr:低12位有[11:0]
16 // 喂狗時間計算: T = (4*(2^pre)*rlr)/40;(ms)
17 void Iwdg_Init(u8 pre,u16 rlr)
18 {
19 IWDG ->KR = 0x5555; //使能對PR RLR寄存器的寫操作
20 IWDG ->PR = pre; //設置分頻數
21 IWDG ->RLR = rlr; //設定重裝值
22 IWDG ->KR = 0xAAAA; //裝載RLR值到看門狗計數器,即喂狗
23 IWDG ->KR = 0xCCCC; //啟動看門狗
24 }
25
26 //獨立看門狗喂狗
27 void Iwdg_Feed()
28 {
29 IWDG -> KR = 0xAAAA; //喂狗
30 }
31
32 //窗口看門狗初始化
33 //參數說明:
34 // cnt 計數器的值,最大 127,0x7F
35 // w_cnt 窗口值,最大 127,0x7F
36 // pre 預分頻器的時基值,低兩位有效;實際時鐘為: PLCK1/4096/2^pre
37 //需要再主函數中開啟中斷 WWDG_IRQChannel
38 //設定喂狗時間范圍必須在:(WWDG時鐘為PCLK1,36Mhz)
39 // Tmax =(4096*2^pre*(cnt-63)/36) (us)
40 // Tmin =(4096*2^pre*(cnt-w_cnt)/36) (us)
41 //超出次時間喂狗復位
42
43 void Wwdg_Init(u8 cnt,u8 w_cnt,u8 pre)
44 {
45 u8 Cnt_Max = 0x7f; //計數器最大值
46
47 Wwdg_Cnt = Cnt_Max&cnt; //設定計數器的值,防止溢出
48
49 RCC->APB1ENR |= 1<<11;
50
51 WWDG -> CFR |= pre <<7; //設定預分頻器的時基,實際分頻值我
52 WWDG -> CFR |= 1<<9; //使能中斷
53
54 WWDG -> CFR &= 0xFF80; //初始化低七位,即窗口值清0
55 WWDG -> CFR |= w_cnt; // 設定窗口值
56
57 WWDG -> CR |= Wwdg_Cnt|(1<<7); //設定計數器值,并激活開門狗
58
59 }
60
61 //窗口看門狗喂狗
62
63 void Wwdg_Feed()
64 {
65 WWDG->CR |= (Wwdg_Cnt&0x7F);
66
67 }
Library/wdg.h
1 #include <stm32f10x_lib.h>
2
3 void Iwdg_Init(u8 pre,u16 rlr);
4 void Iwdg_Feed(void);
5
6 void Wwdg_Init(u8 cnt,u8 w_cnt,u8);
7 void Wwdg_Feed(void);
需要注意的是 獨立看門狗沒有響應的中斷。
庫函數操作
main.c
view source
print?
001 #include "stm32f10x.h"
002 #include "stdio.h"
003
004 #define PRINTF_ON 1
005
006 void RCC_Configuration(void);
007 void GPIO_Configuration(void);
008 void NVIC_Configuration(void);
009 void USART_Configuration(void);
010 void IWDG_Configuration(void);
011 void EXTI_Configuration(void);
012
013 vu32 DelayTime;
014
015 int main(void)
016 {
017 RCC_Configuration();
018 GPIO_Configuration();
019 NVIC_Configuration();
020 USART_Configuration();
021 EXTI_Configuration();
022 IWDG_Configuration();
023
024 while(1){
025 if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)
026 {
027 printf("\r\n The Stm32 has been reset by IWDG .\r\n");
028 RCC_ClearFlag();
029 }
030
031 //do sth. here
032 DelayTime = 100000;
033 while(--DelayTime);
034 // 延時17ms
035
036 IWDG_ReloadCounter(); //80ms不喂狗復位
037 GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1- GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
038 }
039 }
040
041 void EXTI_Configuration(void)
042 {
043 EXTI_InitTypeDef EXTI_InitStructure;
044
045 EXTI_InitStructure.EXTI_Line = EXTI_Line0;
046 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
047 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
048 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
049 EXTI_Init(&EXTI_InitStructure);
050
051 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
052
053 }
054
055 void GPIO_Configuration(void)
056 {
057 GPIO_InitTypeDef GPIO_InitStructure;
058 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
059 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
060 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
061 GPIO_Init(GPIOA , &GPIO_InitStructure);
062
063 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
064 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
065 GPIO_Init(GPIOA , &GPIO_InitStructure);
066
067
068 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
069 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
070 GPIO_Init(GPIOA , &GPIO_InitStructure);
071
072 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
073 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
074 GPIO_Init(GPIOA , &GPIO_InitStructure);
075
076 }
077
078 void IWDG_Configuration(void)
079 {
080 RCC_LSICmd(ENABLE); //打開LSI
081 while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY)==RESET);
082
083 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
084 IWDG_SetPrescaler(IWDG_Prescaler_32);
085 IWDG_SetReload(100); //80ms ,max 0xFFF 0~4095
086 IWDG_ReloadCounter();
087 IWDG_Enable();
088 }
089
090
091 void RCC_Configuration(void)
092 {
093 /* 定義枚舉類型變量 HSEStartUpStatus */
094 ErrorStatus HSEStartUpStatus;
095
096 /* 復位系統時鐘設置*/
097 RCC_DeInit();
098 /* 開啟HSE*/
099 RCC_HSEConfig(RCC_HSE_ON);
100 /* 等待HSE起振并穩定*/
101 HSEStartUpStatus = RCC_WaitForHSEStartUp();
102 /* 判斷HSE起是否振成功,是則進入if()內部 */
103 if(HSEStartUpStatus == SUCCESS)
104 {
105 /* 選擇HCLK(AHB)時鐘源為SYSCLK 1分頻 */
106 RCC_HCLKConfig(RCC_SYSCLK_Div1);
107 /* 選擇PCLK2時鐘源為 HCLK(AHB) 1分頻 */
108 RCC_PCLK2Config(RCC_HCLK_Div1);
109 /* 選擇PCLK1時鐘源為 HCLK(AHB) 2分頻 */
110 RCC_PCLK1Config(RCC_HCLK_Div2);
111 /* 設置FLASH延時周期數為2 */
112 FLASH_SetLatency(FLASH_Latency_2);
113 /* 使能FLASH預取緩存 */
114 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
115 /* 選擇鎖相環(PLL)時鐘源為HSE 1分頻,倍頻數為9,則PLL輸出頻率為 8MHz * 9 = 72MHz */
116 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
117 /* 使能PLL */
118 RCC_PLLCmd(ENABLE);
119 /* 等待PLL輸出穩定 */
120 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
121 /* 選擇SYSCLK時鐘源為PLL */
122 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
123 /* 等待PLL成為SYSCLK時鐘源 */
124 while(RCC_GetSYSCLKSource() != 0x08);
125 }
126 /* 打開APB2總線上的GPIOA時鐘*/
127 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
128
129 //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE);
130
131 }
132
133
134 void USART_Configuration(void)
135 {
136 USART_InitTypeDef USART_InitStructure;
137 USART_ClockInitTypeDef USART_ClockInitStructure;
138
139 USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
140 USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
141 USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
142 USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
143 USART_ClockInit(USART1 , &USART_ClockInitStructure);
144
145 USART_InitStructure.USART_BaudRate = 9600;
146 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
147 USART_InitStructure.USART_StopBits = USART_StopBits_1;
148 USART_InitStructure.USART_Parity = USART_Parity_No;
149 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
150 USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
151 USART_Init(USART1,&USART_InitStructure);
152
153 USART_Cmd(USART1,ENABLE);
154 }
155
156
157 void NVIC_Configuration(void)
158 {
159 NVIC_InitTypeDef NVIC_InitStructure;
160
161
162 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
163 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
164 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
165 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
166 NVIC_Init(&NVIC_InitStructure);
167
168
169 }
170
171
172 #if PRINTF_ON
173
174 int fputc(int ch,FILE *f)
175 {
176 USART_SendData(USART1,(u8) ch);
177 while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
178 return ch;
179 }
180
181 #endif