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

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

ccwgpt 2024-11-19 02:17 32 浏览 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 的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性。”


设备对象具体的定义如下


?


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



?

相关推荐

盲盒小程序背后的技术揭秘:如何打造个性化购物体验

在2025年的今天,盲盒小程序作为一种新兴的购物方式,正以其独特的魅力和个性化体验吸引着越来越多的消费者。这种将线上购物与盲盒概念相结合的应用,不仅为消费者带来了未知的惊喜,还通过一系列技术手段实现了...

小程序·云开发已支持单日亿级调用量,接口可用率高达99.99%

2019-10-1914:1210月19日,由腾讯云与微信小程序团队联合举办的“小程序·云开发”技术峰会在北京召开。会上,微信小程序团队相关负责人表示“小程序·云开发”系统架构已经支持每天亿级别的...

程序员副业开启模式:8个GitHub上可以赚钱的小程序

前言开源项目作者:JackonYang今天推荐的这个项目是「list-of-wechat-mini-program-list」,开源微信小程序列表的列表、有赚钱能力的小程序开源代码。这个项目分为两部分...

深度科普:盲盒小程序开发的底层逻辑

在当下的数字化浪潮中,盲盒小程序以其独特的趣味性和互动性,吸引着众多消费者的目光。无论是热衷于收集玩偶的年轻人,还是享受拆盒惊喜的上班族,都对盲盒小程序情有独钟。那么,这种备受欢迎的盲盒小程序,其开发...

微信小程序的制作步骤

SaaS小程序制作平台,作为数字化转型时代下的创新产物,不仅将易用性置于设计的核心位置,让非技术背景的用户也能轻松上手,快速制作出功能丰富、界面精美的小程序,更在性能和稳定性方面投入了大量精力,以确保...

携程开源--小程序构建工具,三分钟搞定

前言今天推荐的这个项目是「wean」,一个小程序构建打包工具。在wean之前,大量小程序工具使用webpack进行打包,各种loader、plugin导致整个开发链路变长。wean旨在解...

校园小程序的搭建以及营收模式校园外卖程序校园跑腿校园圈子系统

校园小程序的架构设计主要包括云端架构和本地架构两部分。云端架构方面,采用Serverless架构可以降低技术门槛,通过阿里云、腾讯云等平台提供的云服务,可以实现弹性扩容和快速部署。例如,使用云数据库、...

盲盒小程序开发揭秘:技术架构与实现原理全解析

在2025年的今天,盲盒小程序作为一种结合了线上购物与趣味性的创新应用,正受到越来越多用户的喜爱。其背后的技术架构与实现原理,对于想要了解或涉足这一领域的人来说,无疑充满了神秘与吸引力。本文将为大家科...

月活百万的小程序架构设计:流量暴增秘籍

从小程序到"大"程序的蜕变之路当你的小程序用户量从几千跃升至百万级别时,原有的架构就像一件不合身的衣服,处处紧绷。这个阶段最常遇到的噩梦就是服务器崩溃、接口超时、数据丢失。想象一下,在...

认知智能如何与产业结合?专家学者共探理论框架与落地实践

当前,以大模型为代表的生成式人工智能等前沿技术加速迭代,如何将认知智能与产业结合,成为摆在各行各业面前的一个问题。论坛现场。主办方供图7月4日,2024世界人工智能大会暨人工智能全球治理高级别会议在...

现代中医理论框架

...

认知行为(CBT)中的ABC情绪理论

情绪ABC理论是由美国心理学家阿尔伯特·艾利斯(AlbertEllis1913-2007)创建的理论,A表示诱发性事件(Activatingevent),B表示个体针对此诱发性事件产生的一些信...

说说卡伦霍妮的理论框架,对你调整性格和人际关系,价值很大

01自在今天我主要想说下霍妮的理论框架。主要说三本书,第一本是《我们时代的神经症人格》,第二本是《我们内心的冲突》,第三本是《神经症与人的成长》。根据我的经验,三本书价值巨大,但并不是每个人都能读进去...

供应链管理-理论框架

一个最佳价值的供应链,应该是一个具有敏捷性、适应性和联盟功能(3A)的供应链,其基本要素包括战略资源、物流管理、关系管理以及信息系统,目标是实现速度、质量、成本、柔性的竞争优势。篇幅有...

微信WeUI设计规范文件下载及使用方法

来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。WeUI是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信Web开发量身设计,可以令用户的使用感知...

取消回复欢迎 发表评论: