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

发布于:2023-02-17 ⋅ 阅读:(842) ⋅ 点赞:(0)

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

8e67bf9005284d2ea3a68f0b383cb335.png

开启后重新编译工程,e0c734233c634bfca4ed0d79251090cd.png

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

ce2cdf592e4447a2bcbb76360e6ef1d3.png

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

b5a5dbf824374e4085a9bca34a48116e.png

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

f76397062fdf4da1b351201eb8242cf1.png

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

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;
}

 98573e39731e462091cf07563b0de27e.png

稍作修改的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);
	}
}

d35e0289bf684816b6d198af4cf0a20d.png

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

最后,那个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;
}


网站公告

今日签到

点亮在社区的每一天
去签到