嵌入式面向对象 RT-Thread I/O 设备管理框架 设备驱动层
注:本文介绍性内容转载于《RT-Thread记录(十、全面认识 RT-Thread I/O 设备模型)》
注: 本次使用的开发板 :
兆易创新GD32F407VET6开发板
雅特力科技AT32A403A开发板
一、RT-Thread 的 I/O设备管理
1.1 什么是 I/O 设备模型
1.2 I/O 设备模型框架解析
1.3 I/O 设备操作逻辑说明
1.4 I/O 设备模型框架用处
二、I/O 设备模型操作 API
2.1 I/O 设备控制块
2.2 创建 I/O 设备相关
2.3 访问 I/O 设备相关
三、新建 I/O 设备模型实例
3.1 设备驱动层编写
3.2 应用层测试
一、RT-Thread 的 I/O设备管理
我们先从基本概念说起,了解RT-Thread 对 I/O 设备的管理方式,以及 I/O 设备模型框架的用途:
1.1 什么是 I/O 设备模型
有些小伙伴在刚接触到这个概念的时候还不太明白, I/O 设备模型,IO口?IO口的模型?
注意这里的 I/O 指的是 Input/Output。I/O 设备,就是指的输入 / 输出设备。
所谓 I/O 设备模型,指的是 RT-Thread 把所有的 输入 / 输出设备当做一类对象,然后通过自己的一套体系对这类对象进行管理,这类 I/O 设备对象就可以认为是 I/O 设备模型。
RT-Thread 提供了一套模型框架用来对所有的输入/输出设备进行管理的,名叫 I/O 设备模型框架 ,其 位于硬件层和应用程序之间,包括IO设备管理层、设备驱动框架层和设备驱动层, 向上层层抽象,目标是针对各种不同的I/O设备提供给应用程序相同的接口,如下图:
内容太多,具体内容在 嵌入式面向对象学习 RT-Thread I/O 设备管理框架 设备驱动层
三、新建 I/O 设备模型实例
前面的大部分内容都是参考文章的东西,对于I/O 设备模型进行了大量的说明。接下来进行案例测试
RT-Thread 驱动都是在 drivers 目录下面:
我们在目录下新建一个文件,作为驱动示例:
我们写一个简单的基本框架:
1、创建一个设备;
使用 rt_device_create 创建一个设备,需要定义一个 rt_device_t 接口体接收设备设备句柄。
2、实现设备操作的函数:
实现设备对象中对于 设备操作的init,open,close,read,write,control等 函数。
3、注册设备到 I/O 设备管理器;
使用 rt_device_register 将设备注册到设备管理器。
在 drv_demo.c
中,我们实现如下代码:
其次,我们需要实现一下设备操作的函数:
最后,别忘了使用 INIT_BOARD_EXPORT
把设备初始化的代码加入板级硬件初始化:
设备模型实例代码:
#include <rtdevice.h>
#include <rtdbg.h>
rt_err_t demo_init(rt_device_t dev)
{
rt_kprintf("demo_init ok!\n");
return RT_EOK;
}
rt_err_t demo_open(rt_device_t dev, rt_uint16_t oflag)
{
rt_kprintf("demo_open ok!\n");
return RT_EOK;
}
rt_err_t demo_cloes(rt_device_t dev)
{
rt_kprintf("demo_cloes ok!\n");
return RT_EOK;
}
rt_ssize_t demo_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
buffer = "this is device read example";
rt_kprintf("read example: %s\n",buffer);
return RT_EOK;
}
rt_ssize_t demo_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
buffer = "this is device write example";
rt_kprintf("write example: %s\n",buffer);
return RT_EOK;
}
rt_err_t demo_control(rt_device_t dev, int cmd, void *args)
{
switch(cmd)
{
case 0:
rt_kprintf("this is demo_control example cmd-1!\n");
break;
case 1:
rt_kprintf("this is demo_control example cmd-2!\n");
break;
case 2:
rt_kprintf("this is demo_control example cmd-3!\n");
break;
default:
break;
}
}
int rt_drvdemo_init(void)
{
rt_device_t demo_dev = RT_NULL;
demo_dev = rt_device_create(RT_Device_Class_Char, 0);
if(demo_dev == RT_NULL){
LOG_E("demo device create failed...\n");
return RT_ERROR;
}
demo_dev->init=demo_init;
demo_dev->open=demo_open;
demo_dev->close=demo_cloes;
demo_dev->read = demo_read;
demo_dev->write = demo_write;
demo_dev->control =demo_control;
rt_device_register(demo_dev,"drvdemo",RT_DEVICE_FLAG_RDWR);
return 0;
}
INIT_BOARD_EXPORT(rt_drvdemo_init);
上面我们完成的是 设备驱动层的 代码,接下来我们还需要简单演示一下,如果在应用层 使用这个 demo 设备。
我们根据上文所介绍的 访问 I/O 设备 进行对应操作,这里直接上图说明一下使用流程:
应用测试函数
rt_device_t dev = RT_NULL;
void *read_buf;
void *write_buf;
int main(void)
{
dev = rt_device_find("drvdemo");
rt_device_init(dev);
rt_thread_mdelay(1000);
rt_device_open(dev,RT_DEVICE_OFLAG_OPEN);
rt_device_read(dev,0,read_buf,20);
rt_device_write(dev,0,write_buf,20);
rt_device_control(dev,0,RT_NULL);
rt_device_close(dev);
return RT_EOK;
}
看一下测试结果,我们实现的 3 个驱动函数都只有打印输出,所以我们可以通过打印信息查看是否正确执行的驱动函数的内容:
下面是在雅特力AT32开发板的测试结果。
(原本打算使用gd32去做实验的,但是从官方的MDK工程打开一直存在问题,然后就换了一块板子,进行测试,从这里可以发现,统一的API接口的好处,应用层无缝对接)
下面是在对于立创开发板GD32F407进行详细实验。
- 下载rt-thread 源码
- 寻找bsp-gd32工程,gd32有两个类型一个是arm,一个risc-v
- 寻找立创开发板GD32F407模板工程 gd32f407v-lckfb
- 使用env工具进行配置
- 如果直接编译,会报一个错误bug,需要修改,解决方法是添加condvar.c文件即可。
- 添加编写的drv_demo.c文件,完成设备驱动的添加
7. 编写应用层程序 测试
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-13 yuanzihao first implementation
*/
#include <stdio.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
/* defined the LED1 pin: PB2 */
#define LED_PIN GET_PIN(B, 2)
rt_device_t dev = RT_NULL;
void *read_buf;
void *write_buf;
int main(void)
{
int count = 1;
dev = rt_device_find("drvdemo");
rt_device_init(dev);
rt_thread_mdelay(1000);
rt_device_open(dev,RT_DEVICE_OFLAG_OPEN);
rt_device_read(dev,0,read_buf,20);
rt_device_write(dev,0,write_buf,20);
rt_device_control(dev,1,RT_NULL);
rt_device_close(dev);
/* set LED1 pin mode to output */
rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
while (count++)
{
rt_pin_write(LED_PIN, PIN_HIGH);
rt_thread_mdelay(500);
//rt_kprintf("this is led_control on [ok]!\n");
rt_pin_write(LED_PIN, PIN_LOW);
rt_thread_mdelay(500);
//rt_kprintf("this is led_control off [ok]!\n");
}
return RT_EOK;
}
通过上面的测试,我们实现了一个简单的设备驱动的设计,虽然demo比较简单,但是经过这么一个过程可以让我们更加的理解 RT-Thread I/O 设备模型的工作方式和流程。
在这里,同时介绍一个开源项目mr-library。
mr-library 是一个开源项目,设备抽象层应该比RT-Thread更加轻量级,是一个学习设备框架,设备驱动,面向对象操作的好东西。
设备抽象层的开源库 mr-library
mr-library 框架是专为嵌入式系统设计的轻量级框架。充分考虑了嵌入式系统在资源和性能方面的需求。 通过提供标准化的设备管理接口,极大简化了嵌入式应用开发的难度,帮助开发者快速构建嵌入式应用程序。
框架为开发者提供了标准化的开启(open
)、关闭(close
)、控制(ioctl
)、读(read
)、写(write
) 等接口。它将应用程序与底层硬件驱动进行解耦。应用程序无需了解驱动的实现细节。 当硬件发生改变时,只需要适配底层驱动,应用程序就可以无缝迁移到新硬件上。这大大提高了软件的可重用性和应对新硬件的可扩展性。
关键特性
- 标准化的设备访问接口
- 应用程序和驱动开发解耦
- 简化底层驱动和应用程序开发
- 轻量易上手,资源占用低
- 模块化设计,各部分解耦合并独立开发,极低的硬件迁移成本
- 支持在裸机环境和操作系统环境下使用
主要组成
- 设备框架:提供设备访问标准接口
- 内存管理:动态内存管理
- 工具:链表、队列、平衡树等常用数据结构
- 各类功能组件
标准化设备接口
设备的所有操作都可通过以下接口实现:
接口 | 描述 |
---|---|
mr_dev_register | 注册设备 |
mr_dev_open | 打开设备 |
mr_dev_close | 关闭设备 |
mr_dev_ioctl | 控制设备 |
mr_dev_read | 从设备读取数据 |
mr_dev_write | 向设备写入数据 |
测试案例