百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

RT-Thread I/O设备模型及驱动框架学习(一)

ccwgpt 2024-11-19 02:17 36 浏览 0 评论

近期由于公司项目需求,需要借鉴类似RT-Thread的驱动框架,所以就来好好学习下,为下一步的移植做准备。

目录


1. 概述


2. 原理解析


3. 源码解析


3.1. 创建设备


3.2. 注册到驱动框架


3.3. 注册到IO设备管理器


4. 小结


1. 概述


本着由简入繁的原则,分析源码以STM32平台的看门狗源码为例,正好参考官方资料辅助学习下。


硬件平台及软件版本如下:


硬件平台:STM32F407ZG


RT-Thread版本:4.0.4


在分析源码前需要了解的基础知识如下:


自动初始化机制


RT-Thread 文档中心


I/O驱动模型


RT-Thread 文档中心


看门狗驱动框架


RT-Thread 文档中心


2. 原理解析


通过基础知识的准备,切回到我们的主题。那么在I/O设备模型下,使用watchdog驱动框架如何驱动硬件看门狗呢?


看下官方资料给出的流程图:



通过这张框图明确的流程是


1,创建看门狗设备,并实现底层驱动


2,注册看门狗设备到看门狗设备驱动框架


3,注册I/O设备到I/O设备管理器


4,应用程序使用看门狗


3. 源码解析


原则上说,分析源码是要明确框架才能进一步分析,但是为了方便与简化理解,不妨从设备驱动开始,往上层一步一步追踪来研究源码。


几个关键的RT-Thread的代码目录


设备的驱动代码在


libraries/HAL_Drivers


驱动框架


rt-thread/components/drivers


设备模型


rt-thread/src


3.1. 创建设备


按照上面的流程图理解,首先是要创建看门狗设备,这时看驱动文件drv_wdt.c。


看门狗设备的结构体定义如下,该看门狗设备采用静态初始化的方法,定义了看门狗设备对象及看门狗设备的操作方法。


struct stm32_wdt_obj
{
    //看门狗设备定义
    rt_watchdog_t watchdog;
    //看门狗的硬件结构体定义
    IWDG_HandleTypeDef hiwdg;
    //是否初始化的标志
    rt_uint16_t is_start;
};
//看门狗实例
static struct stm32_wdt_obj stm32_wdt;
//看门狗的操作方法
static struct rt_watchdog_ops ops;


其中使用了看门狗驱动框架的看门狗相关的结构体定义,后面再说。硬件结构体,就是stm32官方的定义,这个可以去看官方驱动示例。那么结合结构体的定义,看门狗的初始化如下,主要是配置硬件参数,然后向驱动框架中注册该设备,名字即为“wdt”。


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) || defined(SOC_SERIES_STM32L0)
    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;

    //向驱动框架中注册该设备
    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);



此处看门狗设备的创建采用自动初始化机制,按照自动初始化机制描述,该初始化是板级初始化。初始化相关宏如下:


?
接下来再看下,看门狗设备定义的驱动功能,就两个,一个是看门狗的初始化,另一个是看门狗的各种功能控制(喂狗、设置超时时间等)。此处的初始化是一个空函数,看门狗的初始化是通过


static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)



结合RT_DEVICE_CTRL_WDT_START命令来实现的。


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)
    {
        /* 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 ;
        }
        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;
    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:
        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;
}



那么通过以上的源码就实现了硬件层面的看门狗的创建,或者说参数配置及功能实现。


接下来就是注册看门狗设备到看门狗设备框架了。


3.2. 注册到驱动框架


上一小节的的看门狗初始化中调用了注册函数,该函数即实现了注册到驱动框架的功能。


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



看门狗的驱动框架实现见watchdog.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_Security;
    //回调处理
    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);
}



此处,就是把看门狗设备的类型、回调函数、操作方法设置或绑定好,由于看门狗设备操作简单,此处的回调直接为空。


其中RT_USING_DEVICE_OPS宏,个人理解是为了兼容不同版本的问题,除了ram和rom使用大小有区别,其他没有什么影响。


接下来看下看门狗框架中的设备操作方法的实现,按照官方的I/O设备模型需要实现如下操作方法。


?


在看门狗设备中,由于不需要使用读写功能,则只定义了初始化、打开、关闭、控制的函数。如下的操作方法实现,最终都是调用上一小结中驱动功能的初始化功能控制来实现的。


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

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

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

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



通过以上源码,就实现了看门狗设备注册到驱动框架中,接下来就是把它注册到IO设备管理器中。


3.3. 注册到IO设备管理器


注册到IO设备管理器的函数如下,在文件device.c中。


rt_device_register(device, name, flag);



函数实现如下:


rt_err_t rt_device_register(rt_device_t dev,
                            const char *name,
                            rt_uint16_t flags)
{
    if (dev == RT_NULL)
        return -RT_ERROR;

    if (rt_device_find(name) != RT_NULL)
        return -RT_ERROR;

    rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
    dev->flag = flags;
    dev->ref_count = 0;
    dev->open_flag = 0;

#ifdef RT_USING_POSIX
    dev->fops = RT_NULL;
    rt_wqueue_init(&(dev->wait_queue));
#endif /* RT_USING_POSIX */

    return RT_EOK;
}
RTM_EXPORT(rt_device_register);



