第十章 看门狗定时器实验
在上一章实验中,简单介绍了外部中断的相关应用,本章将介绍Kendryte K210的WDT外设,即看门狗定时器。通过本章的学习,读者将学习到SDK编程技术使用Kendryte K210的WDT外设。 本章分为如下几个小节: 10.1 WDT介绍 10.2 硬件设计 10.3 程序设计 10.4 运行验证
10.1 WDT介绍 WDT是APB的一种从外设,并且也是 “同步化硬件组件设计”的组成部分。具有两个WDT, 分别为WDT0、WDT1看门狗定时器主要包含模块有一个 APB 从接口,一个当前计数器同步的寄存器模块,一个随着计数器递减而中断或者系统重置的模块和逻辑控制电路,一个同步时钟域来为异步时钟同步做支持的模块。 看门狗定时器支持如下设置: 1. APB总线宽度可配置为8、16和32位 2. 时钟计数器从某一个设定的值递减到0来指示时间的计时终止 3.当一个时钟超时,WDT可以执行以下任务:1.产生一个系统复位信号;2.产生一个中断。如果没有清除中断,第二次中断它会产生一个系统复位信号 4.占空比可调节 5.可编程超时周期 6.外部异步时钟支持。当该项功能启用时,也可以产生时钟中断和系统复位信号,即使在APB总线时钟关闭的情况下 看门狗主要用于在应用程序崩溃且最终进入不可恢复的状态时重启系统,一旦看门狗被开启,应用程序就应当每间隔一段不超过设定的时间对看门狗进行喂狗操作,否则,看门狗产生一个看门狗超时中断,在看门狗超时中断回调函数中,应用程序还有一次喂狗的机会,若应用程序使用没有对看门狗进行喂狗操作,看门狗将在下一次超时时产生一次看门狗复位,使得系统重新启动。 Kendryte K210提供了操作WDT的函数,这里我们只讲述本实验用到的函数,这些函数介绍如下: 1,wdt_init函数 该函数用来启动两个看门狗定时器,包括设置看门狗定时器超时时间、回调函数和回调参数等,如下代码所示: /* 函数原型 */ uint32_t wdt_init(wdt_device_number_t id, uint64_t time_out_ms, plic_irq_callback_t on_irq, void *ctx); /** * wdt_init启动定时器的id配置参数 */ typedef enum _wdt_device_number { WDT_DEVICE_0, WDT_DEVICE_1, WDT_DEVICE_MAX, } wdt_device_number_t; 下面我们来讲解一下这几个变量的作用。 ①:参数id为启动看门狗定时器编号,id为WDT_DEVICE_0或WDT_DEVICE_1。 ②:参数time_out_ms为看门狗超时时间,单位为毫秒。 ③:参数on_irq为看门狗超时回调函数。 ④:参数ctx为看门狗超时回调函数的参数。 该函数返回看门狗实际超时时间,我们例程中也有使用到。 2,wdt_feed函数 该函数用来重启看门狗计时器,也就是给对应看门狗喂狗,函数代码如下所示: void wdt_feed(wdt_device_number_t id); 该函数只有一个参数,用于选择看门狗喂狗的id。 3,wdt_clear_interrupt函数 该函数用来清除对应看门狗的中断,当在中断函数中清除对应看门狗的中断可避免系统复位,该函数原型如下所示: void wdt_clear_interrupt(wdt_device_number_t id); 该函数只有一个参数,用于选择要清除中断的看门狗id。
10.2 硬件设计 10.2.1 例程功能 1. 创建看门狗WDT0,并配置其超时时间为2000毫秒 2. 按下KEY0按键可对WDT对象进行喂狗操作 3. 若喂狗次数达5次,则停止WDT0并退出 10.2.2 硬件资源 1. 独立按键 KEY0按键 - IO18 2. 看门狗0 10.2.3 原理图 本章实验内容,主要讲解WDT外设的使用,无需关注原理图。
10.3 程序设计 10.3.1 看门狗驱动代码 看门狗定时器驱动源码包括两个文件:watchdog.c和watchdog.h,我们先介绍watchdog.h。 /* * 默认WDT_TIMEOUT_REBOOT为1,看门狗如果没有在设置的时间内feed,则超时后重启系统。 * 如果修改WDT_TIMEOUT_REBOOT为0,则看门狗超时后只会串口发信息提示超时,不会重启。 */ #define WDT_TIMEOUT_REBOOT 1 可以通过设置WDT_TIMEOUT_REBOOT这个宏来决定在看门狗超时后是否进行系统复位,我们默认是设置复位, watchdog.h内容比较少,我们直接看对应的源文件。 void watchdog_init(void) { /* 系统中断初始化 */ plic_init(); sysctl_enable_irq(); /* 启动看门狗,设置超时时间为2秒后调用中断函数wdt0_irq_cb */ int timeout = wdt_init(WDT_DEVICE_0, 2000, wdt0_irq_cb, NULL); /* 打印看门狗实际超时的时间 */ printf("wdt timeout is %d ms!\n", timeout); } 这个是看门狗定时器初始化代码,因为看门狗需要用到中断功能,所以首先我们需要先初始化中断并使能,然后选择启动看门狗,这里定义了变量timeout用于获取实际的看门狗超时时间并打印出来,实际超时时间一般会比设置值稍微大些,这里需要注意的是中断回调函数是在实际超时时间的一半调用的,比如我们超时时间设置的是2秒,实际超时时间是2.56秒,那么如果在1.28秒内没有喂狗,则会调用中断回调函数。 int wdt0_irq_cb(void *ctx) { #if WDT_TIMEOUT_REBOOT printf("%s:The system will reboot soon!\n", __func__); while(1); #else printf("%s:The system is busy but not reboot!\n", __func__); wdt_clear_interrupt(WDT_DEVICE_0); #endif return 0; } 看门狗的中断回调函数,当未及时喂狗就会进入中断服务函数wdt0_irq_cb,执行中断程序,这里我们用WDT_TIMEOUT_REBOOT这个宏来执行不同函数,默认WDT_TIMEOUT_REBOOT设置为1,看门狗超时就会系统复位,当它设置为0时,看门狗超时后会在中断函数清除中断,这样就只是会打印信息,不会重启。 10.3.2 main.c代码 main.c中的代码如下所示: int main(void) { uint8_t key; key_init(); /* 按键初始化 */ watchdog_init(); /* 看门狗定时器初始化 */ /* 打印系统启动信息 */ printf("system start!\n"); /* 记录feed的次数 */ int feed_times = 0; while(feed_times < 5) { key = key_scan(0); /* 得到键值 */ if (key == KEY0_PRES) { feed_times ++; /* 打印feed的次数 */ printf("Feed WDT0 %d times!\n", feed_times); /* 重置看门狗的计时器,重新开始计时 */ wdt_feed(WDT_DEVICE_0); } else msleep(1); } wdt_stop(WDT_DEVICE_0); /* 停止看门狗 */ } 可以看到,首先是初始化按键和看门狗,然后使用printf打印系统启动信息,这样系统每次启动我们都能知道,然后定义了feed_times这个变量用于统计喂狗次数,当我们每按下KEY0时,就会进行一次喂狗,每次喂狗都会打印提示信息,且在连续喂狗达到5次后,停止WDT并结束程序。
10.4 运行验证 将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,此时,若连续在看门狗实际超时的一半内按下板载的KEY0按键进行喂狗操作,则能看到“串行终端”窗口打印输出WDT0被喂狗的次数提示,如下图所示: 图10.4.1 “串行终端”窗口打印输出喂狗次数提示 若喂狗次数达到5次,则脚本程序运行完毕。 但若没有在看门狗实际超时的一半内按下板载的KEY0按键进行喂狗操作,则“串行终端”将打印输出WDT超时的提示,并且WDT将在下一次超时时对Kendryte K210进行复位,此时,就能从“串行终端”窗口看到系统重新启动的信息,如下图所示: 图10.4.2 系统重启
|