31. WDT——看门狗定时器¶
31.1. WDT简介¶
一般来说,看门狗也叫看门狗定时器,从本质上面来看,其实它就是一个计数器,在使用的时候,需要给它一个数值, 随后看门狗的计数器根据计数方向开始累计,在看门狗的计数器达到预设的数值之前,可以进行重置看门狗计数器的操作,简称“喂狗”。 但当没有在计数器发生溢出之前进行及时喂狗的话,看门狗就会产生复位请求或者不可屏蔽中断请求(NMI-Non Maskable Interrupt)。
31.1.1. WDT特性¶
- WDT 特性:
使用外设时钟 (PCLKB)
使用14位下行计数器进行计数
可以设置安全属性TrustZone
31.1.2. WDT模块功能¶
- WDT 模块功能:
- 当WDT在允许的刷新窗口之外下溢或刷新时,可以产生以下两种事件之一:
重置设备
NMI 的生成
- WDT 有两种受支持的的模式,分别为:
在自启动模式下,WDT 在复位时开始计数
在寄存器模式下,WDT 可以从应用程序中启动
31.2. WDT功能框图剖析¶
31.2.1. WDT 时钟源¶
31.2.1.1. 计数器时钟¶
看门狗时钟来自外部时钟电路 PCLKB , PCLKB 最大的时钟频率是 50MHz , 可以使用 RA 配置编辑器的时钟选项卡或在运行时使用 CGC 接口设置 PCLKB 频率。设置 PCLKB 频率。 PCLKB 以 50MHz 运行时的最大超时周期约为2.6秒,下面进行看门狗超时时间的计算。
31.2.1.2. 计算看门狗超时时间¶
首先,假设 PLCKB 为 50MHz
时钟分频比等于 PCLKB / 8192
将循环周期设为 16384 cycles
那么,可以得到看门狗的时钟频率 = 50 MHz / 8192 = 6.103 kHz
周期时间等于 1 / 6.103 kHz = 163.85 us
最终,最大超时时间等于 163.85 us * 16384 cycles = 2.68 秒
注解
cycles :配置的循环周期次数,此数值越大,超时时间越长。
31.2.1.3. 窗口值¶
可以设置在哪个区间进行喂狗,但是只能在窗口期内进行喂狗,如果超出总周期还没有进行喂狗操作, 或是在非允许刷新周期进行刷新,单片机都会自动复位或者是处理NMI中断。
31.2.2. WDT 模块核心电路¶
31.2.2.1. WDT控制电路¶
WDT的控制寄存器位WDTCR,主要用于在寄存器启动模式下设置分频比、刷新窗口的开始以及结束位置以及向下计数器下溢的超时时间。 在自启动模式下,WDTCR寄存器中的设置将被禁用,而选项功能寄存器0(OFS0)中的设置会被启用,WDTCR寄存器的设置也可以在OFS0寄存器中进行。
31.2.2.2. 14位递减计数器¶
看门狗的计数器是一个递减计数器,共有14位,其值存在于控制寄存器WDTCR中的TOPS[1:0]位。 从1024、4096、8192和16384个周期开始,将CKS[3:0]位中指定的分频时钟取为一个周期。 在程序中,当此递减计数器向下溢出之前还没有进行喂狗操作,则看门狗产生复位信号或不可屏蔽中断信号。
31.2.3. WDT 输出及中断请求¶
看门狗计数溢出时单片机会有两种处理方式, 第一种处理方式是直接复位,第二种处理方式是机器停止运行然后执行一段不可屏蔽的中断服务函数。
直接复位的方式通常运用在当程序运行出错的时候或者程序跑飞的时候,直接将其复位使其重新开始。
执行中断服务函数的方式通常运用在单片机运行出错的时候, 如果需要保存一些重要的数据,或者需要改变某些模块状态来避免损失,此时增加一段中断代码是非常有用的。
31.2.3.1. 输出到时钟控制电路¶
当复位中断选择位(WDTRCR.RSTIRQS)在寄存器启动模式下或WDT复位时设置为1时, 或当WDT复位选项功能选择寄存器0(OFS0)中的中断请求选择位(OFS0.WDTRSTIRQS)在自启动模式时设置为1时, 当看门狗向下计数器下溢或者发生刷新错误时,输出1个周期计数的复位信号到时钟控制电路。
31.2.3.2. 事件信号输出¶
当复位中断选择位(WDTRCR.RSTIRQS)在寄存器启动模式下或WDT复位时设置为0时, 或当WDT复位选项功能选择寄存器0(OFS0)中的中断请求选择位(OFS0.WDTRSTIRQS)在自启动模式时设置为0时, 当看门狗向下计数器下溢或者发生刷新错误时,产生中断(WDT_NMIUNDF)信号,此信号产生的中断用于不可屏蔽中断,也就是事件输出信号。
31.3. 看门狗启动模式详细介绍¶
31.3.1. 自启动模式¶
使用自启动模式时,堆栈选项卡上的配置将被忽略。使用 BSP 选项卡上的 OFS 设置配置监视器。 在自启动模式下,根据Flash中 Option Function Select register 0 (OFS0)的设置,在复位状态释放后自动开始计数。
当选项功能中WDT启动方式选择位(OFS0. wdtstrt)为0时,选择自动启动方式,关闭WDT控制寄存器(WDTCR)、WDT复位控制寄存器(WDTRCR)、WDT计数停止控制寄存器(WDTCSTPR),打开OFS0寄存器的设置。
在复位状态下,WDT寄存器中的选项功能选择寄存器0 (OFS0)的设置值如下:
时钟分频比
窗口起始和结束位置
超时时间
复位输出或中断请求
在睡眠模式时计数器停止控制
31.3.2. 寄存器启动模式¶
当WDT的启动模式被选择时 OFS0.WDTSTRT 位被置1, 选择寄存器启动模式和WDT控制寄存器(WDTCR),WDT重置控制寄存器(WDTRCR), WDT计数停止控制寄存器(WDT)释放重置状态后,在WDTCSTPR寄存器中将以下内容设置为休眠模式:
时钟分频比
窗口的开始和结束位置
在WDTCR寄存器中的超时时间
在WDTRCR寄存器中重置输出或中断请求输出
在WDTCSTPR寄存器转换到睡眠模式期间的计数器停止控制
此后,只要计数器在允许刷新的时间段内刷新,计数器的值就会在每次刷新时重置,并继续向下计数。 只要计数继续,WDT就不输出复位信号。 但是,如果由于程序失控而导致下计数器无法刷新,或者由于计数器在刷新允许的时间之外刷新而发生刷新错误, WDT将输出复位信号或不可屏蔽的中断请求(WDT_NMIUNDF)。 可以在WDT复位中断请求选择位(WDTRCR.RSTIRQS)中选择复位输出或中断请求输出。
31.4. WDT实验¶
在本实验中,我们使用NMI中断输出来进行实验。
31.4.2. 软件设计¶
编写两个 WDT 驱动文件,分别是bsp_wdt.c 和 bsp_wdt.h,用来存放 WDT 的初始化配置函数。 并开启按键1的中断,在程序运行中,需要使用按键来不停的“喂狗”,以此来防止程序进入复位或者NMI。 关于按键中断的相关知识可以查阅本教程第16章 ICU—外部中断。
31.4.2.1. 新建工程¶
由于本实验需要LED和按键中断模块,因此我们可以在前面ICU-外部中断章节的实验例程的基础上修改程序。
- 对于 e2 studio 开发环境:
拷贝一份我们之前的 e2s 工程 “16_ICU_External_Interrupt”, 然后将工程文件夹重命名为 “30_WDT”,最后再将它导入到我们的 e2 studio 工作空间中。
- 对于 Keil 开发环境:
拷贝一份我们之前的 Keil 工程 “16_ICU_External_Interrupt”, 然后将工程文件夹重命名为 “30_WDT”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下面新建 wdt 文件夹, 再进入 “wdt” 文件夹里面新建源文件和头文件:“bsp_wdt.c” 和 “bsp_wdt.h”。 工程文件结构如下。
30_WDT
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ wdt
│ ├─ bsp_wdt.c
│ └─ bsp_wdt.h
└─ hal_entry.c
31.4.2.2. FSP配置¶
首先打开 “30_WDT” 项目的 FSP 配置界面,接下来我们要在这个界面里配置芯片的引脚及其相应的功能。
双击 configuration.xml 打开配置界面: 然后点开依次点击 Stacks -> New Stack -> Search… 里输入 WDT 选着 Watchdog。
随后点击 “Properties” 对刚刚加入的WDT模块进行配置。
点击 BSP 的属性settings界面,找到 OFS0 register settings -> WDT 去设置 WDT 的属性。
配置完成之后可以按下快捷键“Ctrl + S”保存, 最后点右上角的 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
31.4.2.3. NMI 和 复位¶
NMI就是会执行一个中断服务函数,这个中断函数是不能够被打断的,拥有最高的中断优先级。 一般是用在一些重要的数据上面,如果直接复位,这些数据将不被保存,这个时候我们可以写一个服务程序去保存重要数据, 这也是看门狗最重要的功能。 可以在NMI中断程序中通过软件复位或跳转到程序开头进行程序的重新运行。
复位就是在不断电的情况下,把当前 MCU 及运行数据清零后的启动。在本实验中使用NMI,也就是不可屏蔽中断来进行试验。
31.4.2.4. WDT初始化函数¶
/*初始化看门狗并启动计数器*/
void WDT_Init(void)
{
//如果使用J-Link调试器进行调试的话需要加上这一句话
R_DEBUG->DBGSTOPCR_b.DBGSTOP_WDT = 0;
//初始化看门狗(WDT)模块
R_WDT_Open(&g_wdt0_ctrl, &g_wdt0_cfg);
/*刷新看门狗计数器,在这里的作用是初次启动寄存模式下的看门狗计数器
*要注意,除非是在刷新允许的范围内,否则自启动模式下不应该使用该函数*/
R_WDT_Refresh(&g_wdt0_ctrl);
}
31.4.2.5. 喂狗函数¶
/*喂狗*/
void WDT_Feed(void)
{
/*喂狗,刷新递减计数器的值*/
R_WDT_Refresh(&g_wdt0_ctrl);
}
31.4.2.6. NMI中断服务函数¶
/* 当看门狗NMI发生时中断回调 */
void wdt_callback (wdt_callback_args_t * p_args)
{
/*防止编译器产生关于函数中没有使用形参的警告*/
(void) p_args;
/*蓝色LED亮,请注意,在这里LED灯函数为代指,
*实际应用中,这里应该放最重要的函数,比如保存重要数据等*/
LED2_ON;
R_BSP_SoftwareDelay(3, BSP_DELAY_UNITS_SECONDS);
/* 通过软件复位MCU*/
__NVIC_SystemReset();
}
31.4.2.7. 主函数¶
/*按键中断回调函数*/
void key1_callback(external_irq_callback_args_t *p_args)
{
(void) p_args;
/*按键按下触发中断,进行喂狗操作*/
WDT_Feed();
}
void hal_entry(void)
{
/*红色LED亮3秒*/
LED1_ON;
R_BSP_SoftwareDelay(3,BSP_DELAY_UNITS_SECONDS);
/*按键中断初始化*/
g_external_irq_on_icu.open(&key1_ctrl, &key1_cfg);
g_external_irq_on_icu.enable(&key1_ctrl);
/*初始化看门狗并开启计数器*/
WDT_Init();
while(1)
{
/*红色LED灯灭,在这里只是代指,
*实际上这部分应该写需要被WDT监控的程序*/
LED1_OFF;
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
注解
使用 J-Link 调试器时,WDT 计数器不计数,因此不会重置设备或生成 NMI。若要使监视器能够在调试时计数并生成重置或 NMI,请在应用程序中添加 R_DEBUG->DBGSTOPCR_b.DBGSTOP_WDT = 0 代码。
31.4.2.8. 下载验证¶
将程序下载到开发板内,红色LED灯亮三秒钟,随后启动看门狗,关闭红色LED灯, 用户需要在不超过2.6秒左右内的时间按下按键1(SW2)来刷新计数器的数值(喂狗)。 一旦超过这个时间,看门狗计数器下溢,将产生NMI,也就是不可屏蔽中断,届时蓝色LED灯会亮三秒, 随后软件复位,程序从头运行。