RT-Thread完整版如何使用Watchdog总结

今天在RT-Thread完整版开发过程中引入watchdog,踩到一个坑,系统一直重启,喂狗一直失败,搞了一天才解决,总结一下。

我的RT-Thread完整版系统是最新版4.0.3(截止2020年12月30日),版本信息如下:

\ | /
- RT -     Thread Operating System
 / | \     4.0.3 build Dec 30 2020
 2006 - 2020 Copyright by rt-thread team

我去官网参考例程,写了一个watchdog demo,源代码如下:

#define WDT_DEVICE_NAME    "wdt"    /* 看门狗设备名称 */
static rt_device_t wdg_dev;         /* 看门狗设备句柄 */

static void idle_hook(void)
{
    /* 在空闲线程的回调函数里喂狗 */
    rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
    rt_kprintf("feed the dog!\n ");
    
}

int main(void)
{
    int count = 1;
    
        rt_uint32_t timeout = 1;        /* 溢出时间,单位:秒 */
        rt_err_t ret = RT_EOK;
        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_NULL)
    {
        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;
    }
        rt_thread_mdelay(600);
        /* 设置空闲线程回调函数 */
    rt_thread_idle_sethook(idle_hook);

    return RT_EOK;
}

然后下载代码运行,发现系统在反复重启。但是从main()函数以及idle_hook()函数打印看,看门狗的控制,启动都没有问题,  rt_kprintf("feed the dog!\n ");也打印出来了,

因此深入系统内核源代码进行分析drv_wdt.c为watchdog的驱动源代码,我在里面加了log进行分析,如下:

static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
{
    switch (cmd)
    {
        /* feed the watchdog */
    case RT_DEVICE_CTRL_WDT_KEEPALIVE:
                rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_KEEPALIVE 1\n");
        if(HAL_IWDG_Refresh(&stm32_wdt.hiwdg) != HAL_OK)
        {        rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_KEEPALIVE 2\n");
            LOG_E("watch dog keepalive fail.");
        }
        break;
        /* set watchdog timeout */
    case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
                rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_SET_TIMEOUT 1\n");
#if defined(LSI_VALUE)
                rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_SET_TIMEOUT 2\n");
        if(LSI_VALUE)
        {
                        //rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_SET_TIMEOUT 3");
            stm32_wdt.hiwdg.Init.Reload = (*((rt_uint32_t*)arg)) * LSI_VALUE / 256 ;
                    rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_SET_TIMEOUT 3 stm32_wdt.hiwdg.Init.Reload=%d   LSI_VALUE=%d\n",stm32_wdt.hiwdg.Init.Reload,LSI_VALUE);
        }
        else
        {
            rt_kprintf("Please define the value of LSI_VALUE!\n");
        }
        if(stm32_wdt.hiwdg.Init.Reload > 0xFFF)
        {
            rt_kprintf("wdg set timeout parameter too large, please less than %ds",0xFFF * 256 / LSI_VALUE);
            return -RT_EINVAL;
        }
#else
  #error "Please define the value of LSI_VALUE!"
#endif
        if(stm32_wdt.is_start)
        {
                        rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_SET_TIMEOUT 4\n");
            if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK)
            {
                rt_kprintf("wdg set timeout failed.");
                return -RT_ERROR;
            }
        }
        break;
    case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
#if defined(LSI_VALUE)
        if(LSI_VALUE)
        {
            (*((rt_uint32_t*)arg)) = stm32_wdt.hiwdg.Init.Reload * 256 / LSI_VALUE;
        }
        else
        {
            LOG_E("Please define the value of LSI_VALUE!");
        }
#else
  #error "Please define the value of LSI_VALUE!"
#endif
        break;
    case RT_DEVICE_CTRL_WDT_START:
                rt_kprintf("wdt_control() RT_DEVICE_CTRL_WDT_START 1\n");
        if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK)
        {
            rt_kprintf("wdt start failed.");
            return -RT_ERROR;
        }
        stm32_wdt.is_start = 1;
        break;
    default:
        LOG_W("This command is not supported.");
        return -RT_ERROR;
    }
    return RT_EOK;
}

从上面的打印看,启动,设置溢出时间,喂狗也没有走到报错流程。

后来和别人交流,发现是因为:

int rt_wdt_init(void)
{
#if defined(SOC_SERIES_STM32H7)
    stm32_wdt.hiwdg.Instance = IWDG1;
#else
    stm32_wdt.hiwdg.Instance = IWDG;
#endif
    stm32_wdt.hiwdg.Init.Prescaler = IWDG_PRESCALER_256;

    stm32_wdt.hiwdg.Init.Reload = 0x00000FFF;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) \
    || defined(SOC_SERIES_STM32H7)
    stm32_wdt.hiwdg.Init.Window = 0x00000FFF;
#endif
    stm32_wdt.hiwdg.Init.Window = 0x00000FFF;//加这一句!!我是stm32L053芯片
    stm32_wdt.is_start = 0;

发现缺少stm32_wdt.hiwdg.Init.Window = 0x00000FFF,因为STM32L0的芯片没有走上面的流程,因此这个算RT-Thread内核的一个bug,我猜可能是因为STM32L系列芯片用的人太少了导致的。

再总结一下stm32的独立看门狗的相关知识:

