RTT(RT-Thread)IO设备模型(RTT保姆级教程)

RTT(RT-Thread)IO设备模型(RTT保姆级教程)目录 IO 设备模型 模型框架原理 IO 设备类型 创建和注册 IO 设备 RTT 设备管理程序实现原理 访问 IO 设备 查找设备 初始化设备 打开设备 关闭设备 控制设备 读写设备 数据收发回调 数据接收回调 数据发送回调 设备模型实例 IO 设备模型 RT Thread

大家好,我是讯享网,很高兴认识大家。

目录

IO设备模型

模型框架原理

IO设备类型

创建和注册IO设备

RTT设备管理程序实现原理

访问IO设备

查找设备

初始化设备

打开设备

关闭设备

 控制设备

 读写设备

数据收发回调

数据接收回调

数据发送回调

设备模型实例



讯享网

IO设备模型

        RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。

  • 应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行交互。
  • I/O 设备管理层实现了对设备驱动程序的封装
  • 设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
  • 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。

简单设备的注册不经过设备驱动框架层,直接将设备注册到I/O设备管理器中

  • 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到 I/O 设备管理器中
  • 应用程序通过 rt_device_find()接口查找到设备,然后使用 I/O 设备管理接口来访问硬件

模型框架原理

图中:在左边是应用层代码,在右边是设备驱动代码,设备驱动层是与硬件最接近的(用于直接访问硬件)。而设备驱动和应用程序通过同一的IO设备管理器来统一管理起来。

如何去管理呢?设备驱动在要操作硬件的时候,要向IO设备管理器去注册,一旦注册完之后,设备管理器就知道了哪个设备的驱动注册到系统里面了。当注册成功以后,当应用层想要访问硬件的时候,通过调用rt_device_find函数来找到相应的设备驱动,一旦找到以后,就可以打开设备,接着进行读写操作,最后要记得关闭设备。

应用层想要去访问硬件的时候,只需要查找到相应的设备驱动,然后调用统一的接口就能对硬件设备进行操作,不需要关注硬件的实现原理。而对应硬件驱动来讲,我们只需要提供对硬件的访问方法。具体如何去访问,以及访问到的数据如何去处理也跟设备驱动没有关系。也就是说将不同的事情交给不同的层去完成,实现解耦(高内聚、低耦合)

对于一些复杂设备,需要使用到对应的设备驱动框架层,进行注册,它们拥有自己专属的注册函数 如:看门狗定时器

  • 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过特定的函数 rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中
  • 看门狗设备驱动框架通过 rt_device_register()接口将看门狗设备注册到 I/O 设备管理器中
  • 应用程序通过 I/O 设备管理接口来访问看门狗设备硬件

IO设备类型

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

RT_Device_Class_Char = 0, /< character device */

RT_Device_Class_Block, /< block device */

RT_Device_Class_NetIf, /< net interface */

RT_Device_Class_MTD, /< memory device */

RT_Device_Class_CAN, /< CAN device */

RT_Device_Class_RTC, /< RTC device */

RT_Device_Class_Sound, /< Sound device */

RT_Device_Class_Graphic, /< Graphic device */

RT_Device_Class_I2CBUS, /< I2C bus device */

RT_Device_Class_USBDevice, /< USB slave device */

RT_Device_Class_USBHost, /< USB host bus */

RT_Device_Class_SPIBUS, /< SPI bus device */

RT_Device_Class_SPIDevice, /< SPI device */

RT_Device_Class_SDIO, /< SDIO bus device */

RT_Device_Class_Timer, /< Timer device */

RT_Device_Class_Miscellaneous, /< misc device */

RT_Device_Class_Sensor, /< Sensor device */

RT_Device_Class_Touch, /< Touch device */

RT_Device_Class_Unknown /< unknown device */

创建和注册IO设备

  • 驱动层负责创建设备实例,并注册到 I/O 设备管理器中
/ * This function creates a device object with user data size. * * @param type, the kind type of this device object. * @param attach_size, the size of user data. * * @return the allocated device object, or RT_NULL when failed. */ rt_device_t rt_device_create(int type, int attach_size)

讯享网
  • 当一个动态创建的设备不再需要使用时可以通过如下函数来销毁
讯享网/ * This function destroy the specific device object. * * @param dev, the specific device object. */ void rt_device_destroy(rt_device_t dev)
  • 设备被创建后,需要实现它访问硬件的操作方法
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); };
  • 设备被创建后,需要注册到 I/O 设备管理器中,应用程序才能够访问
讯享网/ * This function registers a device driver withspecified name. * * @param dev the pointer of device driverstructure * @param name the device driver's name * @param flags the capabilities flag ofdevice 设备模式标志 * * @return the error code, RT_EOK oninitialization successfully. */ rt_err_t rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags) #define RT_DEVICE_FLAG_RDONLY 0x001/*只读*/ #define RT_DEVICE_FLAG_WRONLY 0x002/*只写*/ #define RT_DEVICE_FLAG_RDWR 0x003 /*读写*/ #define RT_DEVICE_FLAG_REMOVABLE0x004 /*可移除*/ #define RT_DEVICE_FLAG_STANDALONE0x008 /*独立*/ #define RT_DEVICE_FLAG_SUSPENDED0x020 /*挂起*/ #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发送*/
  • 设备注销后的,设备将从设备管理器中移除,也就不能再通过设备查找搜索到该设备。注销设备不会释放设备控制块占用的内存
