你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32实例-窗口看门狗实验

[复制链接]
STMCU小助手 发布时间:2022-6-29 19:18
   我们介绍过了独立看门狗 IWDG,本文我们来学**窗口看门狗(以下简称WWDG)。本章要实现的功能是:使用窗口看门狗的中断来喂狗,通过D1、D2 指示灯提示程序运行状态。

7 U! N, }; O. D, \9 Z
WWDG简介
    已经介绍过IWDG,知道它的工作原理就是一个 12 位递减计数器不断递减计数,当减到 0 之前还未进行喂狗的话,产生一个 MCU 复位。窗口看门
狗 WWDG其实和独立看门狗类似, 它是一个 7 位递减计数器不断的往下递减计数,当减到一个固定值 0X40 时还不喂狗的话,产生一个 MCU 复位,这个值叫窗口的下限,是固定的值,不能改变。这个和独立看门狗是类似的,不同的是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位, 这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口之间才可以刷新(喂狗),这也是窗口看门狗中“窗口”两个字的含义。窗口看门狗时序图如下图所示。
  q$ i9 Q3 p1 k8 R2 t0 U9 c- b4 G
微信图片_20220627231007.jpg
& S3 x9 [5 m/ R
    从上图可以看到, T[6:0]是窗口控制寄存器 (WWDG_CR) 的低7位, W[6:0]是窗口配置寄存器(WWDG_CFR)低 7 位。T[6:0]就是窗口看门狗的计数器值,而W[6:0]是窗口看门狗的上窗口,下窗口是固定值 0X40。当窗口看门狗的计数器在上窗口值之外或者低于下窗口值被刷新都会产生复位。
    上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保窗口值大于0X40,否则窗口就不存在了。窗口看门狗 WWDG 通常被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。
WWDG结构框图
    要更好的理解窗口看门狗,就需要了解它内部的结构,如下图所示。

8 l7 ], q0 @/ [
微信图片_20220627231104.jpg

- @! K2 v3 q  c; U9 d) T0 z( k4 u
    我们把 WWDG 结构框图分成5 个子模块,按照顺序依次进行简单介绍。
(1)标号 1:WWDG时钟
    窗口看门狗(WWDG)的时钟来自 PCLK1,即挂接在 APB1 总线上,由 RCC 时钟控制器开启。APB1 时钟最大为36M。
(2)标号 2:WDG 预分频器器
    PCLK1 时钟并不是直接提供给窗口看门狗计数器时钟,而是通过一个WDG 预分频器分频后输入给计数器时钟。我们可以操作配置寄存器 WWDG_CFR 的位 8:7 WDGTB[1:0]来设置分频因子,分频因子可以为 0、1、2、3。
    分频后的计数器时钟为:CK_CNT= PCLK1/4096/(2^WDGTB),除以 4096 是中文参考手册内公式规定,没有为什么。PCLK1 等于 APB1 时钟,WDGTB 为分频因子(0-3),2^WDGTB 大小就是1、2、4、8,与库函数中的分频参数对应。每经过一个计数器时钟,计数器就减 1。
(3)标号 3:计数器
    窗口看门狗的计数器是一个 7 位的递减计数器,计数最大值为 0X7F,其值存放在控制寄存器 WWDG_CR中的6:0 位, 即 T[6:0]。当递减到 T6 位变成 0 时,即从 0X40 变为 0X3F 时候,会产生看门狗复位。这个值 0X40 是窗口看门狗能够递减到的最小值,所以计数器的值只能在 0X40~0X7F 之间,实际上用来计数的是 T[5:0]。当递减计数器递减到 0X40 的时候,还不会马上产生复位,如果使能了提前唤醒中断,窗口配置寄存器(WWDG_CFR)位 9 EWI 置 1,则产生提前唤醒中断,也就是在快产生复位的前一段时间提醒我们,需要进行喂狗了,否则将复位。我们通常都是在提前唤醒中断内向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。需要注意的是:在进入中断后,必须在不大于 1 个窗口看门狗计数周期的时间(在 PCLK1 频率为 36M 且 WDGTB 为 0 的条件下,该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
    如果不使用提前唤醒中断来喂狗,我们要会计算窗口看门狗的超时时间,计算公式如下:
  1. Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /PCLK1;
复制代码
7 T- G* w) }3 K) R2 ^
其中:
  1. Twwdg 为窗口看门狗的超时时间,单位为 ms。& l0 n4 }/ U. Z. X
  2. PCLK1 为 APB1 的时钟频率,最大 36MHz。% Z3 j* x/ z$ |# E
  3. WDGTB 为窗口看门狗的预分频系数。
    + T8 \: X5 a9 `/ |
  4. T[5:0] 为窗口看门狗的计数器低 6 位。
复制代码
% }) \( i  Z8 n7 o
    根据上面的公式,假设 PCLK1=36Mhz,那么可以得到最小-最大超时时间表,如下:

( w8 b! Q7 R9 P4 f& g0 [- W  M
微信图片_20220627231201.jpg
+ D" s9 Q& r( C! ?* Z/ w
(4)标号 4:看门狗配置寄存器
    我们知道窗口看门狗必须在窗口范围内进行喂狗才不会产生复位, 窗口中的下窗口是一个固定值 0X40,上窗口值可以改变,具体的由配置寄存器 WWDG_CFR的位 W[6:0]设置。其值必须大于 0X40,如果小于或者等于 0X40 就是失去了窗口的意义,而且也不能大于计数器的最大值 0X7F。窗口值具体要设置成多大,这个得根据我们需要监控的程序的运行时间来决定。假如我们要监控的程序段 A运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。一般计数器的值 TR 设置成最大 0X7F,窗口值为 WW,计数器减一个数的时间为 T,那么时间:(TR-WW)*T 应该稍微大于 Ta 即可,这样就能做到刚执行完程序段 A 之后喂狗,起到监控的作用,这样也就可以算出 WW 的值是多少。
(5)标号 5:系统复位信号
    当计数器值超过配置寄存器内的上窗口设置值或者低于下窗口值,并且
WDGA位置 1,即开启窗口看门狗,将产生一个系统复位信号,促使系统复位。由于篇幅限制,本章并没有对相关寄存器进行介绍,大家可以参考
《STM32F10x 中文参考手册》-18窗口看门狗(WWDG)章节,里面有详细的讲解。如果看不懂的可以暂时放下,因为我们使用的是库函数开发。

9 ]" y1 ]# E0 B& A
WWDG 配置步骤
    接下来我们介绍下如何使用库函数对 WWDG 进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(WWDG 相关库函数在 stm32f10x_wwdg.c 和stm32f10x_wwdg.h 文件中)
(1)使能 WWDG 时钟
    WWDG 不同于 IWDG,IWDG 有自己独立的 LSI 时钟,所以不存在使能问题,而WWDG使用的是 APB1 时钟,需要先使能时钟。在库函数中实现函数如下:
  1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
复制代码
. C% h; p; g3 n# h6 [+ }6 H
(2)设置 WWDG 窗口值和分频数( {9 f3 f: V" [3 ~
    设置 WWDG 窗口值函数为:
  1. void WWDG_SetWindowValue(uint8_t WindowValue);
复制代码

) B4 |4 F" W; x/ R! T: {    窗口值最大值为 0X7F,最小不能低于 0X40,否则就失去了窗口的意义。
) Q8 T( a+ n3 q- `
设置 WWDG 分频数函数为:
  • 8 `5 C- f5 a! N' H) G
  1. void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
复制代码

! S! [" W* Z! b2 }* {8 p3 S    分 频 系 数 可 以 为 WWDG_Prescaler_1 、 WWDG_Prescaler_2 、9 j! R. l7 W, X/ E; J" M
WWDG_Prescaler_4、WWDG_Prescaler_8。
(3)开启 WWDG 中断并分组
    通常对窗口看门狗进行喂狗是在提前唤醒中断内操作,所以需要打开 WWDG的中断功能,并且配置对应的中断通道及分组。中断分组及通道选择是在NVIC_Init 初始化内完成, 这个在前面章节中都介绍过,这里我们看下使能WWDG中断,库函数如下:
  1. WWDG_EnableIT();
复制代码
1 A9 ?! ?- e$ L( k; Q: {
(4)设置计数器初始值并使能 WWDG) z; }, J) j& x3 F/ `
    库函数中提供了一个同时设置计数器初始值和使能 WWDG 的函数,如下:
  1. void WWDG_Enable(uint8_t Counter);
