9.【NXP 号令者RT1052】开发——实战-看门狗
9.【NXP 号令者RT1052】开发——实战-看门狗
这一章,我们将向大家介绍如何使用 RT1052 的普通看门狗(WDOG1)。RT1052 内部自带了 3 个看门狗:普通看门狗(WDOG1 和 WDOG2)和 RT 看门狗(WDOG3)。这一章我们只介绍普通看门狗,RT 看门狗将在下一章介绍。在本章中,我们将利用看门狗的中断功能来喂狗。
9.1 RT1052 看门狗简介
看门狗可以保证系统的正常运行,当系统运行出现异常/错误时,看门狗可以及复位系统,以免带来不能挽回的后果。
RT1052 的看门狗时钟一般由外部 32.768Khz 低速时钟(外部低速晶振)驱动,即使主时钟发生故障,它也仍然有效。注意:当外部 32.768Khz 晶振失效时,芯片将自动切换到内部 RC 时钟,保障看门狗的正常运行。内部 RC 时钟频率一般为:10Khz~45Khz。RT1052 看门狗框图如下:

看门狗的计数时钟来自低速参考时钟(Low Frequency Reference Clock 1),通常为外部 32.768 kHz 晶振或内部 10~45 kHz RC 振荡器,一般使用外部 32.768 kHz 晶振。看门狗有三个输出信号:wdog_rst(用于复位 MCU)、Interrupt(用于产生中断)、WDOG-1(输出 WDOG_B 信号)。其中中断信号由内部中断计数器产生,可用于在复位前触发中断喂狗。
RT1052 内部包含两个普通看门狗:WDOG1 和 WDOG2。两者功能相似,但 WDOG1 溢出时会产生复位信号,直接复位 MCU;WDOG2 溢出则触发 SNVS 中断,用于错误报告。本章以 WDOG1 为例。
看门狗控制寄存器(WCR)用于控制看门狗的工作,常用位功能如下:
- WT[7:0]:设置溢出时间,范围 0~127,对应 0.5~128 秒。喂狗或使能后该值加载到计数器并递减至 0,产生溢出并复位 MCU。喂狗操作会重新加载该值。
- WDA/WDT:控制 WDOG_B 信号输出。一般保持默认(WDA=1,WDT=0)。
- SRS:输出软复位信号,通常设置为 1。
- WDE:看门狗使能位,必须设置为 1 才能启用看门狗。注意该位为一次性设置,硬件复位前无法修改;WDT 位同样如此。

接下来,我们介绍看门狗服务(喂狗)寄存器(WSR),该寄存器用于实现喂狗操作

我们依次往该寄存器(WSR)写入两个值:0X5555、0XAAAA,即可实现喂狗操作。注意,这两个值必须按顺序写入!当写完这两个值以后,看门狗计数器值就会自动从 WT[7:0]重新加载,从而实现喂狗。
接下来,我们介绍看门狗中断控制寄存器(WICR),该寄存器用于控制看门狗的中断。

WIE 位,用于设置是否使能看门狗中断。本章我们将使用中断来喂狗,所以需要设置该位为 1。
WTIS 位,用于表示看门狗中断是否发生。该位为写 1 清零。
WICT[7:0]位,用于设置看门狗中断计数器溢出时间,表示中断溢出到看门狗溢出之间的时间,即提前看门狗溢出多少时间进入中断,其设置范围为 0~255,表示 0~127.5 秒。例如,设置WICT[7:0]=0X01,表示看门狗中断产生后,0.5 秒就会产生看门狗溢出,即提前 0.5 秒进入中断;如设置 WICT[7:0]=0X06,表示看门狗中断产生后,3 秒就会产看门狗溢出,即提前 3 秒进入中断。注意:WICT[7:0]设置的提前时间,必须小于 WT[7:0]设置的溢出时间!!
最后,我们看看门狗中的杂项控制寄存器(WMCR)。

