一、stm32的WWDG说明
% a, a3 F; o. ?; ]4 _4 m6 L0 Z 1.1 WWDG特点:0 V" Y" X6 D0 _: ^: o
在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗,独立看门狗和窗口看门狗。
1 B0 a; i# U0 @) {, I! H5 O, e
- f7 ~ Q, C/ m! S 相比独立看门狗,窗口看门狗WWDG有以下特点:
V! ?8 R& x; z3 g% F 【1】WWDG计数器依赖于系统时钟,准确来说依赖于系统时钟通过分频、倍频得到的PCLK1时钟,即APB1 peripheral clocks输出频率。
9 a1 b' T; P, J- ` 【2】WWDG支持中断功能以及早期唤醒中断。
! h( r0 \! F7 p' l( h/ S
+ W7 d/ W' t) O5 E8 }+ I) F 1.2 WWDG的cubeMX配置及说明
. f0 f: q' }9 c- {1 W2 q& g* N 现在看一下WWDG在CubeMX中的设置问题,WWDG和IWDG一样,在系统内核栏目开启,开启激活选项后,可以设置5项参数,分别是计数时钟分频值,窗口值、递减计数预设值、早期唤醒中断使能以及中断功能。- E8 s' W; U+ P% R4 Y! @
* F' Z( w4 U1 Z: C& K s$ L/ T9 n* Y$ ^6 x' u4 n
【1】 一旦WWDG启用,将生成系统重置功能并启动其递减计数器,喂狗时,必须在设定的上限时间和下限时间之间喂狗,如果喂狗时间高于上限时间或者低于下限时间,都会导致看门狗触发重启机制。其中下限时间是系统固定的0X40。上限时间(窗口值)是用户可以定义的,大于0X40同时小于0X7F即可。
# b; x% l& j9 ?, e: \( e L2 G, D5 }* `9 H
【2】程序在达到0x3F值之前刷新计数器(计数器的低六位值,最大值111111=0X3F),到期时触发系统重置(即,当计数器值从0x40向下滚动到0x3F时生成重置,即再次减一时,0x40=0X3F+0X01)。并在计数器达到刷新窗口值之前刷新了计数器值,也会生成MCU重置。这就要求必须在有限的窗口中刷新计数器。另外WWDG启用后,除非通过系统重置,否则无法禁用WWDG。2 a8 ^5 w4 b8 K* R; ~6 e5 ?
. M' |# c! H2 D( @3 y
【3】如果启用了早期唤醒中断(Early Wakeup Interrupt,EWI)功能,在WWDG到期之前发出警告(即计数器达到0x40时会产生中断)。使用早期唤醒中断(EWI),在生成MCU重置之前可执行特定的安全操作或数据记录。此机制要求在NVIC中启用WWDG中断设置。同样,EWI启用后,EWI中断不能被禁用,除非通过系统重置。中断状态感知可在RCC_CSR寄存器中的WWDGRST标志判断何时发生WWDG重置。3 r* Z' g. }5 u/ ?; x- A5 J
6 Q B3 I7 A: O# i3 |* v1 j
: x3 \, y0 c3 [ }- G
1.3 WWDG喂狗时间计算
! L5 a8 |# w' }0 a WWDG计数器输入时钟由PCLK1频率(APB1 peripheral clocks输出频率)除以计数时钟分频值得出。
. l/ l) @ H6 J* g9 z' Y' [) O9 I' W) f1 b: D {: @$ `0 H6 c o
3 r6 |$ a |! |, P+ u4 v/ i* R2 _- T
; d* ~4 o' b8 I G7 U6 o) a
WWDG时钟(Hz)=PCLK1/(4096*预分频器);
- P9 L: M7 v1 M* E d8 k: p8 C6 l' s
WWDG周期(mS)=1000/WWDG时钟(Hz);5 n( e4 z1 w; C6 m9 j9 l/ g
5 \' T3 k% m) Z# T& A( v% M WWDG超时MinT(mS)=WWDG周期(mS)*(计数器-窗口),因此最好设置递减计数值≥窗口值。! D( b3 }+ ^! |/ k. g6 D( L6 _
& ?1 v- t1 [8 D/ ]9 i
WWDG超时MaxT(mS)=WWDG周期(mS)*(计数器-0x40),0x40=0X3F+0X01;5 @! O9 _7 ]4 l/ H
! o! e( [3 s6 K$ E& u WWDG喂狗时间MinT(mS)<t<MaxT(mS)。
6 l+ V. [9 {3 ^1 l, r! m9 A! w. m5 R" @
二、WWDG工程创建及源码分析
" h# t0 S |* G) D B# X6 B: I 2.1 cubeMX界面配置WWDG: B4 r$ D L6 A& o% }6 i3 o
本博文基于前面独立看门狗的工程直接增加窗口看门狗功能,并关闭独立看门狗。% z. k$ v! j. P
3 _6 c8 {& i9 d% u4 j9 R o' I 双击.ioc文件打开cubeMX界面,开启WWDG,设置其参数如下,其中分频值2,窗口值0X64(100),递减计数预设值0X64(100),开启早期唤醒中断:
4 T' D" ~1 F. v7 o4 V: x
5 D8 [9 z Y, C
- c6 K2 Y3 {& a9 p I
2 t/ a0 }; A' O E5 M2 q 开启WWDG中断功能 ]. z [" P' z% i
) r* W: w3 K2 L' {; e, Y2 |
- X# g' Y; V. w: h" x
, q- l; t U3 t( {1 ?+ _
APB1时钟输出频率设置10MHz(10 000 000Hz),设置该值小一些,主要是本博文采用按键触发切换喂狗时间间隔,而按键捕获到松开是人为操作,反应较慢,实时性不好体现。. w+ M8 R# A% D
$ h# J, o. w( p/ z! F/ I
2.2 WWDG超时时间实例计算, e& g3 u% W s7 J$ f- ?
在WWDG的喂狗时间按前面计算公式,喂狗时间范围:
; r" t7 w, l. `, S2 C
4 o% x, C$ Z- g9 B3 ?0 g WWDG周期(mS)=1000ms/10MHz/(4096*8)=3.2768ms。
2 W) E7 j8 |8 @3 |! n
, J7 D/ v9 V- `: B WWDG喂狗时间是在WWDG启动后的t时间内:
$ j ^* q) _) ]2 p* z( C/ ^: ]2 C: o* B
t>3.2768ms*(0X64-0X64)=0ms;5 c# `5 N2 p7 k
4 \5 m( G+ a1 O& T3 V& t
t<3.2768ms*(0X64-0X40)=117.9648ms。
7 O5 K7 p7 z( u8 \8 A3 \! u3 J% }9 ^! t* J) U% H
1 g& w" [4 F/ k1 z0 z4 X
2.3 工程高级配置
( T$ Z; d- t, L: w! z 在工程高级配置中,取消WWDG的自动初始化,本博文将自己添加初始化,主要是HLA的WWDG初始化启动就立即进入计数,会给第一次喂狗造成时间差异较大。3 A. }4 e1 L i5 q8 j5 K) N: ?
]' t) W/ {4 E/ X( E! U ]( T
/ ~, o9 O7 L8 l. R) r- e
. A3 a: i2 k' ? 生成代码输出。
) @$ C3 x* v* z4 K J
" ?% ~1 W; s& }: W三、WWDG源码分析及应用$ |1 Y( E( H+ E t' n3 B g) E
3.1 WWDG的HLA库源码分析( G6 k" U, u, u) s) e% U5 o& B6 T# Z
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函数。
6 i# A" q% ^1 w9 a1 P+ }( r6 w8 c3 M- j3 [1 r5 q
; T" s- G% s$ E# k& Y/ Z
, r+ ~# C6 @5 }+ ^/ G1 q$ z- l 在stm32l4xx_hal_wwdt.c源文件中定义了HAL_WWDG_Init函数,它做以下事情:诊断配置参数是否合规(如果不是采用CubeMX配置,而是自己手动配置或调整过参数的,这里可能异常);调用HAL_WWDG_MspInit函数完成WWDG时钟以及中断初始化;最后将依据参数写入WWDG寄存器。
) [- }& N: n: y% w+ O$ c0 ` b- Z
; t+ s* d1 m7 x/ Y
. @& M+ ?0 C' z. R+ v f/ _) R 程序在依据参数将WWDG计数器值写入WWDG寄存器CR,将窗口值和时钟分频值写入WWDG寄存器CFR内。- y/ U% q% J( W: r# f6 ?" X
+ M" `! ~! [! \& U" a
$ Q! @+ V7 L( I6 N6 {
& e# h4 L% d+ @! J' L" L% Y/ g
! l, `' n! W4 Q, G2 N# K( }# D1 m5 Y8 l
再回到wwdg.c内,HAL_WWDG_MspInit函数实现了WWDG时钟启动设置和中断初始及启动设置。
* ^) w- p; _: z2 D: u' ?, S! c/ c1 d- z
' e F2 _& W! b3 g) A, f' G
7 @# @& a3 Y# R' |8 T HAL_NVIC_EnableIRQ启动后,如果有WWDG中断事件时,当中断服务例程触发HAL_WWDG_IRQHandler时,将自动清除标志,并执行HAL_WWDG_WakeupCallback用户回调函数报警。该回调函数在stm32l4xx_hal_wwdt.c定义,是个弱函数,用户可以通过自定义回调AL_WWDG_WakeupCallback来添加自己的代码。例如本文将在wwdg.c文件内重新定义该函数:9 \* }" I' j3 j" W R* f! h
- /* USER CODE BEGIN 1 */$ Z9 ^0 T4 p4 |6 I/ [
- void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
2 }" M; m- Y; L! W+ o; J; y - {
8 C1 ~( E0 r( d. K- x {' e) G0 ? - printf("I know, but do nothing!\r\n");( n: c9 C! h i% d6 e( O
- }# {' x! L: [3 u5 Z; t0 J: a
- /* USER CODE END 1 */
复制代码 5 }$ y& F3 N; N- z" p3 L% x' A
3.2 延时函数设计
# J( V, T4 E- n 在ICore目录下添加delay目录,并在该目录下创建delay.h和delay.c源文件,实现自定义延时函数,主要满足本博文更高精度的延时要求。3 ^# b, q: }5 c) ^- a6 f
7 a* C1 r- d9 w8 B# G; ? delay.h,声明微妙、毫秒、秒的延时函数。
" M$ G' n8 o* R G- #ifndef DELAY_DELAY_H_
: x+ {; s' A9 D- r# ^ - #define DELAY_DELAY_H_
) g6 x( ^, [8 d6 q - 5 x9 z5 O1 z. _
- #include "stm32l4xx_hal.h" //HAL库文件声明
3 j3 G. X8 N' }) h- M - + Q' t; D/ @/ Z+ Q( }; j! j
- void delay_us(uint32_t us); //延时微妙 |8 Q% \2 L6 h0 ?; j, k' X" ^
- void delay_ms(uint32_t ms); //延时毫秒
8 x- U- R: m5 ~ q - void delay_s(uint32_t s); //延时秒
& B) d2 A5 g+ D9 } X% X* Y -
- |* J7 _$ Y% s# F3 t5 q3 I9 p3 W5 z& { - #endif /* DELAY_DELAY_H_ */
复制代码 % f/ N* [( e1 N# J' l
delay.c,$ A( D( k2 X& w, I8 Y% |' p8 e# y
- #include "delay.h"
/ E4 c# J0 D- V; N6 y -
$ {8 {3 q. u8 P9 j - #define MFP_VAL 8000000 //80MHz- ?( t( r9 k- I& R" y* g) _* r
-
. H, D- P2 F- A$ S - void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数& c0 v1 s. s' K* J5 ?
- {
: G/ d' ?8 ]0 X. [- t - uint32_t delay = (HAL_RCC_GetHCLKFreq() / MFP_VAL* us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值,经算法得到1微秒的循环次数/ A0 |9 _% F* ]; W& Q+ j- O
- while (delay--); //循环delay次,达到1微秒延时6 @+ h/ }) J! p- {
- }) {, ~* y3 k: P
-
9 S: _& p3 n) D- n% L* m1 \ - void delay_ms(uint32_t ms){ //mS毫秒级延时程序/ A5 H* z" ?& ]. }
- while( ms-- != 0){# ]2 r2 v, p. i# F
- delay_us(1000); //调用1000微秒的延时
7 U# s% h7 x3 H0 m! w - }% V4 S! p7 G" P4 _
- }* [& D6 f& L }2 a% ?, G
-
# A% R" U$ h4 r5 T - void delay_s(uint32_t s){ //S秒级延时程序+ `* i/ E9 ^5 S* h
- while( s-- != 0){4 O7 L2 d0 N/ T7 k _- u* ^3 s
- delay_ms(1000); //调用1000毫秒的延时
' Y( l5 }$ b3 \4 `6 [1 `' v - }
d8 C( }( i4 x. H- c - }
复制代码
$ U; U. Z! U# ~9 u2 Y 3.3 WWDG使用程序设计+ \! Y3 w% V/ d5 s
在本博文中,我们设计通过按键切换喂狗时间间隔,来测试喂狗窗口及时间间隔效果。1 U* [: f V7 I; Y
" I L/ z. j! ]+ S( }
1 \9 Y, ~0 l- x# N3 j8 }# J! l 在main.c源文件中,添加驱动头文件,注意由于在CubeMX取消了自动初始化WWDG,因此需要手动添加wwdg.h头文件:
2 N4 g% Y: _$ t# O0 |% }- /* Private includes ----------------------------------------------------------*/
% J1 l& ]! ^% f1 \ - /* USER CODE BEGIN Includes */: m, f+ p+ I% v$ @# R0 H
- #include "wwdg.h"* K9 r |* ?; d4 D8 @' \
- #include "../../ICore/key/key.h"( |& B% A* B0 T( B8 g; r) s
- #include "../../ICore/led/led.h"
1 F$ S- c6 _3 ~ - #include "../../ICore/print/print.h"" f0 ]/ F: c# _
- #include "../../ICore/usart/usart.h"' I% D& n4 ?5 Z! s m
- #include "../../ICore/delay/delay.h"
( S0 H+ z, N0 R. G - /* USER CODE END Includes */
复制代码 * Q+ P3 Y( F+ W% z& V9 {
在main主函数前,声明WWDG句柄,用于喂狗函数是调用8 u5 ]% v G8 u {; l g
- /* Private user code ---------------------------------------------------------*/
. A i7 ~/ K2 Q5 e0 c" N4 M2 ? - /* USER CODE BEGIN 0 */. m9 d& h. y+ `* c1 L
- extern WWDG_HandleTypeDef hwwdg;9 Q/ V: ? E, P7 k1 N! m+ C* b
- /* USER CODE END 0 */
复制代码 3 Y3 m; K, H; a6 r$ u8 V8 E
在main主函数内,设置各外设功能及手动初始化WWDG驱动。
- z V8 {: |; S# s- /* Initialize all configured peripherals */ r* F5 M2 \, K
- MX_GPIO_Init();8 ~- Y! w4 W& z: i3 p, y! A. I
- MX_LPUART1_UART_Init();
5 o |2 S. D2 D$ ] - /* USER CODE BEGIN 2 */
0 ~ A0 d6 F4 x- w) V - ResetPrintInit(&hlpuart1);0 N# N% s; D ]# ]: E* E
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断# r4 ]; v, \1 i; `( U$ _3 q
- HLPUSART_RX_STA = 0;
% w3 C3 A8 w* ?; o2 S - //* A6 j, k" E c
- printf("app restart now!\r\n");: w P3 J# m4 f* N+ @- E
- uint8_t wdt_flag = 1;+ }8 e& f9 n" O
- uint8_t printf_flag = 1;
2 R1 L0 d$ A# A; v ^ - MX_WWDG_Init();
- c s, d! ]! C/ o4 ] - /* USER CODE END 2 */
复制代码
4 L0 a% E% ?. @8 c9 r% n; ?6 Y. _ 在main函数主循环体内,进行喂狗测试,默认是10微妙喂狗一次,按键0停止喂狗,按键1间隔10毫秒喂狗,按键2间隔1秒喂狗。注意前面计算得知0<t<117.9648ms内喂狗有效。按键1间隔10毫秒喂狗主要考虑到按键按下及松开时间效应问题,因此不能设置太大,否则可能因为按键动作耽搁时间太久无效。在实际项目中,我们通常会采用独立线程持续喂狗,各种异常触发打断喂狗来设计。+ z5 A6 r0 G1 H' p
- /* Infinite loop */
! Y" q, ]' d- j4 g) ~# R - /* USER CODE BEGIN WHILE */% K: y+ D1 q' t
- while (1)
+ e& K5 T6 m6 |$ T1 F: L - {: I( Y" [7 G' R8 ?: J N: V
- if(1==wdt_flag){( T1 d4 \% y3 x' b; p2 f8 | Y, k
- HAL_WWDG_Refresh(&hwwdg);
0 O0 h x2 D# n. I - delay_us(10);//等待
7 Y+ w8 d( N7 y( V5 x - }* O' h2 X4 X; l6 b4 \( ~6 Q- B
- if(2==wdt_flag){
& s2 @# V+ x7 y - HAL_WWDG_Refresh(&hwwdg);0 [. z/ H% P q* C* q3 M5 ?
- delay_ms(10);//等待
. O4 J8 a6 \. x6 R, { - }- E% n% M" \3 _ W9 c
- if(3==wdt_flag){" G0 r7 _2 [9 O
- HAL_WWDG_Refresh(&hwwdg); P0 U4 q+ ]. J' A" v. @
- delay_s(1);//等待6 I7 n" d7 K1 F9 v3 `; D1 _
- }
4 e i2 n! Z) Y, O2 `" k - if(printf_flag)4 g) a" ]4 w0 x( v' N
- { M" z( Y6 z2 M. v
- printf("current wdt_flag=%u!\r\n",wdt_flag);
$ G$ ~8 H0 g$ h/ O - printf_flag = 0;. p0 r7 E5 i2 @( w2 `0 a5 u3 H& k
- }* F4 E0 s3 P/ d& E6 z
- if(KEY_0()); t% u$ m+ O9 f' n
- {+ H# z3 M$ k' C/ e; o' ^; V m
- wdt_flag = 0;
5 Y1 y6 v+ ]7 j# z - printf("WWDG_Refresh stop!\r\n");
( L& I' X0 d; Z" B- U& a - delay_us(1);//等待
2 [* X6 o/ m4 o0 D, i - printf_flag =1;0 b* d5 B& S5 i( [# ^: J* \/ v+ y$ T
- }' L$ m1 |+ W$ D# {: m1 }" r
- if(KEY_1())+ }& [/ v+ r) I. e, o" y; p
- {
. ~% J) x8 W, U; v! K6 X9 n - wdt_flag = 2;# [$ K4 X7 k9 {' e: G$ }
- printf("WWDG_Refresh before WIN Time!\r\n");. x! Q; n& j; M! o. l
- delay_us(1);//等待
. {1 H6 V; V* C7 ?7 ]+ h7 ` - printf_flag =1;$ ~' V1 C. B/ [3 d7 ]
- } k2 B8 c2 g7 T4 A3 A+ n' {
- if(KEY_2())
6 n# D P* V6 @5 v* X% B; z0 A - {
4 x% R0 [$ X4 @* v+ K& C* K - wdt_flag = 3;
% o. k$ y9 Q* ~! q$ A; P1 K w - printf("WWDG_Refresh after 0X40 Time!\r\n");
1 ?( x' E3 S5 M1 c& h% U - delay_us(1);//等待) p# C) u" X0 Q) A+ ~( S
- printf_flag =1;8 P0 q K+ I' q3 _$ o8 r
- }
, i- m' _9 S7 N: E4 e6 I5 q - /* USER CODE END WHILE */
复制代码
" O' _7 V# A2 F' u" G/ |" g; h四、编译及测试$ f2 `/ _* J1 x; l
4.1 编译' I5 y4 ~5 J$ y! P, a; y, D0 Z$ C
; p0 Z& x! a8 ]
, e! H% J% B! f9 h
$ e. U1 z8 T5 W4 V- |6 s
7 ]0 Y$ r0 `/ u2 j! P 4.2 测试+ i& F, S1 _0 u: N0 s, [
打开串口助手,连接上开发板,按键0、1、2观察效果:
: M. v. h( g# z$ Y' S0 z/ p! }0 ]
9 e" e# K$ G, `( K
2 l; f" H4 d: M8 z9 T- k, `( c2 ]0 _4 i1 L8 Y+ L6 ~4 b" D6 K5 z W
————————————————1 M' l& W2 f, D! {- Z
版权声明:py_free-物联智能
" C' n I& ^* J2 P. A8 N如有侵权请联系删除/ F/ J. t/ ^0 M4 t
' }$ @! h, N5 l8 j' `' S& w5 w6 ~0 G& s
|