看门狗框架分析
核心数据结构说明
根据数据结构在具体的芯片上仅仅需要提供2个接口实现
c
#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函数;
c
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_OPS
device->ops = &wdt_ops;
#else
device->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;//控制看门狗
#endif
device->user_data = data;
/* register a character device */
return rt_device_register(device, name, flag);
}
核心层驱动分析
1.初始化rt_watchdog_init
直接调用底层的init函数
c
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
c
static rt_err_t wdt_init(rt_watchdog_t *wdt)
{
return RT_EOK;
}
2.打开rt_watchdog_open
无具体操作
c
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停止看门狗
c
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
c
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
c
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!"
#endif
if(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!"
#endif
break;
/* 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 接口
c
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;//设置预分频器为256
stm32_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;
#endif
stm32_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);
