看门狗

看门狗(Watchdog) 是一种硬件计时器,旨在确保系统在出现异常时能够自动复位或采取其他措施

在 ARM32 微控制器中,通常有两种类型的看门狗:

  1. 独立看门狗(IWDG, Independent Watchdog)
  2. 窗口看门狗(WWDG, Window Watchdog)

看门狗定时器在内部计数值达到预设门限的时候,会触发一个复位(对于窗口看门狗定时器来 说,会产生一个中断)。

独立看门狗

独立看门狗定时器(FWDGT)有独立时钟源(IRC40K)。即使主时钟失效,FWDGT 依然能 保持正常工作状态,适用于需要独立环境且对计时精度要求不高的场合。

当内部向下计数器的计数值达到 0,独立看门狗会产生一个系统复位。

使能独立看门狗的寄存器写保护功能可以避免寄存器的值被意外的配置篡改。

独立运行的12位向下计数器。

原理框图

F407dlkmgcxkt

IRC32K 时钟源:这是一个 32kHz 的内部低速 RC 振荡器,为看门狗模块提供时钟信号。

预分频器:预分频器用于对时钟源进行分频。预分频的选择范围为 4 到 256,实际分频因子可以通过控制寄存器配置。分频后的时钟会作为输入,驱动后面的计数器工作。

12位向下递减计数器:这是一个 12 位的向下递减计数器。计数器的初始值由重装载寄存器的值决定。当计数器从初始值开始递减到零时,如果没有被重载(即未“喂狗”),则会触发系统复位信号。

重装载寄存器:重装载寄存器用于设置计数器的初始值,开发者可以通过编程将需要的初值加载到重装载寄存器中。该值会在“喂狗”操作时重新加载到计数器中。

控制寄存器:控制寄存器负责配置看门狗的一些参数,比如预分频器的分频系数,以及使能或禁用看门狗的功能。通过控制寄存器,用户可以控制看门狗的主要参数。

状态:PUD 和 RUD

  • PUD(预分频器更新):指示预分频器的状态,当 PUD 位被置位时,表示预分频器配置已更新。
  • RUD(重装载寄存器更新):指示重装载寄存器的状态,当 RUD 位被置位时,表示重装载寄存器配置已更新。

超时周期表

F407dlkmgcszqb

以 32 分频为例:011 表示寄存器的值、0.03125 表示在对应预分频设置下的最小超时值,4095.03125在对应预分频设置下的最大超时值。

就是说在 32 分频的周期下,我们设置的计数值最大不可以超过 4095.03125,最小不能小于 0.03125;

工作流程

由 40kHz 的 IRC 时钟通过预分频器进行分频处理,生成一个较低频率的时钟信号。

预分频后的时钟信号驱动 12 位的向下递减计数器。

当计数器递减到零时,若没有在此之前重载(即没有“喂狗”),会触发一个复位信号,从而实现系统复位。

通过重装载寄存器和控制寄存器,用户可以调整看门狗的超时时间(通过设置计数器初值和分频值)。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "bsp_usart0.h"

// 独立看门狗
static void watchdog_config(void) {
// 开启晶振
// 查看时钟树了解他硬件使用的是哪一个时钟源
rcu_osci_on(RCU_IRC32K);
// 等待晶振频率稳定
ErrStatus status = rcu_osci_stab_wait(RCU_IRC32K);
if (status == ERROR) {
printf("osci wait error...");
return;
}
// 开启写使能
fwdgt_write_enable();
// 配置看门狗
// 时钟频率配置 比如我要配置 1ms
// 我们启用的是 32khz ==> 32000hz / 32 = 1000Hz
fwdgt_prescaler_value_config(FWDGT_PSC_DIV32);
// 设置计数值
fwdgt_reload_value_config(4000);
// 重加载计数值
fwdgt_counter_reload();
// 上面三条语句可以合并为这一条
// fwdgt_config(1000, FWDGT_PSC_DIV32);
// 看门狗使能
fwdgt_enable();
// 关闭写保护
fwdgt_write_disable();
}

int main(void) {
systick_config();
bsp_USART0_Init();
watchdog_config();
printf("Run...\n");
while(1) {
// 我们在配置中设置的时间为 4000 也就是 4m
// 我们只需要在这个时间内喂狗即可
fwdgt_counter_reload();// 重载计数值也就是'喂狗'
}
}

在配置时候一定要执行 fwdgt_counter_reload 操作也就是上面框图中的 ’重加载‘,只有操作之后计数器才可以正常拿到我们设定的计数值;

问题

一、独立看门狗为什么不需要开启外部中断使能

如果独立看门狗已经由硬件选项或软件启动,LSI 振荡器将被强制在打开状态,并且不能被关闭,在 LSI 振荡器稳定后,时钟供应给 IWDG。

也就是说我们可以不用手动开启晶振和使能时钟;

二、独立看门狗为什么要开启写使能

独立看门狗(Independent Watchdog, IWDG)需要 写保护(Write Protection)的主要原因是确保其可靠性和防止系统或软件错误对看门狗寄存器的意外或恶意修改。写保护机制是为了防止看门狗的配置或状态被不恰当地改变,特别是在关键时刻,以免破坏看门狗的监控功能,导致系统失去对死锁或故障的检测。

