|
STM32学习笔记14—看门狗实验 14.1 STM32看门狗简介 在单片机构成的微型计算机系统中,单片机的工作可能会受到外界的电磁干扰或者程序运行的BUG导致程序指针错误,或者其他错误导致的死循环,引发整个系统陷入停滞状态,所以需要一个与系统独立的定时器来监控单片机的运行状态,这个定时器在系统正常运转的时候,不停的刷新定时器的计数器,例如隔一段时间给这个定时器的计数器写100,然后在定时器减运算到0之前再一次写入100,这样,就保证了定时器不计数到0,也就意味着通过判断这个定时器是否计数到0来判断系统是否陷入死机状态,实现这种功能的定时器就称为看门狗,不停的刷新计数器值的行为就称为“喂狗”,一般计数器计数到0后会直接对单片机进行复位,用于避免系统陷入死循环。 STM32内部有两种看门狗模块,一种是窗口看门狗WWDG,另一种是独立看门狗IWDG,STM32的独立看门狗由内部专门的40Khz低速时钟驱动,即使主时钟发生故障,它也仍然有效。独立看门狗的时钟是一个内部RC时钟,所以并不是准确的40Khz,而是在30~60Khz之间的一个可变化的时钟,只是我们在估算的时候,以40Khz的频率来计算。窗口看门狗通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在T6位变成0前被刷新,看门狗电路在达到预置的时间周期时,会产生一个MCU复位。在递减计数器达到窗口配置寄存器数值之前,如果7位的递减计数器数值在控制寄存器中被刷新,那么也将产生一个MCU复位。这表明递减计数器需要在一个有限的时间窗口中被刷新。 14.2 独立看门狗相关寄存器 14.2.1 键值寄存器IWDG_KR 该寄存器属于只写寄存器,读取的值为0x0000,软件必须以一定间隔写入0xAAAA,否则,当计数器为0时,看门狗会产生复位; 写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器; 写入0xCCCC表示启动看门狗工作,如果选择了硬件看门狗则不受此命令字限制。 14.2.2 预分频寄存器IWDG_PR Bit2~Bit 0:预分频因子 000:预分频因子=4 001:预分频因子=8 010:预分频因子=16 011:预分频因子=32 100:预分频因子=64 101:预分频因子=128 110:预分频因子=256 111:预分频因子=256 14.2.3 重装载寄存器IWDG_RLR Bit 11~Bit 0:看门狗计数器重装载值:每当向IWDG_KR寄存器写入0xAAAA时,重装载值会被传送到计数器中,随后计数器从这个值开始递减计数。 14.3 窗口看门狗相关寄存器 14.3.1 控制寄存器WWDG_CR Bit 7:激活位:,此位由软件置1,但仅能由硬件在复位后清0。当WDGA=1时,看门狗可以产生复位 0:禁止看门狗 1:启用看门狗 Bit 6~Bit 0:7位计数器,存储看门狗的计数器值。每(4096x2WDGTB)个PCLK1周期减1。当T6变成0产生看门狗复位 14.3.2 配置寄存器WWDG_CFR Bit 9:提前唤醒中断,此位若置1,则当计数器值达到40h,即产生中断,此中断只能由硬件在复位后清除 Bit 8:预分频器时基 00:CK计时器时钟不分频 01:CK计时器时钟2分频 10:CK计时器时钟4分频 11:CK计时器时钟8分频 Bit6~Bit 5:7位窗口值,用来与递减计数器进行比较用的窗口值 14.3.3 状态寄存器WWDG_SR Bit 0:提前唤醒中断标志,当计数器值达到40h时,此位由硬件置1。它必须通过软件写0来清除。若中断未被使能,此位也会被置1 14.4 实验例程 14.4.1 独立看门狗实验 功能:如果看门狗没有复位,接在PB5上的LED常亮,如果PA0的按键按下,就喂狗,只要按键不停的按,看门狗就一直不会产生复位,保持DS0的常亮,一旦超过看门狗定溢出时间,那么将导致程序重启,这将导致DS0熄灭一次。 (1)创建wdg.h文件输入以下代码。 - #ifndef _WDG_H_
/ `/ K9 G* ~$ G2 w - #define _WDG_H_$ F) t1 r7 _0 h; n& }
. }' H9 O' G/ Q- #include "sys.h"
, e' z- m- H* _' v$ j. r - /*********************************************************************************************************
0 Q8 N* V( u4 D - 函 数 列 表
7 `( x2 l t; _/ }" @$ e - *********************************************************************************************************/ ~( }1 c% N, G x( {
- void IWDG_Init( u8 prer, u16 rlr ) ; //独立看门狗初始化, V. G+ h* `2 e6 U8 l% C& f+ D. A
- : O5 z9 X; \3 i% T
- #endif
复制代码
5 P0 ?6 [+ M/ o) E(2)创建wdg.c文件输入以下代码。 - #include "wdg.h" U& p" G ~/ c& Z* X# q
- /***************************************************
9 l3 p, I1 A1 T i2 O+ O6 i6 f - Name :IWDG_Init
/ ~! r% u& p) b6 j- E - Function :独立看门狗初始化+ o, j, q7 i" I# M3 u3 @0 g- o
- Paramater :
! Q! l2 q7 `0 D - prer:分频数:0~7 U; t8 e, w: X
- rlr:重装载寄存器值2 I) ^+ x$ q, s) G2 \: |
- Return :None1 @9 `. Z" z. O) Q# b) V2 P
- ***************************************************/
) r \) m9 ~0 ]7 S - void IWDG_Init( u8 prer, u16 rlr )
* i+ c' x0 c& z( Z! _0 i - {
0 r$ x8 n+ A4 |7 H5 W7 x T! u - IWDG->KR = 0x5555 ; //使能对IWDG->PR和IWDG->RLR的写* d& ?' Q5 Z0 |0 {' {$ c
- IWDG->PR = prer ; //设置分频系数
7 ^9 _ p M; \( h' ?. d, h8 [ - IWDG->RLR = rlr ; //从加载寄存器 IWDG->RLR
: ~1 T& P( u' D - IWDG->KR = 0xAAAA ; //更新计数器; v) a( `7 G" r
- IWDG->KR = 0xCCCC ; //使能看门狗4 Z6 D+ O2 J! r' `( a+ u" `
- }
4 }8 @! ^: d4 j3 X" a- { P; t: X( \/ B
复制代码 , @9 O6 B) [ a2 U+ m( X3 g7 N; W
(3)在1.c文件中输入以下代码。 - #include "sys.h"
' }: |2 {+ N d- _ - #include "delay.h"" L* Y( M- H- ?# m& @
- #include "usart1.h"1 f, f: @+ |8 y0 t
- #include "wdg.h"
5 o+ W9 f) r. Q' i - /***************************************************6 h( t7 X- G# X' X( S
- Name :LED_Init$ t' P6 I9 V8 L5 k+ D
- Function :LED初始化
# ^/ |8 e( c: A2 ~/ I# T# A; P- _ v - Parameter :None( j- X* ?( e3 }. } r
- Return :None2 H6 i" M& p8 ^2 G1 i
- ***************************************************/
( a# z& v. Z( ^# q) a# @ - #define LED PBout( 5 ) //定义LED端口
( {5 H2 a0 j( h4 |& } - void LED_Init()& p, B' i4 q6 R( a8 {
- {" F0 s" M8 s8 }& e2 i' c
- RCC->APB2ENR |= 1<<3 ;! E1 F) P2 q/ U2 W- x
- GPIOB->CRL &= 0xFF0FFFFF ;$ E* ? s d" Y) ^6 p" r
- GPIOB->CRL |= 0x00300000 ;( c& j }. N3 w" G& D6 _ G& E `
- LED = 1 ;7 |6 y0 ^# ?6 F: `2 L1 J- ?" T
- } u& Y! C% C, f) q" w7 g
- /***************************************************9 s _" J q" U2 J! V5 Y9 ?
- Name :KEY_Init
; N) H* G! l! F# E$ Q) a - Function :KEY初始化
5 e5 }0 b9 y& Q; V! C. J - Parameter :None9 L6 H8 f7 ?8 S/ H+ i6 G
- Return :None4 i6 N k ?& k) X2 w! `; C: c
- ***************************************************/- D/ z% o: S- E
- #define KEY PAin( 0 ) //定义按键端口: c9 L/ V4 h2 r1 s9 W
- void KEY_Init()3 [2 J. W% w, k, s0 a0 U$ @
- {/ h' b# H$ a- A
- RCC->APB2ENR |= 1<<2 ;2 ~4 R0 e' Y2 B9 S! j
- GPIOA->CRL &= 0xFFFFFFF0 ;7 Q9 A' T: y \
- GPIOA->CRL |= 0x00000008;
" R9 Y2 I% s2 k4 q7 N - }& W* R G3 k/ [# ?* e' \
- /***************************************************2 Q5 L, O0 H% L9 X
- Name :main
" o/ n$ R: u/ W - Function :主函数
& a0 N2 }9 c0 S u0 b - Parameter :None. ?- n9 [/ }% \# ~: N2 ~3 ]& h
- Return :None+ W1 ^' V. P9 P8 x
- ***************************************************/; `5 A, ?; _' z( i$ G. Q G
- int main()2 f, }1 {) E, d6 k8 T
- {! r$ U' G* p, x
- STM32_Clock_Init( 9 ) ; //STM32时钟初始化+ Z& X* ~1 G3 p2 Q! G
- SysTick_Init( 72 ) ; //SysTick初始化
6 P. Z% G3 u5 H - USART1_Init( 72, 115200 ) ; //初始化串口1波特率1152006 I7 x8 G1 h3 |
- LED_Init() ; //LED初始化
" C% r7 |# E# d* y+ u) |4 h - KEY_Init() ; //按键初始化5 H+ p `' O0 i! v- T
- delay_ms( 500 ) ; //延时500ms,让人可以看到DS0灭的状态7 I7 N9 }% {& P, ?
- IWDG_Init( 4, 625 ) ; //与分频数为64,重载值为625,溢出时间为1s
: ^9 o% v0 a; C: U8 h( D0 {& e - LED = 0 ; //点亮DS0, l& `! t5 }; v# u9 B; {8 N O# o
- while( 1 )
/ X/ i. j1 L; ?: M! a; Y - {% L" o& \# r+ ]& `9 N4 ~. z
- if( KEY==1 )2 F, N; J3 o- W0 ?! q# n
- {
% t' L4 ?& e" j* U5 d" `* _; r - delay_ms( 10 ) ;' E) M( Y' g0 q4 f9 e- A
- if( KEY==1 )+ L# b; h6 t2 y2 f0 ^/ K) q
- {" @4 [* u N' Y+ R6 o0 f2 K+ L
- IWDG->KR = 0xAAAA ; //喂狗
8 L- M- s! M4 s# h' N* E5 N - }+ Q1 \2 P( l4 m" l
- }* t4 b* R9 i, ~+ L# ~
- delay_ms( 20 ) ;
7 o! d/ y: q! E. N+ s# v" g - }0 P/ K( N, B* e* L5 o/ O1 ^5 S7 N
- }5 j9 p$ ~" D: Q& \
复制代码
' J* S+ g( L- ?3 h14.4.2 窗口看门狗实验 功能:程序一运行使得接在PB5上的LED1亮300ms后关闭,进入死循环。等待WWDG中断的到来,在中断里面,喂狗,并对PE5上的LED2进行翻转操作。可以看到LED2不停的闪烁,LED1只在刚启动的时候闪一下。 (1)在上一个实验的wdg.h文件的函数列表区域添加以下代码。 - void WWDG_Init( u8 tr, u8 wr, u8 fprer ) ; //窗口看门狗初始化
复制代码
7 o' \7 e+ s8 F- a2 v0 D: R+ g(2)在上一个实验的wdg.c文件末尾添加以下代码。 - /***************************************************
/ u6 [( c8 Z7 I' u! o - Name :WWDG_IRQHandler
( r7 \8 |5 q" C- b3 @+ V- U: _, m - Function :窗口看门狗中断服务程序
" K$ i2 O p _' |5 x - Paramater :None$ s( C. g2 d7 a* D9 f. N
- Return :None
% N! H8 L4 G8 H# I) T+ \ }; t - ***************************************************/
. ?4 C' M& z4 p0 W, L4 B$ D7 U4 f - void WWDG_IRQHandler()+ b) u0 g7 p- {
- {
* ^* y Z# @. S( E - WWDG->CR = 0x7F ; //重设置7位计数器4 i% }/ |8 u& z4 g: x
- WWDG->SR = 0x00 ; //清除提前唤醒中断标志位' ]2 O5 \6 C3 x: C+ D G0 i5 g
- LED2 != LED2 ;1 u# E2 Z* U2 ^7 V
- } A E4 W0 Q6 Q# W0 D) q, M- j
- /***************************************************
- w0 v4 X1 S+ t; s! D - Name :WWDG_Init
v3 g2 d; H4 o9 p3 X! Z - Function :窗口看门狗初始化3 I( k6 Z' u0 C+ ^! b0 j. r
- Paramater : D* y- f7 U9 Z- `/ g9 N5 O6 v
- tr:计数器值1 W$ a' A( s- d) {
- wr:窗口值! [# V/ w, a" z6 ?+ b
- fprer:分频系数
' J( z+ k+ B' o, R% B" T; W5 c - Return :None; q6 V, y8 |' }0 {
- ***************************************************/, S! o. ]3 B' h& p6 d4 N6 r
- void WWDG_Init( u8 tr, u8 wr, u8 fprer )) d; T/ [* F0 c: ]7 ?" q- U
- {. @* n# W9 l+ Z
- RCC->APB1ENR |= 1<<11 ; //使能wwdg时钟. ^; m# O* v. q" I1 o
- WWDG->CFR |= fprer<<7 ; //PCLK1/4096再除2^fprer
2 u) ?( K7 l, [# @3 I - WWDG->CFR &= 0xFF80 ;
# H9 \+ l3 _' D; h: P - WWDG->CFR |= wr ; //设定窗口值
. ~/ J+ |: T( \7 v: C/ \ - WWDG->CR |= tr&0x7F ; //设定计数器值
7 F2 u0 Z+ N/ L8 `1 E - WWDG->CR |= 1<<7 ; //开启看门狗
; @$ f4 B! u3 n3 |* R - NVIC_Init( 2, 3, WWDG_IRQn, 2 ) ; //抢占2,子优先级3,组28 j, u' X( @9 ~/ ^8 b. u' x( s
- WWDG->SR = 0x00 ; //清除提前唤醒中断标志位1 ]( {+ x4 J g; @" b
- WWDG->CFR |= 1<<9 ; //使能提前唤醒中断
4 ]8 L1 s m- D) S$ A) m: T - }<span style="background-color: rgb(255, 255, 255);"> </span>
复制代码注:由于在中断服务函数中引用了LED2,所以需要添加头文件#include “led.h”。
w$ Q; B5 R b$ _7 z8 _' ^(3)创建led.h文件,并输入以下代码。 - #ifndef _LED_H_- m9 }$ ~$ Y) L
- #define _LED_H_9 {! N3 R8 m @8 Q! M
9 C k* L% A" U4 W/ S- #include "sys.h"
2 B5 q, w& U$ S: d - /*********************************************************************************************************7 Z% q2 n' D) `% b- s, c
- 硬 件 端 口
) {- f$ B5 \1 Q5 m8 ^ - *********************************************************************************************************/3 P; K% |7 l R0 @, `
- #define LED1 PBout( 5 ) //定义LED1端口
7 c2 Y% @) V9 g( e; U7 Q - #define LED2 PEout( 5 ) //定义LED2端口
+ s9 E( c: v. |. V7 A - /*********************************************************************************************************
, ^, p4 k# B! w; s' c+ T - 函 数 列 表: O; F% `1 W) ~- r# C
- *********************************************************************************************************/, R8 K. P' }, }, E* L+ M6 T
- void LED_Init( void ) ; //LED初始化
# m3 X5 o3 w2 _0 R0 J - 6 U, x4 `) E, H5 C) F8 B: `
- #endif
复制代码
+ [; K5 R; `' d; X6 s6 R3 Y(4)创建led.c文件,并输入以下代码。 - #include "led.h"
! d- }1 [! T4 R9 g0 } - / `9 |( L* g# F. Y/ S. q5 I
- /***************************************************
1 S, G) r" v+ V' n7 Z0 M! Q - Name :LED_Init
- A3 F1 O9 G9 [( V' h0 O - Function :LED初始化
1 B2 m% P1 Q, B+ ~8 B w - Paramater :None
! g- I$ q9 d0 _$ j3 n - Return :None( z6 u( S& @1 G% t5 C
- ***************************************************/6 o- I- ~+ L" h1 n" }
- void LED_Init()
7 f; ?7 |/ _1 k7 u, X, { - {: e* G+ |- r: |# N; `; A+ A- u# n1 j
- RCC->APB2ENR |= 1<<3 ;
% F& _! ]7 D( F1 T9 j( S - GPIOB->CRL &= 0xFF0FFFFF ;
9 K2 F& c& G* n% Q8 l - GPIOB->CRL |= 0x00300000 ;
( @6 @$ a' a' n. g$ A - $ i. M8 i" s9 k' L0 X1 A5 c
- RCC->APB2ENR |= 1<<6 ;( T7 F; _6 h- w. q9 j- n
- GPIOE->CRL &= 0xFF0FFFFF ;8 b! V; W5 T3 Z5 B+ P0 I) ?1 u
- GPIOE->CRL |= 0x00300000 ;
- `2 a! j- K+ ?+ }: D -
) N7 t5 x* |8 J, A4 Y0 b) E - LED1 = 1 ;
& G. @" F3 l3 |5 F - LED2 = 1 ;* h, l3 w z6 u1 h- f
- }
复制代码 , ], U& e, f7 G6 J ]; T0 T
(5)在1.c文件中输入以下代码。 - #include "sys.h"
6 J& @3 N5 U h$ L - #include "delay.h"
% b r0 A" w" r$ H9 I8 n4 Q7 b - #include "usart1.h"& W0 q e; q/ H- b" ~0 w, `
- #include "led.h"
5 D4 R2 Z9 l& A8 I l" ]+ T - #include "wdg.h"% W! m- S6 [( v1 |0 q! y
- /***************************************************
% H7 x1 ^" R4 i6 I+ B* q& w( S - Name :main7 w# v- J. E: I
- Function :主函数+ x, J. J' u, e$ ?# y8 o( r+ B
- Parameter :None! A' j9 V0 j9 M# j0 G- | e* J% d
- Return :None* R% `, s& d j$ X
- ***************************************************/
* p2 u, k; X, l4 @5 e - int main()
7 {8 X5 J( N: {$ k - {, y6 W, c# ~$ ~1 T9 L4 q2 R
- STM32_Clock_Init( 9 ) ; //STM32时钟初始化
0 Z' X, {) T" j/ n - SysTick_Init( 72 ) ; //SysTick初始化. G. b& _9 \% I# t" P3 e
- USART1_Init( 72, 115200 ) ; //初始化串口1波特率115200
Y6 E5 e) m( B: z5 U5 C4 J) s; ` - LED_Init() ; //LED初始化
# }9 F7 w1 M; W9 d( l - LED1 = 0 ; //点亮DS0* A$ H8 A% g H' F) o
- delay_ms( 300 ) ; //延时300ms,让人可以看到DS0亮的状态$ r$ n0 p, W* z7 H* {& c
- WWDG_Init( 0x7F, 0x5F, 3 ) ; //计数器值为7f,窗口寄存器为5f,分频数为8
& {* S! Z. M% x- r+ D% H# v) l9 O - while( 1 )
+ a7 u- S* P" A4 e- b" R; l - {* w/ `# S) i; m
- LED1 = 1 ; //熄灭LED1' H9 I% z$ Y; V- A. X
- }9 W6 r; G: G$ v0 s
- }: { r, `% E0 `: ^& L
复制代码
5 k7 \/ ]" T8 \1 \14.5 为何STM32要同时存在窗口看门狗与独立看门狗 14.5.1 独立看门狗的使用条件 (1)程序跑飞 (2)出现死循环 (3)睡眠与休眠不合理 (4)外部主晶振损坏 (5)需要重新复位,且不保留任何数据 14.5.2 窗口看门狗使用条件 (1)软件逻辑出现错误 (2)死机或者死循环 (3)软件执行不按预期效果执行 (4)软件需要重新复位,但是保留所有数据 14.5.3 两者的区别 (1)独立看门狗使用内部专用40kHz低速时钟 窗口看门狗则使用PCLK1的时钟 (2)独立看门狗没有中断,超时直接复位 窗口看门狗有中断,超时可以在中断服务函数中操作或者喂狗 (3)独立看门狗一般用于避免程序跑飞或者死循环 窗口看门狗则是为了避免程序不按照预先设定的逻辑执行 (4)独立看门狗是12位递减操作 窗口看门狗则是6位递减操作 文章出处: 滑小稽笔记
9 F3 O* R! q: |$ O& R. P |