Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
设备驱动框架
浅析RT-Thread设备驱动框架
发布于 2023-06-10 16:48:20 浏览:3286
订阅该版
[tocm] # 浅析RT-Thread设备驱动框架 ## 前言介绍 RT-Thread 设备框架属于**组件**和服务层,是基于 RT-Thread 内核之上的上层软件。设备框架是针对某一类外设,抽象出来的一套**统一的操作方法及接入标准**,可以屏蔽硬件差异,为应用层提供统一的操作方法。 RT-Thread 设备框架分为三层:**设备驱动层**、**设备驱动框架层**、**I/O 设备管理层**。其中设备驱动层直接对接底层硬件设备;I/O 设备管理层向应用层提供了rt_device_find、open、read、write、close、register等访问设备的统一标准接口。而设备驱动框架层就是就是将同类型硬件设备的共同特特征提取抽象出来,并且还预留了接口,可以添加不同设备的独有特性。 其中的设备模型被认为是一类对象,每个设备对象都是由基对象派生的,每个设备都可以继承其父类对象的属性,并派生其私有属性。 正是这样的框架,使得 RT-Thread 设备框架中各模块高内聚低耦合,对于已有的设备类型,只要将底层硬件的驱动对接到设备驱动层,就可以在应用程序中,调用统一的标准接口,使用的不同厂家不同类型的硬件设备。 ## 示例分析 下文将以 CAN 设备为例,并从 void 类型指针、结构体等方向,分析 RT-Thread 设备驱动框架。 ### 设备对象 以下是设备对象在 `rt-thread\include\rtdef.h` 中的具体定义:(去除了其中一些扩展的功能,方便理解) ```c /** * Device structure */ struct rt_device { struct rt_object parent; /**< inherit from rt_object */ enum rt_device_class_type type; /**< device type */ rt_uint16_t flag; /**< device flag */ rt_uint16_t open_flag; /**< device open flag */ rt_uint8_t ref_count; /**< reference count */ rt_uint8_t device_id; /**< 0 - 255 */ /* device call back */ 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_ssize_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); rt_ssize_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 /* RT_USING_DEVICE_OPS */ void *user_data; /**< device private data */ }; ``` 然后从 `struct rt_device` 中派生出新的 CAN 设备类型 `struct rt_can_device` ,其中依据 CAN 类型设备共有的特性新增了一些结构体成员,位于`rt-thread\components\drivers\include\drivers\can.h` 中: ```c struct rt_can_device { struct rt_device parent; const struct rt_can_ops *ops; struct can_configure config; struct rt_can_status status; rt_uint32_t timerinitflag; struct rt_timer timer; struct rt_can_status_ind_type status_indicate; #ifdef RT_CAN_USING_HDR struct rt_can_hdr *hdr; #endif #ifdef RT_CAN_USING_BUS_HOOK rt_can_bus_hook bus_hook; #endif /*RT_CAN_USING_BUS_HOOK*/ struct rt_mutex lock; void *can_rx; void *can_tx; }; ``` 在 stm32 的 CAN 设备驱动中,又从 `struct rt_can_device` 中派生出了新的 CAN 设备模型 `struct stm32_can` 其中添加了这个设备类型的私有数据,便于底层驱动的对接: ```C /* stm32 can device */ struct stm32_can { char *name; CAN_HandleTypeDef CanHandle; CAN_FilterTypeDef FilterConfig; struct rt_can_device device; /* inherit from can device */ }; ``` 可以看出,RT-Thread 通过结构体和函数指针使用 C 语言实现了一些面向对象编程的特性,如封装和继承等,这样面向对象、模块化的思维框架,有助于实现各模块之间高内聚低耦合,提高开发效率。 ### 操作方法 在 `rt-thread\components\drivers\include\drivers\can.h` 中可以看到针对 CAN 设备有以下操作方法,这些操作方法是需要我们在设备驱动层去针对不同的硬件设备进行对接实现的。对于一些特殊的设备类型,可以不用对接其所有的操作方法。 ```c struct rt_can_ops { rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg); rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg); int (*sendmsg)(struct rt_can_device *can, const void *buf, rt_uint32_t boxno); int (*recvmsg)(struct rt_can_device *can, void *buf, rt_uint32_t boxno); }; ``` 在`libraries\HAL_Drivers\drv_can.c`中,就实现了所有 CAN 设备的操作方法,并赋值给对应的函数指针,可以直接调用。并且通过 `static const` 修饰符,使结构体变量`_can_ops`的值在编译时被确定,并且不能在程序运行时修改。这样可以确保该结构体变量的内容是固定的,提高了代码的安全性和可维护性。然后通过`rt_hw_can_register`注册。 ```c static const struct rt_can_ops _can_ops = { _can_config, _can_control, _can_sendmsg, _can_recvmsg, }; /* register CAN1 device */ rt_hw_can_register(&drv_can1.device, drv_can1.name, &_can_ops, &drv_can1); ``` 可以看一下`rt_hw_can_register`函数的具体声明: ``` rt_err_t rt_hw_can_register(struct rt_can_device *can, const char *name, const struct rt_can_ops *ops, void *data); ``` 其中最后一个参数为 void 类型指针,在注册同类型的不同设备时,就可以通过最后一个参数,在注册时传入其特有的私有数据。例如 STM32 的 drv_can 中传入的就是 `stm32_can`结构体类型,其中就包含了 stm32 can 的私有数据域,在对接底层硬件驱动的时候就会方便很多。 ## 总结 可以感受到,其中结构体和指针的运用是非常重要的。尽管C语言是一种面向过程的语言,但通过使用结构体和函数指针,可以模拟实现封装、继承和多态这三个面向对象编程的特性,享受面向对象编程的好处。 对于指针的运用,主要是 **void 类型指针**和**函数指针**。由于 void 指针没有特定的类型,因此它可以指向任何类型的数据。也就是说,任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换,便于传递同类型的不同设备的私有数据。通过函数指针,也就是回调函数,可以大大提高程序的灵活性和并起到封装的作用。 这些优秀的思想是值得我们学习和借鉴的。 ## 题外话 最近写了一个可以生成 RT-Thread 外设驱动模板文件的脚本,并且**开源**啦! 欢迎各位想要对接外设驱动的小伙伴们使用体验,还有很多的不足的地方,欢迎大家贡献PR 仓库链接:[RTT_drv_template_generator ](https://github.com/Z8MAN8/RTT_drv_template_generator) 要是觉得做的还不错,请给个小星星支持一下!
4
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
螺丝松掉的人
这家伙很懒,什么也没写!
文章
42
回答
0
被采纳
0
关注TA
发私信
相关文章
1
设备驱动框架是否线程安全?
2
硬件初始化函数 HAL_XXX_MspInit在哪里被调用了?
3
rtt uart设备驱动半双工模式
4
关于I2C驱动框架存在的问题
5
添加自定义设备驱动进入RTT的框架
6
RTT的驱动框架怎么学
7
这三个驱动有什么区别呀?
8
rt驱动框架是否像rt官方希望的方式在被使用?
9
设备驱动开发指南是不是rtthread已经都实现了
10
关于串口DMA接收时,重新配置波特率会导致接收异常的问题?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
SFUD
msh
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
549
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部