当前位置: 首页 > news >正文

设备与驱动:I/O设备模型

        大部分的嵌入式系统都包括一些I/O设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上模拟数据采样、用于保存数据的Flash/SD卡以及网络设备上的以太网接口等,都是嵌入式系统中容易找到的I/O设备例子。

        本专栏主要是分享RT-Thread是如何对不同的I/O设备进行管理的。读完本专栏,我们会了解到如何从宏观角度去管理I/O设备,为我们在编写代码时有一个参考的管理模型,从而提高我们代码的复用性、降低耦合性,从而实现换硬件不动软件的目标。

1、《设备与驱动:I/O设备模型》

2、《设备与驱动:UART设备》

3、《设备与驱动:PIN设备》

目录

一、I/O设备介绍

1.1、I/O设备模型框架

1.2、I/O设备模型

1.3、I/O设备类型

二、创建和注册I/O设备

2.1、访问I/O设备

2.2、查找设备

2.3、初始化设备

2.4、打开或关闭设备

2.5、控制设备

2.6、读写设备

2.7、数据收发回调


一、I/O设备介绍

1.1、I/O设备模型框架

        如图所示:,I/O设备位于硬件和应用程序之间,分为三层,从上到下分别是I/O设备管理层、设备驱动框架层、设备驱动层。

        应用程序通过I/O设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层I/O硬件设备进行数据(或控制)交互。

        I/O设备管理层实现了对设备驱动程序的封装。应用程序通过图中的“I/O设备管理层”提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。

        设备驱动框架是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。

        设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册I/O设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到I/O设备管理器中。

1.2、I/O设备模型

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

设备对象具体定义如下所示:

struct rt_device{struct rt_object          parent;        /* 内核对象基类 */enum rt_device_class_type type;          /* 设备类型 */rt_uint16_t               flag;          /* 设备参数 */rt_uint16_t               open_flag;     /* 设备打开标志 */rt_uint8_t                ref_count;     /* 设备被引用次数 */rt_uint8_t                device_id;     /* 设备 ID,0 - 255 *//* 数据收发回调函数 */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);const struct rt_device_ops *ops;    /* 设备操作方法 *//* 设备的私有数据 */void *user_data;};typedef struct rt_device *rt_device_t;

1.3、I/O设备类型

        RT-Thread支持多种I/O设备类型,主要设备类型如下所示:

RT_Device_Class_Char             /* 字符设备       */RT_Device_Class_Block            /* 块设备         */RT_Device_Class_NetIf            /* 网络接口设备    */RT_Device_Class_MTD              /* 内存设备       */RT_Device_Class_RTC              /* RTC 设备        */RT_Device_Class_Sound            /* 声音设备        */RT_Device_Class_Graphic          /* 图形设备        */RT_Device_Class_I2CBUS           /* I2C 总线设备     */RT_Device_Class_USBDevice        /* USB device 设备  */RT_Device_Class_USBHost          /* USB host 设备   */RT_Device_Class_SPIBUS           /* SPI 总线设备     */RT_Device_Class_SPIDevice        /* SPI 设备        */RT_Device_Class_SDIO             /* SDIO 设备       */RT_Device_Class_Miscellaneous    /* 杂类设备        */

        其中字符设备、块设备是常用的设备类型,它们的分类依据是设备数据与系统之间的传输处理方式。字符模式设备允许非结构的数据传输,即通常数据传输采用串行的形式,每次一个字节。字符设备通常是一些简单设备。如串口、按键等。

        块设备每次传输一个数据块,例如每次传输512个字节数据。这个数据块是硬件强制性的,数据块可能使用某类数据接口或某些强制性的传输协议,否则就可能发生错误。因此,有时块设备驱动程序对读或写操作必须执行附加的一些工作,如下图所示:

        当系统服务于一个具有大量数据的写操作时,设备驱动程序必须首先将数据划分为多个包,每个包采用设备指定的数据尺寸。而在实际过程中,最后一部分数据尺寸有可能小于正常的设备块尺寸。如上图所示,每个块使用单独的写请求写入设备中,头3个直接进行写操作。但最后一个数据块尺寸小于设备块尺寸,设备驱动程序必须使用不同于前3个块的方式处理最后的数据块。通常情况下,设备驱动程序需要首先执行相对应的设备块的读操作,然后把写入数据覆盖到读出的数据上,然后再把这个“合成”的数据块作为一整个块写回到设备中。例如上图中的块4,驱动程序需要先把块4所对应的设备块读出来,然后将需要写入的数据覆盖至从设备块读出的数据上,使其合并成一个新的块,最后再写回到块设备中。

二、创建和注册I/O设备

        驱动层负责创建设备实例,并注册到I/O设备管理器中,可以通过静态申明的方式创建设备实例,也可以动态创建。

下述代码为RT-Thread源码定义:

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

操作方法如下表所示:

方法名称

方法描述

init

初始化设备。设备初始化完成后,设备控制块的 flag 会被置成已激活状态 (RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块中的 flag 标志已经设置成激活状态,那么再运行初始化接口时会立刻返回,而不会重新进行初始化。

open

打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应默认已经使能并开始接收数据。所以建议在写底层驱动程序时,在调用 open 接口时才使能设备。

close

关闭设备。在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行 + 1 操作,在关闭设备时进行 - 1 操作,当计数器变为 0 时,才会进行真正的关闭操作。

read

从设备读取数据。参数 pos 是读取数据的偏移量,但是有些设备并不一定需要指定偏移量,例如串口设备,设备驱动应忽略这个参数。而对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。例如块设备的数据块大小是 512,而参数中 pos = 10, size = 2,那么驱动应该返回设备中第 10 个块 (从第 0 个块做为起始),共计 2 个块的数据。这个接口返回的类型是 rt_size_t,即读到的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。

write

向设备写入数据。参数 pos 是写入数据的偏移量。与读操作类似,对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。这个接口返回的类型是 rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。

control

根据 cmd 命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数 RT_DEVICE_CTRL_BLK_GETGEOME,意思是获取块设备的大小信息。

当一个动态创建的设备不再需要时,需要通过调用特定的函数进行销毁。

当设备被创建后,需要注册到I/O设备管理器中,应用程序才能够访问。

调用注册函数时可以设置模式标志,标志信息如下:

#define RT_DEVICE_FLAG_RDONLY       0x001 /* 只读 */#define RT_DEVICE_FLAG_WRONLY       0x002 /* 只写  */#define RT_DEVICE_FLAG_RDWR         0x003 /* 读写  */#define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */#define RT_DEVICE_FLAG_STANDALONE   0x008 /* 独立   */#define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 挂起  */#define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */#define RT_DEVICE_FLAG_INT_RX       0x100 /* 中断接收 */#define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */#define RT_DEVICE_FLAG_INT_TX       0x400 /* 中断发送 */#define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 发送 */

2.1、访问I/O设备

        应用程序通过I/O设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。

2.2、查找设备

        应用程序根据设备名称获取设备句柄,进而可以操作设备。

2.3、初始化设备

        获得设备句柄后,应用程序可调用相关函数对设备进行初始化操作。

2.4、打开或关闭设备

        通过设备句柄,应用程序可以打开或关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则默认调用初始化接口对设备进行初始化。

2.5、控制设备

        通过命令控制字,应用程序可以对设备进行控制。

设备命令如下:

#define RT_DEVICE_CTRL_RESUME           0x01   /* 恢复设备 */#define RT_DEVICE_CTRL_SUSPEND          0x02   /* 挂起设备 */#define RT_DEVICE_CTRL_CONFIG           0x03   /* 配置设备 */#define RT_DEVICE_CTRL_SET_INT          0x10   /* 设置中断 */#define RT_DEVICE_CTRL_CLR_INT          0x11   /* 清中断 */#define RT_DEVICE_CTRL_GET_INT          0x12   /* 获取中断状态 */

2.6、读写设备

        应用程序可以通过相关函数写入设备句柄读写数据。

2.7、数据收发回调

        当硬件设备收到数据时,可以通过相关函数回调来设置数据接收指示。通知上层应用线程有数据到达。该回调函数由调用者提供。当硬件设备接收到数据时,会回调这个函数并把接收到的数据长度传递给上层应用。上层应用线程应在收到指示后。立即从设备中读取数据。

        应用程序调用写入函数写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个函数会在底层硬件数据发哦是那个完成后(例如:DMA传送完成或FIFO已经写入完毕产生完成中断时)调用。调用写入函数时,回调函数由调用者提供,当硬件设备发送完数据时,由驱动程序回调这个函数并把发送完成的数据块地址Buffer作为参数传递给上层应用。上层应用(线程)在收到指示后会根据发送Buffer的情况,释放Buffer内存块或将其作为下一个写数据的缓存。

http://www.xdnf.cn/news/312463.html

相关文章:

  • Selenium模拟人类行为,操作网页的方法(全)
  • 简单面试提问
  • LIO-Livox
  • 攻防世界 反应釜开关控制
  • 室内烟雾明火检测数据集VOC+YOLO格式2469张2类别
  • 【JEECG】BasicTable单元格编辑,插槽添加下拉组件样式错位
  • 模板模式 VS 建造者模式
  • SQL报错注入
  • AI-02a5a2.神经网络的学习
  • OrcaFex11.5
  • 颠覆监测体验!WM102无线温湿度记录仪开启智能物联新时代
  • 生成式人工智能技术在高校心理健康服务中的应用; 希尔的三阶段助人理论:探索、领悟和行动
  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月6日第70弹
  • 传输层UDP协议
  • 开发搭载OneNet平台的物联网数据收发APP的设计与实现
  • Vue3中的package.json依赖是否有更新
  • 探索编程世界:从“爱编程的小黄鸭”B站账号启航
  • 关于串口读写NAND闪存的用法
  • SIwave仿真之提高效率及精度
  • 3.1监督微调
  • element-ui日期时间选择器禁止输入日期
  • Linux/AndroidOS中进程间的通信线程间的同步 - 内存映射
  • 【Linux】Linux入门——权限
  • ZYNQ-UART串口中断
  • 基于计算机视觉的试卷答题区表格识别与提取技术
  • HarmonyOS 5.0 低时延音视频开发​​
  • LeetCode 1. 两数之和(Java)
  • 高频电流探头:通信测试中的隐形守护者
  • 力扣118,1920题解
  • 工业质检/缺陷检测领域最新顶会期刊论文收集整理-AAAI2025【持续更新中】