Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-PSOC】RT-Thread_day4_Device驱动设备开发
发布于 2024-07-26 19:57:47 浏览:144
订阅该版
[tocm] # 【2024-RSOC】RT-Thread_day4_Device ## 学习目标 ### RT-Thread I/O 设备模型 #### I/O 设备模型框架 RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。 在RT-Thread中,应用程序通过I/O设备管理接口与底层硬件进行交互。 * **I/O 设备管理层**:提供了一组标准接口,使得应用程序可以透明地访问底层硬件设备。这一层封装了具体的设备驱动细节,使得应用程序无需关心驱动的具体实现,从而降低了代码的耦合性和提高了系统的可维护性。 * **设备驱动框架层**:对同类硬件设备进行了抽象,将通用部分标准化,而将特定于制造商的部分留作驱动程序实现。这一层确保了不同厂家的同类硬件设备可以以一致的方式被管理和使用。 * **设备驱动层**:直接与硬件交互的程序,实现了对硬件设备的控制和数据传输。驱动程序通过设备模型创建设备实例,并将其注册到I/O设备管理器中。 #### 设备驱动流程 设备驱动层的实现遵循以下步骤: 1. ***设备实例创建*** 2. ***设备注册*** 3. ***设备查找*** 4. ***设备访问*** ### RT-Thread SPI设备驱动 #### SPI 协议工作原理 **SPI特点**: * **全双工** * **同步通信** * **单主多从** * **片选信号** SPI分为使用两线、三线、四线制: ##### 四线制 - **MOSI**(Master Out Slave In):主设备输出 / 从设备输入数据线。 - **MISO**(Master In Slave Out):主设备输入 / 从设备输出数据线。 - **SCLK**(Serial Clock):时钟信号线,由主设备提供。 - **CS**(Chip Select):从设备选择信号线,用于选择特定的从设备进行通信。 ##### 三线制 SPI三线制通讯模式是指SDO/MOSI与SDI/MISO共用一条总线的通讯方式,采用的是半双工通讯。 ##### 两线制 SDO/MOSI与SDI/MISO共用了一条总线,所以并不能同时传输,也就是所谓的半双工通讯。而通讯过程中同样要用到CS/SS线进行片选,并不是没有CS/SS。而所谓的不需要CS/SS 线是指的如果SPI线上有一个主机和一个从机的情况下,是可以省略CS/SS片选线的,因为只有1个从机,所以该从机的CS/SS可以设置成常选状态,不能采用CS/SS拉高来作为结束,如果出现数据错误后果会很严重,后面会一直错误,这种3线确实也是3条线,但是跟半双工3线制模式的SPI通讯还是有区别的,往往很多IC所明确的只能采用3线制一般都是指半双工模式。 ##### SPI 工作流程 * 数据传输通常会包含一次数据交换。当主节点向从节点发送数据时,从节点也会向主节点发送数据。为此,主节点的内部移位寄存器和从节点被设置成环形。 * 在数据交换之前,主节点和从节点使其内部移位寄存器加载存储器数据。产生时钟信号时,主节点会通过 MOSI 线同步输出其移位寄存器。同时,从节点在 SIMO 处从主节点读取第一位,并将其存储到存储器中,然后通过 SOMI 输出 **MSB**。主节点会在 MISO 处读取从节点的第一位,并将其存储到存储器中以待稍后处理。整个过程将一直持续,直至交换完所有数据位,然后主节点使时钟空闲并通过 /SS 禁用从节点。 * SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。 SPI主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。 1. **选择从设备**:主设备通过拉低从设备的片选信号线(CS)来选择与之通信的从设备。 2. **数据加载**:主设备和从设备将待发送的数据加载到各自的移位寄存器中。 3. **时钟同步**:主设备产生时钟信号(SCLK),用于同步数据的发送和接收。 4. **数据交换**: - **主设备发送数据**:主设备通过MOSI(Master Out Slave In)线将数据一位一位地发送给从设备。 - **从设备接收数据**:从设备通过MISO(Master In Slave Out)线将数据一位一位地发送给主设备,同时接收来自MOSI线的数据。 - **数据移动**:主设备和从设备的移位寄存器在每个时钟周期移动一位数据,直到所有的数据位都被交换完毕。 5. **结束通信**:主设备释放从设备的片选信号线(CS),结束此次通信。 ### RT-Thread SPI设备 [SPI设备文档中心](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi?id=%e6%8c%82%e8%bd%bd-spi-%e8%ae%be%e5%a4%87) ## I2C 总线设备 ### I2C 总线工作原理 I2C只使用两条双向漏极开路(Open Drain)线,其中一条线为传输数据的串行资料线(SDA, Serial DAta line),另一条线是启动或停止传输以及发送时钟序列的串行主频(SCL, Serial CLock line)线,这两条线上都有上拉电阻。 | 模式 | 速度 | | ------------------------------ | ------- | | 标准模式(Standard Mode) | 100kb/s | | 快速模式(Fast Mode) | 400kb/s | | 增强快速模式(Fast Mode Plus) | 1Mb/s | | 高速模式(High Speed Mode) | 3.4Mb/s | | 极速模式(Ultra-FastMode) | 5Mb/s | ##### 总线结构 IIC使用两根信号线进行通信,要求两根线都使用 开漏输出接上拉电阻 的配置,以此实现总线上所有节点SDA、SCL信号的 线与 逻辑关系。 上拉电阻的取值有一定的要求,太小会导致灌入电流过大,使低数据不稳定,甚至损坏端口;太大会导致信号上升缓慢,使得数据传输出错。在不同应用场景及供电电压下有不同的取值要求。 漏极开路/集电极开路的缺点是对于一个距离长的数据线,信号传输速率得不到有效保证。更长的走线对于输出驱动器表现为更大的容性负载,等效容性负载C和信号线的上拉电阻R构成RC振荡器。RC越大,意味着反射和振荡越强,从而影响总线的信号完整性。这也是I2C规范对总线电容值约束在400pf以内的原因。高速模式对信号完整性的要求更高,协议有定义相关SDA/SCL处理办法,以保证在数据线够长、速率够高时,信号完整性也可以得到满足。 ##### 工作时序 * I2C的数据读取动作都在SCL为高时产生,SCL为低时是数据改变的时期,无论SDA如何变化都不影响读取。所以,传输数据的过程中,当SCL为高时,数据应当保持稳定,避免数据的采集出错。 **开始信号(START/S):** SCL为高时,SDA从高到低的跳变产生开始信号 **结束信号(STOP/P) :** SCL为高时,SDA从低到高的跳变产生结束信号 ![i2c数据传输](../image/day4iic开始结束信号.png) I2C总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器,即一帧共有9位。I2C每次发送数据必须是8位,MSB固定,先发高位,再发低位。 协议规定数据传输过程必须包含应答(ACK)。接收器通过应答告知发送的字节已被成功接收,之后发送器可以进行下一个字节的传输。主机产生数据传输过程中的所有时钟,包括用于应答的第9个时钟。发送器在应答时钟周期内释放对SDA总线的控制,这样接收器可以通过将SDA线拉低告知发送器:数据已被成功接收。 #### RT-Thread IIC 总线设备 [IIC 总线设备文档中心](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c) ### 结合实现RT-Thread的设备驱动框架实现一个自己的驱动 首先在`rt-thread\bsp\stm32\stm32f407-rt-spark\dist\project\libraries\HAL_Drivers\drivers\drv_a_test.o`新建一个自己要实现的驱动文件 ```c #include
#include
#include
//#ifdef (BSP_USING_TEST) #define DRV_DEBUG #define LOG_TAG "devtest" #include
rt_err_t devtest_init(rt_device_t dev) { rt_kprintf(" devtest is init ok!\n"); return RT_EOK; } rt_err_t devtest_open(rt_device_t dev, rt_uint16_t oflag) { rt_kprintf(" devtest is init open!\n"); return RT_EOK; } rt_err_t devtest_close(rt_device_t dev) { rt_kprintf(" devtest is init close!\n"); return RT_EOK; } rt_size_t devtest_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { buffer = "this is device read test\n"; rt_kprintf("buffer = %s", buffer); return RT_EOK; } rt_size_t devtest_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { buffer = "this is device write test\n"; rt_kprintf("buffer = %s", buffer); return RT_EOK; } rt_err_t devtest_control(rt_device_t dev, int cmd, void *args) { switch (cmd) { case 0: rt_kprintf("this is first cmd\n"); break; case 1: rt_kprintf("this is second cmd\n"); break; default: rt_kprintf("this is error cmd\n"); break; } return RT_EOK; } int rt_devtest_init(void) { rt_device_t devtest = RT_NULL; devtest = rt_device_create(RT_Device_Class_Char, 16); if (devtest == RT_NULL) { rt_kprintf("IO device create failed\n"); return RT_ERROR; } devtest->init = devtest_init; devtest->open = devtest_open; devtest->close = devtest_close; devtest->read = devtest_read; devtest->write = devtest_write; devtest->control = devtest_control; rt_device_register(devtest, "devtest", RT_DEVICE_FLAG_RDWR) ; return RT_EOK; } INIT_APP_EXPORT(rt_devtest_init); //#endif ``` 这段代码实现了一个简单的 RT-Thread 设备驱动程序,用于演示如何创建一个虚拟设备,并为其定义基本的操作接口。这个驱动程序包括了初始化、打开、关闭、读写和控制等基本功能。 其主要就是定义了下面几个**设备操作函数**: - **初始化函数** (`devtest_init`):初始化设备,通常用于执行一些必要的设置,如配置寄存器等。在这个例子中,它只是记录一条初始化的日志。 - **打开函数** (`devtest_open`):打开设备时调用,记录打开操作及其标志参数。 - **关闭函数** (`devtest_close`):关闭设备时调用,记录关闭操作。 - **读取函数** (`devtest_read`):从设备读取数据时调用,记录读取位置和大小,并返回成功状态。 - **写入函数** (`devtest_write`):向设备写入数据时调用,记录写入位置和大小,并返回成功状态。 - **控制函数** (`devtest_control`):处理设备控制命令时调用,记录命令编号,并返回成功状态。 同时定义了宏 `BSP_USING_TEST` 来控制是否编译这部分代码,并通过menuconfig和scons工具控制是否编译。 然后我们可以在`rt-thread\bsp\stm32\stm32f407-rt-spark\dist\project\libraries\HAL_Drivers\drivers\SConscript`这个文件下添加刚刚我们预设的宏以及文件名部分代码,用于控制scons工具是否将源文件编译。 ```c if GetDepend(['BSP_USING_TEST']): src += ['drv_a_test.c'] ``` 接下来在`rt-thread\bsp\stm32\stm32f407-rt-spark\dist\project\board\Kconfig`中添加menuconfig工具的config代码,这样就可以在工具中选择是否编译了。 ``` menu "Board extended module Drivers" config BSP_USING_TEST bool "Enable test driver" default n ``` 当在menuconfig中打开定义,就可以在应用层使用这个驱动了。 ```c #include
#include
#include
#define LOG_TAG "test.dev" #define LOG_LVL LOG_LVL_DBG #include
rt_device_t dev = RT_NULL; void *read_buf; void *send_buf; int test_dev(void) { dev = rt_device_find("devtest"); rt_device_init(dev); rt_thread_mdelay(1000); rt_device_open(dev, RT_DEVICE_OFLAG_OPEN); rt_thread_mdelay(1000); rt_device_write(dev, 0, send_buf, 8);//句柄,偏移量,数据,大小 rt_thread_mdelay(1000); rt_device_read(dev, 0, send_buf, 8); rt_thread_mdelay(1000); rt_device_control(dev, 1, RT_NULL); rt_thread_mdelay(1000); rt_device_close(dev); return RT_EOK; } MSH_CMD_EXPORT(test_dev, test_dev); ```
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
单字一个轩
这家伙很懒,什么也没写!
文章
4
回答
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
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
本月文章贡献
程序员阿伟
6
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部