以单片机为核心的微型计算机系统中,单片机经常会受到来自外界电磁场的干扰。
造成程序跑飞,只是程序的正常运行状态被打断而进入死循环,从而使单片机控制的系统无法正常工作。看门狗就是一种专门用于检测单片机程序运行状态的硬件结构。
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
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 |