(3)看门狗 WDT:基于GD32F303RCT6单片机在RT-Thread下的零基础学习记录

使用看门狗功能,一如既往地需要先使用ENV工具,打开看门狗功能,如下图

开启后重新编译工程,

然后当我开开心心地去粘贴官方的demo(WATCHDOG设备 (rt-thread.org))(文章最后会放出我稍微改动的代码) 并编译运行才发现有问题!

在keil工程中检索错误码发现:

这里是一个判断传入的设置喂狗时间的值的大小,如果不在 g_wdt_dev.min_threshold_s和 g_wdt_dev.max_threshold_s之间时候会报错invalid param@%u.,那么我就debug一下看看这个max_threshold_s和min_threshold_s是多少,结果是0

浏览了一下,在这个函数的上面有一个初始化函数 :

static rt_err_t gd32_wdt_init(rt_watchdog_t *wdt)
{rcu_osci_on(RCU_IRC40K);if (ERROR == rcu_osci_stab_wait(RCU_IRC40K)){LOG_E("failed init IRC40K clock for free watchdog.");return -RT_EINVAL;}g_wdt_dev.min_threshold_s = 1;g_wdt_dev.max_threshold_s = (0xfff << 8) / 40000;LOG_I("threshold section [%u, %d]", \g_wdt_dev.min_threshold_s, g_wdt_dev.max_threshold_s);fwdgt_write_enable();fwdgt_config(0xfff, FWDGT_PSC_DIV256);fwdgt_enable();return 0;
}

 由此看来,是因为没有执行到这个初始化函数,所以min_threshold_s和max_threshold_s是0,而如果初始化的话,值应该是上面代码中的。然而gd32_wdt_init这个函数需要用户去调用的吗?这个是我的疑问,为了方便,我直接对gd32_wdt_control函数中那两个变量赋值了,发现能正常使用。


static rt_err_t gd32_wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
{rt_uint32_t param;g_wdt_dev.min_threshold_s = 1;g_wdt_dev.max_threshold_s = (0xfff << 8) / 40000;switch (cmd){case RT_DEVICE_CTRL_WDT_KEEPALIVE:fwdgt_counter_reload();break;case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:param = *(rt_uint32_t *) arg;if ((param > g_wdt_dev.max_threshold_s) || \(param < g_wdt_dev.min_threshold_s)){LOG_E("invalid param@%u.", param);return -RT_EINVAL;}else{g_wdt_dev.current_threshold_s = param;}fwdgt_write_enable();fwdgt_config(param * 40000 >> 8, FWDGT_PSC_DIV256);fwdgt_write_disable();break;case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:*(rt_uint32_t *)arg = g_wdt_dev.current_threshold_s;break;case RT_DEVICE_CTRL_WDT_START:fwdgt_enable();break;default:LOG_W("This command is not supported.");return -RT_ERROR;}return RT_EOK;
}

 

稍作修改的demo,改动如下:将原本空闲线程改成了一个自己建立的优先级较高的线程,同时避免了空闲时反复执行喂狗(只要在设置的2秒内喂狗一次就可以了),如下,设置了1.5s喂狗一次:

/** 程序清单:这是一个独立看门狗设备使用例程* 例程导出了 wdt_sample 命令到控制终端* 命令调用格式:wdt_sample wdt* 命令解释:命令第二个参数是要使用的看门狗设备名称,为空则使用例程默认的看门狗设备。* 程序功能:程序通过设备名称查找看门狗设备,然后初始化设备并设置看门狗设备溢出时间。*           然后设置空闲线程回调函数,在回调函数里会喂狗。
*/#include <rtthread.h>
#include <rtdevice.h>#define WDT_DEVICE_NAME    "wdt"    /* 看门狗设备名称 */static rt_device_t wdg_dev;         /* 看门狗设备句柄 */
rt_thread_t FeedDog = NULL; //创建一个喂狗的线程句柄
static void fd_entry(void *parameter)
{while(1){rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);rt_kprintf("feed the dog!\n ");rt_thread_mdelay(1500);//这里是实现了1.5s喂狗一次,如果此值大于2s会导致重启}
}static int wdt_sample(int argc, char *argv[])
{rt_err_t ret = RT_EOK;rt_uint32_t timeout = 2;        /* 溢出时间,单位:秒,也就是要在这个值内喂狗一次 */char device_name[RT_NAME_MAX];/* 判断命令行参数是否给定了设备名称 */if (argc == 2){rt_strncpy(device_name, argv[1], RT_NAME_MAX);}else{rt_strncpy(device_name, WDT_DEVICE_NAME, RT_NAME_MAX);}/* 根据设备名称查找看门狗设备,获取设备句柄 */wdg_dev = rt_device_find(device_name);if (!wdg_dev){rt_kprintf("find %s failed!\n", device_name);return RT_ERROR;}/* 设置看门狗溢出时间 */ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);if (ret != RT_EOK){rt_kprintf("set %s timeout failed!\n", device_name);return RT_ERROR;}/* 启动看门狗 */ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL);if (ret != RT_EOK){rt_kprintf("start %s failed!\n", device_name);return -RT_ERROR;}FeedDog = rt_thread_create("fd",fd_entry,NULL,512,1,5);rt_thread_startup(FeedDog);return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(wdt_sample, wdt sample);

(PS:这个代码无需放到main函数中执行,只需在命令行调用即可,方法见前面两章) 

当设置大于2s喂狗(2.5s):

static void fd_entry(void *parameter)
{while(1){rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);rt_kprintf("feed the dog!\n ");rt_thread_mdelay(2500);}
}

系统被狗重启了。说明看门狗是有效的。

最后,那个gd32_wdt_init为什么没有执行呢?有没有人能告诉我,或者这个就是个BUG?

 最后的最后,贴上一个我改成了无需命令行开启的看门狗:


#include <stdio.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>/* defined the LED2 pin: PF0 */
#define LED2_PIN GET_PIN(F, 0)
#include <rtdevice.h>
#define WDT_DEVICE_NAME    "wdt"    /* 看门狗设备名称 */
static rt_device_t wdg_dev;         /* 看门狗设备句柄 */
rt_thread_t FeedDog = NULL; //创建一个喂狗的线程句柄static void fd_entry(void *parameter)
{while(1){rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);rt_kprintf("feed the dog!\n ");rt_thread_mdelay(1500);}
}static int wdt_sample()
{rt_err_t ret = RT_EOK;rt_uint32_t timeout = 2;        /* 溢出时间,单位:秒 */char device_name[RT_NAME_MAX];rt_strncpy(device_name, WDT_DEVICE_NAME, RT_NAME_MAX);/* 根据设备名称查找看门狗设备,获取设备句柄 */wdg_dev = rt_device_find(device_name);if (!wdg_dev){rt_kprintf("find %s failed!\n", device_name);return RT_ERROR;}/* 设置看门狗溢出时间 */ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);if (ret != RT_EOK){rt_kprintf("set %s timeout failed!\n", device_name);return RT_ERROR;}/* 启动看门狗 */ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL);if (ret != RT_EOK){rt_kprintf("start %s failed!\n", device_name);return -RT_ERROR;}FeedDog = rt_thread_create("fd",fd_entry,NULL,512,1,5);rt_thread_startup(FeedDog);return ret;
}
//extern int wdt_sample();
int main(void)
{int count = 1;/* set LED2 pin mode to output */rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);wdt_sample();while (count++){rt_pin_write(LED2_PIN, PIN_HIGH);rt_thread_mdelay(500);rt_pin_write(LED2_PIN, PIN_LOW);rt_thread_mdelay(500);}return RT_EOK;
}

本文链接:https://my.lmcjl.com/post/8719.html

展开阅读全文

4 评论

留下您的评论.