该寄存器只有最低位(PDE)有效,其他都为保留位。当设置 PDE=0 时,禁止 WDOG 掉电计数器工作。当设置 PDE=1 时,使能 WDOG 掉电计数器工作。为了让 WDOG 正常工作,我们设置改位为 0 即可。
只要对以上几个寄存器进行相应的设置,我们就可以启动 RT1052 的看门狗。
看门狗相关的库函数在 fsl_wdog.c 和 fsl_wdog.h 这两个文件中。我们以 WDOG1 为例,看看库函数的看门狗配置步骤
1. 使能 WDOG1 时钟
-
要正常设置 WDOG1 寄存器,必须先使能其时钟。
-
使用函数:
c
CLOCK_EnableClock(kCLOCK_Wdog2); -
注意:
WDOG_Init会自动调用此函数打开时钟,因此一般无需手动调用。
2. 初始化 WDOG1
-
使用 FSL 库函数:
c
void WDOG_Init(WDOG_Type *base, const wdog_config_t *config); -
参数说明:
base:指定看门狗(WDOG1 或 WDOG2)。config:看门狗配置结构体指针,类型为wdog_config_t。
c
typedef struct _wdog_config
{bool enableWdog; // 使能看门狗wdog_work_mode_t workMode; // 工作模式bool enableInterrupt; // 使能中断uint16_t timeoutValue; // 超时时间uint16_t interruptTimeValue; // 超时前几秒产生中断bool softwareResetExtension; // 软件复位扩展bool enablePowerDown; // Power Down 模式bool softwareAssertion; bool softwareResetSignal;
} wdog_config_t;
- 关键参数:
timeoutValue:喂狗超时时间,公式:
超时时间=(timeoutValue+1)×0.5 秒超时时间 = (timeoutValue + 1) \times 0.5 \ \text{秒}
范围:0.5 ~ 128 秒。
interruptTimeValue:提前中断时间,公式:
中断时间=interruptTimeValue×0.5 秒中断时间 = interruptTimeValue \times 0.5 \ \text{秒}
范围:0.5 ~ 127.5 秒。
-
使用示例:
c
wdog_config_t wdog1_config; WDOG_GetDefaultConfig(&wdog1_config); // 默认配置wdog1_config.workMode.enableWait = false; // 关闭等待模式 wdog1_config.workMode.enableStop = false; // 关闭 stop 模式 wdog1_config.workMode.enableDebug = false; // 关闭 debug 模式 wdog1_config.timeoutValue = wdgtimeout; // 设置超时值 wdog1_config.interruptTimeValue = inttimeout; // 设置提前中断时间 wdog1_config.enableInterrupt = true; // 使能中断 wdog1_config.enableWdog = true; // 使能看门狗WDOG_Init(WDOG1, &wdog1_config); // 初始化 WDOG1
3. 喂狗操作
-
使用函数:
c
void WDOG_Refresh(WDOG_Type *base); -
实际上是向寄存器 WSR 写入特定序列:
0xAAAA和0x5555。
4. 开启 WDOG 中断并设置优先级
-
配置中断优先级并使能:
c
RT1052_NVIC_SetPriority(WDOG1_IRQn, 4, 0); // 抢占优先级 4,子优先级 0 EnableIRQ(WDOG1_IRQn); // 使能 WDOG1 中断
5. 编写中断服务函数
- 在 ISR 中完成喂狗操作:
- 调用
WDOG_Refresh(WDOG1)写入序列(0x5555、0xAAAA)。 - 使用
WDOG_ClearInterruptStatus(WDOG1)清除中断标志位 WTIS。
- 调用
实验现象
-
DS0:指示 MCU 是否被复位,若复位则点亮 300ms。
-
DS1:指示中断喂狗,每次中断喂狗翻转一次。
9.2 硬件设计
- 指示灯 DS0 和 DS1
- 看门狗 1(WDOG1)
其中指示灯前面介绍过了,看门狗 1(WDOG1)属于 RT1052 的内部资源,只需要软件设
置好即可正常工作。我们通过 DS0 和 DS1 来指示 RT1052 的复位情况和看门狗 1 的喂狗情况。
9.3 软件设计
软件设计我们依旧是在前面的代码基础上往上加。在 HARDWARE 文件夹下面新建一个 WDOG 的文件夹,用来保存与看门狗相关的代码。然后打开工程,新建 wdog.c 和wdog.h 两个文件,并保存在WDOG 文件夹下,并将 WDOG 文件夹加入头文件包含路径**(注意:3 个目标工程需要添加 3 次!)**
wdog.c
#include "wdog.h"
#include "lpuart.h"
#include "led.h"wdog_config_t wdog1_config; //WDOG配置结构体//初始化WDOG1
//wdgtimeout:看门狗超时时间,实际时间为(wdgtimeout+1)*0.5s
// 比如如果要设置超时时间为2s的话wdgtimeout=2/0.5-1=3
//inttimeout:在看门狗超时发生前几秒产生中断?实际时间为inttimeout*0.5s
void WDOG1_Init(u8 wdgtimeout, u8 inttimeout)
{//WDOG_Init()函数里面会使能看门狗时钟,这里写出来是提醒大家使用看门狗//的时候看门狗时钟是要使能的。CLOCK_EnableClock(kCLOCK_Wdog1); //使能看门狗时钟WDOG_GetDefaultConfig(&wdog1_config); //先将WDOG1配置为默认值wdog1_config.workMode.enableWait=false; //等待模式关闭wdog1_config.workMode.enableStop=false; //stop模式关闭wdogwdog1_config.workMode.enableDebug=false; //debug模式关闭wdog wdog1_config.timeoutValue=wdgtimeout; //设置超时值,周期为(wdgtimeout+1)*0.5wdog1_config.interruptTimeValue=inttimeout; //设置wdog超时前几秒产生中断? inttimeout*0.5wdog1_config.enableInterrupt=true; //使能wdog中断wdog1_config.enableWdog=true; //使能wdogWDOG_Init(WDOG1,&wdog1_config); //初始化看门狗//配置WDOG中断RT1052_NVIC_SetPriority(WDOG1_IRQn,4,0); //抢占优先级4,子优先级0EnableIRQ(WDOG1_IRQn); //使能WDOG1中断
}//喂狗
void WDOG1_Feed(void)
{ WDOG_Refresh(WDOG1);//重装载
}//看门狗中断服务函数
void WDOG1_IRQHandler(void)
{if(WDOG_GetStatusFlags(WDOG1) & kWDOG_InterruptFlag) //超时要发生{WDOG1_Feed(); //喂狗LED1_Toggle;}WDOG_ClearInterruptStatus(WDOG1,kWDOG_InterruptFlag); //清除中断标志__DSB(); //数据同步屏蔽指令
}
该代码就 3 个函数:WDOG1_Init 函数用于初始化 WDOG1 看门狗,根据我们前面介绍的步骤,设置看门狗的溢出时间,以及喂狗中断溢出时间,打开相应的中断,设置中断优先级;WDOG1_Feed 函数用于喂狗操作,此函数就是对库函数 WDOG_Refresh 的简单封装;
WDOG1_IRQHandler 函数是看门狗 1(WDOG1)的中断服务函数,当中断发生时,我们可以在该函数进行喂狗,并清零中断标记,最后对 LED1(DS1)取反,来监测中断服务函数的执行状况。
保存 wdog.c,然后把该文件加入到 HARDWARE 组下。
wdog.h
#ifndef _WDOG_H
#define _WDOG_H
#include "sys.h"void WDOG1_Init(u8 wdgtimeout, u8 inttimeout);
void WDOG1_Feed(void);
#endif
保存两个代码。