到此为止,看门狗设备就被注册到IO设备管理器中了,可以使用IO设备管理接口操作看门狗设备了。


?


4. 小结


总体来看,要明白这套驱动框架及模式,首先要理解整体流程,其次是RT-Thread的设备对象,所有的设备都是通过设备基类派生出来的,或者用结构体的方式理解就是具体的设备类结构体包含了设备基类的结构体。


?


用官方的话讲:“RT-Thread 的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性。”


设备对象具体的定义如下


?


通过以上描述,一系列的注册过程就是把硬件驱动的参数设置,功能函数绑定到具体的设备实例上,然后通过设备名字找到该设备实例,接着通过操作接口完成各种功能的使用。



?

相关推荐

PPT 139 | 粉色渐变小清新春暖花开PPT模板

春暖花开,这是你制作PPT的世界粉色渐变小清新春暖花开PPT模板,共22P适用场合:工作总结/个人汇报/演讲培训等喜欢的可以赞一个更多类似PPT模板,搜【小清新】也可以,在线编辑,一键下载...

框架完整岗位竞聘报告PPT模板

需要源文件de可私!氢元素为您提供PPT模板、PNG元素免费、办公模板。工作述职汇报、计划总结、培训课件、节日庆典、营销策划、商业计划、宣传企业、产品发布、个人简历、毕业答辩、岗位竞聘、护理培训,...

PPT与视频相关的几个操作要点

都知道PPT中可以插入视频,而2010及以上版本插入后还可以对视频做各种处理,另外别忘了还可以直接将PPT导出成视频格式。插入视频方式往PPT中插入视频,除了【插入】|【视频】|【PC上的视频】这种方...

书写主题品管圈汇报PPT模板,主题框架,简约设计,品管圈必备

Hello大家好,我是帮帮。今天跟大家分享一张书写主题品管圈汇报PPT模板,主题框架,简约设计,品管圈必备。有个好消息!为了方便大家更快的掌握技巧,寻找捷径。请大家点击文章末尾的“了解更多”,在里面找...

【教学成果框架图】国家级获奖案例解析与可视化方案(实战版)

教学成果逻辑框架图的绘制精髓总结为“逻辑为骨,视觉为翼”。下面结合具体案例,手把手教你制作既专业又美观的成果框架图。一、设计理念:教育逻辑与视觉传达的融合教学成果框架图需体现三重逻辑:教育目标层(立德...

工作总结PPT模板完整框架 (30)

年中汇报PPT的超强框架来袭,职场人士的必备神器!

这套框架堪称完美,适用于各类工作汇报场景。它逻辑清晰,内容丰富,涵盖个人介绍、工作回顾、业绩成果、问题分析以及未来工作计划等常见汇报模块。PPT已包含600多页,所有元素均可自由编辑,数据图表也能轻松...

三个说话框架,提升逻辑思维,让你清晰表达

#暑期创作大赛#建立清晰的逻辑思维:三个说话框架的力量我们生活在一个充满语言交流的世界中。无论是在学校,工作场所,还是在社交场合,我们都需要有效地表达我们的观点和想法。然而,许多人都有表达上的困扰,他...

《石头记》人物原型故事之逻辑框架(一)

话说空空道人将《石头记》带往人世,又经东鲁孔梅溪醒题《风月宝鉴》,曹雪芹定名《金陵十二钗》,加之警幻仙子提醒防备新谱《红楼梦十二支曲》。蛮以为他人在闲适风月故事之于能够了然背后真实故事,怎耐一万年老怪...

如何搭建高效沟通与精彩演讲的逻辑结构

对于大多数人而言,说话有逻辑这件事难于登天。很多人在演讲、工作汇报中都会遇到诸如“我不知道你在说什么”、“你的重点是什么”、“你说话毫无逻辑”此类的评价,被认为是说话缺乏逻辑的人。那么如何成为一个说话...

「书讯」论证逻辑框架下说理写作模式研究

《论证逻辑框架下说理写作模式研究》作者:金建龙出版日期:2018年11月开本:16开出版社:经济管理出版社小编推荐提升大学生批判意识和理性说理能力是新时代背景下高等教育中通识教育和博雅教育的全新探索...

【一元脑花】青少年4D逻辑训练的基本框架

一、核心训练模块多维认知构建资源分布图谱:通过分析社会资源层级与流动规律,建立立体空间认知模型2DOC时空维度整合:将历史局势演变(纵向时间轴)与未来趋势预判(横向可能性轴)结合训练2DOC动态干预系...

提升写作逻辑,这5个框架你搭建好了吗?

每个人都有写作的愿望,也都想表达心中浩荡的情感,但多年过后,许多人依旧卡在“无话可说”“写不出结构”的怪圈里。有人慨叹:“浮云一别后,流水十年间”,梦想与现实总有一道沟壑横亘——此岸是满腹心事,彼岸...

2023年主观题法治思想知识框架图

...

学霸:2天吃透初一语文上学期核心预习知识框架图|暑假弯道超车

学霸:2天吃透初一语文上学期核心预习知识框架图|暑假弯道超车。具体如下:查看作者的个人主页获悉剩余的~...

取消回复欢迎 发表评论: