一、stm32的WWDG说明( w% H& U6 i& z
1.1 WWDG特点:
: l& b, i( n& _) g6 x+ X5 K 在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗,独立看门狗和窗口看门狗。
0 c; d$ w8 Q' J" ?6 C
. ^ f2 `# A2 H0 \0 k/ @" u 相比独立看门狗,窗口看门狗WWDG有以下特点:( x- n' s6 |: ^2 I* s6 [1 X5 ^
【1】WWDG计数器依赖于系统时钟,准确来说依赖于系统时钟通过分频、倍频得到的PCLK1时钟,即APB1 peripheral clocks输出频率。
, @8 m! G4 @$ N 【2】WWDG支持中断功能以及早期唤醒中断。
2 ^+ X" W4 R' `
, M3 C. V o D% q! E0 t" }) t 1.2 WWDG的cubeMX配置及说明
: m/ ^$ Z1 `5 d b( v: E/ {3 v. s 现在看一下WWDG在CubeMX中的设置问题,WWDG和IWDG一样,在系统内核栏目开启,开启激活选项后,可以设置5项参数,分别是计数时钟分频值,窗口值、递减计数预设值、早期唤醒中断使能以及中断功能。7 w, M: b# O9 }0 N# }
! m; |( e! W$ X$ U. @. \; a0 L' j9 w: P5 B' K
【1】 一旦WWDG启用,将生成系统重置功能并启动其递减计数器,喂狗时,必须在设定的上限时间和下限时间之间喂狗,如果喂狗时间高于上限时间或者低于下限时间,都会导致看门狗触发重启机制。其中下限时间是系统固定的0X40。上限时间(窗口值)是用户可以定义的,大于0X40同时小于0X7F即可。, S& |4 m" `' t7 Y2 P9 W
- O" d# G0 V/ t' o7 j6 ^# @ 【2】程序在达到0x3F值之前刷新计数器(计数器的低六位值,最大值111111=0X3F),到期时触发系统重置(即,当计数器值从0x40向下滚动到0x3F时生成重置,即再次减一时,0x40=0X3F+0X01)。并在计数器达到刷新窗口值之前刷新了计数器值,也会生成MCU重置。这就要求必须在有限的窗口中刷新计数器。另外WWDG启用后,除非通过系统重置,否则无法禁用WWDG。# F8 P9 _6 b2 W9 i; r% N0 I' R; T
' E! T) f* B4 k. F2 q5 I
【3】如果启用了早期唤醒中断(Early Wakeup Interrupt,EWI)功能,在WWDG到期之前发出警告(即计数器达到0x40时会产生中断)。使用早期唤醒中断(EWI),在生成MCU重置之前可执行特定的安全操作或数据记录。此机制要求在NVIC中启用WWDG中断设置。同样,EWI启用后,EWI中断不能被禁用,除非通过系统重置。中断状态感知可在RCC_CSR寄存器中的WWDGRST标志判断何时发生WWDG重置。/ D4 t6 c0 D) z# K3 Y- ?/ R* G! S
% M: F& n# x7 n$ {& L; }8 w
( s$ m+ C5 {1 B1 ?, N& f 1.3 WWDG喂狗时间计算 {: t2 d1 c5 p" t% i# h
WWDG计数器输入时钟由PCLK1频率(APB1 peripheral clocks输出频率)除以计数时钟分频值得出。' y7 t3 }- J+ ~8 \7 E& t
D" ^$ e, [& [/ Z$ ?3 q
7 f% [' Q9 r9 G, F1 }8 v& F! G( ]) T O( l
WWDG时钟(Hz)=PCLK1/(4096*预分频器);- P- @ ~( @! }& k0 }
* Y; F" \. B1 R7 U
WWDG周期(mS)=1000/WWDG时钟(Hz);
5 J: u$ T7 [ n! M; f6 T5 U+ e
4 i' Z- j# W: z+ n WWDG超时MinT(mS)=WWDG周期(mS)*(计数器-窗口),因此最好设置递减计数值≥窗口值。
& F: r, K$ [+ F% l2 R5 w2 O/ g4 z5 a0 y: ~. ?) A1 j- `: h
WWDG超时MaxT(mS)=WWDG周期(mS)*(计数器-0x40),0x40=0X3F+0X01;
5 R5 q3 x% O3 A6 M0 R( \
/ U4 t$ f' h, n7 J) q WWDG喂狗时间MinT(mS)<t<MaxT(mS)。) w. Z, w2 Y7 B/ N$ \
* A) e) e) V! ^! J8 j4 v* M二、WWDG工程创建及源码分析# H/ n/ X6 n& E" L& c2 P
2.1 cubeMX界面配置WWDG# d4 s3 Y( i8 G; e- s3 B8 [
本博文基于前面独立看门狗的工程直接增加窗口看门狗功能,并关闭独立看门狗。
2 L% u/ l# I8 V- D" T) _! n! j2 R* T4 W' p
双击.ioc文件打开cubeMX界面,开启WWDG,设置其参数如下,其中分频值2,窗口值0X64(100),递减计数预设值0X64(100),开启早期唤醒中断:
$ y4 z3 ~$ Y; J, z8 W6 V9 D' H/ D6 c0 u2 y
$ s. Y# f$ c+ m- x4 h* p; a( I' Y8 A8 I( X0 z! q1 r: ]
开启WWDG中断功能3 _& \5 i+ J! X0 a. ]
" B# u8 ?& b: V. K
* S1 e2 w1 c, c8 {" G, D, ^" O5 o" A3 s1 V% u
APB1时钟输出频率设置10MHz(10 000 000Hz),设置该值小一些,主要是本博文采用按键触发切换喂狗时间间隔,而按键捕获到松开是人为操作,反应较慢,实时性不好体现。- S3 {0 e# t5 E1 C+ L8 A
# V2 @ z5 W; w4 ?: a 2.2 WWDG超时时间实例计算
8 ?# s$ l; m3 A j 在WWDG的喂狗时间按前面计算公式,喂狗时间范围:
- t9 X" j/ H% I: j
5 a! d1 K- U7 l4 d- v. ]9 u7 C WWDG周期(mS)=1000ms/10MHz/(4096*8)=3.2768ms。2 b' b t' ^* N% \+ _( S# `
$ t8 G/ m# ^4 U1 c2 u, l) U8 K WWDG喂狗时间是在WWDG启动后的t时间内:! }/ k- Q3 f. l: U
$ R( \, X! a5 I$ a- X) w
t>3.2768ms*(0X64-0X64)=0ms;3 L. Z c/ `5 r
: N' Z( ^; p H0 H t<3.2768ms*(0X64-0X40)=117.9648ms。: a' h8 M. ~1 |: |% _
4 W* `# ^1 [. V! {$ @
2 ]) @) O' X/ y- m. Z% I: i 2.3 工程高级配置! ~7 O) \: S* v. {; f0 g4 Q6 {
在工程高级配置中,取消WWDG的自动初始化,本博文将自己添加初始化,主要是HLA的WWDG初始化启动就立即进入计数,会给第一次喂狗造成时间差异较大。$ C) k$ e; Q$ \7 e3 F$ m5 v
* ^$ l: f" y) R* u
' T; K" T* Y# ?% _1 N5 `8 O) h
- u$ _5 U# q) L ~/ v% ^ 生成代码输出。* G4 q& v9 }" a' Z9 u7 G
7 W0 P1 f& Q5 l9 W. W3 g0 v
三、WWDG源码分析及应用
/ ]- [9 f5 J2 n4 |; g3 G 3.1 WWDG的HLA库源码分析
6 j. u0 p! G3 B z+ g0 q WWDG会在Core源码目录下的Inc及Src目录,分别生成wwdg.h和wwdg.c驱动文件。在wwdg.c文件中,主要定义了MX_WWDG_Init函数和HAL_WWDG_MspInit函数。MX_WWDG_Init主要做两件事情,一是将CubeMX上配置的参数传递给WWDG缓存Init和生成WWDG句柄Instance,二是调用HLA库的HAL_WWDG_Init来实现真正的初始化设定。HAL_WWDG_MspInit是HLA内的弱函数,根据实际配置CubeMX会生成新的函数覆盖原来的弱函数,而在HAL_WWDG_Init函数中会调用到HAL_WWDG_MspInit函数。
2 |% K' k* v# H" R8 n" t
* R9 ]1 a: g. [5 P( x! N7 e5 p4 E
/ a5 X2 d+ ], `7 V( z
, p* M8 ^& X$ y$ E1 @" w) q 在stm32l4xx_hal_wwdt.c源文件中定义了HAL_WWDG_Init函数,它做以下事情:诊断配置参数是否合规(如果不是采用CubeMX配置,而是自己手动配置或调整过参数的,这里可能异常);调用HAL_WWDG_MspInit函数完成WWDG时钟以及中断初始化;最后将依据参数写入WWDG寄存器。
/ C; W) q) E) s K! u m8 E9 L v# x/ s+ q" |
8 w' }) w. O' ^: M( A1 U$ t/ z- n- ?9 o1 x; ~0 N9 b
程序在依据参数将WWDG计数器值写入WWDG寄存器CR,将窗口值和时钟分频值写入WWDG寄存器CFR内。) l7 u, o7 S. D+ O% x
; L. w; ~: Z# x# Y) i5 M
& O- N* m6 [* }- a; `' K8 f/ L) ?' \& i1 i; Y* r
- R+ J, O0 J9 T$ ]2 P$ U1 E& B; O k
再回到wwdg.c内,HAL_WWDG_MspInit函数实现了WWDG时钟启动设置和中断初始及启动设置。" S. c4 i q: G7 e# G! E
$ F% l1 G: ~5 U" E5 |
m! ] M* d, [1 l6 J7 q
( V: V' V* _) b% T7 p
HAL_NVIC_EnableIRQ启动后,如果有WWDG中断事件时,当中断服务例程触发HAL_WWDG_IRQHandler时,将自动清除标志,并执行HAL_WWDG_WakeupCallback用户回调函数报警。该回调函数在stm32l4xx_hal_wwdt.c定义,是个弱函数,用户可以通过自定义回调AL_WWDG_WakeupCallback来添加自己的代码。例如本文将在wwdg.c文件内重新定义该函数:
o8 S- a& v, Q5 x' w6 q4 A- /* USER CODE BEGIN 1 */
, c, {! O( d3 I* u( G* V - void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)1 x3 k9 @0 J6 R/ R2 z' \
- {
3 x$ G( O& `$ e$ I3 {; g2 I9 U - printf("I know, but do nothing!\r\n");
1 M& R% v8 Q$ r; E5 ~ - }
8 i# _: A/ F. j) r - /* USER CODE END 1 */
复制代码
& q% d; I5 _2 X/ G: r+ r+ T4 V 3.2 延时函数设计8 I6 D2 |" r+ `+ }3 ~# Q. w
在ICore目录下添加delay目录,并在该目录下创建delay.h和delay.c源文件,实现自定义延时函数,主要满足本博文更高精度的延时要求。
: x+ B; z9 t# ~6 \$ \$ \ U/ C, f3 `$ K. {$ ~; x" R& ?
delay.h,声明微妙、毫秒、秒的延时函数。
6 E$ S# [1 y/ `1 s2 v, Z- n4 ]- #ifndef DELAY_DELAY_H_: r* O/ {7 N! p! ], e3 }
- #define DELAY_DELAY_H_
# Y& B7 g) }6 @) ?- E3 p) {" O7 v -
4 D7 P. G! W/ ]& u: e" e! N - #include "stm32l4xx_hal.h" //HAL库文件声明
. ?) e" c5 }# m5 L - 2 C& p6 V0 s% [6 E @
- void delay_us(uint32_t us); //延时微妙8 Q* l7 d* c/ h! K% {
- void delay_ms(uint32_t ms); //延时毫秒
4 c" j9 y* L0 y - void delay_s(uint32_t s); //延时秒
: @+ ^" q# g6 Q) U' i -
: F b V `9 L" X: f9 [ - #endif /* DELAY_DELAY_H_ */
复制代码
: C! [: v+ n) t; w8 d! | delay.c,3 ?, G! ^3 P7 k3 J1 e3 s; E5 V+ k
- #include "delay.h"
+ |. o4 X r! h1 X7 i R7 X2 L2 W -
3 Y: ^# T& \1 B - #define MFP_VAL 8000000 //80MHz
& f8 ?- T8 R$ m; h Y -
4 H3 j# L3 }+ W4 L3 t; i; `1 B - void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
N6 K" ~+ ?4 y7 S, k& o4 E - {
' e- y7 ^8 |6 B* V - uint32_t delay = (HAL_RCC_GetHCLKFreq() / MFP_VAL* us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数3 z# l# H+ Q5 d8 D3 \
- while (delay--); //循环delay次,达到1微秒延时4 n' i/ W! n8 \, X7 k5 l) z7 t
- }
0 Y+ m7 R) i- N! d; t- G6 W -
$ @) i7 P( S ?) f% n: \ - void delay_ms(uint32_t ms){ //mS毫秒级延时程序
) B$ j# C8 B6 T - while( ms-- != 0){4 L. D1 S9 `0 i# X1 _
- delay_us(1000); //调用1000微秒的延时# O& p, r Z) Q. f1 z: O
- }
" U3 x; _* }% F' b$ o - }4 H. D4 I B3 H J+ M( C- R `
-
0 i4 i3 j. u) U; z2 O& h+ J - void delay_s(uint32_t s){ //S秒级延时程序, Z% ^# n: q0 R
- while( s-- != 0){
: H5 H7 R$ p2 p# H# b - delay_ms(1000); //调用1000毫秒的延时
" w; Q/ ~+ z1 `# n - }) X! a2 w% ]( {6 J: |& k5 \
- }
复制代码 / t2 k* f& K, ]8 E$ _) w
3.3 WWDG使用程序设计
) h0 X" P. i/ `% u# U 在本博文中,我们设计通过按键切换喂狗时间间隔,来测试喂狗窗口及时间间隔效果。# l/ A& g, V y5 [ Z
' Y1 c8 q8 }: c/ o) F% k6 X) Q3 w' A2 }5 v
在main.c源文件中,添加驱动头文件,注意由于在CubeMX取消了自动初始化WWDG,因此需要手动添加wwdg.h头文件:4 X0 B) o! G) |& g6 q/ ^; O: @
- /* Private includes ----------------------------------------------------------*/. x( D: b$ b9 T3 r6 { J% x5 G4 k
- /* USER CODE BEGIN Includes */
! i7 h: {7 V' g R$ S, O - #include "wwdg.h"
9 H, _/ I6 D- H \8 ~ - #include "../../ICore/key/key.h"
8 G' v) r$ G' d - #include "../../ICore/led/led.h"8 ?9 Z8 Z/ f1 A# b8 P* C
- #include "../../ICore/print/print.h"
* B, g. h" J/ l' h- ]4 l1 c - #include "../../ICore/usart/usart.h"" ^9 Z# P. e y9 ]. O4 J, g
- #include "../../ICore/delay/delay.h"
# o* B* E% v9 u - /* USER CODE END Includes */
复制代码 9 Y+ @# S* {% ?' ?* r. K( ^
在main主函数前,声明WWDG句柄,用于喂狗函数是调用5 l! |& f4 E9 E: w+ P& p
- /* Private user code ---------------------------------------------------------*/
, s7 E& C6 r2 L0 p' P5 f - /* USER CODE BEGIN 0 */
" U, e3 |5 K1 _# e5 l+ k) L. f# l( L; S - extern WWDG_HandleTypeDef hwwdg;% n0 p5 v% ?2 s. w: L% T/ P7 n
- /* USER CODE END 0 */
复制代码 4 v1 k' g7 C p: n8 x. V. ~8 {- r
在main主函数内,设置各外设功能及手动初始化WWDG驱动。
/ b V. [6 P, d- W* r/ Y- /* Initialize all configured peripherals */( J* h w' G' J9 T
- MX_GPIO_Init();( [) c, I4 d% B# D
- MX_LPUART1_UART_Init(); j0 W3 O$ e3 K" h; Q7 N2 X% K Q% b, D
- /* USER CODE BEGIN 2 */
O' D5 Z* |. u! r5 p) A - ResetPrintInit(&hlpuart1);! W i1 P% |9 G5 F6 J
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
; ~8 ]. O5 ^; o - HLPUSART_RX_STA = 0;
% y+ T% k* r, @9 b/ ~8 L* C: \- Z - //1 o e6 \9 i# {! V
- printf("app restart now!\r\n");3 h/ f% I1 E+ b; i0 Y4 Y( \
- uint8_t wdt_flag = 1;
8 j6 p5 k+ p- j. l) k8 d - uint8_t printf_flag = 1;8 ?1 m, v# M9 n
- MX_WWDG_Init();
' x# n8 \7 B6 y6 f$ U - /* USER CODE END 2 */
复制代码 ! T- b) O! h& i% y7 h7 e8 y
在main函数主循环体内,进行喂狗测试,默认是10微妙喂狗一次,按键0停止喂狗,按键1间隔10毫秒喂狗,按键2间隔1秒喂狗。注意前面计算得知0<t<117.9648ms内喂狗有效。按键1间隔10毫秒喂狗主要考虑到按键按下及松开时间效应问题,因此不能设置太大,否则可能因为按键动作耽搁时间太久无效。在实际项目中,我们通常会采用独立线程持续喂狗,各种异常触发打断喂狗来设计。
, y7 l$ l9 q/ p' j1 w/ [7 ~- q- /* Infinite loop */5 B' j* J/ K. r; {6 O
- /* USER CODE BEGIN WHILE */' G" R3 W' b- K4 C
- while (1)
, t: i1 U+ T) F; v2 E - {
& U4 S G( U5 g- D% Q5 {7 a - if(1==wdt_flag){- U O/ x2 o! u" }$ l
- HAL_WWDG_Refresh(&hwwdg);
( e, W' ^6 [9 g - delay_us(10);//等待
" f+ U8 P4 [, ]9 W( y: ? - }
# F8 ^/ L( L. y- P( c9 O - if(2==wdt_flag){0 A/ M* @# h/ t G) N4 ~( a1 P
- HAL_WWDG_Refresh(&hwwdg);
5 v' s+ P; p5 M/ H. `2 Y - delay_ms(10);//等待4 E' d5 ]: m# J, H( T0 \) G
- }+ c7 b$ V8 N8 E- C
- if(3==wdt_flag){
/ y: y2 _* Z6 `' ~! R/ T - HAL_WWDG_Refresh(&hwwdg);
; i0 f2 q3 j2 D2 f9 ^" { - delay_s(1);//等待3 a% b: w/ J% X. U* g: m
- }7 n' k. Y: ~8 @
- if(printf_flag)
n! k& L5 h- I - {7 b' x# d2 {( _( r
- printf("current wdt_flag=%u!\r\n",wdt_flag);) K z5 F8 W# w: t' i% ~6 a
- printf_flag = 0;
+ S; G9 s% U) o3 U+ K% { - }
6 b7 u) V* e5 C( `2 y - if(KEY_0())
/ W& x9 ~) X1 I" p+ y! E2 [0 d" d8 | - {
+ F/ M0 N1 i. i$ A! k - wdt_flag = 0;6 O( e* a* ]5 d. p# _# L" s
- printf("WWDG_Refresh stop!\r\n");6 r8 Y9 ]4 J2 e3 E+ V6 o; r+ R
- delay_us(1);//等待
5 e; M) p g& L6 C - printf_flag =1;
1 ~# {) D) |$ T2 {9 l7 E - } k% c$ u, B+ ~
- if(KEY_1())
9 y; U+ o, r2 S! ~ - {
3 h3 M4 E$ G1 j: Z - wdt_flag = 2;
* o. f' v" z+ D1 l+ y - printf("WWDG_Refresh before WIN Time!\r\n");4 [0 c8 V6 ]5 V' f3 l& M! Y
- delay_us(1);//等待% S/ x( g6 G; a ~# n
- printf_flag =1;
2 N8 n& |& f* d( `* ^ P" T - }: U) V! I+ w( r
- if(KEY_2())
5 @/ Q& I/ `5 s. y1 J: q - {
! ~. _' m4 X, s1 D+ u! [ - wdt_flag = 3;
" p' {7 B( i) `; C6 n8 [5 J - printf("WWDG_Refresh after 0X40 Time!\r\n");7 k k; L0 Y% m: i0 m/ Y
- delay_us(1);//等待
: a0 B5 [; \& O( T - printf_flag =1;
* Q3 ~/ q. O! l1 e1 ?+ C% R7 b - }
6 z' ^3 k# i3 h& `) f S - /* USER CODE END WHILE */
复制代码
* B; e) W7 T0 J# E; s0 L四、编译及测试( s/ y1 v" C8 P% ?/ r: D: K( h L: w
4.1 编译4 }8 p9 l# t" E# n! k8 C8 [0 _. F
/ ?# B' a; {" Y% Q
2 V7 } x; |$ o
& D0 C* {- h. `) l+ {2 I" ~& T* t, X" r/ Z/ F8 @, ?
4.2 测试" \$ a3 f0 Q+ O! C, I& P, {( T* Z h
打开串口助手,连接上开发板,按键0、1、2观察效果:' @, Q' y8 \& v- S9 H& M. i
$ Z* K* S1 D. ^2 v' y
- ~* h0 y! n& a H l/ _
" m p3 A+ C& {
————————————————
9 \/ K5 p* |( y: ~版权声明:py_free-物联智能
7 Q- i. P* a8 }" ^: u! E+ S如有侵权请联系删除
5 w4 v- _. M8 \/ Y1 g+ R8 N9 g5 q2 W. W1 `5 i
' T- }5 c) x1 Z4 U; x" z( G* \. j
|