复制代码
5 U' r# {5 [* U) h- p; o
    注意计数器最大值不能大于 OX7F。) U9 L1 _( w9 C4 S& c
    库函数还提供了一个独立设置计数器值的函数,如下:
  1. void WWDG_SetCounter(uint8_t Counter);
复制代码

, h/ @; U. }! p( M; B& b. A  m(5)编写 WWDG 中断服务函数
2 {5 }" S, O+ W# W4 U; A
    最后我们还需要编写一个WWDG 中断服务函数, 通过中断函数进行喂狗。WWDG中断服务函数名在 STM32F1启动文件内就有,WWDG 中断函数名如下:
  1. WWDG_IRQHandler
复制代码
3 [9 u. z8 P0 V+ K3 z* t6 d; f
    在中断内要进行喂狗,可以直接调用 WWDG_SetCounter()函数,给它传递一个窗口值即可,特别注意,在中断内喂狗一定要快,否则当看门狗计数器值减到0X3F时将产生复位。然后清除WWDG 中断状态标志位 EWIF,函数如下:
  1. WWDG_ClearFlag(); //清除窗口看门狗状态标志
复制代码

5 t+ s0 _  p2 l& J( S    通过以上几步配置后,我们就可以正常使用窗口看门狗了,我们需要在中断内快速喂狗,否则系统即会重新启动。
    由于 WWDG 是 STM32F1 内部资源,因此本硬件电路非常简单,只有 D1、D2指示灯连接,对应的电路图在前面章节中都有介绍,这里就不多说。D1 指示灯用来提示系统是否被复位,D2 指示灯用来作为喂狗提示,每进入中断喂狗D2 指示灯状态翻转一次。
    所要实现的功能是:系统开启时 D1 指示灯点亮 500ms 时间,然后熄灭。
D2 指示灯不断闪烁表示正在喂狗。如果喂狗超时将重启系统,D1 指示灯点亮500ms,然后熄灭,继续喂狗。程序框架如下:
(1)初始化 WWDG(使能WWDG 时钟,设置窗口及分频值,使能中断等)
(2)编写窗口看门狗中断函数
(3)编写主函数
    在前面介绍 WWDG 配置步骤时,就已经讲解如何开启 WWDG、设置窗口及分频值等。下面我们打开“窗口看门狗实验”工程,在APP 工程组中可以看到添加了wwdg.c 文件,在 StdPeriph_Driver 工程组中添加了 stm32f10x_wwdg.c 库 文 件 。窗 口 看 门 狗 操 作 的 库 函 数 都 放 在
stm32f10x_wwdg.c 和 stm32f10x_wwdg.h 文件中,所以使用到窗口看门狗就必须加入 stm32f10x_wwdg.c文件,同时还要包含对应的头文件路径。
! h4 h+ S& S  {& Y" l. W% c
WWDG初始化函数
    要使用 WWDG,我们必须先对它进行配置。WWDG 初始化代码如下:
  1. /****************************************************************! Z6 o& x6 W5 b( J( y: v
  2. * 函 数 名 : WWDG_Init
    3 y$ W' K/ q+ ~# J- q1 o
  3. * 函数功能 : WWDG 初始化+ v2 x6 n% m3 A5 B  n" R% L5 A& i- w
  4.                     窗口值是:0x5f6 I, I: t$ m" C2 W# C
  5.                     定时器计数值是:0x7f
    4 I' N' s0 c, e6 o8 L
  6.                     预分频值是:8
    6 N2 ~5 S' n( T4 l* |+ g
  7.             窗 口 看 门 狗 进 入 中 断 的 频 率 计 算 公 式: PCLK1/(4096*2^pre)4 {8 P9 B7 @) Y
  8. * 输 入 : 无+ j# I9 M7 _% D9 t* P
  9. * 输 出 : 无' P# ?* n' Q( X+ N+ \. \
  10. *****************************************************************/
    ) v1 W7 }1 N6 Y
  11. void WWDG_Init(void)
    $ z6 f. `. H( v* ~1 E
  12. {
    % j$ i, N# t, i4 Q& n
  13.   NVIC_InitTypeDef NVIC_InitStructure;* J; i% c4 Q: {5 C& [9 e8 w" |
  14.   RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE); //开启窗口看门狗的时钟% _4 {6 P& v, I% {" `' X; |
  15.   WWDG_SetWindowValue(0x5f);//设置窗口值3 ]5 P* p9 N  `7 h- V
  16.   WWDG_SetPrescaler(WWDG_Prescaler_8);//设置分频值
    * t8 D+ |* a, y* ]" d8 B; P$ X
  17.   NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;//窗口中断通道
    1 Q! c' [6 o$ g! I! q( N
  18.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// 抢 占优先级
      [% T% D# q& X+ U3 m* H- q2 ~
  19.   NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级
    - [  Y: B9 t+ s5 v+ E, m
  20.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能3 ~/ \' i- c, o! Z3 L& B
  21.   NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
    5 r  q6 L( `/ K& ~5 m3 k2 |) n1 @
  22.   WWDG_Enable(0x7f); //使能窗口看门狗并初始化计数器值2 h) m1 w$ ^2 u+ C3 y( _
  23.   WWDG_ClearFlag(); //清除窗口看门狗状态标志(这一句必须加上,否则进入不了中断)7 T8 v0 y; ]) N3 f
  24.   WWDG_EnableIT(); //开启中断
    * G- ?8 p0 ?4 f" h9 k" Q" q* Z
  25. }
复制代码
1 v0 S( g3 D# \, t
    在WWDG_Init()函数中,首先使能 WWDG 时钟,设置 WWDG 窗口值为 0X5F,分频值为 WWDG_Prescaler_8,然后设置中断分组并开启中断,最后设置计数器值为 0X7F并使能 WWDG。这一过程在前面步骤介绍中已经提了。
/ n9 ~; T+ m! x% V5 E2 w* m0 N+ Q9 e
0 u8 g% ^3 w) B$ r
窗口看门狗中断函数
    初始化 WWDG 后,中断就已经开启了,当窗口看门狗计数器递减到 0X40时,就会产生一次提前唤醒中断,具体代码如下:
  1. /****************************************************************
    ' Z' G- G" l' ^2 E( m
  2. * 函 数 名 : WWDG_IRQHandler
    $ h0 A4 k/ V9 L& n/ X
  3. * 函数功能 : WWDG 中断函数3 h( _3 @) A$ ~# z- A& \: c  |4 H
  4. * 输 入 : 无( }# o' _8 t; z+ b! V4 P3 S! _. v
  5. * 输 出 : 无
    ' _6 R0 p% O0 W* Z. Q2 `. }2 d+ f
  6. *****************************************************************/0 U9 p) w4 O, k; Q9 ?! V6 @
  7. void WWDG_IRQHandler(void)0 n1 ?  V6 e. R7 M9 s$ _
  8. {
    0 W3 v) H$ F' U1 B
  9.   WWDG_SetCounter(0x7f); //重新赋值! Y  C5 v9 N* j* @
  10.   WWDG_ClearFlag(); //清除窗口看门狗状态标志- D" s6 ?+ b2 v3 D. y, j8 H- _( p
  11.   led2=!led2;
    ; s4 T4 B3 {7 g, A$ F6 y
  12. }
复制代码
8 H# Q& d, B  x& i" L# k! C" N
    在中断内必须快速进行喂狗,也就是重新对窗口看门狗计数器赋值。然后清除中断状态标志,这里我们使用一个 D2 指示灯来提示喂狗,如果喂狗 D2指示灯状态翻转一次。
主函数
    编写好窗口看门狗初始化和中断函数后,接下来就可以编写主函数了,代码如下:
  1. /****************************************************************
    1 [; ~, k% e# W$ G* M& G
  2. * 函 数 名 : main
    8 F  E9 c- ?4 Y6 j
  3. * 函数功能 : 主函数0 z* z( {' ?/ f
  4. * 输 入 : 无) G4 k7 o2 N# D. Q  }5 E7 K
  5. * 输 出 : 无
    ; x( T! H$ }- |0 @3 ?1 s
  6. *****************************************************************// G1 z: X1 y) Z- h( N) F
  7. int main()* o/ q! T  Y- r% S
  8. {: A, Z8 R/ U1 A' }9 y! N
  9.   SysTick_Init(72);9 i; `6 L" {# o0 U: U7 V. e
  10.   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2 组
    , P6 R2 h) o, C% _# c3 B
  11.   LED_Init();5 p; T: b. E% W! i4 G2 Z; c5 G  Q
  12.   USART1_Init(9600);
    ; o, v. @2 d/ N' q) V
  13.   led1=0;
    ; t0 g9 O2 F$ W: ^
  14.   delay_ms(500);
    ! S2 W/ ^- w- k8 z3 l, d3 D9 j! H
  15.   WWDG_Init();
    " I* r4 z' w8 U3 I
  16.   while(1)
    6 E" o. z7 M  L" [
  17.   {/ d- m  k7 P! S% h% y" ]& K. Z5 k
  18.     led1=1;
    6 Q7 V; R# F$ t2 x# D6 F! i" {
  19.   }
    ! ~* ]5 B$ a9 Q3 `
  20. }
复制代码

, s- U$ j0 W( ~. m- b+ H
    主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括
9 Z1 k  h5 @1 \. R& I0 ^
SysTick 系统时钟,中断分组,LED 初始化等。然后让 D1 指示灯点亮 500ms,再调用我们前面编写的 WWDG初始化函数,最后进入 while 循环,关闭D1指示灯。
    在主函数内并没有看到喂狗操作,这是因为我们使用窗口看门狗中断进行喂狗,当计数器递减到 0X40 时,进入中断喂狗,D2 指示灯状态翻转一次,如果喂狗失败,将使系统复位,那么D1指示灯又会点亮 500ms 后熄灭。
    将工程程序编译后下载到开发板内, 可以看到 D1 指示灯点亮 500ms后熄灭。然后 D2指示灯不断闪烁,表示正在喂狗。

2 o9 Y- T! H8 s* ]# w
收藏 评论0 发布时间:2022-6-29 19:18

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版