Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】DAY4设备与驱动
发布于 2024-07-25 21:05:38 浏览:240
订阅该版
#DAY04 ##一、I/O设备 ###设备介绍 ####设备框架 RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240725/11af0e6b2875ea960e9a942642978223.png) 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册 I/O 设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到 I/O 设备管理器中,使用序列图如下图所示,主要有以下 2 点: 1.设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过 rt_device_register() 接口注册到 I/O 设备管理器中。 2.应用程序通过 rt_device_find() 接口查找到设备,然后使用 I/O 设备管理接口来访问硬件。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240725/31a9a3316fc58a6d6cd8b1dffd55877a.png) ###使用和管理 ####创建和注册 驱动层负责创建设备实例,并注册到 I/O 设备管理器中,可以通过静态申明的方式创建设备实例,也可以用下面的接口进行动态创建: ```c rt_device_t rt_device_create(int type, int attach_size); ``` 参数: type:设备类型 ```c 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 /* 杂类设备 */ ``` 可以从以上选择其一 attach_size:用户数据大小 返回值: 句柄:成功创建返回:RT_EOK ####访问硬件操作方法 调用该接口时,系统会从动态堆内存中分配一个设备控制块,大小为 struct rt_device 和 attach_size 的和,设备的类型由参数 type 设定。设备被创建后,需要实现它访问硬件的操作方法。 ```c 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,意思是获取块设备的大小信息。 ###以i2c总线为例 ####访问i2c总线设备 `rt_device_find()` 根据 I2C 总线设备名称查找设备获取设备句柄 name:总线设备名字 ```c i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); ``` `rt_i2c_transfer()` 传输数据 bus:总线设备句柄 msgs:待传输的消息 num:元素个数 ```c rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num); ``` 数据类型结构: ```c struct rt_i2c_msg { rt_uint16_t addr; /* 从机地址 */ rt_uint16_t flags; /* 读、写标志等 */ rt_uint16_t len; /* 读写数据字节数 */ rt_uint8_t *buf; /* 读写数据缓冲区指针 */ } ``` ####i2c从设备数据写 ```c rt_size_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, rt_uint16_t addr, rt_uint16_t flags, const rt_uint8_t *buf, rt_uint32_t count); ``` bus:I2C 总线设备句柄 flags:标志位,可为上文提到的除 RT_I2C_WR RT_I2C_RD之外的其他标志位,可以进行 “|” 操作 buf:待发送数据缓冲区 count:待发送的数据大小 ####i2c从设备数据读 ```c rt_size_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, rt_uint16_t addr, rt_uint16_t flags, rt_uint8_t *buf, rt_uint32_t count); ``` bus:I2C 总线设备句柄 addr:从机地址 flags:标志位,可为上文提到的除 RT_I2C_WR RT_I2C_RD之外的其他标志位,可以进行 “|” 操作 buf:待发送数据缓冲区 count:待发送的数据大小 ###代码展示 main.c ```c /* * Copyright (c) 2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-07-06 Supperthomas first version * 2023-12-03 Meco Man support nano version */ #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #include
#define UART2 #define UART2_TX GET_PIN(A, 2) #define UART2_RX GET_PIN(A, 3) struct rt_semaphore sem; rt_thread_t th1; rt_device_t u2_dev; rt_device_t dev_demo; //定义结构体(默认值) struct serial_configure u2_config = RT_SERIAL_CONFIG_DEFAULT; rt_err_t rx_callback(rt_device_t dev , rt_size_t size) { rt_sem_release(&sem); return RT_EOK; } void u2_entry(void *p) { char buffer; while (1) { /* code */ while (rt_device_read(u2_dev,0,&buffer,1) != 1) { rt_sem_take(&sem,RT_WAITING_FOREVER); } rt_kprintf("%d",buffer); } } int main(void) { rt_err_t ret = 0; u2_dev = rt_device_find("uart2"); dev_demo = rt_device_find("test"); if(u2_dev == RT_NULL) { LOG_E("cant find!\n"); return -ENOMEM;//返回无效码 } ret = rt_device_open(u2_dev,RT_DEVICE_OFLAG_RDWR|RT_DEVICE_FLAG_INT_RX); if (ret<0) { LOG_E("cant open!\n"); return ret; } if (dev_demo == RT_NULL) { LOG_E("device failed"); return RT_EINVAL; } rt_device_init(dev_demo); rt_device_open(dev_demo,RT_DEVICE_OFLAG_RDWR); rt_device_close(dev_demo); rt_device_control(u2_dev,RT_DEVICE_CTRL_CONFIG,(void *)&u2_config); //设置接收回调函数 rt_device_set_rx_indicate(u2_dev,rx_callback); /************设置信号量和线程:***********/ rt_sem_init(&sem,"sem",0,RT_IPC_FLAG_FIFO); th1 = rt_thread_create("th1",u2_entry,RT_NULL,1024,10,5); rt_thread_startup(th1); rt_device_write(u2_dev , 0 , "hello init ...\n",rt_strlen("hello init ...\n")); return RT_EOK; } ``` demo.c ```c //自定义驱动 //并完成注册 #include
#include
rt_err_t demo_init(rt_device_t dev) { rt_kprintf("demo_init...\n"); return 0; } rt_err_t demo_open(rt_device_t dev) { rt_kprintf("demo_open...\n"); return 0; } rt_err_t demo_close(rt_device_t dev) { rt_kprintf("demo_close...\n"); return 0; } int rt_demo_init(void) { rt_device_t test_dev; test_dev = rt_device_create(RT_Device_Class_Char,32); if(!test_dev) { LOG_E("failed"); return -RT_ERROR; } test_dev->init = demo_init; test_dev->open = demo_open; test_dev->close = demo_close; rt_device_register(test_dev,"test",RT_DEVICE_FLAG_RDWR); return RT_EOK; } INIT_COMPONENT_EXPORT(rt_demo_init); MSH_CMD_EXPORT(rt_demo_init, rt_demo_init); ``` ###效果展示 ![screenshot_810c480a972a71ecbe41b0b1612fa28.png](https://oss-club.rt-thread.org/uploads/20240725/80d793496bc1ed2bd316a1da579e6003.png.webp) ![screenshot_10b7d6864893a83bba6a7c5218aac6f.png](https://oss-club.rt-thread.org/uploads/20240725/eb6ac935124f02f6a806f8435faedf27.png)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
24届暑期夏令营王伽利
这家伙很懒,什么也没写!
文章
3
回答
0
被采纳
0
关注TA
发私信
相关文章
推荐文章
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
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
a1012112796
13
个答案
1
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
6
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部