一、stm32的WWDG说明/ ^: r6 `7 u! I# q1 M! A; v
1.1 WWDG特点:* h* C! g) b; l" y
在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗,独立看门狗和窗口看门狗。
; T0 j7 u0 l3 G* U/ `/ D5 w& U$ d7 h& F3 H( D" t+ }3 u% [) ]( ]- K
相比独立看门狗,窗口看门狗WWDG有以下特点:
1 L I+ _3 [) ^$ r 【1】WWDG计数器依赖于系统时钟,准确来说依赖于系统时钟通过分频、倍频得到的PCLK1时钟,即APB1 peripheral clocks输出频率。* m0 C, \: N, B5 q: ?
【2】WWDG支持中断功能以及早期唤醒中断。
" K+ N- J8 A# p9 O6 {% T' \0 H& N+ U3 a+ M( D, x1 F
1.2 WWDG的cubeMX配置及说明$ N- u7 f, u# l5 O4 K7 K9 L
现在看一下WWDG在CubeMX中的设置问题,WWDG和IWDG一样,在系统内核栏目开启,开启激活选项后,可以设置5项参数,分别是计数时钟分频值,窗口值、递减计数预设值、早期唤醒中断使能以及中断功能。5 t! x. Q8 e$ l" {- J0 s0 y$ i9 y
" K3 y/ D/ S* V9 g$ [, L/ W
" b; l. f$ N( i0 C1 z 【1】 一旦WWDG启用,将生成系统重置功能并启动其递减计数器,喂狗时,必须在设定的上限时间和下限时间之间喂狗,如果喂狗时间高于上限时间或者低于下限时间,都会导致看门狗触发重启机制。其中下限时间是系统固定的0X40。上限时间(窗口值)是用户可以定义的,大于0X40同时小于0X7F即可。
* o+ A0 f# H6 p' i# U) R2 g
7 V! h$ B) B5 Y0 m* f 【2】程序在达到0x3F值之前刷新计数器(计数器的低六位值,最大值111111=0X3F),到期时触发系统重置(即,当计数器值从0x40向下滚动到0x3F时生成重置,即再次减一时,0x40=0X3F+0X01)。并在计数器达到刷新窗口值之前刷新了计数器值,也会生成MCU重置。这就要求必须在有限的窗口中刷新计数器。另外WWDG启用后,除非通过系统重置,否则无法禁用WWDG。" R8 t3 X& v: L0 ]
4 \; D# I+ y& t/ ]2 A1 y
【3】如果启用了早期唤醒中断(Early Wakeup Interrupt,EWI)功能,在WWDG到期之前发出警告(即计数器达到0x40时会产生中断)。使用早期唤醒中断(EWI),在生成MCU重置之前可执行特定的安全操作或数据记录。此机制要求在NVIC中启用WWDG中断设置。同样,EWI启用后,EWI中断不能被禁用,除非通过系统重置。中断状态感知可在RCC_CSR寄存器中的WWDGRST标志判断何时发生WWDG重置。; h( V- P! Y* `' L+ N& e* a5 d: M
0 Q# T m1 g8 v+ S& x* b [% e0 q7 e& [3 }; P1 h
1.3 WWDG喂狗时间计算 z; x" G1 r+ k( j3 |
WWDG计数器输入时钟由PCLK1频率(APB1 peripheral clocks输出频率)除以计数时钟分频值得出。
% r0 ?& r3 F. J& _% B c# |) \! z7 R9 m
- Q1 ~$ [0 j$ O0 ?5 a' e
3 e2 p' d$ T( J% H WWDG时钟(Hz)=PCLK1/(4096*预分频器);
1 k' H) r- t7 V! Q! U( P2 i" [
WWDG周期(mS)=1000/WWDG时钟(Hz);
X+ u2 E Q# {6 l5 |# L9 s
6 K; \/ Y4 T0 T' ` WWDG超时MinT(mS)=WWDG周期(mS)*(计数器-窗口),因此最好设置递减计数值≥窗口值。3 S# p8 N/ j* i
/ J* G6 o9 h# K. O, Z2 c6 B
WWDG超时MaxT(mS)=WWDG周期(mS)*(计数器-0x40),0x40=0X3F+0X01;" C# U, K4 [; z/ ?. F% \ z
' V* o# a- ~$ r9 c& z4 o WWDG喂狗时间MinT(mS)<t<MaxT(mS)。3 r2 D2 b8 T- @( b9 A$ a
' t7 X7 N7 t1 Q1 {# V% c二、WWDG工程创建及源码分析% o2 \ b! @5 }2 w7 N
2.1 cubeMX界面配置WWDG
5 V7 s; n) V( D& a7 p 本博文基于前面独立看门狗的工程直接增加窗口看门狗功能,并关闭独立看门狗。$ P0 V* G( t% h. `6 _* R% g3 Z8 s" O
: h* C9 p; d; c0 \3 @# d' T 双击.ioc文件打开cubeMX界面,开启WWDG,设置其参数如下,其中分频值2,窗口值0X64(100),递减计数预设值0X64(100),开启早期唤醒中断:3 Q7 d2 ^/ }' V! \: d
5 {( P; x9 p# ^8 V6 ?& V
% {3 m4 c. N* F% I5 M8 q& N4 n: I' @; ?' G1 u+ b! a* h
开启WWDG中断功能
X5 q5 K" h; j/ w
% N- p+ ^$ g7 Z E1 i$ ]4 [
# y7 U- h3 q3 R. g* y7 r" I
g# d8 L3 e" R! |- }, K2 P APB1时钟输出频率设置10MHz(10 000 000Hz),设置该值小一些,主要是本博文采用按键触发切换喂狗时间间隔,而按键捕获到松开是人为操作,反应较慢,实时性不好体现。) A/ o T; A" t) L, v4 ~- t
8 @, H2 {- `* E9 s U
2.2 WWDG超时时间实例计算6 x; \- p* o4 ^% L/ R/ U
在WWDG的喂狗时间按前面计算公式,喂狗时间范围:
) q) D$ _2 i, t( P2 z8 k9 }
6 g [' W I! K4 E& X0 H0 @ WWDG周期(mS)=1000ms/10MHz/(4096*8)=3.2768ms。) W* H/ h# M/ k
) X) [: g X4 Z6 n* l I: U4 B& X7 D0 W+ w WWDG喂狗时间是在WWDG启动后的t时间内:; [' ^! @/ I0 E* y) l5 C
; n, z. Z0 p: N1 @3 B! W5 ?& @' x t>3.2768ms*(0X64-0X64)=0ms;
8 K. \, e1 c, d1 D6 N/ @ x
9 w, g) {; t. h% j t<3.2768ms*(0X64-0X40)=117.9648ms。
, P" v# A9 k3 s/ ^" h! B0 n u3 t
7 m1 b3 \) N; m; `0 p2 L2 w4 F" h+ H2 ]) n
2.3 工程高级配置
2 B: j) ^% n/ H3 ^7 \+ R 在工程高级配置中,取消WWDG的自动初始化,本博文将自己添加初始化,主要是HLA的WWDG初始化启动就立即进入计数,会给第一次喂狗造成时间差异较大。
8 Q7 J3 b4 R. v, P0 t D c2 O4 b$ F# Q' e8 L+ T9 H
; S* H1 {6 x% w9 B3 D/ Y( {2 G8 t4 B0 Q6 p5 q# Y$ B4 B- u
生成代码输出。9 L. O4 T: b2 [% L3 O9 A3 O5 c
* m8 X( |1 \9 M5 P
三、WWDG源码分析及应用$ L0 x2 p1 \- y- \+ {% t) L e
3.1 WWDG的HLA库源码分析$ w- y' u7 b r5 ]' u' n
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函数。- T1 j# y6 c* W( r* f
7 q6 t$ K2 J6 W: q
& ~- L* U* |+ R0 {" O* V: E
% \/ y8 p- p$ o5 Y& m6 s: c 在stm32l4xx_hal_wwdt.c源文件中定义了HAL_WWDG_Init函数,它做以下事情:诊断配置参数是否合规(如果不是采用CubeMX配置,而是自己手动配置或调整过参数的,这里可能异常);调用HAL_WWDG_MspInit函数完成WWDG时钟以及中断初始化;最后将依据参数写入WWDG寄存器。5 ]* ~8 ]) e5 a8 @
+ G$ h9 G# Z7 Z
/ K4 |6 I& u" v# d( D
1 z# x; p m: E: r# s 程序在依据参数将WWDG计数器值写入WWDG寄存器CR,将窗口值和时钟分频值写入WWDG寄存器CFR内。
+ j% S. c* v+ m' y0 o4 V2 P) f0 U- P% e2 s7 z
+ s% k! B) ?$ H
) u# n* C9 Q. Q8 L6 X# M2 x
6 D7 t- \0 d' m% z; I9 V) h! r& ]- r: T) p) P1 ?
再回到wwdg.c内,HAL_WWDG_MspInit函数实现了WWDG时钟启动设置和中断初始及启动设置。
3 M( f/ j z) P8 O4 J( \* Z& S( R- W1 b8 r& `
) Y i1 ~/ e/ I$ R7 [* N+ n2 B& {# v
# T5 {; D9 B. G HAL_NVIC_EnableIRQ启动后,如果有WWDG中断事件时,当中断服务例程触发HAL_WWDG_IRQHandler时,将自动清除标志,并执行HAL_WWDG_WakeupCallback用户回调函数报警。该回调函数在stm32l4xx_hal_wwdt.c定义,是个弱函数,用户可以通过自定义回调AL_WWDG_WakeupCallback来添加自己的代码。例如本文将在wwdg.c文件内重新定义该函数:
1 L8 q( ?6 W# G- /* USER CODE BEGIN 1 */" `* `" r- f [- d6 z, l2 ^/ R
- void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg): j$ R8 f" R1 \, J/ e
- {/ g# L( \* G. V* \+ b
- printf("I know, but do nothing!\r\n");8 O& i* P3 }$ K* o6 g M" L
- }' ]1 |1 ?( b B/ H# u* ^
- /* USER CODE END 1 */
复制代码
9 U7 p5 r/ z1 E. f 3.2 延时函数设计
& V: Q8 m( U) v 在ICore目录下添加delay目录,并在该目录下创建delay.h和delay.c源文件,实现自定义延时函数,主要满足本博文更高精度的延时要求。: J) `. n) O! `& T* j1 ^
( F# N- f* z4 U" m delay.h,声明微妙、毫秒、秒的延时函数。
" o7 v0 D* t! Q. l- #ifndef DELAY_DELAY_H_
; ]8 Z4 z! Z5 J' ^ - #define DELAY_DELAY_H_
9 d, f* p# x N: U - 9 q9 e: n0 n+ e) T' i) \$ a. y
- #include "stm32l4xx_hal.h" //HAL库文件声明
1 V( ~! ]& a( K0 W - + F. v( I$ q+ `3 \ i; E |
- void delay_us(uint32_t us); //延时微妙9 ?9 K, | q$ |+ ^& Y7 @, O
- void delay_ms(uint32_t ms); //延时毫秒: M+ u: ]( L! I' N/ N' Y
- void delay_s(uint32_t s); //延时秒
; }$ d& c) b/ S+ y% |' |" S - + i) s( N- H) J' \
- #endif /* DELAY_DELAY_H_ */
复制代码 , C, }- e/ ^0 D5 ]- d$ n% W* r
delay.c,
' @. m3 P4 [0 Q: b8 J7 m- \! ~" U- #include "delay.h"
+ z5 t. k% e+ N$ \2 d+ [ -
3 T7 r5 `2 B& S1 w) l - #define MFP_VAL 8000000 //80MHz+ r. Y- s1 `0 b6 `/ O- P g0 H
- 5 U9 h$ k8 N$ o4 z( m
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数& u' N( n: _- i9 A
- {
* \4 x U, k; i9 d! W+ o - uint32_t delay = (HAL_RCC_GetHCLKFreq() / MFP_VAL* us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数( z2 J0 O) G- f* U" J: C1 M- K
- while (delay--); //循环delay次,达到1微秒延时
4 n0 L1 R" ^% `$ k p1 s" p - }5 b2 q( M& x0 r
-
8 U1 ~, r' h; M" V0 G2 H8 b" R0 | - void delay_ms(uint32_t ms){ //mS毫秒级延时程序 B8 a D6 n2 Y: P; M% t
- while( ms-- != 0){, Y+ N U$ _8 M6 w* g0 k
- delay_us(1000); //调用1000微秒的延时% \$ T2 M: p% S Z9 X# u* P) I6 c
- }4 g; o/ b$ V @2 M6 ?- A+ Z
- }
4 f3 U+ D6 U5 f$ E -
3 u. X. p+ H8 ?' Y$ t - void delay_s(uint32_t s){ //S秒级延时程序
+ C6 |( u, P$ y, P& o+ O N2 ^8 F - while( s-- != 0){
5 i: a9 |# W7 Y( s0 [( Y; q - delay_ms(1000); //调用1000毫秒的延时7 P+ f/ F( X1 t2 [
- }
7 |* s) I- G; n- ~; L - }
复制代码 5 w* b, y4 U' B% v( ~& G
3.3 WWDG使用程序设计
8 k% l6 o( L% r( \ H 在本博文中,我们设计通过按键切换喂狗时间间隔,来测试喂狗窗口及时间间隔效果。0 A# S' t; U1 l
, O& P+ b- Y# |( e8 p0 p
, o$ t: F: x1 u) U2 M& h% F 在main.c源文件中,添加驱动头文件,注意由于在CubeMX取消了自动初始化WWDG,因此需要手动添加wwdg.h头文件:
1 S8 s: W% X$ g8 \$ m' F; g0 X3 A0 @- /* Private includes ----------------------------------------------------------*/" A9 {9 @- V# H1 K% U. v x3 {
- /* USER CODE BEGIN Includes */
# s6 t8 [9 x8 K. n I6 w - #include "wwdg.h"/ U1 L( h7 q' }( e' K: ~4 F
- #include "../../ICore/key/key.h"7 n+ w5 g: \5 n+ X- U, N4 ]
- #include "../../ICore/led/led.h"
3 \6 }/ l0 ~# X, L* b( U - #include "../../ICore/print/print.h"3 Z; W( O' X7 m! m# V# r
- #include "../../ICore/usart/usart.h"
. p7 ] u: m9 L0 ^( t* s7 S - #include "../../ICore/delay/delay.h"8 j$ T0 j b' o5 ?* ^% _, J9 m
- /* USER CODE END Includes */
复制代码
( w! @+ x9 x6 k) A, D# C5 J 在main主函数前,声明WWDG句柄,用于喂狗函数是调用8 L* r; i, `- K0 X r
- /* Private user code ---------------------------------------------------------*/
- P" W! I2 w! L- N, l - /* USER CODE BEGIN 0 */2 b7 J6 y/ M* U3 J1 X8 v0 Z: s
- extern WWDG_HandleTypeDef hwwdg;( P/ ^" Z% [" D2 [6 I: k7 L
- /* USER CODE END 0 */
复制代码 % q# x' q, z5 I( V
在main主函数内,设置各外设功能及手动初始化WWDG驱动。! W5 t. o0 M/ |% b5 N
- /* Initialize all configured peripherals */- Q* }5 `8 Q: g$ d' B8 w
- MX_GPIO_Init();
, { E/ w5 Q0 B. x - MX_LPUART1_UART_Init();6 S+ c+ [7 M1 V' k* y8 ]
- /* USER CODE BEGIN 2 */+ `- A5 b! F* @4 f" Z8 g
- ResetPrintInit(&hlpuart1);
5 L5 J ]) f( @ - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断$ j$ Y: O6 q" |& ^: M
- HLPUSART_RX_STA = 0;7 C( X! N' e2 c, _0 B. p6 _
- //
3 Z$ k4 b* {: k% @ ^ - printf("app restart now!\r\n");( {$ g0 f {. ?9 F
- uint8_t wdt_flag = 1;% P5 z" `: @6 A- U F6 S- i) w6 ]# h, b
- uint8_t printf_flag = 1;
4 V u1 S5 l4 D4 T7 g* R* @# X1 N3 Y - MX_WWDG_Init();
4 `0 j5 X4 n, K: A' W6 G6 |" g) v& C - /* USER CODE END 2 */
复制代码 * t" @! s& J& g+ X+ w) d! n) c
在main函数主循环体内,进行喂狗测试,默认是10微妙喂狗一次,按键0停止喂狗,按键1间隔10毫秒喂狗,按键2间隔1秒喂狗。注意前面计算得知0<t<117.9648ms内喂狗有效。按键1间隔10毫秒喂狗主要考虑到按键按下及松开时间效应问题,因此不能设置太大,否则可能因为按键动作耽搁时间太久无效。在实际项目中,我们通常会采用独立线程持续喂狗,各种异常触发打断喂狗来设计。, k/ u0 m$ g. G8 }
- /* Infinite loop */
5 N: O, q7 n4 f% O% n - /* USER CODE BEGIN WHILE */! B0 z a7 K9 A/ ]9 O; a
- while (1)
5 v3 `& Q2 [5 t& k3 `. Y - {
% Q7 `% p ~* {% b; V - if(1==wdt_flag){
- r* v0 I: a: J' H& R - HAL_WWDG_Refresh(&hwwdg);- d3 V; m. \$ q2 l
- delay_us(10);//等待* @# ~ A9 o1 \7 T. n
- }5 w% h: Q" J) C* L) E8 r1 G" ]
- if(2==wdt_flag){, M$ e; M- y+ F! t6 K1 q `/ A
- HAL_WWDG_Refresh(&hwwdg);2 g" S; L6 e Y) G3 ^% n1 R
- delay_ms(10);//等待4 a, v* m! _1 l' {! n+ v/ Y3 G
- }6 P0 C# E4 M$ m5 _; B) B
- if(3==wdt_flag){
: s7 ^* j" D3 r! i/ X3 e - HAL_WWDG_Refresh(&hwwdg);
! R. {) L o5 @. n+ z& H! Q - delay_s(1);//等待9 E$ o1 E( e$ C8 V+ E! C7 c
- }
' p6 a+ R& P$ E% J, Z) Z9 j - if(printf_flag)
% t) d0 U9 D4 B; i - {
: D/ X+ @1 g, s7 b9 S - printf("current wdt_flag=%u!\r\n",wdt_flag);# D: l1 c3 M. d6 b$ d# C) Q
- printf_flag = 0;8 q' `# G9 x9 q+ ]
- }
/ t4 |, ]% c$ x' W1 j - if(KEY_0())
! T4 M5 R8 i7 G% s8 d- x' ` - {
' P% [+ S5 ?+ K6 N$ ~, a - wdt_flag = 0;
# H- [2 @/ N5 @. R - printf("WWDG_Refresh stop!\r\n");5 K& }) J1 j0 `: C+ [ j
- delay_us(1);//等待
" a% y! M; r8 N* x( C2 c0 c* J - printf_flag =1;7 \! y1 F8 D9 q/ I
- }' `2 h% b+ t) `5 n9 k. H
- if(KEY_1())
+ ]; m& M% t' K& w2 c' ` - {
: D2 L8 r! b( B) \& p5 o1 X7 ` - wdt_flag = 2;
% J9 V9 X: a% u+ U/ D- F - printf("WWDG_Refresh before WIN Time!\r\n");
+ M7 C$ W2 l8 X2 q7 m" q4 Q8 |/ ] - delay_us(1);//等待
4 F: B9 c+ n2 Q0 w: @ - printf_flag =1;
) r R: S8 m$ l% B& [ - }( K' W: `- }1 L
- if(KEY_2())/ P& `9 ^( u$ G9 A H" A
- {* }/ ?0 S$ r( E" ~- K0 e6 B* P% J& U
- wdt_flag = 3;+ _$ y/ V) F* O+ }
- printf("WWDG_Refresh after 0X40 Time!\r\n");- [7 B: r+ I- K% j1 o' q1 W2 i; n
- delay_us(1);//等待
+ l. \3 ^3 b7 Z, o - printf_flag =1;
( i& ~* r7 [7 k! {5 i2 H - }
4 Y$ a, u) Y% M0 { - /* USER CODE END WHILE */
复制代码
- H R3 f& e1 f4 J+ U1 q3 N; n四、编译及测试
3 o9 h5 Y; K! ~* \% x. b5 P# q4 E: E+ ` 4.1 编译$ m, V% L0 L4 Q0 A. f& o
# S, ?: F5 @' M8 ]4 ]- z
% F. M! r3 |+ w; v
: k; m6 h- o6 o6 P! O
0 i4 t9 U, \; T9 h/ \ 4.2 测试
3 P' M' K6 }% _/ c. Y8 g G, K 打开串口助手,连接上开发板,按键0、1、2观察效果:7 A/ @2 {! K* J, z8 B6 o
+ D1 V: m2 y6 E: l( O9 t( s
4 U- ~- z0 K4 p7 ?9 _* V+ E* Q
$ E8 p* o0 |$ Z: ~, T+ _( K2 S————————————————7 W) t) M8 Z( W0 I( l, I
版权声明:py_free-物联智能6 H: {2 N) \: ^0 g% B1 V
如有侵权请联系删除. ? s0 V0 \0 E. `; I/ S" ^8 a
9 X" l/ ^/ G8 {: w7 h3 T. y+ d [8 N4 Y! N" {
|