窗口看门狗

用于监测程序的异常行为并在发生问题时对系统复位。它与普通的独立看门狗(IWDG)有所不同,增加了“窗口期”限制,要求喂狗操作必须在一个特定的时间窗口内进行,否则系统会被复位。

可编程的7位自由运行向下递减计数器。

窗口看门狗引入了一个“窗口期”的概念,以防止系统因为太早或太晚喂狗导致潜在的错误:

  • 当计数器达到0x3F时产生复位(CNT[6] 位被清0)。
  • 当计数器的值大于窗口寄存器的值时,更新计数器会产生复位。

原理框图

F407ckkmgylt

PCLK1/4096:窗口看门狗的时钟源来自于系统的 APB1 总线时钟(即 PCLK1),经过固定的 1/4096 分频后作为看门狗的输入时钟源。这样可以使看门狗计数器的计时频率较低,从而方便实现较长的超时时间。

预分频系数:在经过 1/4096 分频后,还可以进一步选择 1、2、4 或 8 的预分频系数,进一步降低时钟频率。这使得计数器可以更慢地递减,从而实现更大的计时范围。

7位递减计数器 CNT:这是 WWDG 的核心计数器,由 7 位组成(最大值 0x7F,最小值 0x3F)。在启动 WWDG 后,计数器开始从设定的初始值递减,当计数器值达到 0x40 时,如果没有被重装载(喂狗),系统将触发复位。该计数器会根据预分频后的时钟频率递减。

窗口值 WIN:这是一个可配置的窗口值,用于设定喂狗的时间窗口。当计数器的值递减到窗口值以下时,可以进行喂狗操作。如果计数器值高于窗口值时喂狗,WWDG 会立即触发复位。因此,喂狗操作必须在计数器值介于窗口值和 0x40 之间的特定窗口内进行。

WDGTEN:这是窗口看门狗的使能信号。只有在 WDGTEN 使能的情况下,窗口看门狗才会开始计数和触发复位功能。

复位条件

  • CNT[6] = 0 时触发复位:计数器递减到 0x40(计数器的最小值)时,如果没有及时喂狗,系统将被复位。
  • 计数器大于窗口值时喂狗触发复位:如果在计数器值大于窗口值的情况下进行了喂狗操作(即写入 WWDGT_CTL),也会触发复位。这是窗口看门狗的独特之处,要求喂狗必须在窗口期内进行,否则会被认为是异常情况。

复位输出:当满足任一复位条件(计数器值达到 0x40 或喂狗在窗口之外)时,WWDG 将触发复位信号,使系统重新启动。

时序图

F407ckkmgsxt

超时周期表

F407ckkmgcszqb

工作流程

启用窗口看门狗

  • 在使用 WWDG 之前,需要启用 WWDG 的时钟。
  • 配置看门狗的参数,包括:
    • 计数器初始值:通常为 7 位(0x7F),从这个值开始递减。
    • 窗口值:用于设定喂狗的时间窗口上限。
    • 分频系数:控制计数器递减的频率。

配置完成后使能窗口看门狗,计数器开始从设定的初始值递减。

喂狗

在 WWDG 中,喂狗操作(也称“刷新”操作)需要在特定的时间窗口内进行,才能避免系统复位。

窗口时间由两个值决定:

  • 上限窗口值(WIN):喂狗时计数器值不能大于这个值。
  • 下限值:计数器值递减到 0x3F 时会触发系统复位。

喂狗操作就是将计数器重新加载到初始值(通常是 0x7F)以避免复位。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "bsp_usart0.h"

// 窗口看门狗
static void wingod_config(void) {
// 使能窗口看门狗外部时钟
rcu_periph_clock_enable(RCU_WWDGT);
// 看门狗配置
// 1.从这个数开始递减 7位数最大为 0x7F
// 2.窗口上限 下限为 0x3F,
// 3.分频系数 42000000 / 4096 = 10253 / 8 = 1281Hz <==> 1000ms
// 也就是说数一个数大概 0.78ms
wwdgt_config(0x7F, 100, WWDGT_CFG_PSC_DIV8);
// 窗口看门狗使能
wwdgt_enable();
}

int main(void) {
systick_config();
bsp_USART0_Init();
wingod_config();
printf("Run1\n");
while(1) {
delay_1ms(30);
// 设置中窗口期为 100(0x64)~0x3F
// 0x7F-0x67 = 27 0.78 * 27 = 21(ms) 期间不可以喂狗
// 0x7F-0x3F = 64 0.78 * 64 = 49(ms) 超过这就自动复位了
// 也就是说我们要在数数递减到 100~64之间喂狗才有效 21~49之间
wwdgt_counter_update(0x7F);
printf("Run2...\n");
}
}

APB1 总线时钟频率可以在 时钟树上查看;

问题

一、为什么最小值为 0x3F

因为它是通过计数器的最高位来进行判断的;

WWDG 的计数器是一个 7位递减计数器,当计数器值递减到 0x3F 时, CNT[6] 位清零也就是计数器的最高位(第7位)被清零时触发复位。