独立看门狗的时钟由独立的RC振荡器LSI提供,即使主时钟发生故障它仍然有效,非常独立。LSI的频率一般在30~60KHZ之间,根据温度和工作场合会有一定的漂移,我们一般取40KHZ,所以独立看门狗的定时时间并一定非常精确,只适用于对时间精度要求比较低的场合。

typedef struct
{
  uint32_t Prescaler;  /*!< Select the prescaler of the IWDG.
                            This parameter can be a value of @ref IWDG_Prescaler */

  uint32_t Reload;     /*!< Specifies the IWDG down-counter reload value.
                            This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */

  uint32_t Window;     /*!< Specifies the window value to be compared to the down-counter.
                            This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */

} IWDG_InitTypeDef;

 uint32_t Reload; / *!<指定IWDG减计数器重载值。

                            此参数必须是介于Min_Data = 0和Max_Data = 0x0FFF之间的数字* / 

 

 

独立看门狗的计数器是一个12位的递减计数器,最大值为0XFFF,当计数器减到0时,会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到0之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗。

stm32l0xx_hal_iwdg.c

HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
{
  uint32_t tickstart;

  /* Check the IWDG handle allocation */
  if(hiwdg == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
  assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));
  assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));
  assert_param(IS_IWDG_WINDOW(hiwdg->Init.Window));

  /* Enable IWDG. LSI is turned on automaticaly */
  __HAL_IWDG_START(hiwdg);

  /* Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers by writing
  0x5555 in KR */
  IWDG_ENABLE_WRITE_ACCESS(hiwdg);

  /* Write to IWDG registers the Prescaler & Reload values to work with */
  hiwdg->Instance->PR = hiwdg->Init.Prescaler;
  hiwdg->Instance->RLR = hiwdg->Init.Reload;

  /* Check pending flag, if previous update not done, return timeout */
  tickstart = HAL_GetTick();

   /* Wait for register to be updated */
  while(hiwdg->Instance->SR != RESET)
  {
    if((HAL_GetTick() - tickstart ) > HAL_IWDG_DEFAULT_TIMEOUT)
    {
      return HAL_TIMEOUT;
    }
  }

  /* If window parameter is different than current value, modify window 
  register */
  if(hiwdg->Instance->WINR != hiwdg->Init.Window)
  {
    /* Write to IWDG WINR the IWDG_Window value to compare with. In any case,
    even if window feature is disabled, Watchdog will be reloaded by writing 
    windows register */
    hiwdg->Instance->WINR = hiwdg->Init.Window;
  }
  else
  {
    /* Reload IWDG counter with value defined in the reload register */
    __HAL_IWDG_RELOAD_COUNTER(hiwdg);
  }

  /* Return function status */
  return HAL_OK;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的Rt-thread看门狗程序的示例: ``` #include <rtthread.h> #define WATCHDOG_TIMEOUT (5000) // 5秒超时时间 /* 定义看门狗设备对象 */ static rt_device_t watchdog_dev; /* 定义看门狗线程 */ static rt_thread_t watchdog_thread = RT_NULL; static void watchdog_thread_entry(void* parameter) { rt_uint32_t timeout; while (1) { /* 表示没有任务需要看门狗保护 */ if (rt_thread_self()->stat == RT_THREAD_CLOSE) { rt_kprintf("No task to watch!\n"); break; } /* 看门狗开始计时 */ timeout = rt_device_control(watchdog_dev, RT_DEVICE_CTRL_WDT_START, (void*)WATCHDOG_TIMEOUT); /* 如果返回0,则表示看门狗时间到达 */ if (timeout == 0) { rt_kprintf("Watchdog timeout\n"); rt_hw_reset(); // 通过硬件复位系统 } rt_thread_mdelay(WATCHDOG_TIMEOUT / 2); // 线程等待WATCHDOG_TIMEOUT / 2毫秒 } } int watchdog_init(void) { watchdog_dev = rt_device_find("wdt"); // 找到看门狗设备 /* 如果找不到看门狗设备,则返回错误 */ if (watchdog_dev == RT_NULL) { rt_kprintf("Watchdog device not found!\n"); return -1; } /* 控制看门狗的设备注册为看门狗模式 */ rt_device_control(watchdog_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, (void*)WATCHDOG_TIMEOUT); rt_device_control(watchdog_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL); // 看门狗计时开始 /* 创建看门狗线程 */ watchdog_thread = rt_thread_create("watchdog", watchdog_thread_entry, RT_NULL, 1024, 25, 10); if (watchdog_thread != RT_NULL) { rt_thread_startup(watchdog_thread); rt_kprintf("Watchdog thread create success!\n"); return 0; } rt_kprintf("Watchdog thread create fail!\n"); return -1; } /* 注册看门狗初始化函数到Rt-thread */ INIT_APP_EXPORT(watchdog_init); ``` 这个看门狗程序中,我们使用Rt-thread提供的看门狗接口来实现计时功能并防止系统因为某个线程的执行超时而崩溃的情况发生。当超时时间到达时,看门狗会向外部硬件发送一个复位信号,从而复位整个系统。这个看门狗程序需要在初始化的时候调用 watchdog_init() 函数来完成注册到Rt-thread内核的初始化过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值