然后更新main.c
#include "sys.h"
#include "lpuart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "wdog.h"int main(void)
{u8 key=0;u8 led0sta=1,led1sta=1; //LED0,LED1的当前状态u8 len; //接收数据长度u16 times=0; //延时计数器MPU_Memory_Protection(); //初始化MPURT1052_Clock_Init(); //配置系统时钟DELAY_Init(600); //延时函数初始化LPUART1_Init(115200); //初始化串口1RT1052_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);//优先级分组4LED_Init(); //初始化LED
#ifdef KEY_DEBUGKEY_Init(); //初始化KEY
#endif
#ifdef LPUART_DEBUGLED0(0); //先点亮红灯
#endif
#ifdef EXTIX_DEBUGEXTIX_Init(); //初始化外部中断
#endifWDOG1_Init(3,2); //初始化看门狗1,2秒溢出,提前1秒进入中断,方便喂狗LED0(0); //先点亮LED灯delay_ms(300); //延时300ms再初始化看门狗,LED0的变化"可见"while(1){LED0(1); //关闭DS0,如不复位,DS0将一直处于关闭状态.delay_ms(100);
#ifdef EXTIX_DEBUGprintf("Int example driver!\r\n");delay_ms(1000);
#endif
#ifdef KEY_DEBUGkey=KEY_Scan(0); //得到键值if(key){ switch(key){ case WKUP_PRES: //控制LED0,LED1互斥点亮led1sta=!led1sta;led0sta=!led1sta;break;case KEY2_PRES: //控制LED0翻转led0sta=!led0sta;break;case KEY1_PRES: //控制LED1翻转 led1sta=!led1sta;break;case KEY0_PRES: //同时控制LED0,LED1翻转 led0sta=!led0sta;led1sta=!led1sta;break;}LED0(led0sta); //控制LED0状态LED1(led1sta); //控制LED1状态}else delay_ms(10);
#endif // KEY_DEBUG
#ifdef LPUART_DEBUGif(LPUART_RX_STA&0x8000){ len=LPUART_RX_STA&0x3fff;//得到此次接收到的数据长度printf("\r\n发送的消息为:\r\n");LPUART_WriteBlocking(LPUART1,LPUART_RX_BUF,len);//发送接收到的数据printf("\r\n\r\n");//插入换行LPUART_RX_STA=0;}else{times++;if(times%5000==0){printf("\r\nALIENTEK RT1052开发板 串口实验\r\n");}if(times%200==0)printf("请输入数据,以回车键结束\r\n"); if(times%30==0)LED0_Toggle;//闪烁LED,提示系统正在运行.delay_ms(10); }
#endif // LPUART_DEBUG}
}
该函数通过 LED0(DS0)来指示是否正在初始化。而 LED1(DS1)用来指示是否发生了中断。我们先让 LED0 亮 300ms,然后关闭以用于判断是否有复位发生了。在初始化 WDOG1(2 秒溢出,提前 1 秒中断)之后,我们回到死循环,关闭 LED0,并等待看门狗中断的触发/复位。
编译,报错???
哦哦哦哦哦!!!!!忘记导入看门狗的FSL库了。
在Keil MDK FSLLIB里添加对应文件

编译,OK!没问题,下载代码
可以看到 DS0 亮一下之后熄灭,紧接着 DS1 开始不停的闪烁。每 2 秒亮灭一次,和我们预期的一
致,说明我们的实验是成功的。
总结
看门狗其实以及很形象了,就像一条狗一直叫,饿了就不叫了,但是你喂它吃一口,它就继续叫。我需要它一直叫看门,所以隔一段时间就喂一口。如果太长时间没有喂就饿死了,但是过一会就生了一条新狗接着叫。哈哈哈哈,反正我是这么理解的。
OK!谢谢大家!
