一、STM32 的IWDG简介( Z$ f: G3 T5 e1 U
2.1 看门狗原理, q) I- [: b3 s- l0 A* A
看门狗本质上就是一种计数器,和我们现实生活中一炷香现象、沙漏现象等是同理的,计数器一般有两种做法,一种是递增,超过固定阀值报警;一种是递减,通常值降到0时报警。后面一种比较贴切生活场景,因此较常用,STM32的独立看门狗(Independent Watchdog,IWDG)就是采用后面这一种做法。
+ N8 W1 g8 n8 i# V6 @) X
+ [3 I6 B1 ?" S4 d$ e4 a( a 如下图所示,假设我们设定一个初始值RL,设定一个计数变量V在开始时等于RL,然后每经过一个时钟周期就减1,如果减到为0值时,触发报警。如果在V值降为0前,重新设定一下V值等于RL,然后V值又重新计算,从而又争取到一段时间,定期去反复设定V值(喂狗),保持V值不降为0,就能一直保持正常态势。8 S5 i* B3 l- g
# s0 s" s. W8 Y. K( X- \
6 i; D6 q- o" C& Q: S, M6 E+ j5 ?$ r
- N+ J8 P/ t- J: [( q! H. W STM32中的看门狗有两种,一个是独立看门狗(Independent Watchdog,IWDG),就是本博文要分析学习的,另一种是窗口看门狗wwdg(window Watchdog,WWDG)。
6 h. R U/ L- p# N6 a, {/ O o2 `( b. G
p8 S( p( `6 a9 Q6 L/ j7 V
1.2 STM32独立看门狗时钟计算- p& g/ }" W& N; c8 r/ L
Iwdg由独立时钟(内部低速时钟LSI)计时,所以不受系统硬件影响的系统故障探测器。主要用于监视硬件错误。而wwdg由系统时钟计时,如果系统时钟不走了,它也就失去作用了,主要用于监视软件错误。1 }9 F* o) z' F0 x# M4 W9 R: h
" f$ ]7 z4 C7 z. o1 m0 s6 ~7 `' I2 R3 B
前面提到看门狗的计数是每经过一个时钟周期减1,那么时钟周期CT是如何获得计算呢。STM32内,IWDG依赖于内部低速时钟LSI,采用该时钟的频率来计时。频率是单位时间内完成周期性变化的次数,是描述周期运动频繁程度的量,常用符号f或ν表示,单位为秒分之一。为了纪念德国物理学家赫兹的贡献,人们把频率的单位命名为赫兹,简称“赫”,符号为Hz。2 X! {7 C' R1 B
6 h- }% ^/ P8 k* Z: D4 h 而时钟周期CT和频率成反比,即f=1/CT(其中f为频率,CT为时钟周期)。
' o0 M! b6 B0 @( I o; k2 n* N* G+ E% e3 V6 S
在STM32CubeMX上,我们开启IWDT后,用户可以设置的数值依次是计数时钟分频值,窗口值及重装载值,其中计数时钟分频值取值是固定的枚举值(2<n<8),即4、8、16、32、64、128、256,而窗口值及重装载值的设置范围12bit的数据,即0X000~0XFFF,如下图所示:) h9 u+ k1 e) ~7 m
% w+ h$ I! T0 y. h$ {; Y9 {
6 n5 i( [5 f1 r
- c- |: ^9 d; F' h7 U. a 而IWDG的最终输出时钟频率是直接依赖于LSI,MCU芯片的内部低速时钟是多少,它就是多少。0 u, r V# l0 D. y2 b, O& P
% \ @2 V3 r2 T+ s) M. z7 P9 Q
% M6 ?0 s( a( b% ]% g7 b$ M ?$ j7 j" ?) G5 E
那么,看门狗时钟频率=LSI(内部低速时钟)的频率F/计数时钟分频值P;2 c! [ b3 S1 H( m* H, X A
C; A$ @ B9 v& v J B
如上述两图设置的数值,RL=4095,F=32KHz,P=32,那么时钟周期:; n4 N3 K% c6 N. V
# u; l/ U$ A/ p2 c# a. m ct = 1s/f32KHz/32=1000ms/32000HZ/32=1ms,也就大概一个时钟周期是1毫秒,喂狗要求在4095*1ms以内。
& Y6 I2 F7 i! V" L9 c* ?
7 M- ?' [5 W+ C5 J* t" _: D3 ~
+ n+ f2 ~ S( H; A j3 x 1.3 STM32的IWDT使用注意
6 M; O5 ^; e- E5 c- t 需要注意的是,看门狗的时钟计算不是精确的,所以在喂狗的时候,最好不要太晚了,否则,有可能发生看门狗复位。另外应该还注意到窗口值的存在,通常窗口值和重装载值一致的,如果Window参数(WV)与Window寄存器值相同,只需重新加载计数器值即可退出,具有精确时基函数。否则修改窗口寄存器。这将自动重新加载看门狗计数器:9 b8 J8 [; }% b; G
) J( q/ ]/ \6 G S2 z+ M+ s9 B 窗口计数值WV决定什么时候喂狗。狗喂早了,复位就“早”,即在重装载值(V)>窗口值(WV),也就是计数器值还没有减到窗口值以下,当 0x0 < 计数器值(V) < 窗口值(WV) 时,这时候最适合喂狗了,也只有在这时候喂狗才合适。$ B8 w/ S4 I# ^% W. b
+ |8 T1 k% S* d2 }( w 另外IWDG可以通过软件或硬件启动(可通过选项字节配置),由于是低速内部时钟(LSI)计时,因此即使主时钟发生故障,IWDG仍保持活动状态。并IWDG是在VDD电压域中实现,在STOP(停止)和STANDBY(待机)模式下仍可正常工作(IWDG重置可将CPU从STANDBY唤醒)。RCC控制状态寄存器中的IWDGRST标志可用于通知何时发生IWDG重置。
8 T( G7 x0 `& I: Q! V4 y# N% T* D9 [: O2 J) A* q8 o
( z4 l; l& O! {' Z' \9 z
二、IWDG工程创建
% G* I. J2 ^ r; u# f, a 2.1 创建工程及配置IWDG$ J, l5 a! P( J: t1 {: _
本文采用的是STM32L496VGT6-ali开发板,是基于前面工程的.ioc在CubeIDE开发平台创建了新工程,并移植了前面工程已经实现的按键、LED灯及lpuart1串口调试功能,可参考前面博文:$ Y7 D6 S2 L. ?$ D1 y5 [1 y2 G
/ Y/ v& M2 K" d) i# y+ o5 e
假设已经完成了按键、LED灯及lpuart1串口调试功能后,双击.ioc文件打开cubeMX界面,配置IWDG,如下图所示,开启IWDG并设置参数32、4095,另外IWDT时钟频率是32KHz。
; U( f) I) ^; I1 c/ j7 S) h' O/ O! c3 h B( d" [
5 r4 l) w- j$ y! T1 P a
! t/ p5 _8 q6 w+ Y* l- z
7 C5 b) R3 J' W7 w8 @$ H
' {, \% ]4 l3 Y, L9 ^ 配置很简单,完成配置后,单击保存或生成代码按钮,输出生成代码。
8 X4 L+ N4 ?5 m7 E0 T: X9 K1 j. v5 \2 O: w
2.2 IWDG的HAL实现源码分析
+ x% O* u: ]" ^3 M cubeMX会为IWDG在Core/Inc和Core/Src生成独立看门狗的头文件及源文件,实现源码也很简单,定义独立看门狗句柄(IWDG寄存器)及初始化函数MX_IWDG_Init。( }6 b R/ s+ X2 w3 d e m
5 B9 F0 {' T5 e
* Y3 Y( n2 K: a. ?( v" H! j. V
/ i+ h; b9 [+ o
在MX_IWDG_Init函数中,只做了两件事,将在cubeMX上配置的参数传递给IWDG参数缓存区及实例化IWDG寄存器,调用HLA看门狗初始化函数(HAL_IWDG_Init)实现真正的初始化。) n$ L1 {/ l' s" b
! p9 y4 `0 b' [
% u2 c+ s# @5 i; O( l, X9 z4 u6 }) U/ r; ~
+ ~ D! M }7 d- W+ N0 u
) O/ V2 k3 h6 x- C3 x
在stm32L4xx_HLA_iwdg.c驱动文件中,HAL_IWDG_Init函数内,首先对cubeMX上配置参数的合理性诊断,然后开启IWDG和使能写入访问,将已经缓存的配置参数写入IWDG寄存器,并检查设置是否合理,在HAL_IWDG_DEFAULT_TIMEOUT内完成寄存器更新,最后设置计数器值V等于重装载值RL,开启真正计数。
3 X% ~7 ]2 }/ Q! Q: S- HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
4 t+ g! s$ M4 ~, J: a1 \3 _ - {1 N2 o3 U( ~5 o6 C( {
- uint32_t tickstart;) G9 w O( Y. n4 O
- + T- f A% \. H9 u
- /* Check the IWDG handle allocation */
2 D1 g9 E# I- |5 ~% A; w - if (hiwdg == NULL)
5 I& t; F/ K5 g5 N3 B - {2 E9 u% i8 P9 c( E
- return HAL_ERROR;
! T% I2 }. z" z - }
2 e7 m; z9 d( C) B - 4 A* E- w% y/ X0 f# u
- /* Check the parameters */
+ x: V+ }0 K6 S; S2 f! s - assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
3 G8 D5 D. B9 @+ m# X) ]6 @, c - assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));% K& j1 d+ K$ d+ I3 c7 q( T
- assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));
+ b/ E) C. ?: O/ F, l6 { - assert_param(IS_IWDG_WINDOW(hiwdg->Init.Window));
7 t2 |( e- P+ i8 T/ [" J - , i7 D9 y5 g0 ?: F X4 L
- /* Enable IWDG. LSI is turned on automatically */, ], i6 Q) x0 x; Z$ B3 {. I* Q
- __HAL_IWDG_START(hiwdg);
5 T/ ?* w/ g0 c! S -
5 q* d2 w/ X' L3 u8 M+ Z% ] - /* Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers by writing
4 j' k! D6 n; y* ~+ S2 y - 0x5555 in KR */! O( Q! H/ y: ~3 k
- IWDG_ENABLE_WRITE_ACCESS(hiwdg);; d* ?1 k. d7 D% u
- $ n$ |7 l2 \. E9 |& u/ U/ O/ B! f
- /* Write to IWDG registers the Prescaler & Reload values to work with */
6 w$ v+ t. r! f) `$ A+ t - hiwdg->Instance->PR = hiwdg->Init.Prescaler;4 J( ?" Q! o6 |1 S
- hiwdg->Instance->RLR = hiwdg->Init.Reload; M4 p& d9 p1 t2 E4 `8 c3 e
-
$ }! o+ _! e5 C# H1 D - /* Check pending flag, if previous update not done, return timeout */3 N: Y& w0 o4 z; \1 ?6 c, ^ x1 r2 B
- tickstart = HAL_GetTick();
7 x# Z9 _" \: s, c2 D3 [# k -
S5 X% c O( C$ \ - /* Wait for register to be updated */! S* a" \ u5 C4 q4 m2 v
- while ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u)- `3 ~$ m% \% ?+ c5 K9 j: |, d* J
- {7 q4 ~) a0 d! d, n: m( o9 y
- if ((HAL_GetTick() - tickstart) > HAL_IWDG_DEFAULT_TIMEOUT)5 u1 I( _" W! L' c3 j& M0 E6 Z
- {3 _0 O% o6 C1 s. H. V5 E0 |
- if ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u)
% t* O0 t: w* k8 t7 F- {: i - {5 O) N- }# p* p5 ]8 a' n, U3 v
- return HAL_TIMEOUT;
% _) k3 R S# c; U6 u - }$ U. w( y2 h; _; [$ R( I6 I& \
- }: K# Y2 l5 E c5 |+ p
- }' l( K2 ?) ]; e. @: e
-
' ]2 r8 X7 M! ~. i - /* If window parameter is different than current value, modify window
: u- ~. _% o. v% t: m$ u5 g. v - register */
9 |6 }$ @: N/ ~* R0 I1 V - if (hiwdg->Instance->WINR != hiwdg->Init.Window)
/ `$ u# @8 e1 d6 J4 Y/ j% V/ ` - {
' L# w) @# o5 h7 e* D - /* Write to IWDG WINR the IWDG_Window value to compare with. In any case,; C$ o/ Z0 Z% e8 u! K; U
- even if window feature is disabled, Watchdog will be reloaded by writing
1 O; Z4 M" m& p3 y - windows register */
9 o/ ?6 B+ N2 r9 e, w - hiwdg->Instance->WINR = hiwdg->Init.Window;2 M$ B7 G a6 M
- }
- g- ~ @, R: }$ r - else$ H9 F- ~7 I7 S. W `" `
- {: S% x6 G- K" E5 F+ w2 c
- /* Reload IWDG counter with value defined in the reload register */5 e& i! l: m) i Z
- __HAL_IWDG_RELOAD_COUNTER(hiwdg);
* V! \# ?* \& W7 Z, o1 b6 \3 k' e - }
, _9 r' u5 D+ h) G9 h; E5 V -
, f) Z7 Q2 @& [$ O - /* Return function status */: c- V0 m' W9 O! v) k
- return HAL_OK;
' p" Y) V2 J1 x( A& t - }
复制代码 ) |+ [" ?1 ?/ R! f
同样,在stm32L4xx_HLA_iwdg.c驱动文件中,HLA库提供了喂狗函数HAL_IWDG_Refresh,需要我们在规定时间内(由在cubeMX上配置的参数和时钟频率来决定),使用中调用该函数喂狗,就可以防止看门狗复位。
1 X+ e$ ^" E1 M) \% A) ]$ u- /**/ o C; H0 J q6 z
- * @brief Refresh the IWDG.
: n; m) T; C1 ]4 R2 P' @6 z - * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
* q X# C. n6 F' @! O( |$ F - * the configuration information for the specified IWDG module.2 ]2 `* j% l6 @( C1 ^# l8 t
- * @retval HAL status
# P2 T% X/ u* g3 V, l, J - */, p" Q8 z! d. l3 C
- HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
0 Q8 ?1 ]+ M# g! w4 O0 q# r - {; L" O" l8 s3 A2 I
- /* Reload IWDG counter with value defined in the reload register */0 c$ `1 n' ]9 }. u
- __HAL_IWDG_RELOAD_COUNTER(hiwdg);
0 {1 a% f2 e: ?1 e -
& W9 @8 j# x1 k8 } - /* Return function status */, r/ I5 m+ w J5 }4 b7 f3 N
- return HAL_OK;
9 Y9 C- [, l% c- b$ U2 m: _2 M% a - }
复制代码 5 C! \9 [+ L/ S/ w
2.3 IWDG使用
: ~9 Q' w* k2 {/ ` 本文在IWDG使用设计是,通过按键输入触发不再喂狗,这时是否系统复位,通过lpuart1调试信息确认是否成功。5 O$ T% T4 P$ ~: {6 @2 N
{, X j, Q8 X5 {' _5 V
在main.c文件中,引入按键、LED灯及串口驱动头文件。5 @- } m9 w% F1 X- ^0 U0 b
- /* Private includes ----------------------------------------------------------*/8 J. } y" C" [) `8 P! p
- /* USER CODE BEGIN Includes */
* ^* k, |! I1 W0 Q" P8 S; T% O - #include "../../ICore/key/key.h"
" y% |0 J& O! @' [& A - #include "../../ICore/led/led.h"5 W# M; Z! q8 A& C
- #include "../../ICore/print/print.h"# T) g Q$ D5 R" S' A( U1 k
- #include "../../ICore/usart/usart.h"" @( o1 }" `# l& z" f! H0 `
- /* USER CODE END Includes */
复制代码 # b: I# d( l _* ?% R
在main.c文件中,extern声明IWDG寄存器句柄。
2 {3 Y8 x6 H. Z7 g# q5 l' v* ]- /* Private user code ---------------------------------------------------------*/
, _/ {3 H6 p' j- t. [* V. d - /* USER CODE BEGIN 0 */4 `( r& c9 u: W
- extern IWDG_HandleTypeDef hiwdg;( b0 s% ^ `0 Z+ M
- /* USER CODE END 0 */
复制代码
7 x' N- S2 a9 n, S6 e 在main主函数中,初始化串口,开启中断接收。
) A+ I9 Y) Y4 S; Q% B: M- /* Initialize all configured peripherals */
- \' f- D0 D9 F - MX_GPIO_Init();6 Q% P/ q; ]; g7 K) I
- MX_LPUART1_UART_Init();
$ f" t; _' d% C9 V/ L- [1 u- P - MX_IWDG_Init();
) n7 R( Q7 t3 g" J0 o+ O - /* USER CODE BEGIN 2 */4 w/ v' ^& e4 v; j4 t7 f7 d
- ResetPrintInit(&hlpuart1);5 ~& O4 d' J+ J4 X5 ]3 q* W$ P- ^
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断& T0 e' z2 E$ C# y
- HLPUSART_RX_STA = 0;
3 {" C$ n: `3 `/ o+ T1 ` - //
9 n6 N5 x B, L$ @ - uint8_t wdt_flag = 1; //是否继续喂狗标记8 d+ c2 A; e2 z6 b9 W
- printf("app restart now!\r\n");
; S e% l9 s% @* v - /* USER CODE END 2 */
复制代码
9 M# w! W' y4 y7 d7 @3 l3 T 在main主函数中,实现喂狗功能。% y8 e% ^! _- l/ F* J. x7 Y7 K
- /* USER CODE BEGIN WHILE */
8 Z$ {2 H- J0 C: b/ m( [ j* ` - while (1)
8 K/ E& D* N: C+ j& c! J0 p - {
, d. W5 b+ n# Y" P! I) u9 O/ D - if(KEY_2())" y! I0 R- ^4 d* B( X
- {) H. R b' a$ Z* d0 ^, v/ ^) g
- wdt_flag = 0;
! ?, ?7 o6 D& } - printf("IWDG_Refresh stop!\r\n");
/ N1 Y% D( X: e$ P( c - }
* \- c6 y, S& T$ E! ` - if(wdt_flag){+ R- A; ?: r, n" N' N
- HAL_IWDG_Refresh(&hiwdg);6 ~% l% {( C8 q: o k) d( Y6 t, @
- HAL_Delay(10);//等待+ n, u1 f6 J7 X; v
- }% O: D* P8 E3 q l6 c' z2 ^+ q E
- Toggle_led1();4 m2 o9 {' B. ]4 b8 Z# J5 ^. Y# R
- /* USER CODE END WHILE */
复制代码
& T+ o) O, }5 z* E: B$ d三、编译及测试/ S7 j! I* S- N
3.1 编译2 {" F) j1 C& j( u
编译通过
T$ m; y9 r0 G; W+ n1 [
4 c6 H2 Q0 x% J- g' F0 P
, a* F2 t* s. Z' }
# P5 L. ~% X% |# J+ \ 下载程序
0 l/ p* i& D* ]7 U
4 v& b5 ]9 R! ]: I2 k7 t# z! t
) Q W+ H* ?# B- W6 D: A% v
* @# l" j* i* Y: R
P* o" c/ v' B. A; c u- Y 3.2 测试, B& I7 [3 ^+ f# v
打开串口工具,连接上串口,观察窗口日志输出情况,按键KEY2测试如下:
" ]/ N1 @0 z) m; [0 y' d1 P4 O/ v! v
3 e& N3 Z3 I) Q! S2 l2 P
v. }. a6 k( h$ |8 v( O+ ?: x 测试成功,不在喂狗时,等待一、两秒系统重启。4 o6 l) p- ?8 u6 p1 t
————————————————
8 l) W5 T/ i$ C4 n8 B版权声明:py_free-物联智能6 z4 p4 h' ~6 N4 S
如有侵权请联系删除
" n, n/ F+ O( Y6 i: Z" b {* E4 z x; s1 h
+ {6 g' @# m8 }* o2 J0 J6 E
|