查看: 533|回复: 0

[分享] rtthread看门狗框架分析

[复制链接]
  • TA的每日心情
    开心
    昨天 17:36
  • 签到天数: 188 天

    连续签到: 7 天

    [LV.7]常住居民III

    47

    主题

    6517

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    10126
    最后登录
    2025-7-18
    发表于 2025-2-25 17:37:13 | 显示全部楼层 |阅读模式

           看门狗,是一个确保嵌入式平台稳定性的必要措施,虽然正常操作不应触发看门狗,但在一些特殊场景,比如ESD等情况导致的系统跑飞,就需要看门狗做复位了。

    源码分析

    源码路径

    view plaincopy to clipboardprint?

    • components\drivers\watchdog\watchdog.c  

    注册入口

    view plaincopy to clipboardprint?

    • #ifdef RT_USING_DEVICE_OPS  
    • const static struct rt_device_ops wdt_ops =  
    • {  
    •     rt_watchdog_init,  
    •     rt_watchdog_open,  
    •     rt_watchdog_close,  
    •     RT_NULL,  
    •     RT_NULL,  
    •     rt_watchdog_control,  
    • };  
    • #endif  
    •   
    • 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;  
    •   
    •     return rt_device_register(device, name, flag);  
    • }  

    查看了这么多设备驱动,其实我们会发现,注册接口其实都差不多,基本上就设置设备类型,指定device的操作函数表(不同类型设备实现方法不一),之后注册设备。唯一可能有差异的是部分设备,框架层存在一些操作,需要初始化一些框架层维护的数据。看门狗设备也不例外符合这么个规律。

    初始化入口

    view plaincopy to clipboardprint?

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

    从初始化入口看,基本上看门狗框架本身不维护任何信息,所有信息的维护都是交由驱动自己维护了。

    打开入口

    view plaincopy to clipboardprint?

    • static rt_err_t rt_watchdog_open(struct rt_device *dev, rt_uint16_t oflag)  
    • {  
    •     return (RT_EOK);  
    • }  

    看起来这个入口并没有实现任何功能,而实际上,这个函数所实现的功能已通过control接口实现,而不知为何open函数未调用control实现的接口。

    关闭入口

    view plaincopy to clipboardprint?

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

    关闭入口需要驱动实现control中的RT_DEVICE_CTRL_WDT_STOP,具体功能是停止看门狗功能。

    控制入口

    view plaincopy to clipboardprint?

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

    控制入口的实现,算是现在看的这么多驱动之中,实现最为迷惑的部分了,从实现上看,看不出任何对驱动层的实现要求,但实际上,还是有要求的,这些要求需要跳转到看门狗的头文件中查看。

    view plaincopy to clipboardprint?

    • #define RT_DEVICE_CTRL_WDT_GET_TIMEOUT    (RT_DEVICE_CTRL_BASE(WDT) + 1) /* 获取秒级的超时时间 */  
    • #define RT_DEVICE_CTRL_WDT_SET_TIMEOUT    (RT_DEVICE_CTRL_BASE(WDT) + 2) /* 设置秒级的超时时间 */  
    • #define RT_DEVICE_CTRL_WDT_GET_TIMELEFT   (RT_DEVICE_CTRL_BASE(WDT) + 3) /* 获取秒级的剩余时间(至重启) */  
    • #define RT_DEVICE_CTRL_WDT_KEEPALIVE      (RT_DEVICE_CTRL_BASE(WDT) + 4) /* 刷新看门狗计时 */  
    • #define RT_DEVICE_CTRL_WDT_START          (RT_DEVICE_CTRL_BASE(WDT) + 5) /* 启动看门狗 */  
    • #define RT_DEVICE_CTRL_WDT_STOP           (RT_DEVICE_CTRL_BASE(WDT) + 6) /* 关闭看门狗 */  

          这些变量就是control接口的参数cmd,也就是说,驱动需要实现这些入口。

    总结

        至此,我们基本上可以梳理出驱动层的看门狗实现框架。即:

    view plaincopy to clipboardprint?

    • #include <board.h>  
    •   
    • #ifdef RT_USING_WDT  
    •   
    • //#define DRV_DEBUG  
    • #define LOG_TAG             "drv.wdt"  
    • #include <drv_log.h>  
    •   
    • struct wdt_obj  
    • {  
    •     rt_watchdog_t watchdog;  
    •     // TODO: wdt inside param  
    • };  
    •   
    • static rt_err_t wdt_init(rt_watchdog_t *wdt)  
    • {  
    •     return RT_EOK;  
    • }  
    •   
    • static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)  
    • {  
    •     switch (cmd)  
    •     {  
    •     case RT_DEVICE_CTRL_WDT_KEEPALIVE:  
    •         // TODO: 执行喂狗动作   
    •         break;  
    •         /* set watchdog timeout */  
    •     case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:  
    •         // TODO:设置看门狗超时时间  
    •         break;  
    •     case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:  
    •         // TODO:获取设置的超时时间  
    •         break;  
    •     case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:  
    •         // TODO:获取看门狗的剩余计时  
    •         break;  
    •     case RT_DEVICE_CTRL_WDT_START:  
    •         // TODO:启动看门狗  
    •         break;  
    •     case RT_DEVICE_CTRL_WDT_STOP:  
    •         // TODO:关闭看门狗  
    •         break;  
    •     default:  
    •         LOG_W("This command is not supported.");  
    •         return -RT_ERROR;  
    •     }  
    •     return RT_EOK;  
    • }  
    •   
    • static struct rt_watchdog_ops ops =  
    • {  
    •     .init = wdt_init,  
    •     .control = wdt_control,  
    • };  
    •   
    • int rt_wdt_init(void)  
    • {  
    •     struct wdt_obj *wdt;  
    •       
    •     wdt =  (struct wdt_obj *)rt_malloc(sizeof(struct wdt_obj));  
    •     if(wdt == RT_NULL)  
    •     {  
    •         LOG_E("wdt device malloc failed.");  
    •         return -RT_ERROR;  
    •     }  
    •     wdt.watchdog.ops.ops = &ops;  
    •     if (rt_hw_watchdog_register(&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);  
    •   
    • #endif /* RT_USING_WDT */  


    哎...今天够累的,签到来了~
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-19 00:49 , Processed in 0.081479 second(s), 19 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表