一、stm32的WWDG说明3 J+ j6 _' B, z
1.1 WWDG特点:
, Q1 H" p5 m) E& X U8 A/ F 在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗,独立看门狗和窗口看门狗。4 ?; a$ h5 a- c, X3 F: c
9 q4 S2 F- Y D6 F4 y: V0 r
相比独立看门狗,窗口看门狗WWDG有以下特点:: Q2 s3 D$ z& k8 K5 A7 m' q1 l
【1】WWDG计数器依赖于系统时钟,准确来说依赖于系统时钟通过分频、倍频得到的PCLK1时钟,即APB1 peripheral clocks输出频率。" `5 J8 L0 ?. V3 f* j
【2】WWDG支持中断功能以及早期唤醒中断。. q+ d2 C, ^& k% O
9 H5 L S; r$ u* u4 ?8 W 1.2 WWDG的cubeMX配置及说明
F* D) h1 d4 z. \* {. S 现在看一下WWDG在CubeMX中的设置问题,WWDG和IWDG一样,在系统内核栏目开启,开启激活选项后,可以设置5项参数,分别是计数时钟分频值,窗口值、递减计数预设值、早期唤醒中断使能以及中断功能。4 }% ^; D7 q* u8 O
) ?. ~1 R% @# P$ Z# W
3 b1 j: F3 P# ]' G* F+ Z 【1】 一旦WWDG启用,将生成系统重置功能并启动其递减计数器,喂狗时,必须在设定的上限时间和下限时间之间喂狗,如果喂狗时间高于上限时间或者低于下限时间,都会导致看门狗触发重启机制。其中下限时间是系统固定的0X40。上限时间(窗口值)是用户可以定义的,大于0X40同时小于0X7F即可。
5 T% ^! Y0 ~6 T+ o, U l7 \# |5 Q2 D5 y
【2】程序在达到0x3F值之前刷新计数器(计数器的低六位值,最大值111111=0X3F),到期时触发系统重置(即,当计数器值从0x40向下滚动到0x3F时生成重置,即再次减一时,0x40=0X3F+0X01)。并在计数器达到刷新窗口值之前刷新了计数器值,也会生成MCU重置。这就要求必须在有限的窗口中刷新计数器。另外WWDG启用后,除非通过系统重置,否则无法禁用WWDG。/ i2 J* V1 r; r1 |2 K
6 o9 o. D( P4 R
【3】如果启用了早期唤醒中断(Early Wakeup Interrupt,EWI)功能,在WWDG到期之前发出警告(即计数器达到0x40时会产生中断)。使用早期唤醒中断(EWI),在生成MCU重置之前可执行特定的安全操作或数据记录。此机制要求在NVIC中启用WWDG中断设置。同样,EWI启用后,EWI中断不能被禁用,除非通过系统重置。中断状态感知可在RCC_CSR寄存器中的WWDGRST标志判断何时发生WWDG重置。; [; `' }4 w3 ^6 Y" {
. X; {/ k# }9 ]5 n4 n5 ^
' f1 L% `3 Y" N+ |. P3 H
1.3 WWDG喂狗时间计算
( W; ]; I% `9 B WWDG计数器输入时钟由PCLK1频率(APB1 peripheral clocks输出频率)除以计数时钟分频值得出。
p3 h* @* M& O2 v s. j! u; b) J f0 ?; a
, D. ?( V! x+ v, B
, I: t3 A# v5 C2 ], ]7 l WWDG时钟(Hz)=PCLK1/(4096*预分频器);
1 n l/ }" K1 b$ p6 Q" e7 ]: J
4 P* _2 v" y) H& }# y WWDG周期(mS)=1000/WWDG时钟(Hz);
1 k* Z: Y+ }% d6 k
7 c* L# K* `5 ~' B WWDG超时MinT(mS)=WWDG周期(mS)*(计数器-窗口),因此最好设置递减计数值≥窗口值。
8 T( _) N& ~/ i7 G3 |) m- B% i: H$ q' j! o9 A7 b5 Y
WWDG超时MaxT(mS)=WWDG周期(mS)*(计数器-0x40),0x40=0X3F+0X01;
- H7 T" N! J. M4 G7 k
# ?. Q& l4 b" ]6 j' | WWDG喂狗时间MinT(mS)<t<MaxT(mS)。
& U0 k2 g) {" I5 X6 F
/ n9 a) Z" V1 |! U二、WWDG工程创建及源码分析
) g; j: s6 p+ a% R0 N8 y 2.1 cubeMX界面配置WWDG
/ c+ _$ t/ p: {! h, y 本博文基于前面独立看门狗的工程直接增加窗口看门狗功能,并关闭独立看门狗。
- K/ N& b: y! Q$ p# N# h* ?) j) o) u
双击.ioc文件打开cubeMX界面,开启WWDG,设置其参数如下,其中分频值2,窗口值0X64(100),递减计数预设值0X64(100),开启早期唤醒中断:
# j7 r5 I! w! g" r9 _+ T+ K8 {& Q7 o0 @7 h2 R$ w8 `
4 P' z6 h9 P8 V9 }4 G
) @* b+ }( {( O1 u4 [ 开启WWDG中断功能0 Y q9 o* V+ X
+ E9 ^6 F1 ~: X; y: _
/ `6 F+ X( z' T/ A3 j" H
. n: M9 ~) G$ x9 E! z# o APB1时钟输出频率设置10MHz(10 000 000Hz),设置该值小一些,主要是本博文采用按键触发切换喂狗时间间隔,而按键捕获到松开是人为操作,反应较慢,实时性不好体现。
5 ]( x& Y+ l4 \3 U( }7 N
' c' p/ o( H9 C) T 2.2 WWDG超时时间实例计算2 t2 Y0 J: Q' @1 h
在WWDG的喂狗时间按前面计算公式,喂狗时间范围:
0 J; s% \; @8 B3 o& x9 b0 B$ [# d4 l9 J4 w9 Y0 t
WWDG周期(mS)=1000ms/10MHz/(4096*8)=3.2768ms。
( {, X4 O; w4 I! h5 l8 K& i4 |4 s' J* t; C' L, x: w) t& s: S4 g' `1 w. Q9 a
WWDG喂狗时间是在WWDG启动后的t时间内:
6 J5 X9 ?3 e5 d
4 d% p/ b: k3 L t>3.2768ms*(0X64-0X64)=0ms;+ J/ D7 D5 X2 S7 Q9 G
& K! `8 f/ j- W% s( J# v( v3 c
t<3.2768ms*(0X64-0X40)=117.9648ms。
5 \' s8 k9 I5 [) U
: I: y2 d' |6 [+ b/ \6 v4 u0 A2 S2 U* k; G. k0 g
2.3 工程高级配置
/ r+ w z& C6 W8 d5 J 在工程高级配置中,取消WWDG的自动初始化,本博文将自己添加初始化,主要是HLA的WWDG初始化启动就立即进入计数,会给第一次喂狗造成时间差异较大。
! T* _1 L4 l: ?& S- S. Z1 w" T( T; l+ q# x6 d; ~# K2 a+ F
8 }" V- n% ~! w7 g: y# N- \) S' t E) Y
生成代码输出。3 }# a6 ?0 z! W+ n
9 _& q m3 x6 r+ j, B% _三、WWDG源码分析及应用
1 }4 c5 R: u% S. }- X9 y# K 3.1 WWDG的HLA库源码分析- a1 m9 Y4 ?5 `7 P
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函数。
7 f; d7 I U" _, S( K9 s' d' k! _) l
) e! ^! H6 z! u) b1 [' r; |; a
- i, M" U/ s6 v 在stm32l4xx_hal_wwdt.c源文件中定义了HAL_WWDG_Init函数,它做以下事情:诊断配置参数是否合规(如果不是采用CubeMX配置,而是自己手动配置或调整过参数的,这里可能异常);调用HAL_WWDG_MspInit函数完成WWDG时钟以及中断初始化;最后将依据参数写入WWDG寄存器。6 S* y6 L i8 p1 ~0 [
7 r$ x/ m( l1 _/ @
7 R, F, E, y8 e5 r0 F; ~3 S- o! P: {
* `& p7 s# _- Q+ Q- i
程序在依据参数将WWDG计数器值写入WWDG寄存器CR,将窗口值和时钟分频值写入WWDG寄存器CFR内。
( V2 \+ c9 h% T7 F2 E, H3 S# y" }: [$ [
' X b" A/ T, L- q# H6 Q# _# M& g7 l) ?# K4 A; v/ o0 B
" I8 U; \/ W! z) s! X0 l& P8 L; T* B2 m9 ^/ k; \! x
再回到wwdg.c内,HAL_WWDG_MspInit函数实现了WWDG时钟启动设置和中断初始及启动设置。
( l" J0 @1 l/ V2 a8 P4 c6 Z* t7 ?6 r. s4 R# A4 c7 k
5 x, f3 r+ M- C$ J& F
% `, J" t% A) f HAL_NVIC_EnableIRQ启动后,如果有WWDG中断事件时,当中断服务例程触发HAL_WWDG_IRQHandler时,将自动清除标志,并执行HAL_WWDG_WakeupCallback用户回调函数报警。该回调函数在stm32l4xx_hal_wwdt.c定义,是个弱函数,用户可以通过自定义回调AL_WWDG_WakeupCallback来添加自己的代码。例如本文将在wwdg.c文件内重新定义该函数:
9 o; O' }7 F9 C- /* USER CODE BEGIN 1 */. Q# ~5 E' P. k
- void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
, N* b! q1 I" ~0 m, ? - {' ?: j9 r( x* u9 q9 [3 d( s
- printf("I know, but do nothing!\r\n");
5 s8 Q" r0 \) X' y8 S$ U1 w - }
- |! |- k$ X( r2 r. Q/ W - /* USER CODE END 1 */
复制代码 3 q+ D$ L, B; ?; P
3.2 延时函数设计
; d+ V* |. i# R" c- U. Z 在ICore目录下添加delay目录,并在该目录下创建delay.h和delay.c源文件,实现自定义延时函数,主要满足本博文更高精度的延时要求。8 @# z: _4 e8 M: c1 M6 w+ _5 ~; J
) O% g, n9 u5 G8 B) j' m' q) P
delay.h,声明微妙、毫秒、秒的延时函数。/ S3 I+ j* X$ K& {" z0 q
- #ifndef DELAY_DELAY_H_
& p! N( u/ b. x3 f6 a* _ - #define DELAY_DELAY_H_
& z) t6 H. |) z! D! q - - S7 ?- ^( N7 y" g6 F6 S R
- #include "stm32l4xx_hal.h" //HAL库文件声明6 b3 e W% M( t* v" D6 s
-
6 V* S( k3 x5 c* Y - void delay_us(uint32_t us); //延时微妙! D' c ^; K$ p9 t' M# E
- void delay_ms(uint32_t ms); //延时毫秒; t3 r4 n! N9 [
- void delay_s(uint32_t s); //延时秒
* B9 G! i0 K# } - " F* ]% R9 P Y
- #endif /* DELAY_DELAY_H_ */
复制代码 8 B" S& i7 |% G' d( U; j2 g
delay.c,7 Q* J6 r( D5 h9 e, {1 l5 U+ v
- #include "delay.h"
' F: U7 R! H5 P1 j, d - & Z. v7 Z, _. f+ C
- #define MFP_VAL 8000000 //80MHz& h/ Q* ?2 a# }
-
9 P: A. f1 l. u# {8 \) A# n$ o - void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数/ a( ~, R) f% Y0 u9 }
- {
1 m* E, Q- N h5 L$ o# G; G+ t - uint32_t delay = (HAL_RCC_GetHCLKFreq() / MFP_VAL* us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数3 ~% E2 R! G4 P; A' R5 X
- while (delay--); //循环delay次,达到1微秒延时
: ], U7 Q" z k# m - }
4 s+ M( s: e% Y5 H6 k$ \+ @ - $ G3 M% p8 t0 ~. x1 t6 I% h
- void delay_ms(uint32_t ms){ //mS毫秒级延时程序/ n! A! `; F' m+ ?! E# i) \
- while( ms-- != 0){. M# Z C8 p- f" W5 H: W; o
- delay_us(1000); //调用1000微秒的延时8 \' K! O7 N' O$ l" H3 B) H8 b
- }! c6 j4 G% W! r, l8 G% S5 a
- }" N0 ?6 r0 ]1 R$ S* A) e* D
- ' e- f7 h$ l) {: `1 j- V( Z( d
- void delay_s(uint32_t s){ //S秒级延时程序
) A2 P$ @! `6 _. S7 V - while( s-- != 0){4 H( F. d* {6 |. a2 B
- delay_ms(1000); //调用1000毫秒的延时2 z: u' t) M9 n" ?! G2 M- R
- }& B0 \4 q Z4 q) R+ R( d7 {
- }
复制代码
, F- }; @2 ^& u4 K: _ 3.3 WWDG使用程序设计4 _( J: ~+ S3 `: N
在本博文中,我们设计通过按键切换喂狗时间间隔,来测试喂狗窗口及时间间隔效果。" v( r: J5 B1 w* F5 i: w+ {
/ _8 _# [( C. R& F% E' {2 l
5 _# ?7 [+ s* c 在main.c源文件中,添加驱动头文件,注意由于在CubeMX取消了自动初始化WWDG,因此需要手动添加wwdg.h头文件:
& O8 d# ~0 f& Z4 L! [! W6 \- /* Private includes ----------------------------------------------------------*/
+ F6 i0 r6 ^; A8 \ - /* USER CODE BEGIN Includes */
, }; |1 A& X8 O, f6 P6 {! i - #include "wwdg.h"' X8 I" T- j: P+ ?- _: e. a2 }/ r$ X
- #include "../../ICore/key/key.h"
# A7 q5 G. Y! a3 | - #include "../../ICore/led/led.h"( d. ?# k N6 |& V
- #include "../../ICore/print/print.h"% k, T! a) j. b @3 A- l
- #include "../../ICore/usart/usart.h"
" g5 U5 ]+ n, P9 n# p! G, v h" R - #include "../../ICore/delay/delay.h"
* q; ?, x; L4 x; c* F) ? - /* USER CODE END Includes */
复制代码
" h: j/ Z3 ^) o( p! i0 E2 D4 K 在main主函数前,声明WWDG句柄,用于喂狗函数是调用
) i0 y* ^% R/ f% P- /* Private user code ---------------------------------------------------------*/
2 t2 D+ Z# o5 R& r- G - /* USER CODE BEGIN 0 */* @6 p/ J2 q0 B' f$ [
- extern WWDG_HandleTypeDef hwwdg;
( E- G# y% h2 X6 Y - /* USER CODE END 0 */
复制代码
* J7 r; y' J3 K 在main主函数内,设置各外设功能及手动初始化WWDG驱动。" h9 c# ?3 v; `4 Y4 j2 R
- /* Initialize all configured peripherals */
$ E$ [ ?" ~0 `+ Q M5 J - MX_GPIO_Init();
: O- @ M9 v% D5 e5 b+ W - MX_LPUART1_UART_Init();6 J2 f. S( n* Z( }
- /* USER CODE BEGIN 2 */
9 s( ~# I, A/ x# H, v - ResetPrintInit(&hlpuart1);* J |* |& D1 k- I
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断/ H. z: b* }+ x) E# l1 `- C
- HLPUSART_RX_STA = 0;& B0 B- r5 L0 `: { u" K
- //
( R: M% ?! ] ]! w2 n$ _% u6 S - printf("app restart now!\r\n");3 I5 g: H7 Y. ^# _& |- s D
- uint8_t wdt_flag = 1;
: D. ~% J+ b0 k - uint8_t printf_flag = 1;1 F5 g9 O8 @* C6 J& Y& I+ b
- MX_WWDG_Init();, n' l" W- d: O/ ]5 H1 r! ^9 K
- /* USER CODE END 2 */
复制代码 / q- r g% J+ y# N5 E& Q& X/ D4 q
在main函数主循环体内,进行喂狗测试,默认是10微妙喂狗一次,按键0停止喂狗,按键1间隔10毫秒喂狗,按键2间隔1秒喂狗。注意前面计算得知0<t<117.9648ms内喂狗有效。按键1间隔10毫秒喂狗主要考虑到按键按下及松开时间效应问题,因此不能设置太大,否则可能因为按键动作耽搁时间太久无效。在实际项目中,我们通常会采用独立线程持续喂狗,各种异常触发打断喂狗来设计。
2 S# n! J |6 ~6 b- /* Infinite loop */
~3 [6 H8 S/ V2 Y - /* USER CODE BEGIN WHILE */) }: b8 M! V- Z
- while (1)
: X/ X; P, ~/ r/ z5 v - {/ H; p7 o. H+ F) Z( I
- if(1==wdt_flag){
2 X1 X5 w/ E' D, W2 a( n7 F - HAL_WWDG_Refresh(&hwwdg);
* h' V2 L" U2 Y! n2 J, [+ b& \ e - delay_us(10);//等待 S3 I5 J& [# D
- }/ X( d: i8 b$ E3 D+ L9 ]
- if(2==wdt_flag){
S8 y2 I" P3 q1 Q - HAL_WWDG_Refresh(&hwwdg);
3 X! i4 d7 Z2 Z8 D- p& O - delay_ms(10);//等待
& D8 ]0 ]# Z2 n1 N& h5 W, L! s7 w - }
' ~" k. l; W& ~# k7 X9 i+ u - if(3==wdt_flag){
i6 Q4 n" ]9 _7 _4 {% M - HAL_WWDG_Refresh(&hwwdg);
% }( z6 U+ I, Q% t# Z" E/ O* |; ] - delay_s(1);//等待, S) R1 s0 T6 G2 ]
- }& U, I7 M: \# N+ ?' J0 Q
- if(printf_flag)1 j7 w; s1 h; {! p N, Z( N
- {2 J% V) q' J+ Z; _2 `' \
- printf("current wdt_flag=%u!\r\n",wdt_flag);) E% e( I P0 Y+ n
- printf_flag = 0;# O& \% G( h& w
- }% k: V; B4 j S) ~+ B
- if(KEY_0())/ E% t1 I& V7 Q" r: j3 G, f5 P
- {% c' v. l# O5 W, f0 E F; x
- wdt_flag = 0;
2 ~$ ~/ r0 x* p) H$ t - printf("WWDG_Refresh stop!\r\n");5 ?# i2 y% `% y/ N
- delay_us(1);//等待1 A: [- b9 E9 q6 ]; y
- printf_flag =1;
$ ^! A( J3 L! d. L& |; _( V2 U/ z - }" Z# x' b* U: ?4 W# U: Z$ j
- if(KEY_1())5 W4 ?1 C% {3 n# j& N+ m
- {
0 e; B1 M* Q' x4 W1 s) Z7 s7 r - wdt_flag = 2;0 E7 O7 u, T G5 |8 h( j5 s( p& Z
- printf("WWDG_Refresh before WIN Time!\r\n");$ J/ a k h2 w* }5 ]' v9 B
- delay_us(1);//等待
9 Z0 P/ N% x+ t/ R" J - printf_flag =1;& p# M( ?, r# N7 c" ^+ K
- }/ T" H5 {6 w. p! m5 U' n: H
- if(KEY_2())2 F/ p3 \6 ~1 P/ J* p7 g
- {
1 c3 k8 m0 B. K x5 G7 P: H; O - wdt_flag = 3;
2 k* e; Z8 Y: n6 \- T/ T" ~5 a" r/ m. I - printf("WWDG_Refresh after 0X40 Time!\r\n");6 S: x" O9 {+ b8 R7 v; c$ |0 n: ?6 q
- delay_us(1);//等待
L: M3 t4 S5 y, L: \3 h1 A/ B - printf_flag =1;! Q! ]3 ?0 n+ A9 S4 A8 H3 ^
- }7 R+ r8 F, X% E- P) O4 {9 c4 e+ [
- /* USER CODE END WHILE */
复制代码 2 d5 v' s% B! b1 D2 v1 [4 e F
四、编译及测试
0 Q, L# j9 }2 b v. q* r* y 4.1 编译5 y; T- q) n" _) q0 c
8 e8 r9 s* K X
( w Q$ L3 O4 g8 T# S" ]% C; ?( j) v! p( f2 b6 i+ H
, E/ K9 o" e& X) r; m
4.2 测试. } C# ]8 T" c# {" K% X' y7 `- E( b
打开串口助手,连接上开发板,按键0、1、2观察效果:
' U7 ^4 {) ^- t* X( G
# X& B& ^6 l! g3 K+ ]1 D
5 b& Q- M! e$ ]( k8 A4 U9 {2 `
0 L. i% T" m7 `
————————————————0 m: _: T2 e0 F: A% I
版权声明:py_free-物联智能) v1 i$ t* l2 N% g
如有侵权请联系删除3 d1 H* t4 |3 D! K! h+ `9 q! J( E! J
/ d- E6 w6 A5 \, C: m3 k: ]# m1 d w4 L8 ]1 O. I( [1 |7 o9 k
|