RT thread 的看门狗框架分析

看门狗框架分析

核心数据结构说明

根据数据结构在具体的芯片上仅仅需要提供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);
相关推荐
Xxtaoaooo13 天前
Rust Actix-web框架源码解析:基于Actor模型的高性能Web开发
rust·源码分析·高性能·并发安全·actix-web
冷凝雨14 天前
FreeRTOS源码学习(一)内存管理heap_1、heap_3
嵌入式·c·freertos·内存管理·源码分析
lypzcgf1 个月前
Coze源码分析-资源库-编辑数据库-前端源码-核心组件
前端·数据库·源码分析·coze·coze源码分析·ai应用平台·agent平台
maki0771 个月前
VR大空间资料 04 —— VRAF使用体验和源码分析
android·vr·虚幻·源码分析
maki0771 个月前
VR大空间资料 03 —— VRGK使用体验和源码分析
android·vr·虚幻·源码分析·oculus·htc vive·vrgk
lypzcgf1 个月前
Coze源码分析-资源库-编辑工作流-后端源码-流程/技术/总结
go·源码分析·工作流·coze·coze源码分析·ai应用平台·agent平台
lxsy2 个月前
spring-ai-alibaba-deepresearch 学习(十三)——ResearcherNode
java·源码分析·deepresearch·ai-alibaba
玩代码3 个月前
Spring Boot2 静态资源、Rest映射、请求映射源码分析
java·spring boot·源码分析·spring boot2
摘星编程5 个月前
抽象工厂模式深度解析:从原理到与应用实战
设计模式·抽象工厂模式·源码分析·软件架构·实战案例