RT-Thread源码解读-------I/O设备驱动

本文讲解RT-Thread操作系统的I/O设备驱动模型。所谓的I/O设备驱动就是我们平时所说的输入输出设备。它包含的设备种类多种多样,从最简单的LED到负载的SPI设备、SDIO等等都属于I/O设备。由于我需要结合代码进行讲解,所以本文将PIN设备驱动一块讲解。首先把官方的图片贴上,通过观察这张图片,我尽量分析出一些有用信息。
在这里插入图片描述1)这张图总体上分为三部分,最上面是应用程序;中间是一些设备相关的软件代码;最下面是硬件层。
2)这张图中能够看出,对于应用层程序来说不需要关注具体硬件,实现了应用程序和硬件的完全分离,这样做的好处是一套应用层程序代码可以在多种硬件MCU平台上运行。
3)可以看到浅绿色的I/O设备管理层是连接在一块的,但是下面设备驱动框架层和设备驱动层的各种模块是分离的。从这里可以看出I/O设备管理层的一个作用是给将底层各种设备驱动的多样性屏蔽起来,然后释放出一个统一的接口给应用层;I/O设备管理层向下代用各种设备驱动的时候怎么区分呢,答案是通过分配不同的设备类型,这个会在后续讲解。
下面再把官方关于I/O设备管理层、设备驱动框架层、设备驱动层三者的作用说一下:
1)I/O 设备管理层 :实现了对设备驱动程序的封装。应用程序通过 I/O 设备层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。
2)设备驱动框架层 :是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
3)设备驱动层 :是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。
下面两幅图片为从官网上截取的,通过对比这两幅图,然后说一下不同之处。
在这里插入图片描述在这里插入图片描述通过比较这两幅图发现几个结论,需要注意的是这些结论只是通过观察图片看出来的,后续会分析代码验证这些结论:
1)两幅图的共同点:a、应用程序和I/O设备管理器之间通信的函数是固定的;b、在应用程序操作一个设备之前,底层的设备驱动程序首先需要在I/O设备管理器上注册该设备;c、底层设备驱动程序必须自己创建一个描述设备的结构体。
2)两幅图的不同点:a、对于比较简单的设备,底层设备驱动程序直接向I/O设备管理器注册设备,但是对于一些复杂的设备,在底层硬件驱动程序和I/O设备管理器之间多了一层设备驱动框架(比如前面一片文章讲解的SPI设备就会多了一层SPI设备驱动框架)。
再说明一下,对于设备驱动框架也是RT-Thread团队提供的一套代码,主要是提供的一套函数API接口,供底层设备驱动代码调用。此外该框架还连接到I/O设备管理器为其提供函数API调用接口。
不啰嗦了,开始进行代码相关的分析。
一、结构体讲解
我这里结合PIN设备讲解关于I/O管理和PIN设备的代码,这样可以更好的帮助我们理解I/O设备驱动。我们讲解结构体的顺序是从某一个具体的设备类型讲解,然后深入该设备类型的内部数据域在进行讲解。要结合PIN设备,那么我们就需要看一下PIN设备的结构体。

static struct rt_device_pin _hw_pin;//申明一个静态的PIN设备变量
/* pin device and operations for RT-Thread */
struct rt_device_pin
{
   
    struct rt_device parent;//父设备,主要是为了I/O设备管理层使用的
    const struct rt_pin_ops *ops;//PIN设备操作函数,这是用来连接操作底层硬件函数的
};

继续看一下结构体rt_device:

/**
 * Device structure
 */
struct rt_device
{
   
    struct rt_object          parent;      //rt_object对象,类似于C++中的基类

    enum rt_device_class_type type;    //I/O设备类型,内核中通过枚举类型列举出设备的类型,例如:RT_Device_Class_CAN(表示CAN设备)、RT_Device_Class_Miscellaneous(PIN设备)
    rt_uint16_t               flag;         //记录设备的属性
    rt_uint16_t               open_flag;    //设备打开的标志

    rt_uint8_t                ref_count;    //记录设备被打开的次数
    rt_uint8_t                device_id;    //记录设备的ID

    //设备回调函数,在RT-Thread里面设置为RT_NULL
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

//设备相关的操作函数,应用程序调用设备读写等操作函数,最终会调用这些函数指针所指向的设备底层函数
#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
#else
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
#endif

#if defined(RT_USING_POSIX)//关于支持POSIX规范的定义
    const struct dfs_file_ops *fops;
    struct rt_wqueue wait_queue;
#endif

    void                     *user_data;    //存放设备的私有数据
};

结构体struct rt_pin_ops:

struct rt_pin_ops
{
   
    void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);//设置PIN模式
    void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);//对PIN设备写
    int (*pin_read)(struct rt_device *device, rt_base_t pin);//对PIN设备进行读

    /* 关于设置GPIO为中断相关的内容,这个会在本文的某一部分单独讲解 */
    rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
                      rt_uint32_t mode, void (*hdr)(void *args), void *args);
    rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);
    rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);
};

再看一下在PIN设备中关于上面这些结构体的具体赋值。
二、代码解读
上面一部分讲解了关于PIN设备使用的结构体,那么趁热打铁,就先看一下代码中是怎样对结构体的成员变量进行赋值的吧。
查看代码可以看出,代码中定义了一个静态全局变量**_hw_pin**来表示PIN设备。

<
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值