/ * This function removes a previouslyregistered device driver * * @param dev the pointer of device driverstructure * * @return the error code, RT_EOK onsuccessfully. */ rt_err_t rt_device_unregister(rt_device_t dev)

RTT设备管理程序实现原理

当我们创建一个设备的时候,系统会使用一个结构体描述这个设备的所有信息。当我们创建并注册多个设备的时候,系统就会通过列表的方式将这些结构体统一管理起来。当我们应用层想要找到某个设备的时候,就会调用find函数来找到列表头来根据name遍历查找,找到以后就会调用相应的方法来操作设备。当设备不用的时候,调用unregister函数来移除(将结构体变量从列表中移除),但结构体的空间依然存在,如果我们想要将结构体的空间释放掉,就需要调用destroy函数来进行删除释放。

访问IO设备

应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件,I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示

相应接口函数在设备句柄结构体中

使用应用层接口前,首先要查找到设备

查找设备

返回值为设备句柄指针

讯享网/ * This function finds a device driver byspecified name. * * @param name the device driver's name * * @return the registered device driver onsuccessful, or RT_NULL on failure. */ rt_device_t rt_device_find(const char*name)

操作接口如下:

初始化设备

/ * This function will initialize the specified device * * @param dev the pointer of device driver structure * * @return the result */ rt_err_t rt_device_init(rt_device_t dev)

打开设备

讯享网/ * This function will open a device * * @param dev the pointer of device driver structure * @param oflag the flags for device open * * @return the result */ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)

注:RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 "\n"(对应 16 进制值为 0x0A)时,自动在前面输出一个 "\r"(对应 16 进制值为 0x0D)做分行。

流模式 RT_DEVICE_FLAG_STREAM 可以和接收发送模式参数使用或 “|” 运算符一起使用

关闭设备

/ * This function will close a device * * @param dev the pointer of device driver structure * * @return the result */ rt_err_t rt_device_close(rt_device_t dev)

 控制设备

讯享网/ * This function will perform a variety of control functions on devices. * * @param dev the pointer of device driver structure * @param cmd the command sent to device * @param arg the argument of command * * @return the result */ rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

 读写设备

/ * This function will read some data from adevice. * * @param dev the pointer of device driverstructure * @param pos the position of reading * @param buffer the data buffer to save read data * @param size the size of buffer * * @return the actually read size onsuccessful, otherwise negative returned. * * @note since 0.4.0, the unit of size/pos is ablock for block device. */ rt_size_t rt_device_read(rt_device_tdev, rt_off_t pos, void *buffer, rt_size_t size)
讯享网/ * This function will write some data to adevice. * * @param dev the pointer of device driverstructure * @param pos the position of written * @param buffer the data buffer to be writtento device * @param size the size of buffer * * @return the actually written size onsuccessful, otherwise negative returned. * * @note since 0.4.0, the unit of size/pos is ablock for block device. */ rt_size_trt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)

数据收发回调

当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达

原理:通过设置数据的收发回调来通知我们的应用层某个线程来接收数据。这样就不需要再接收数据的时候写一个while循环一直去读。让它在没有数据的时候去休眠阻塞,一旦有数据的时候,这个函数就会被回调去通知相应线程唤醒去读写,这样可以减少系统的调用,提高系统调用效率。

数据接收回调
/ * This function will set the receptionindication callback function. * This callback function * is invoked when this device receives data. * * @param dev the pointer of device driverstructure * @param rx_ind the indication callbackfunction * * @return RT_EOK */ rt_err_t rt_device_set_rx_indicate(rt_device_tdev, rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
数据发送回调
讯享网/ * This function will set the indicationcallback function when device has * written data to physical hardware. * * @param dev the pointer of device driverstructure * @param tx_done the indication callbackfunction * * @return RT_EOK */ rt_err_t rt_device_set_tx_complete(rt_device_tdev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer))

设备模型实例

(1)首先在drivers下创建一个drv_demo.c的驱动文件

(2)然后刷新工程目录,打开drv_demo.c

(3)我们可以参考drv_wdt.c的设备驱动模型来写

在文件最后我们可以发现有一个板级初始化的导出函数:我们使用INIT_BOARD_EXPORT宏将rt_wdt_init函数导出,那么在板级初始化的时候就会调用rt_wdt_init函数

我们再drv_demo.c中导出自己的设备初始化函数,并编写设备初始化函数

(4)接着我们创建设备,使用rt_device_create函数

第一个参数是设备类型,这里用字符设备

第二个参数是用户的数据大小,如果要传入用户数据的话,我们就根据用户数据的大小来传参。因为我们不需要传入用户数据,因此大小可以随便写一个,如32。

返回值为指针类型rt_device_t

(5)设备创建成功以后,我们需要对设备编写相应的接口函数,我们以init、open、close为例

我们可以按F3跳转到rt_device_t中,并将相应接口函数指针复制到我们自己的设备驱动文件下

(6)将函数进行简单完善

并对接口进行赋值

(7)然后我们以读写的方式注册我们的设备模型

(8)最后我们在main函数中使用,首先查找设备,如果查找成功,返回一个设备对象指针,如果查找失败,我们返回错误:变量无效

然后调用相应的应用层接口函数

(10)编译之后发现有一个警告LOG_E未定义

我们需要将添加调试头文件#include

(11)运行结果

通过列举设备,可以查看到demo是我们自己创建的设备;uart2是用来监控当前STM32单片机的,用作用户的调试接口;pin是GPIO引脚。(其中要注意串口也是属于字符设备类型的)

小讯
上一篇 2025-03-23 09:23
下一篇 2025-01-11 16:49

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/66022.html