一、stm32的WWDG说明
3 q% x# h7 r% ^2 f+ r: R4 B% y* } 1.1 WWDG特点:( Z/ [% ?. U" c" R. ]$ V: a
在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗,独立看门狗和窗口看门狗。
. c7 s d+ B3 g& d4 s" K3 j4 F$ t+ L% B
! D( {9 G- O3 ~% I 相比独立看门狗,窗口看门狗WWDG有以下特点:0 w F- p7 j3 B. V( o
【1】WWDG计数器依赖于系统时钟,准确来说依赖于系统时钟通过分频、倍频得到的PCLK1时钟,即APB1 peripheral clocks输出频率。
1 D. b- ?! W0 f( l 【2】WWDG支持中断功能以及早期唤醒中断。+ T4 l0 v! `" H1 T
2 J" ~1 @3 S+ Y7 |* s8 E% @ 1.2 WWDG的cubeMX配置及说明
8 Y6 ]# B# A% i: H 现在看一下WWDG在CubeMX中的设置问题,WWDG和IWDG一样,在系统内核栏目开启,开启激活选项后,可以设置5项参数,分别是计数时钟分频值,窗口值、递减计数预设值、早期唤醒中断使能以及中断功能。
H/ T1 T/ b2 x! y1 p7 [1 ?
$ M" p+ u& P' c/ V$ L6 F2 X
% P; I: I! ~$ P; W 【1】 一旦WWDG启用,将生成系统重置功能并启动其递减计数器,喂狗时,必须在设定的上限时间和下限时间之间喂狗,如果喂狗时间高于上限时间或者低于下限时间,都会导致看门狗触发重启机制。其中下限时间是系统固定的0X40。上限时间(窗口值)是用户可以定义的,大于0X40同时小于0X7F即可。
! v) r5 P* t( B% ?' b$ i8 \
9 M/ ]& e- _+ H ^6 [1 A 【2】程序在达到0x3F值之前刷新计数器(计数器的低六位值,最大值111111=0X3F),到期时触发系统重置(即,当计数器值从0x40向下滚动到0x3F时生成重置,即再次减一时,0x40=0X3F+0X01)。并在计数器达到刷新窗口值之前刷新了计数器值,也会生成MCU重置。这就要求必须在有限的窗口中刷新计数器。另外WWDG启用后,除非通过系统重置,否则无法禁用WWDG。6 }; Q+ }, I$ s1 M4 c
8 x& K9 v, G4 h: U. y
【3】如果启用了早期唤醒中断(Early Wakeup Interrupt,EWI)功能,在WWDG到期之前发出警告(即计数器达到0x40时会产生中断)。使用早期唤醒中断(EWI),在生成MCU重置之前可执行特定的安全操作或数据记录。此机制要求在NVIC中启用WWDG中断设置。同样,EWI启用后,EWI中断不能被禁用,除非通过系统重置。中断状态感知可在RCC_CSR寄存器中的WWDGRST标志判断何时发生WWDG重置。
! f- h1 W: S/ P0 h) s8 Q# ^) c' O9 V5 l! x, m9 ?
* H3 ]' r1 v, Z+ I9 n
1.3 WWDG喂狗时间计算
4 A4 x+ x& k, p( i WWDG计数器输入时钟由PCLK1频率(APB1 peripheral clocks输出频率)除以计数时钟分频值得出。
1 w. Q h% O9 Z( ?# l
( I2 k) W1 L/ R7 n; k8 g
1 O, t- B, O# L1 R2 d. L
6 u @# u7 i# f7 v U
WWDG时钟(Hz)=PCLK1/(4096*预分频器);9 ]7 E$ a! {7 w0 N" a2 D
& c9 b: c1 a% q; |
WWDG周期(mS)=1000/WWDG时钟(Hz);
$ l" j2 y' D: ?! L) z1 u
6 a6 v9 ^- [ M WWDG超时MinT(mS)=WWDG周期(mS)*(计数器-窗口),因此最好设置递减计数值≥窗口值。& C4 v! `- M* {6 {% T. v6 W' P2 o) x+ U
* _! j' Q# _2 L2 Y* q3 a
WWDG超时MaxT(mS)=WWDG周期(mS)*(计数器-0x40),0x40=0X3F+0X01;9 b) v2 Y' n) X7 ~
2 Z" x- K" ], i5 _0 u
WWDG喂狗时间MinT(mS)<t<MaxT(mS)。3 o4 ]9 }* B; r V% T6 \/ }' E- U
# }2 u p# ?9 O* t- m& h- Q# L二、WWDG工程创建及源码分析
- e7 C; i) B/ v" l/ P$ \ 2.1 cubeMX界面配置WWDG/ H3 I; s- o2 ]8 V9 b
本博文基于前面独立看门狗的工程直接增加窗口看门狗功能,并关闭独立看门狗。, ~- M% ~) i# B
( x! y1 i6 y. p: p# G
双击.ioc文件打开cubeMX界面,开启WWDG,设置其参数如下,其中分频值2,窗口值0X64(100),递减计数预设值0X64(100),开启早期唤醒中断:0 E: ]4 v4 Y& K2 T# o4 ~* n
* s2 V/ _5 C! {& y9 t% h. P: I
5 U) e, {+ h/ J% y9 B) a4 ]7 G5 p* J! \
开启WWDG中断功能6 P6 ^! I" R' ]. A3 h
- h3 V. s+ O6 F
# i3 |8 k! G" O* K5 N
/ n2 C8 c! U, e) R6 z APB1时钟输出频率设置10MHz(10 000 000Hz),设置该值小一些,主要是本博文采用按键触发切换喂狗时间间隔,而按键捕获到松开是人为操作,反应较慢,实时性不好体现。
4 D+ q( K* \: {0 k
' E7 y& x8 }) a 2.2 WWDG超时时间实例计算0 p# d a4 Z h: K0 q
在WWDG的喂狗时间按前面计算公式,喂狗时间范围:
9 Y8 }0 r+ d% }: \! x. p- E0 O4 l1 b5 l1 u
WWDG周期(mS)=1000ms/10MHz/(4096*8)=3.2768ms。' ]3 N) x# {3 o- K1 U9 l# o
6 D# i+ ?1 f2 D$ g( x/ ~) S: r WWDG喂狗时间是在WWDG启动后的t时间内:" D& j! b, e/ j! b: ?- v$ n
( a+ P: O2 L7 i: w Q! ~& {
t>3.2768ms*(0X64-0X64)=0ms;
e) v0 T: ?4 a3 \. _$ b+ Q
3 d! Q# J8 }0 X t<3.2768ms*(0X64-0X40)=117.9648ms。
. c1 a' F- ^. Q4 O* S3 U y) O8 u& m7 I' q
& H6 [2 r" [+ V% t8 M( q9 m' y5 a 2.3 工程高级配置
N* x8 r7 ?. J4 t1 i- Y7 z$ Y+ Y 在工程高级配置中,取消WWDG的自动初始化,本博文将自己添加初始化,主要是HLA的WWDG初始化启动就立即进入计数,会给第一次喂狗造成时间差异较大。
( {0 v& L, E: I6 ]% b l1 C
) k8 X' B6 C% Z% a
! L6 o" J0 g; B u
3 V9 i" E) u8 c& y( [6 {0 k 生成代码输出。
, a+ h: C' P7 {7 o8 x) ?& Q* v0 q4 m. k s
三、WWDG源码分析及应用
7 D4 u8 J. M: t$ c' @7 U 3.1 WWDG的HLA库源码分析+ I5 R9 X1 u% D) X% i2 O- X
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函数。8 u& t' h: ?( G% _ ^
6 e" K( l9 P8 P' z; T1 S8 F$ Z
5 ^. d/ G# \7 Y4 z5 [8 n% I8 Q0 `. [; j) g
在stm32l4xx_hal_wwdt.c源文件中定义了HAL_WWDG_Init函数,它做以下事情:诊断配置参数是否合规(如果不是采用CubeMX配置,而是自己手动配置或调整过参数的,这里可能异常);调用HAL_WWDG_MspInit函数完成WWDG时钟以及中断初始化;最后将依据参数写入WWDG寄存器。3 I' ]! ?/ {9 N+ H
7 d! ~9 k6 I, m; V% w$ [ K0 y5 n
$ k4 }: q1 d6 C1 H& O0 T$ P5 b$ c3 o$ y( Z) k+ c
程序在依据参数将WWDG计数器值写入WWDG寄存器CR,将窗口值和时钟分频值写入WWDG寄存器CFR内。
" C, D2 u; T: W9 X h- {! K' v8 Y4 ^6 R
. i2 b; W/ X! ~8 X5 D7 _0 W3 T" x- ~- R
& o. v/ M' I; R: D
' H; I7 \8 S7 a% g 再回到wwdg.c内,HAL_WWDG_MspInit函数实现了WWDG时钟启动设置和中断初始及启动设置。# @, U2 T6 W6 k4 n" @' H0 c
2 R4 a7 \ F- J) B
$ ~/ Y: P4 ?% A$ _: D5 X6 A# l0 M1 P+ y7 `+ T% k& W! G7 z5 m
HAL_NVIC_EnableIRQ启动后,如果有WWDG中断事件时,当中断服务例程触发HAL_WWDG_IRQHandler时,将自动清除标志,并执行HAL_WWDG_WakeupCallback用户回调函数报警。该回调函数在stm32l4xx_hal_wwdt.c定义,是个弱函数,用户可以通过自定义回调AL_WWDG_WakeupCallback来添加自己的代码。例如本文将在wwdg.c文件内重新定义该函数:
; w" y1 s5 Y# \$ A& w/ I- /* USER CODE BEGIN 1 */
( P, w! f, Z: C9 a - void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
/ E" ~/ d7 g: I - {% F9 \ ` ]$ G
- printf("I know, but do nothing!\r\n");: j$ Z6 Y& @7 z5 k9 h- J3 T
- }
& d# ~, V- L# H- Y2 U - /* USER CODE END 1 */
复制代码
" a2 e9 N/ F7 _/ R. K" `+ F 3.2 延时函数设计9 d* s. q( P$ O, Q/ C. z
在ICore目录下添加delay目录,并在该目录下创建delay.h和delay.c源文件,实现自定义延时函数,主要满足本博文更高精度的延时要求。
4 F; _; u. A/ R9 G, Z
, M0 h; }2 P* L9 @: b5 n, m) x delay.h,声明微妙、毫秒、秒的延时函数。
. a0 b- A7 j' V4 ~- #ifndef DELAY_DELAY_H_6 r% P8 _" L9 Z. y+ @# @6 ?
- #define DELAY_DELAY_H_; h) J; E, D" s, r3 w
-
6 U, p2 F' B( [1 R6 M# X& V - #include "stm32l4xx_hal.h" //HAL库文件声明) {7 p6 l$ H/ K# p2 P1 S# u6 d& a2 u
- 9 B& g( H8 F* V; q9 T+ t. _$ P& l8 u
- void delay_us(uint32_t us); //延时微妙
" R* \! }2 I2 a) |) g- l/ L0 H" ? Y/ ? - void delay_ms(uint32_t ms); //延时毫秒
+ j- ?/ b" x# |2 i8 A f& ? - void delay_s(uint32_t s); //延时秒
' f. N( [3 D% I C0 s' g -
( L1 f7 l9 W2 D! M- @+ n, p6 h$ [ - #endif /* DELAY_DELAY_H_ */
复制代码 ' B- j e' Q4 |& }0 W" A% ~( [
delay.c,
7 c3 [. A6 k; s5 a8 n J- #include "delay.h" w5 D3 u9 V- s
- ; P, ]' y! {2 d8 t4 H8 h: Z5 b9 S
- #define MFP_VAL 8000000 //80MHz
) `: j# W% V- g - ; J6 E& U) A* a* v' P# O- z
- void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数7 t% ], |! D5 S( M) F4 H
- {; r) f' C. n$ G q0 ^" J
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / MFP_VAL* us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数
9 f8 s6 j5 }8 r3 b2 e, _# R - while (delay--); //循环delay次,达到1微秒延时
2 E. ^1 w6 }) ?7 f: D8 t J1 o# [9 A - }5 i/ m: b2 {2 A3 d8 U. y$ u
-
: U J* r, g) F1 g0 u6 Y W3 @' e% v! c - void delay_ms(uint32_t ms){ //mS毫秒级延时程序- ~+ x5 ^+ e* u+ _0 b" l- p a
- while( ms-- != 0){. p `0 o( O, H* m
- delay_us(1000); //调用1000微秒的延时% r# y7 `/ h8 ~: g2 z1 ]
- }
7 m& _3 Z ]; S( P) G7 ~- H. { - }+ S P( s% b" N# w, D
- 8 ~5 }( N" U+ E# x# z- g
- void delay_s(uint32_t s){ //S秒级延时程序
, p# a& v; K2 b1 Q) s c5 d - while( s-- != 0){% _( ?+ _0 W/ X# R( _6 t
- delay_ms(1000); //调用1000毫秒的延时! y4 c; w$ U% w, t! C. D* l$ I
- }" d/ Z0 ?2 K% A6 T7 n9 H- O
- }
复制代码
* g$ ]( U% }2 L! F: Q 3.3 WWDG使用程序设计
! j% Q* y9 B/ r6 | 在本博文中,我们设计通过按键切换喂狗时间间隔,来测试喂狗窗口及时间间隔效果。# ^3 E8 a0 C, _: r' R
& B2 J* T Z2 F- X0 `' ~
/ E; e9 Z$ \, Z 在main.c源文件中,添加驱动头文件,注意由于在CubeMX取消了自动初始化WWDG,因此需要手动添加wwdg.h头文件:
0 ^8 r6 x% Y0 ^! Z- g% d- /* Private includes ----------------------------------------------------------*/
4 o7 g1 k5 w j1 V, N8 C. K8 u2 T - /* USER CODE BEGIN Includes */
3 ~% v9 {) ^ v - #include "wwdg.h"
1 h" o/ Q( E6 x4 n) V0 M - #include "../../ICore/key/key.h"$ E$ }/ d: i* O! F; d, e3 @/ l
- #include "../../ICore/led/led.h"6 X" w2 g3 ^# t1 e
- #include "../../ICore/print/print.h"9 q& j! _ T3 a* _
- #include "../../ICore/usart/usart.h"" y: \( r& V+ k; K" R) }. l; T
- #include "../../ICore/delay/delay.h"
% W5 b d3 `, d* V! b - /* USER CODE END Includes */
复制代码
! x# S/ d9 l$ \# {8 g9 U9 u* D) n8 p 在main主函数前,声明WWDG句柄,用于喂狗函数是调用7 _; N3 o% X. n5 O; O, \! ~
- /* Private user code ---------------------------------------------------------*/
) e# y' b, _+ Q+ S& u) H+ J/ D; u - /* USER CODE BEGIN 0 */0 j5 X1 w( [; U! j8 c" g
- extern WWDG_HandleTypeDef hwwdg;
. m" E/ P2 \' F3 _" i& t- s/ o - /* USER CODE END 0 */
复制代码
4 ^4 S+ K; s0 d, ]2 N3 w 在main主函数内,设置各外设功能及手动初始化WWDG驱动。" ~/ E& u; f% }2 |) s
- /* Initialize all configured peripherals */. d7 p0 f8 Q T
- MX_GPIO_Init();
& ~( o8 l: w) _0 o7 ` - MX_LPUART1_UART_Init();, r) W; _2 ?( X# v+ x
- /* USER CODE BEGIN 2 */
3 N4 F3 K2 x8 v+ T5 l - ResetPrintInit(&hlpuart1);. u+ z9 [( A+ _& a( U( @# X" Z5 e
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
8 z4 j0 E, q# W - HLPUSART_RX_STA = 0;! N- O6 T b1 b6 F2 r1 n8 h
- //* L7 W/ @1 _8 {; {. X& Y9 G
- printf("app restart now!\r\n");
; e4 @9 x0 w1 {( I8 i4 H - uint8_t wdt_flag = 1;
+ u8 i/ ?! J% ?, m - uint8_t printf_flag = 1;
; T2 D; J, t" A! Z2 G- ~/ E - MX_WWDG_Init();# q/ n5 S, V; ? `
- /* USER CODE END 2 */
复制代码
+ B, Q. Z# G+ N2 H- J+ i" L2 W0 z% b 在main函数主循环体内,进行喂狗测试,默认是10微妙喂狗一次,按键0停止喂狗,按键1间隔10毫秒喂狗,按键2间隔1秒喂狗。注意前面计算得知0<t<117.9648ms内喂狗有效。按键1间隔10毫秒喂狗主要考虑到按键按下及松开时间效应问题,因此不能设置太大,否则可能因为按键动作耽搁时间太久无效。在实际项目中,我们通常会采用独立线程持续喂狗,各种异常触发打断喂狗来设计。: S7 |! |) m0 l$ n# ^* M8 z
- /* Infinite loop */ \, V, p( T% K# Y7 H( t
- /* USER CODE BEGIN WHILE */
/ V7 P4 `# U& j - while (1)
# `0 x* E, q2 G, k; p# T( s4 T - {8 ]: u1 W6 F4 B$ h, S/ e; B
- if(1==wdt_flag){" O) Y' i# _( K3 s+ S
- HAL_WWDG_Refresh(&hwwdg);! b! U1 t2 |6 x: |' E
- delay_us(10);//等待
; w. w" W) Y$ ?6 }- E) ?3 ~. S - }' s7 }8 C2 g8 h% N' y
- if(2==wdt_flag){0 q1 F G$ i$ |+ D
- HAL_WWDG_Refresh(&hwwdg);
u+ X, o% O/ s5 E, z/ {" @3 d% g - delay_ms(10);//等待" K2 i7 B5 f' Y
- }* A; T+ E* O0 K$ p# ^6 A2 F
- if(3==wdt_flag){
+ U. Z+ u" c \4 H" W6 R- y* ] - HAL_WWDG_Refresh(&hwwdg);
" K/ L- G# o+ J, M3 ] - delay_s(1);//等待6 ]& [7 n% x- i/ w2 k
- }
. l% ]1 P, V8 V7 v3 J. C. x3 v# c, e" x - if(printf_flag)6 U# N( v# }1 r. o, g0 d* g% ?
- { X* k: z2 n6 o) G- Q p# @
- printf("current wdt_flag=%u!\r\n",wdt_flag);
. _" v1 u+ G8 @# D- z7 M - printf_flag = 0;
o$ y+ U; h; S" f - }" ?1 X, L& s, V; q
- if(KEY_0())* J$ Z. K6 @7 W( _+ _
- {
" p5 e3 x# L [6 C, d - wdt_flag = 0;
: u ?7 ]( ]' ]) @* o - printf("WWDG_Refresh stop!\r\n");
* j1 b0 F9 F6 E8 M+ t \ - delay_us(1);//等待' z$ j+ F( W; P0 U% S
- printf_flag =1;0 q: J3 [$ w$ k; B i& Z, t( U
- }" q6 c0 s: ?: M) H
- if(KEY_1())
K# u- F+ J, n2 E" s - {
+ @3 J: M( O! K' p+ J - wdt_flag = 2;
8 P: A+ i( i* b# ^2 ]& H ` - printf("WWDG_Refresh before WIN Time!\r\n");
7 }9 r- _) `9 l* [ K1 C - delay_us(1);//等待
& P s0 \5 M7 P( O9 R4 U% _( z - printf_flag =1;: g% R5 v2 h+ V+ ]& a- b3 P
- }+ U2 O# Q: o: C$ J5 v; X( o
- if(KEY_2())
' r4 K- ?* \& j$ B X. e: G - {" n* ]. e2 i, t; `5 @
- wdt_flag = 3;' @; ]6 i, ~; g- z8 C8 U# D1 K
- printf("WWDG_Refresh after 0X40 Time!\r\n");
[8 k. S( z, B L - delay_us(1);//等待, r/ y$ _& g' ]5 Z! H) |0 \
- printf_flag =1;( d' k3 N. o- I% O% B8 P0 w
- }
% l i5 y: c) N, s. [ h( ] - /* USER CODE END WHILE */
复制代码
+ w/ i7 R0 a( T1 c$ z$ l3 J四、编译及测试" h) [2 y# f- u% J' L
4.1 编译
5 H) X# M( T( Y/ P0 g% D) \, ^( x: g W
6 U& i1 s R0 [, e. g( A
: T5 g3 B% @( E; D0 a9 @7 F$ q2 V; t k/ W: f4 i+ [: N
4.2 测试
Y/ Y, K( g: E7 s6 F6 ~" U 打开串口助手,连接上开发板,按键0、1、2观察效果:5 S7 Z2 I" m( ~% s# t# E
Y: V7 J( o5 u& ]* I/ L5 a @5 Z
/ J6 v& ]) S l# ?) J' M# l9 |, D
( Y R& N4 w+ u1 \* T————————————————$ o7 _& _6 G6 G" b& j
版权声明:py_free-物联智能 z) _7 R9 a( F
如有侵权请联系删除
$ [6 e8 Z" t: b% w
' k. j7 q+ g& ]5 k7 h+ ]% e4 \4 K
& n$ g$ \/ J. G/ J |