Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SPI
SPI从机
RT-Thread SPI作为从模式接收数据的使用
2.00
发布于 2020-07-07 12:08:17 浏览:2786
订阅该版
[tocm] 最近遇到了如下需求: - MCU作为主控芯片通过SPI与蓝牙芯片连接。 - 蓝牙芯片会时不时向MCU发送大量**定长**的数据包。 这种情况下,如果MCU的SPI接口采用主模式,通过查询的方式询问蓝牙芯片是否有数据要发送,就会非常占用资源,并且遇到突发大量数据也可能会来不及处理。 比较好的一种方法是,MCU采用从模式的SPI。蓝牙芯片无脑向MCU吐数据。如果主控MCU的SPI时钟最大频率大于蓝牙芯片的SPI最大频率,此方法可以跑到蓝牙芯片SPI的传输极限。 ### 大体思路: 1. 初始化SPI为从模式,并为SPI_CS引脚注册中断函数,下降沿触发 2. 在中断函数中,启动SPI的接收。 3. SPI接收完成后,做其他处理,比如解析,转发等 ### 代码实现 下面是如何实现,平台采用了STM32F1系列芯片,启用SPI DMA传输,RT-Thread 4.0.2,SPI约定为Slave,MODE3,MSB,CS active low。一次传输长度为`package_length`。 使用内存池+邮箱的缓冲方式,当然也可以使用消息队列,根据自己的喜好。此处对中断做了底半处理。 #### 初始化 ##### SPI初始化 ```c static struct rt_spi_device *spi_device; // static struct stm32_hw_spi_cs *spi_cs; //中断引脚 static int spi_init(void) { rt_pin_mode(CS_PIN, PIN_MODE_INPUT_PULLUP); /* attach the device to spi bus*/ spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device)); RT_ASSERT(uwb_device != RT_NULL); spi_cs = (struct stm32_hw_spi_cs *)rt_malloc(sizeof(struct stm32_hw_spi_cs)); RT_ASSERT(uwb_spi_cs != RT_NULL); spi_cs->GPIOx = GPIOA; spi_cs->GPIO_Pin = 4; result = rt_spi_bus_attach_device(uwb_device, "spi10", "spi1", (void *)uwb_spi_cs); if (result != RT_EOK) { LOG_E("%s attach to %s faild, %d\n", "spi10", "spi1", result); return result; } LOG_D("%s attach to %s done", UWB_SPI_NAME, UWB_SPI_BUS); /* get SPI bus */ spi_device->bus->owner = spi_device; /* configure SPI device*/ { struct rt_spi_configuration cfg; cfg.data_width = 8; cfg.mode = RT_SPI_SLAVE | RT_SPI_MODE_3 | RT_SPI_MSB; cfg.max_hz = 8 * 1000 * 1000; rt_spi_configure(spi_device, &cfg); } if (rt_device_open((rt_device_t)spi_device, RT_DEVICE_FLAG_DMA_RX) != RT_EOK) { LOG_E("open UWB SPI device %s error.", "spi10"); return -RT_ERROR; } return RT_OK; } ``` **!!!注意**,**这里需要修改一下`rt_spi_configure`函数中的宏定义`RT_SPI_MODE_MASK`**,从 ```c (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB) ``` 改为 ```c (RT_SPI_SLAVE | RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB) ``` 否则无法将SPI接口配置为从模式,调用`rt_spi_revice_message`会崩溃。 ##### 初始化信号量,邮箱和内存池 ```c /* create RX semaphore */ spi_start_sem = rt_sem_create("spi1_start", 0, RT_IPC_FLAG_FIFO); /* create RX mp */ spi_mp = rt_mp_create("spi_mp", SPI_MB_LEN, RT_ALIGN(sizeof(rt_uint8_t), sizeof(intptr_t)) * package_length); /* create RX mailbox */ rt_mb_init(&spi_mb, "UWB_mb", &spi_mb_pool[0], sizeof(spi_mb_pool) / 4, RT_IPC_FLAG_FIFO); ``` ##### 为CS引脚绑定中断函数 ```c rt_pin_attach_irq(4, PIN_IRQ_MODE_FALLING, (void (*)(void *))spi_cs_isr, RT_NULL); ``` 此时,可以先不使能中断,可以等待系统所有初始工作完成后,由其他线程使能中断,以启动SPI接收。 到此,初始化的工作就完成了。接下来,要进行数据的接收工作,为此我们需要创建一些其他的函数。 #### 数据的接收 首先我们需要创建一个中断函数,这个中断函数通过CS引脚的下降沿触发,用来通知系统开始接收数据。 ```c static void spi_cs_isr(void) { /* enter interrupt */ rt_interrupt_enter(); rt_sem_release(spi_start_sem); /* leave interrupt */ rt_interrupt_leave(); } ``` 然后,我们还需要:一个线程用来启动DMA接收;一个中断函数用于通知系统DMA接收已经完成。 使用DMA方式的好处是,全部的SPI接收过程可以交给DMA。这种非阻塞的方式使得,系统在这个时候可以搞搞其他事情(相当于双线程???)。在SPI大量传输数据时尤其好用。 ```c static uint8_t *rx_buff = RT_NULL; static void spi_start_thread_entry(void *parameter) { struct rt_spi_message spi_msg; spi_msg.send_buf = RT_NULL; spi_msg.length = uwb_package_length; spi_msg.cs_take = 0; spi_msg.cs_take = 0; spi_msg.next = RT_NULL; while (1) { if (rt_sem_take(spi_start_sem, RT_WAITING_FOREVER) == RT_EOK) { rx_buff = rt_mp_alloc(spi_mp, RT_WAITING_NO); if (rx_buff != RT_NULL) { rt_spi_revice_message(spi_device, &spi_msg); } } } } ``` 这里使用了`RT_WAITING_NO`的方式来申请空间,如果没有申请到(缓冲区已满),就抛弃这条数据。 使用`rt_spi_revice_message`函数来启动DMA接收,并且约定了接收的长度固定为`package_length`。 DMA接收完成函数 ```c void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { if (rx_buff != RT_NULL) { rt_mb_send(&spi_mb, (rt_uint32_t)rx_buff); //发送邮件 } } } ``` 最后,还需要一个线程用于处理接收到的数据 ```c static void spi_dma_clp_thread_entry(void *parameter) { rt_uint8_t *net_tx_buff = RT_NULL; while (1) { if (rt_mb_recv(&uwb_mb, (rt_ubase_t *)&net_tx_buff, RT_WAITING_FOREVER) == RT_EOK) { //TODO //data process } rt_mp_free(net_tx_buff); net_tx_buff = RT_NULL; } } ``` 到此,基于RT-Thread的SPI从接收就基本完成了。这些只是一个大体的思路,也可以使用自己喜欢的方式,或者添加其他的功能。如果大家有更好的思路,欢迎分享出来。 谢谢~~
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
DCUU_8834
这家伙很懒,什么也没写!
文章
1
回答
2
被采纳
1
关注TA
发私信
相关文章
1
BBB的SPI驱动
2
求个SPI上挂两个或多个设备的使用例子
3
SPI设备有个bug
4
spi flash 的fatfs使用一段时间后读写文件出现故障
5
SPI驱动
6
请教rt_spi_configure函数理解
7
SPI FLASH挂载的问题
8
SPI-FLASH 文件系统 SPIFFS
9
求助一个完整的 spi flash 驱动
10
关于同时使用文件系统与SPI FLASH的问题
推荐文章
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
篇文章
3
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部