01. 窗口看门狗简介$ m) s- _% E2 D$ `5 {! D2 Z
窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在 T6 位(WWDG->CR 的第六位)变成 0 前被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU 复位。在递减计数器达到窗口配置寄存器(WWDG->CFR)数值之前,如果 7 位的递减计数器数值(在控制寄存器中)被刷新, 那么也将产生一个 MCU 复位。这表明递减计数器需要在一个有限的时间窗口中被刷新。他们的关系可以用图 12.1.1 来说明:6 s# m9 x8 Y& _# }6 Z& R+ l
7 @1 Z8 o4 y1 [+ S3 r
, z$ N. Z6 P( ]# `1 E, }% C* d3 ~9 z+ q
图 12.1.1 中,T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0]就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。当窗口看门狗的计数器在上窗口值之外被刷新,或者低于下窗口值都会产生复位。! {1 J4 {5 Q e: B% J5 U7 i& j2 _' b
" G) Q: r, w; A& P5 L; ?上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保窗口值大于 0X40,否则窗口就不存在了。
: |6 M/ x! V# Y) r; a! g2 L! L" d1 y. D, X. B: J1 `/ L
窗口看门狗的超时公式如下:* t& k- `8 l4 F/ S3 H
' m' q) v; }. ]8 z, H
- Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
$ b$ V' U) O6 ^" F" P - 其中:1 w7 y7 D, v9 V6 d& ^ `6 l
- Twwdg:WWDG 超时时间(单位为 ms)# O$ b9 Q) \! s4 y0 U) ]
- Fpclk1:APB1 的时钟频率(单位为 Khz)! @3 T1 \" Z, h, ]
- WDGTB:WWDG 的预分频系数
; D) ?; o- v, g' l* j1 \+ @& j - T[5:0]:窗口看门狗的计数器低 6 位
复制代码
C3 V1 ^* F% s& j# U+ [7 ^02. 硬件设计
1 y/ L! q/ D! t; j% K. c/ X本实验用到的硬件资源有:
' ]# D5 g1 I: b6 R, [
W% i1 X4 X! V5 n( I' }1 I1) 指示灯 DS0 和 DS1
6 f) Z Q/ P) |3 j7 s. J5 A+ ~ U8 l2 B
2) 窗口看门狗" a% W/ M; ^! Y
( r/ U9 D7 ^" a' s9 [& V" _, M窗口看门狗属于 STM32F4 的内部资源,只需要软件设置好即可正常工作。
) G! l0 ^. f. c+ `+ n
& s* v8 P( I9 m1 y7 z4 L03. 编程步骤
' @* k$ g. c! u) }2 _8 D8 n, j1) 使能 WWDG 时钟5 C7 P' A2 f2 v
WWDG 不同于 IWDG,IWDG 有自己独立的 32Khz 时钟,不存在使能问题。而 WWDG使用的是 PCLK1 的时钟,需要先使能时钟。- RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能
复制代码 . k0 y$ J9 `% o, D3 z' J9 K
2) 设置窗口值和分频数
' Y0 k, x1 t1 L; F2 v% r设置窗口值的函数是:
. V% w' u) ~* X8 n' \$ k! `
% j. o+ \8 V: ?8 V$ X X- void WWDG_SetWindowValue(uint8_t WindowValue);1 J, Z$ P! d% R5 g
复制代码
* @# n; O9 N# U# e& `这个函数就一个入口参数为窗口值,很容易理解。; Z1 ^9 R: U) }9 L% J
% C# i' Q( M- _8 v" G8 w7 p设置分频数的函数是:4 L! P' k1 V: ~, c; m
& h7 L. ]- ^, Z8 w& D, W; N8 z
- void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
复制代码
0 p$ a+ E9 f( R8 W. I6 f5 s9 b# @, G* g这个函数同样只有一个入口参数就是分频值。
5 U x0 Q7 w' N
. r- ` l* v3 a( R4 L" k3) 开启 WWDG 中断并分组
# t$ }5 ]6 T7 Q3 t0 K5 e) T( g+ k
/ t, G6 \. R: Q4 ~开启 WWDG 中断的函数为:
( b4 o8 ]" q4 J+ a. ?0 P" G) L; u# O1 ^
% n4 u I! y/ [- WWDG_EnableIT(); //开启窗口看门狗中断
复制代码 0 B0 [1 G2 E/ P: j7 z
接下来是进行中断优先级配置,这里就不重复了,使用 NVIC_Init()函数即可。
! X F, t+ m* w' C; y& J2 g
* E1 Q% j- s& ~5 h4) 设置计数器初始值并使能看门狗9 k( E) [+ ?/ a. g2 s
! `( i/ \) [( b6 E1 J/ G+ ?$ p* o这一步在库函数里面是通过一个函数实现的:2 r" _' i8 D0 [( g& U) g( D
; T) O1 r. y: ~2 K- void WWDG_Enable(uint8_t Counter);
复制代码 " F7 {% |' Z! G
该函数既设置了计数器初始值,同时使能了窗口看门狗。# `/ }3 P5 ~) q X' E* y1 Z/ y( T- P6 U
3 }0 t8 ?5 c( v& i1 ^! M
这里还需要说明一下,库函数还提供了一个独立的设置计数器值的函数为:
. q0 v% V4 ^- w5 [
$ O3 G/ _8 W8 e1 X# }- void WWDG_SetCounter(uint8_t Counter);
复制代码 % M5 ?1 ?, v H
5) 编写中断服务函数
4 o, ~! t8 ^$ }! C% s: o9 P/ `# s4 X( ]
在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗,喂狗要快,否则当窗口看门狗计数器值减到 0X3F 的时候,就会引起软复位了。在中断服务函数里面也要将状态寄存器的 EWIF 位清空。8 X2 o8 ]1 I$ A# M
: z# U7 E# G3 g8 y$ ~04. 程序示例
. ~9 w2 S# ~ X' Q& X6 pwwdg.h- #ifndef __WWDG_H__
8 Y) V% T% Q( G3 I! O j& w2 S - #define __WWDG_H__
L' N; Q! T, m. ~; T - 1 C0 }# q0 Q# }/ Y3 t% {; O
- #include "sys.h"
q6 M; @ h. U$ E
- u _) A0 H1 M1 c" L! [- ^$ R- //初始化函数( y; |% o6 Q4 @7 g
- void WWDG_Init(u8 tr,u8 wr,u32 fprer);
8 E6 [7 Y& X; S
4 s7 | B" T7 h L C; M- //中断处理函数
7 Y; v- }" {, K, p+ K - void WWDG_IRQHandler(void);! z* z, l# O/ n B( e% [
6 H# j: F+ H* b' C2 v4 ]$ q' m/ [- #endif /*__WWDG_H__*/2 G) D* a9 f& y0 u
复制代码
" f4 ^* H+ t* O4 p$ @2 I0 ]wwdg.c0 m0 i7 ?1 m0 y) p! U5 n: N8 X
- P6 _# T/ |3 Z% L% y4 b5 m- c
- #include "wwdg.h"
6 u2 I: p3 N8 z1 K - #include "led.h"4 E7 J8 A& J$ n' ]
- ' D! g" F* w- C, B
- //保存WWDG计数器的设置值,默认为最大.
1 |( W4 C0 _8 j# z( n! m3 U: _% _ - u8 WWDG_CNT=0X7F;
8 [' E9 ?+ S! S4 n1 f1 M
9 [ ~/ L# e0 V. c! f- //初始化函数! E2 R! T$ j& M5 E3 f0 r
- //tr计数器的值
2 }7 s+ n) O0 ~1 ]8 A: n* o - //wr窗口值0 t+ v: U/ P1 o' _. y+ R0 x
- //fprer 预分频值0 s* F4 f: O4 t( P
- void WWDG_Init(u8 tr,u8 wr,u32 fprer)
% w! I# B8 Z6 Z8 L% E - {
3 @8 X5 z5 o2 s, I - NVIC_InitTypeDef NVIC_InitStruct;$ V# z, ]1 C, p
- / g4 [# E. g* `
- //使能WWDG时钟
% H8 B& H) C4 n* X! O6 R2 p - RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);$ O3 T4 h5 {+ L! J
-
9 e$ V4 u( D9 Z' [/ y# ?+ x - //设置预分频$ K# ~1 ]1 g2 v) C
- WWDG_SetPrescaler(fprer);; G; @0 C# B2 c; W! ^ p
-
3 L9 c1 L. ^1 b D - //设置窗口值5 Z% e9 g" B# ~; N7 ?
- WWDG_SetWindowValue(wr & WWDG_CNT);; a! J% F4 f2 E7 ?( r
- # n* u. a' S( y, C' S& E/ [
- //使能看门狗9 I3 ?1 B! Y8 c. ^3 A
- WWDG_Enable(WWDG_CNT);
) s0 O$ Z! P7 @- [' R+ p& K - ) F0 C, D# R" {( {7 ]- U1 Y" u
- //设置中断0 @, N0 s0 N9 p( U
- NVIC_InitStruct.NVIC_IRQChannel = WWDG_IRQn; u: B0 {3 H0 G8 ^+ g$ o( g7 o$ D
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
$ x. T V9 r: p4 V - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;* e- |4 k# B, _1 i/ u. q: N: c
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;3 R) K5 }7 ?' @3 p
- % q# _) K6 s8 L9 w, E
- NVIC_Init(&NVIC_InitStruct);
4 ]% ^+ ^6 ~& M0 w1 g3 H -
/ R5 a% p, S7 o - //清除提前唤醒中断标志位
* e- I) p- p) I' i - WWDG_ClearFlag();
4 e0 @/ i: B% P' ?: {! S# G7 ?) t -
4 v% Q) V4 I% w( k- x2 x! l! ` - //开启提前唤醒中断. c3 Z4 N5 ?4 n# ^- y# O
- WWDG_EnableIT();
X6 K; }, d# h# I, j9 G( _ -
, L) ]8 d3 H7 o9 @! M - }
, w2 A/ D" _, y# G* h - ) o8 b# Y; T+ N, `. r% A( `! U
- //中断处理函数& H5 |- Z; `# \( Q! T7 N: w
- void WWDG_IRQHandler(void)
8 {3 k! j7 t, H0 q% A& p: p0 q - {
- b$ R% Z0 d0 I P3 L Z - //清除提前唤醒中断标志位
: d* A4 W- Z1 @/ ^* x3 h; \2 F - WWDG_ClearFlag();
- W) j6 ]7 c4 H1 b5 S -
* e# Z' v2 ?. y3 ~$ L - WWDG_SetCounter(WWDG_CNT);
3 x( i+ Y8 D7 q& {: }' v - 5 }6 o m) Z. j2 d8 s S
- LED2 = !LED2;* d4 A. y% y* M! c( M
- K! s+ C- [) D3 w7 b) K
- }
复制代码
5 p! l! V( p3 F/ h- U* O) L1 Fmain.c; i2 Y5 `' ]/ @$ N$ U: b# m
" M! d; @; k8 h$ P! ?9 \) e
- #include "sys.h"3 L- T5 @& e' ?5 G& f- {, ~0 \8 ?
- #include "delay.h"9 j0 ~9 |# [4 v) z
- #include "usart.h"* J4 v$ j& |$ D: s: U* [7 L9 @7 J; E
- #include "led.h"
, A; F2 W/ z" ] - #include "beep.h"
( c* Y: S+ E) z6 u - #include "key.h"
3 D. E/ V" s5 {$ ?5 f2 f& k, h - #include "exti.h"
4 m. Y# V: S$ O4 M' p# D0 C - #include "iwdg.h"" r: o5 b! W. ~! p
- #include "wwdg.h"
6 P! ^. v7 X* W* n0 D - . s! z% r5 G5 F" f# `4 e# @
- int main(void)
+ G, H2 k% B- y6 Y9 J' Z - {
3 ?9 B% L2 i- e# A0 M - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
2 y# B0 q( I8 T9 H# |+ |; Y: O$ P# u - 5 q4 G- p0 h! C, B5 e
- delay_init(168);5 k. E2 O0 c0 W! l
- LED_Init();
2 t" ` Y4 h9 ?2 R5 q* x, g - KEY_Init();8 z! U- ?' X% E1 Y$ @7 N7 j. |/ k
- & ]- H' `. m3 ^' A# ^
- LED1 = 0;
: F( L- X; Q. ]$ M7 L$ @( m3 C - LED2 = 0;
- x% Y5 } k5 }9 P -
+ X; U4 x& R2 t, j. ~% d - delay_ms(300);
' w* b) a' {% ^4 D/ P# g' ] - . q; m: I& y* Z2 q) P1 m& I; P. P
- WWDG_Init(0x7F,0X5F,WWDG_Prescaler_8); //计数器值为7f,窗口寄存器为5f,分频数为8 / U) ?3 y5 u1 ]+ |3 M6 r
- 2 }5 U3 K& L. r
- while(1)
1 b+ B" R9 c4 g; J7 I - {
s: W3 s" u# b+ M - LED1 = 1;- z+ z) \: h" O, u5 G
- }
0 a3 g |* V6 W ? - 2 d G; l5 K' U1 r
- }
复制代码 ) I! m- s a( R8 \
05. 测试结果+ Q) [' b! S& y. ^, y
可以看到 DS0 亮一下之后熄灭,紧接着 DS1 开始不停的闪烁。每秒钟闪烁 20 次左右。
9 T. L) F/ F# z0 x$ {- Y, d2 O8 |4 Y; ~ B; a; E- N# ^% E
3 R' w% _. H5 _
|