一、I/O设备控制块
1、I/O设备控制块
struct rt_device
{struct rt_object parent;/* 设备类型 */enum rt_device_class_type type;/* 设备参数及打开参数 */rt_uint16_t flag, open_flag;/* 提供给上层应用的回调函数 */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void* buffer);/* 公共的设备接口(由驱动程序提供) */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, rt_uint8_t cmd, void *args);/* 用于支持电源管理的函数接口 */
#ifdef RT_USING_DEVICE_SUSPENDrt_err_t (*suspend) (rt_device_t dev);rt_err_t (*resumed) (rt_device_t dev);
#endif
/* 设备的私有数据 */void* user_data;
};typedef struct rt_device* rt_device_t;当前RT-Thread支持的设备类型包括:
enum rt_device_class_type
{RT_Device_Class_Char = 0, /* 字符设备 */RT_Device_Class_Block,   /* 块设备 */RT_Device_Class_NetIf,   /* 网络接口 */RT_Device_Class_MTD,    /* 内存设备 */RT_Device_Class_CAN,    /* CAN设备 */RT_Device_Class_RTC,    /* RTC设备 */RT_Device_Class_Sound,   /* 声音设备 */RT_Device_Class_Display, /* 显示设备 */RT_Device_Class_Unknown  /* 未知设备 */
};注:uspend、resume回调函数只会在RT_USING_DEVICE_SUSPEND宏使能的情况下才
会有效。
从设备控制块,我们可以看到,每个设备对象都会在内核中维护一个设备控制块结构,
这种结构是使设备对象继承rt_object基类,然后形成rt_device设备类型。2、注册设备
一个设备能够被上层应用访问前,需要先把这个设备注册到系统中,并添加一些相应的
一些属性。这些注册的设备均可以通过设备名,采用“查找设备接口”的方式从系统中查找
到,从而获得该设备控制块(或设备句柄)。注册设备的函数接口如下:
rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);函数参数:
dev     设备句柄;
name    设备名称;
flag    设备模式标志:
flags参数支持下列参数(可以采用或的方式支持多种参数):#define RT_DEVICE_FLAG_DEACTIVATE 0x000 /* 未初始化设备 */
#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_ACTIVATED 0x010 /* 已激活设备 */
#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发送模式 */设备流模式RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串:当输出的字符
是“\n”时,自动在前面补一个“\r”做分行。
函数返回
返回RT_EOK
警告:应当避免重复注册已经注册的设备,以及注册相同名字的设备。3、移除设备
将设备从设备系统中移除,被卸载的设备将不能再通过“查找设备接口”被查找到。卸
载设备的函数接口如下所示:
rt_err_t rt_device_unregister(rt_device_t dev)函数参数
参数 描述
dev 设备句柄;
函数返回
返回RT_EOK
注:卸载设备并不会释放设备控制块所占用的内存4、初始化所有设备
初始化所有注册到设备对象管理器中的未初始化的设备,可以通过如下函数接口完成:rt_err_t rt_device_init_all(void)函数参数
无
函数返回
返回RT_EO
• 注:此函数将逐渐废弃,不推荐在应用程序中调用。当一个设备初始化完成后它
的flags域中的RT_DEVICE_FLAG_ACTIVATED应该被置位。如果设备的flags域已经是
RT_DEVICE_FLAG_ACTIVATED,调用这个接口将不再重复做初始化。5、查找设备
根据指定的设备名称查找设备,可以通过如下接口完成:rt_device_t rt_device_find(const char* name)使用这个函数接口时,系统会在设备对象类型所对应的对象容器中遍历寻找设备对象,
然后返回该设备的句柄,如果没有找到相应的设备对象,则返回RT_NULL。
函数参数
参数 描述
name 设备名称。
函数返回
查找到对应设备将返回相应的设备句柄;否则返回RT_NULL。6、打开设备
根据设备控制块来打开设备,可以通过如下函数接口完成:rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflags);函数参数
参数    描述
dev    设备句柄;
oflags  访问模式。其中oflags支持以下列表中的参数:#define RT_DEVICE_OFLAG_CLOSE 0x000 /* 设备已经关闭(内部使用) */
#define RT_DEVICE_OFLAG_RDONLY 0x001 /* 以只读方式打开设备 */
#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 以只写方式打开设备 */
#define RT_DEVICE_OFLAG_RDWR 0x003 /* 以读写方式打开设备 */
#define RT_DEVICE_OFLAG_OPEN 0x008 /* 设备已经打开(内部使用) */函数返回
返回驱动的open函数返回值
注:如果设备注册时指定的参数中包括RT_DEVICE_FLAG_STANDALONE参数,此设备将
不允许重复打开,返回-RT_EBUSY。7、关闭设备
根据设备控制块来关闭设备,可以通过如下函数接口完成:rt_err_t rt_device_close(rt_device_t dev)函数参数
参数  描述
dev  设备句柄;函数返回
返回驱动的close函数返回值8、读设备
从设备中读取,或获得数据,可以通过如下函数接口完成:rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)调用这个函数,会从设备dev中获得数据,并存放在buffer缓冲区中。这个缓冲区的最
大长度是size。pos根据不同的设备类别存在不同的意义。
函数参数
参数    描述
dev     设备句柄;
pos     读取数据偏移量;
buffer   内存缓冲区指针,读取的数据将会被保存在缓冲区中;
size    读取数据的大小。
函数返回返回读到数据的实际大小(如果是字符设备,返回大小以字节为单位;如果是块设备,
返回的大小以块为单位);如果返回0,则需要读取当前线程的errno来判断错误状态。9、写设备
向设备中写入数据,可以通过如下函数接口完成:rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)调用这个函数,会把缓冲区buffer中的数据写入到设备dev中。写入数据的最大长度是
size。pos根据不同的设备类别存在不同的意义。
函数参数参数    描述
dev     设备句柄;
pos     读取数据偏移量;
buffer   内存缓冲区指针,放置要写入的数据;
size    写入数据的大小。函数返回
返回写入数据的实际大小(如果是字符设备,返回大小以字节为单位;如果是块设备,
返回的大小以块为单位);如果返回0,则需要读取当前线程的errno来判断错误状态
• 注:在RT-Thread的块设备中,从1.0.0版本开始, rt_device_read()/rt_device_write()接
口的pos、size参数按照以块为单位。0.3.x以前的版本则按字节为单位。10、控制设备
根据设备控制块来控制设备,可以通过下面的函数接口完成:rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);函数参数
参数  描述
dev   设备句柄;
cmd   命令控制字,这个参数通常与设备驱动程序相关;
arg   控制的参数函数返回
返回驱动控制接口的返回值。11、设置数据接收指示
设置一个回调函数,当硬件设备收到数据时回调以通知用程序有数据到达。可以通过如
下函数接口完成设置接收指示:
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind );
(rt_device_t dev,rt_size_t size));在调用这个函数时,回调函数rx_ind由调用者提供。当硬件设备接收到数据时,会回调
这个函数并把收到的数据长度放在size参数中传递给上层应用。上层应用线程应在收到指示
后,立刻从设备中读取数据。
函数参数
参数      描述
dev       设备句柄;
rx_ind     接收回调函数。函数返回
返回RT_EOK12、设置发送完成指示
在上层应用调用rt_device_write写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件给出的发送完成后(例如DMA传送完成或FIFO已经写入完毕产生完成中断时)被调用。可以通过如下函数接口设置设备发送完成指示:
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer))
调用这个函数时,回调函数tx_done参数由调用者提供,当硬件设备发送完数据时,由驱动程序回调这个函数并把发送完成的数据块地址buffer做为参数传递给上层应用。上层应用(线程)在收到指示时应根据发送buffer的情况,释放buffer内存块或将其做为下一个写数据的缓存。
函数参数
参数    描述
dev     设备句柄;
tx_done  发送回调函数。函数返回
返回RT_EOK二、设备驱动设备驱动必须实现的接口
在10.1节中提及了RT-Thread设备接口类,我们着重看看其中包含的一套公共设备接口
(类似上节说的设备访问接口,但面向的层次已经不一样,这里是面向底层驱动):/* 公共的设备接口(由驱动程序提供) */
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, rt_uint8_t cmd, void *args);/* 用于支持电源管理的函数接口 */#ifdef RT_USING_DEVICE_SUSPENDrt_err_t (*suspend) (rt_device_t dev);rt_err_t (*resumed) (rt_device_t dev);
#endif这些接口也是上层应用通过RT-Thread设备接口进行访问的实际底层接口(如设备操作接口与设备驱动程序接口的映射 ):即这些驱动实现的底层接口是上层应用最终访问的落脚点,例如上层应用调用rt_device_read接口进行设备读取数据操作,上层应先调用rt_device_find获得相对应的设备句柄,而在调用rt_device_read时,就是使用这个设备句柄所对应驱动的driver_read。上述的接口是一一对应关系。
I/O设备模块提供的这六个接口(rt_device_init/open/read/write/control),对应到设备驱动程序的六个接口(driver_init/open/read/write/control等),可以认为是底层设备驱动必须提供的接口:1、init
设备的初始化。设备初始化完成后,设备控制块的flag会被置 成已激活状态(RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块 的flag
不是已激活状态,那么在设备框架调用 rt_device_init_all接口时将调用此设备驱动的init接口进行 设备初始化;如果设备控制块中的flag标志已经设置成激活状 态,那么再运行初始化接口时,会立刻返回,而不会重新进行 初始化。
2、open
打开设备。有些设备并不是系统一启动就已经打开开始运行; 或者设备需要进行数据接收,但如果上层应用还未准备好,设 备也
不应默认已经使能并开始接收数据。所以建议在写底层驱 动程序时,应在调用open接口时才使能设备。3、close
关闭设备。建议在打开设备时,设备驱动自行维护一个打开计数,在打开设备时进行+1操作,在关闭设备时进行-1操作, 当计数
器变为0时,进行真正的关闭操作。4、read
从设备中读取数据。参数pos指出读取数据的偏移量,但是有些 设备并不一定需要指定偏移量,例如串口设备,设备驱动应忽 略这
个参数。而对于块设备来说,pos以及size都是以块设备的 数据块大小做为单位的。例如块设备的数据块大小是512,而参 数中pos= 10, size = 2,那么驱动应该返回设备中第10个块 (从第0个块做为起始),共计2个块的数据。这个接口返回的 类型rt_size_t,即读到的字节数或块数目。正常情况下应 该会返回参数中size的数值,如果返回零请设置对应的errno值。
5、write
向设备中写入数据。参数pos指出写入数据的偏移量。与读操作 类似,对于块设备来说,pos以及size都是以块设备的数据块 大小做
为单位的。这个接口返回的类型是rt_size_t,即真实写 入数据的字节数或块数目。正常情况下应该会返回参数中size 的数值,如果返回零请设置对应的errno值。6、control
根据不同的cmd命令控制设备。命令往往是由底层各类设备驱 动自定义实现。例如参数RT_DEVICE_CTRL_BLK_GETGEOME,意思 是获取块设备的大小信息。三、设备驱动实现的步骤在实现一个RT-Thread设备时,可以按照如下的步骤进行(对于一些复杂的设备驱动,例如以太网接口驱动、图形设备驱动,请参看网络组件、GUI部分章节):
• 按照RT-Thread的对象模型,扩展一个对象有两种方式:
• 定义自己的私有数据结构,然后赋值到RT-Thread设备控制块的user_data指针上;
• 从struct rt_device结构中进行派生。
• 实现RT-Thread I/O设备模块中定义的6个公共设备接口,开始可以是空函数(返回类型是rt_err_t的可默认返回RT_EOK);
• 根据自己的设备类型定义自己的私有数据域。特别是在可能有多个相类似设备的情况下(例如串口1、2),设备接口可以共用同一套接口,不同的只是各自  的数据域(例如寄存器基地址);
• 根据设备的类型,注册到RT-Thread设备框架中。

RT-Thread的I/O设备模块及其驱动实现步骤相关推荐

  1. 基于GD32F103C8T6添加RT Thread nano设备框架并添加串口设备(以控制台console( uart0 )为例)

    最近没事琢磨了一下使用设备框架的问题.因为将串口注册到设备框架可以应用十分丰富的软件包. 于是就整理了一下手上的工程,重新将工程梳理了一遍. 像这样是十分清爽了,其中RTOS是操作系统源代码 并且学习 ...

  2. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  3. 使用RT Thread设备框架封装一个I2C设备——DS3231

    使用RT Thread设备框架封装一个I2C设备--DS3231 前言 ENV配置 I2C测试 将ds3231封装成一个字符设备 结语 前言 学习rt thread的I2C的时候,恰巧手上的板子留了d ...

  4. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  5. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  6. RT Thread之 Uart2 操作

    官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...

  7. stm32f407单片机rt thread 片外spi flash OTA升级配置示例

    参考地址https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/ 第一步,生成Boot ...

  8. RT Thread Studio 配置IIC并读取AS5600角度

    RT Thread Studio 配置IIC并读取AS5600角度 一.RT Thread Studio 配置IIC 1.在RT Thread Seting 中开启IIC功能 并保存 一定要保存才能更 ...

  9. linux i2c adapter 增加设备_LINUX设备驱动模型分析之四 设备模块相关(DEVICE)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> <LINUX设备驱动模型分析之三 ...

最新文章

  1. 谈一次单元测试驱动代码重构
  2. SpringBoot整合springDataJpa实现图片上传和显示
  3. EOS 智能合约源代码解读 (5)class contract
  4. java底层原理书籍_不愧是阿里p8大佬!终于把Java 虚拟机底层原理讲清楚了,请签收...
  5. STD中string的常用方法总结
  6. 使用aggregate在MongoDB中查找重复的数据记录
  7. getclass方法_防止数据重复提交的6种方法(超简单)!
  8. 系统调用----sandir
  9. 单例模式-1.单利模式的简单实现
  10. Linux服务器沦陷为肉鸡的全过程实录
  11. vue+elementui+阿里icon unicode 踩坑
  12. 程序员眼中的中国传统文化-王阳明《传习录》21
  13. 文献解读-Physics Informed Deep Learning(PINN)
  14. 社交媒体与社会网络分析,深度分析社交网络问题
  15. SQP验厂要怎么做?
  16. “秒抢红包”锁屏勒索病毒样本分析
  17. blender学习日志
  18. “双花”问题及解决之道
  19. platform驱动模型使用总结
  20. java.util.Date和java.sql.Timestamp转换

热门文章

  1. 在asp.net mvc中使用PartialView返回部分HTML段
  2. 一位ACMer过来人的心得【转】
  3. 专家:中国房地产泡沫崩溃时间就是今明二年
  4. mysql5.7 首次登陆_mysql5.7.20第一次登录失败的快速解决方法
  5. python入门到精通自学_python入门到精通大型视频、自学者的福利
  6. Linux环境搭建 | 手把手教你配置Linux虚拟机
  7. Mybatis基础:增删改查、模糊查询、多条件查询
  8. ACS AD 和本地验证SSL ×××
  9. 显示内容长时,显示部分内容,鼠标移入显示全部内容
  10. 不是VIP用户也不怕 不需任何补丁屏蔽迅雷广告!