Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
【2024_RSOC】设备和驱动——5.SPI设备使用
发布于 2024-07-28 14:47:41 浏览:852
订阅该版
#设备和驱动——5.SPI设备使用 在使用之前,跟iic篇一样,需要打开我spi的菜单配置,详细过程看上一篇。 ##1.挂载 SPI 设备 SPI 驱动会注册 SPI 总线,SPI 设备需要挂载到已经注册好的 SPI 总线上,并向内核注册 SPI 设备。**RT-Thread 5.0.0 添加的新函数,如果低于5.0.0版本不支持这个函数** ```c rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device, const char *name, const char *bus_name, rt_base_t cs_pin, void *user_data) device:SPI 设备句柄 name:SPI 设备名称,一般 SPI 总线命名原则为 spix, SPI 设备命名原则为 spixy ,如 spi10 表示挂载在 spi1 总线上的 0 号设备。 bus_name:SPI 总线名称 cs_pin:SPI 片选引脚号(基于PIN框架),cs_pin可以通过PIN框架rt_pin_get函数来 获取,也可以使用BSP级提供的GET_PIN宏定义来获取。 user_data:用户数据指针,用户使用不到的情况下可以设置为RT_NULL ``` 为了兼容RT-Thread 5.0.0 版本前的SPI设备片选引脚通过user_data挂载的方式,保留了另一种方式挂载。 ```c rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device, const char *name, const char *bus_name, void *user_data) ``` 如果不对user_data作要求的话,一般使用下面两个接口(基于上面接口的包装接口) ```c 硬件SPI: rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin) 软件SPI: rt_err_t rt_hw_softspi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin) ``` ##2.访问 SPI 设备 ```c rt_device_t rt_device_find(const char* name); name:设备名称,一般情况下,注册到系统的 SPI 设备名称为 spi10, qspi10等 返回:设备句柄,查找到对应设备将返回相应的设备句柄,需要强制转化为spi的设备指 针(struct rt_spi_device *) ``` ##3.配置 SPI 设备 ```c 挂载 SPI 设备到 SPI 总线后需要配置 SPI 设备的传输参数。 rt_err_t rt_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg) device:SPI 设备句柄 cfg:SPI 配置参数指针 struct rt_spi_configuration { rt_uint8_t mode; /* 模式 */包含 MSB/LSB、主从模式、 时序模式 等,可取宏组合如下: /* 设置数据传输顺序是MSB位在前还是LSB位在前 */ #define RT_SPI_LSB (0<<2) /* bit[2]: 0-LSB */ #define RT_SPI_MSB (1<<2) /* bit[2]: 1-MSB */ /* 设置SPI的主从模式 */ #define RT_SPI_MASTER (0<<3) /* SPI master device */ #define RT_SPI_SLAVE (1<<3) /* SPI slave device */ /* 设置时钟极性和时钟相位 */ #define RT_SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */ #define RT_SPI_MODE_1 (0 | RT_SPI_CPHA) /* CPOL = 0, CPHA = 1 */ #define RT_SPI_MODE_2 (RT_SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */ #define RT_SPI_MODE_3 (RT_SPI_CPOL | RT_SPI_CPHA) /* CPOL = 1, CPHA = 1 */ #define RT_SPI_CS_HIGH (1<<4) /* Chipselect active high */ #define RT_SPI_NO_CS (1<<5) /* No chipselect */ #define RT_SPI_3WIRE (1<<6) /* SI/SO pin shared */ #define RT_SPI_READY (1<<7) /* Slave pulls low to pause */ rt_uint8_t data_width; /* 数据宽度,可取8位、16位、32位 */ rt_uint16_t reserved; /* 保留 */ rt_uint32_t max_hz; /* 最大频率 */设置数据传输的波特率 }; ``` ##4.配置QSPI 设备 ```c rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg) device:QSPI 设备句柄 cfg:QSPI 配置参数指针 struct rt_qspi_configuration { struct rt_spi_configuration parent; /* 继承自 SPI 设备配置参数 */ rt_uint32_t medium_size; /* 介质大小 */ rt_uint8_t ddr_mode; /* 双倍速率模式 */ rt_uint8_t qspi_dl_width ; /* QSPI 总线位宽,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */ }; ``` ##5.自定义传输数据 获取到 SPI 设备句柄就可以使用 SPI 设备管理接口访问 SPI 设备器件,进行数据收发。 **SPI 数据传输相关接口会调用 rt_mutex_take(), 此函数不能在中断服务程序里面调用,会导致 assertion 报错。** ```c struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device,struct rt_spi_message *message); device:SPI 设备句柄 message:消息指针 返回:返回指向剩余未发送的 message 的指针,如果发送完成则为空指针。 struct rt_spi_message { const void *send_buf; /* 发送缓冲区指针 */为 RT_NULL 时, 表示本次传输为只接收状态 void *recv_buf; /* 接收缓冲区指针 */为 RT_NULL 时, 表示本次传输为只发送状态 rt_size_t length; /* 发送 / 接收 数据字节数 */注意, 是字节数 struct rt_spi_message *next; /* 指向继续发送的下一条消息的指针 */ 若只发送一条消息,则此指针值为 RT_NULL。多个待传输的消息通过 next 指针以单向链表的形式连接在 一起。 unsigned cs_take : 1; /* 片选选中 */cs_take 值为 1 时,表 示在传输数据前,设置对应的 CS 为有效 状态。 unsigned cs_release : 1; /* 释放片选 */cs_release 值为 1 时,表示在数据传输结束后,释放对应 的 CS。 }; ``` ##6.传输一次数据 如果只传输一次数据可以通过如下函数 ```c rt_size_t rt_spi_transfer(struct rt_spi_device *device, const void *send_buf, void *recv_buf, rt_size_t length); device:SPI 设备句柄 send_buf:发送数据缓冲区指针 recv_buf:接收数据缓冲区指针 length:发送/接收 数据字节数 返回:成功传输的字节数,为0说明传输失败。 ``` 等同于调用 rt_spi_transfer_message() 传输一条消息,开始发送数据时片选选中,函数返回时释放片选 ```c message 参数配置如下: struct rt_spi_message msg; msg.send_buf = send_buf; msg.recv_buf = RT_NULL; msg.length = length; msg.cs_take = 1; msg.cs_release = 1; msg.next = RT_NULL; ``` ##7.发送一次数据 调用此函数发送 send_buf 指向的缓冲区的数据,忽略接收到的数据,此函数是对 rt_spi_transfer() 函数的封装。 ```c rt_size_t rt_spi_send(struct rt_spi_device *device, const void *send_buf, rt_size_t length) device:SPI 设备句柄 send_buf:发送数据缓冲区指针 length:发送数据字节数 返回:成功发送的字节数 struct rt_spi_message msg; msg.send_buf = RT_NULL; msg.recv_buf = recv_buf; msg.length = length; msg.cs_take = 1; msg.cs_release = 1; msg.next = RT_NULL; ``` ##8.接收一次数据 如果只接收一次数据可以通过如下函数,跟上面类似。 ```c rt_size_t rt_spi_recv(struct rt_spi_device *device, void *recv_buf, rt_size_t length); device:SPI 设备句柄 recv_buf:接收数据缓冲区指针 length:接收数据字节数 返回:成功接收的字节数 struct rt_spi_message msg; msg.send_buf = RT_NULL; msg.recv_buf = recv_buf; msg.length = length; msg.cs_take = 1; msg.cs_release = 1; msg.next = RT_NULL; ``` ##9.连续两次发送数据 如果需要先后连续发送 2 个缓冲区的数据,**并且中间片选不释放**,可以调用如下函数 ```c rt_err_t rt_spi_send_then_send(struct rt_spi_device *device, const void *send_buf1, rt_size_t send_length1, const void *send_buf2, rt_size_t send_length2); device:SPI 设备句柄 send_buf1:发送数据缓冲区 1 指针 send_length1:发送数据缓冲区 1 数据字节数 send_buf2:发送数据缓冲区 2 指针 send_length2:发送数据缓冲区 2 数据字节数 ``` message 参数配置如下: ```c struct rt_spi_message msg1,msg2; msg1.send_buf = send_buf1; msg1.recv_buf = RT_NULL; msg1.length = send_length1; msg1.cs_take = 1; msg1.cs_release = 0; msg1.next = &msg2; msg2.send_buf = send_buf2; msg2.recv_buf = RT_NULL; msg2.length = send_length2; msg2.cs_take = 0; msg2.cs_release = 1; msg2.next = RT_NULL; ``` **一般用于第一次先发送命令和地址等数据,第二次再发送指定长度的数据,因为在大部分的数据写操作中,都需要先发命令和地址,长度一般只有几个字节** ##10.先发送后接收数据 如果需要向从设备先发送数据,然后接收从设备发送的数据,并且中间片选不释放,可以调用如下函数:(此函数发送第一条数据 send_buf 时开始片选,此时忽略接收到的数据,然后发送第二条数据,此时主设备会发送数据 0XFF,接收到的数据保存在 recv_buf 里,函数返回时释放片选。) ```c rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length); device:SPI 从设备句柄 send_buf:发送数据缓冲区指针 send_length:发送数据缓冲区数据字节数 recv_buf:接收数据缓冲区指针 recv_length:接收数据字节数 ``` message 参数配置如下: ```c struct rt_spi_message msg1,msg2; msg1.send_buf = send_buf; msg1.recv_buf = RT_NULL; msg1.length = send_length; msg1.cs_take = 1; msg1.cs_release = 0; msg1.next = &msg2; msg2.send_buf = RT_NULL; msg2.recv_buf = recv_buf; msg2.length = recv_length; msg2.cs_take = 0; msg2.cs_release = 1; msg2.next = RT_NULL; ``` **一般用于从 SPI 从设备中读取一块数据,第一次会先发送一些命令和地址数据,然后再接收指定长度的数据。** 通过最后这两接口,我可以从中受到启发。在特定情况下去编写我们自己需要的接口。 示例代码: ```c #include
#include
#include
#include
#include "drv_soft_spi.h" // #include "drv_config.h" #define KEY_UP GET_PIN(C,5) #define KEY_DOWN GET_PIN(C,1) #define KEY_LEFT GET_PIN(C,0) #define KEY_RIGHT GET_PIN(C,4) void spi_detach(void) { rt_hw_spi_device_attach("spi2","spi20",GET_PIN(B,12)); } INIT_DEVICE_EXPORT(spi_detach); void spi_tansfer_data() { struct rt_spi_device * spi20; spi20 = (struct rt_spi_device *)rt_device_find("spi20"); struct rt_spi_configuration cfg; cfg.mode = RT_SPI_MSB|RT_SPI_MASTER|RT_SPI_MODE_0; cfg.data_width = 8; cfg.max_hz = 1*1000*1000; rt_spi_configure(spi20, &cfg); int send_buff = 0xDA; int recv_buff = 0XFA; int x = rt_spi_transfer( spi20, &send_buff, &recv_buff, 1); rt_kprintf("spi_tansfer_data = %d",x); } MSH_CMD_EXPORT(spi_tansfer_data,spi_tansfer_data); void spi_send_data() { struct rt_spi_device * spi20; spi20 = (struct rt_spi_device *)rt_device_find("spi20"); struct rt_spi_configuration cfg; cfg.mode = RT_SPI_MSB|RT_SPI_MASTER|RT_SPI_MODE_0; cfg.data_width = 8; cfg.max_hz = 1*1000*1000; rt_spi_configure(spi20, &cfg); int send_buff = 0xDA; int recv_buff = 0XFA; int x = rt_spi_send( spi20, &send_buff, 1); rt_kprintf("spi_send_data = %d",x); } MSH_CMD_EXPORT(spi_send_data,spi_send_data); void spi_recev_data() { struct rt_spi_device * spi20; spi20 = (struct rt_spi_device *)rt_device_find("spi20"); struct rt_spi_configuration cfg; cfg.mode = RT_SPI_MSB|RT_SPI_MASTER|RT_SPI_MODE_0; cfg.data_width = 8; cfg.max_hz = 1*1000*1000; rt_spi_configure(spi20, &cfg); int recv_buff = 0XFA; int x = rt_spi_recv( spi20, &recv_buff, 1); rt_kprintf("spi_recev_data = %d",x); } MSH_CMD_EXPORT(spi_recev_data,spi_recev_data); void spi_send2_data() { struct rt_spi_device * spi20; spi20 = (struct rt_spi_device *)rt_device_find("spi20"); struct rt_spi_configuration cfg; cfg.mode = RT_SPI_MSB|RT_SPI_MASTER|RT_SPI_MODE_0; cfg.data_width = 8; cfg.max_hz = 1*1000*1000; rt_spi_configure(spi20, &cfg); int send_buff1 = 0xDA; int send_buff2[3]= {1,2,3}; int x = rt_spi_send_then_send( spi20, &send_buff1,1, send_buff2,3 ); rt_kprintf("spi_send2_data = %d",x); } MSH_CMD_EXPORT(spi_send2_data,spi_send2_data); void spi_send_recv_data() { struct rt_spi_device * spi20; spi20 = (struct rt_spi_device *)rt_device_find("spi20"); struct rt_spi_configuration cfg; cfg.mode = RT_SPI_MSB|RT_SPI_MASTER|RT_SPI_MODE_0; cfg.data_width = 8; cfg.max_hz = 1*1000*1000; rt_spi_configure(spi20, &cfg); int send_buff1 = 0xDA; int rece_buff2[3]= {1,2,3}; int x = rt_spi_send_then_recv( spi20, &send_buff1,1, rece_buff2,3 ); rt_kprintf("spi_send2_data = %d",x); } MSH_CMD_EXPORT(spi_send_recv_data,spi_send_recv_data); ```
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
马桶盖盖子
这家伙很懒,什么也没写!
文章
7
回答
0
被采纳
0
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
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
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部