Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】DAY4 RT-Thread设备驱动学习
发布于 2024-08-02 17:03:40 浏览:109
订阅该版
[tocm] # 【2024-RSOC】DAY4 RT-Thread设备驱动学习 ## 框架详解 绝大部分的嵌入式系统都包括一些 I/O(Input/Output,输入 / 输出)设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的 Flash 或 SD 卡,以及网络设备的以太网接口等,都是嵌入式系统中容易找到的 I/O 设备例子。 ### 设备驱动的三大层 RT-Thread 提供了一套简单的 I/O 设备模型框架,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。 ![I/O 设备模型框架](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-dev.png) #### IO设备管理层 IO设备管理层是操作系统中负责管理IO设备及其与CPU、内存之间数据交换的层次。 #### 设备驱动框架层 它主要负责对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,形成一套统一的操作方法和接入标准。这样的设计可以屏蔽硬件差异,为应用层提供统一的操作接口,提高系统的可移植性和可扩展性。 #### 设备驱动层 设备驱动层是嵌入式系统或操作系统中的一个重要组成部分,它主要负责为上层程序提供外部设备的操作接口,并实现设备的驱动程序。设备驱动层一般由硬件抽象层(HAL)、板级支持包(BSP)和驱动程序组成。 ## I2C I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。 使用示例: ```c #define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */ #define AHT10_ADDR 0x38 /* 从机地址 */ struct rt_i2c_bus_device *i2c_bus; /* I2C总线设备句柄 */ /* 查找I2C总线设备,获取I2C总线设备句柄 */ i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); /* 读传感器寄存器数据 */ static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf) { struct rt_i2c_msg msgs; msgs.addr = AHT10_ADDR; /* 从机地址 */ msgs.flags = RT_I2C_RD; /* 读标志 */ msgs.buf = buf; /* 读写数据缓冲区指针 */ msgs.len = len; /* 读写数据字节数 */ /* 调用I2C设备接口传输数据 */ if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } } ``` ### I2C从数据结构读写API 向 I2C 从设备发送数据: ```c rt_size_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, //I2C 总线设备句柄 rt_uint16_t addr, //I2C 从设备地址 rt_uint16_t flags, //标志位,可为上文提到的除 RT_I2C_WR RT_I2C_RD之外的其他标志位,可以进行 “|” 操作 const rt_uint8_t *buf, // 待发送数据缓冲区 rt_uint32_t 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); //缓冲区大小(单位:字节,要大于等于最大接收到的数据长度) ``` 示例: ```c #include
#include
#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */ #define AHT10_ADDR 0x38 /* 从机地址 */ #define AHT10_CALIBRATION_CMD 0xE1 /* 校准命令 */ #define AHT10_NORMAL_CMD 0xA8 /* 一般命令 */ #define AHT10_GET_DATA 0xAC /* 获取数据命令 */ static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */ static rt_bool_t initialized = RT_FALSE; /* 传感器初始化状态 */ /* 写传感器寄存器 */ static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *data) { rt_uint8_t buf[3]; struct rt_i2c_msg msgs; rt_uint32_t buf_size = 1; buf[0] = reg; //cmd if (data != RT_NULL) { buf[1] = data[0]; buf[2] = data[1]; buf_size = 3; } msgs.addr = AHT10_ADDR; msgs.flags = RT_I2C_WR; msgs.buf = buf; msgs.len = buf_size; /* 调用I2C设备接口传输数据 */ if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } } /* 读传感器寄存器数据 */ static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf) { struct rt_i2c_msg msgs; msgs.addr = AHT10_ADDR; msgs.flags = RT_I2C_RD; msgs.buf = buf; msgs.len = len; /* 调用I2C设备接口传输数据 */ if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } } static void read_temp_humi(float *cur_temp, float *cur_humi) { rt_uint8_t temp[6]; write_reg(i2c_bus, AHT10_GET_DATA, RT_NULL); /* 发送命令 */ rt_thread_mdelay(400); read_regs(i2c_bus, 6, temp); /* 获取传感器数据 */ /* 湿度数据转换 */ *cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20); /* 温度数据转换 */ *cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50; } static void aht10_init(const char *name) { rt_uint8_t temp[2] = {0, 0}; /* 查找I2C总线设备,获取I2C总线设备句柄 */ i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); if (i2c_bus == RT_NULL) { rt_kprintf("can't find %s device!\n", name); } else { write_reg(i2c_bus, AHT10_NORMAL_CMD, temp); rt_thread_mdelay(400); temp[0] = 0x08; temp[1] = 0x00; write_reg(i2c_bus, AHT10_CALIBRATION_CMD, temp); rt_thread_mdelay(400); initialized = RT_TRUE; } } static void i2c_aht10_sample(int argc, char *argv[]) { float humidity, temperature; char name[RT_NAME_MAX]; humidity = 0.0; temperature = 0.0; if (argc == 2) { rt_strncpy(name, argv[1], RT_NAME_MAX); } else { rt_strncpy(name, AHT10_I2C_BUS_NAME, RT_NAME_MAX); } if (!initialized) { /* 传感器初始化 */ aht10_init(name); } if (initialized) { /* 读取温湿度数据 */ read_temp_humi(&temperature, &humidity); rt_kprintf("read aht10 sensor humidity : %d.%d %%\n", (int)humidity, (int)(humidity * 10) % 10); if( temperature >= 0 ) { rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(temperature * 10) % 10); } else { rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(-temperature * 10) % 10); } } else { rt_kprintf("initialize sensor failed!\n"); } } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample); ``` ## SPI SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于 EEPROM、FLASH、实时时钟、AD 转换器、还有数字信号处理器和数字信号解码器之间。SPI 一般使用 4 根线通信: ![SPI 主设备和从设备的连接方式](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi1.png) 示例: ```c /* * 程序清单:这是一个 SPI 设备使用例程 * 例程导出了 spi_w25q_sample 命令到控制终端 * 命令调用格式:spi_w25q_sample spi10 * 命令解释:命令第二个参数是要使用的SPI设备名称,为空则使用默认的SPI设备 * 程序功能:通过SPI设备读取 w25q 的 ID 数据 */ #include
#include
#define W25Q_SPI_DEVICE_NAME "qspi10" static void spi_w25q_sample(int argc, char *argv[]) { struct rt_spi_device *spi_dev_w25q; char name[RT_NAME_MAX]; rt_uint8_t w25x_read_id = 0x90; rt_uint8_t id[5] = {0}; if (argc == 2) { rt_strncpy(name, argv[1], RT_NAME_MAX); } else { rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX); } /* 查找 spi 设备获取设备句柄 */ spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name); if (!spi_dev_w25q) { rt_kprintf("spi sample run failed! can't find %s device!\n", name); } else { /* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */ rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5); rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:%x%x\n", id[3], id[4]); /* 方式2:使用 rt_spi_transfer_message()发送命令读取ID */ struct rt_spi_message msg1, msg2; msg1.send_buf = &w25x_read_id; msg1.recv_buf = RT_NULL; msg1.length = 1; msg1.cs_take = 1; msg1.cs_release = 0; msg1.next = &msg2; msg2.send_buf = RT_NULL; msg2.recv_buf = id; msg2.length = 5; msg2.cs_take = 0; msg2.cs_release = 1; msg2.next = RT_NULL; rt_spi_transfer_message(spi_dev_w25q, &msg1); rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]); } } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(spi_w25q_sample, spi w25q sample); ```
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
cy_99
这家伙很懒,什么也没写!
文章
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
UART
ota在线升级
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
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
7
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
3
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部