RT thread 的看门狗框架分析
看门狗框架分析
核心数据结构说明
根据数据结构在具体的芯片上仅仅需要提供2个接口实现
#define RT_DEVICE_CTRL_WDT_GET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 1) /* 获取看门狗超时时间(以秒为单位) get timeout(in seconds) */
#define RT_DEVICE_CTRL_WDT_SET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 2) /* 设置看门狗超时时间(以秒为单位) set timeout(in seconds) */
#define RT_DEVICE_CTRL_WDT_GET_TIMELEFT (RT_DEVICE_CTRL_BASE(WDT) + 3) /* 获取重启前的剩余时间(以秒为单位) get the left time before reboot(in seconds) */
#define RT_DEVICE_CTRL_WDT_KEEPALIVE (RT_DEVICE_CTRL_BASE(WDT) + 4) /* 喂狗(刷新看门狗)refresh watchdog */
#define RT_DEVICE_CTRL_WDT_START (RT_DEVICE_CTRL_BASE(WDT) + 5) /* 启动看门狗 start watchdog */
#define RT_DEVICE_CTRL_WDT_STOP (RT_DEVICE_CTRL_BASE(WDT) + 6) /* 停止看门狗 stop watchdog */struct rt_watchdog_ops;
/*** @brief 看门狗设备结构体* */
struct rt_watchdog_device
{struct rt_device parent;const struct rt_watchdog_ops *ops;
};
typedef struct rt_watchdog_device rt_watchdog_t;/*** 2个接口函数:init和control* init:初始化看门狗* control:控制看门狗,包括设置超时时间、获取超时时间、获取剩余时间、喂狗、启动看门狗、停止看门狗*/
struct rt_watchdog_ops
{rt_err_t (*init)(rt_watchdog_t *wdt);rt_err_t (*control)(rt_watchdog_t *wdt, int cmd, void *arg);
};
看门狗注册
在看门狗框架层提供了4个api函数;
rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,const char *name,rt_uint32_t flag,void *data)
{struct rt_device *device;RT_ASSERT(wtd != RT_NULL);device = &(wtd->parent);device->type = RT_Device_Class_WDT;//看门狗设备类device->rx_indicate = RT_NULL;device->tx_complete = RT_NULL;#ifdef RT_USING_DEVICE_OPSdevice->ops = &wdt_ops;
#elsedevice->init = rt_watchdog_init;//初始化看门狗device->open = rt_watchdog_open;//打开看门狗device->close = rt_watchdog_close;//关闭看门狗device->read = RT_NULL;device->write = RT_NULL;device->control = rt_watchdog_control;//控制看门狗
#endifdevice->user_data = data;/* register a character device */return rt_device_register(device, name, flag);
}
核心层驱动分析
1.初始化rt_watchdog_init
直接调用底层的init函数
static rt_err_t rt_watchdog_init(struct rt_device *dev)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;if (wtd->ops->init){return (wtd->ops->init(wtd));}return (-RT_ENOSYS);
}
stm32的init
static rt_err_t wdt_init(rt_watchdog_t *wdt)
{return RT_EOK;
}
2.打开rt_watchdog_open
无具体操作
static rt_err_t rt_watchdog_open(struct rt_device *dev, rt_uint16_t oflag)
{return (RT_EOK);
}
3.rt_watchdog_close
wtd->ops->control停止看门狗
static rt_err_t rt_watchdog_close(struct rt_device *dev)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;if (wtd->ops->control(wtd, RT_DEVICE_CTRL_WDT_STOP, RT_NULL) != RT_EOK){rt_kprintf(" This watchdog can not be stoped\n");return (-RT_ERROR);}return (RT_EOK);
}
4.rt_watchdog_control
直接调用底层的wtd->ops->control
static rt_err_t rt_watchdog_control(struct rt_device *dev,int cmd,void *args)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;return (wtd->ops->control(wtd, cmd, args));
}
5.这里学习下STM32的看门狗
1. 时钟源
- IWDG 使用 内部低速 RC 振荡器(LSI),典型频率约为 40 kHz(范围通常为 30~60 kHz,具体见数据手册)。
- LSI 不受系统时钟控制,即使主系统时钟失效,IWDG 仍可正常工作。
2.超时时间计算公式
IWDG 的超时时间由两个寄存器决定:
- 预分频器(Prescaler):可选值为 4、8、16、32、64、128、256。
- 重装载值(Reload Value):范围 0x000 ~ 0xFFF(即 0~4095)。
超时时间计算公式为:

其中:
- fLS**I≈40kHz
- Prescaler 取值如 4、8、…、256(对应寄存器值 0~7)
- Reload 是 12 位整数(最大 4095)
3. 示例:设置 1 秒超时
假设 fLS**I=40kHz,目标 T=1s
选择 Prescaler = 32(寄存器值 = 3),则:

6.继续STM32的分析控制函数
这里就是实现了几个控制指令
1. 喂狗RT_DEVICE_CTRL_WDT_KEEPALIVE
2. 设置看门狗超时时间 RT_DEVICE_CTRL_WDT_SET_TIMEOUT
3. 获取看门狗超时时间RT_DEVICE_CTRL_WDT_GET_TIMEOUT
4. 启动看门狗 RT_DEVICE_CTRL_WDT_START
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:if(HAL_IWDG_Refresh(&stm32_wdt.hiwdg) != HAL_OK)//刷新看门狗{LOG_E("watch dog keepalive fail.");}break;/* set watchdog timeout 设置看门狗超时时间 */case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
#if defined(LSI_VALUE)if(LSI_VALUE){stm32_wdt.hiwdg.Init.Reload = (*((rt_uint32_t*)arg)) * LSI_VALUE / 256 ;//根据LSI_VALUE计算重新加载值;这里得到了重载值}else{LOG_E("Please define the value of LSI_VALUE!");}if(stm32_wdt.hiwdg.Init.Reload > 0xFFF)//重载的合法性判定{LOG_E("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!"
#endifif(stm32_wdt.is_start)//这里就是在修改了重载值后,重新初始化一次{if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK){LOG_E("wdg set timeout failed.");return -RT_ERROR;}}break;/* get watchdog timeout 获取看门狗超时时间 */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;//获取看门狗超时时间;这里根据重载值和LSI_VALUE计算超时时间}else{LOG_E("Please define the value of LSI_VALUE!");}
#else#error "Please define the value of LSI_VALUE!"
#endifbreak;/* start watchdog 启动看门狗 */case RT_DEVICE_CTRL_WDT_START:if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK){LOG_E("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;
}
7.注册看门狗
- 是初始化 hiwdg结构体;
- 设置wdt_init 和 wdt_control 接口
int rt_wdt_init(void)
{
#if defined(SOC_SERIES_STM32H7)stm32_wdt.hiwdg.Instance = IWDG1;
#elsestm32_wdt.hiwdg.Instance = IWDG;
#endifstm32_wdt.hiwdg.Init.Prescaler = IWDG_PRESCALER_256;//设置预分频器为256stm32_wdt.hiwdg.Init.Reload = 0x00000FFF;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G4)|| defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) \|| defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0)stm32_wdt.hiwdg.Init.Window = 0x00000FFF;
#endifstm32_wdt.is_start = 0;ops.init = &wdt_init;ops.control = &wdt_control;stm32_wdt.watchdog.ops = &ops;/* register watchdog device */if (rt_hw_watchdog_register(&stm32_wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK){LOG_E("wdt device register failed.");return -RT_ERROR;}LOG_D("wdt device register success.");return RT_EOK;
}
INIT_BOARD_EXPORT(rt_wdt_init);

