使用看门狗功能,一如既往地需要先使用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;
}