最近遇到了如下需求:
MCU作为主控芯片通过SPI与蓝牙芯片连接。
蓝牙芯片会时不时向MCU发送大量定长的数据包。
这种情况下,如果MCU的SPI接口采用主模式,通过查询的方式询问蓝牙芯片是否有数据要发送,就会非常占用资源,并且遇到突发大量数据也可能会来不及处理。
比较好的一种方法是,MCU采用从模式的SPI。蓝牙芯片无脑向MCU吐数据。如果主控MCU的SPI时钟最大频率大于蓝牙芯片的SPI最大频率,此方法可以跑到蓝牙芯片SPI的传输极限。
大体思路:
初始化SPI为从模式,并为SPI_CS引脚注册中断函数,下降沿触发
在中断函数中,启动SPI的接收。
SPI接收完成后,做其他处理,比如解析,转发等
代码实现
下面是如何实现,平台采用了STM32F1系列芯片,启用SPI DMA传输,RT-Thread 4.0.2,SPI约定为Slave,MODE3,MSB,CS active low。一次传输长度为package_length。
使用内存池+邮箱的缓冲方式,当然也可以使用消息队列,根据自己的喜好。此处对中断做了底半处理。
初始化
SPI初始化
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, %dn", "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,从
(RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB)
改为
(RT_SPI_SLAVE | RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB)
否则无法将SPI接口配置为从模式,调用rt_spi_revice_message会崩溃。
初始化信号量,邮箱和内存池
/* 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引脚绑定中断函数
rt_pin_attach_irq(4, PIN_IRQ_MODE_FALLING, (void ( )(void *))spi_cs_isr, RT_NULL);
此时,可以先不使能中断,可以等待系统所有初始工作完成后,由其他线程使能中断,以启动SPI接收。
到此,初始化的工作就完成了。接下来,要进行数据的接收工作,为此我们需要创建一些其他的函数。
数据的接收
首先我们需要创建一个中断函数,这个中断函数通过CS引脚的下降沿触发,用来通知系统开始接收数据。
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大量传输数据时尤其好用。
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接收完成函数
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); //发送邮件
}
}
}
最后,还需要一个线程用于处理接收到的数据
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从接收就基本完成了。这些只是一个大体的思路,也可以使用自己喜欢的方式,或者添加其他的功能。如果大家有更好的思路,欢迎分享出来。
-
接收机
+关注
关注
8文章
1186浏览量
53715 -
中断处理
+关注
关注
0文章
94浏览量
11025 -
SPI接口
+关注
关注
0文章
260浏览量
34628 -
RT-Thread
+关注
关注
31文章
1308浏览量
40499 -
MCU芯片
+关注
关注
3文章
255浏览量
11667
发布评论请先 登录
相关推荐
从开源中来到开源中去-RT-Thread北京社区篇
![<b class='flag-5'>从</b>开源中来到开源中去-<b class='flag-5'>RT-Thread</b>北京社区篇](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
RT-Thread上CAN实践
![<b class='flag-5'>RT-Thread</b>上CAN实践](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
2024 RT-Thread全球巡回 线下培训火热来袭!
![2024 <b class='flag-5'>RT-Thread</b>全球巡回 线下培训火热来袭!](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
RT-Thread 新里程碑达成——GitHub Star 破万!
![<b class='flag-5'>RT-Thread</b> 新里程碑达成——GitHub Star 破万!](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
6月6日杭州站RT-Thread线下workshop,探索RT-Thread混合部署新模式!
![6月6日杭州站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
5月16日南京站RT-Thread线下workshop,探索RT-Thread混合部署新模式!
![5月16日南京站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
4月25日北京站RT-Thread线下workshop,探索RT-Thread混合部署新模式
![4月25日北京站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
【4月10日-深圳-workshop】RT-Thread带你探索混合部署新模式
![【4月10日-深圳-workshop】<b class='flag-5'>RT-Thread</b>带你探索混合部署新<b class='flag-5'>模式</b>](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式!
![4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!](https://file1.elecfans.com/web2/M00/C6/D0/wKgaomYDlJyAKUBmAAAgR-TqYwc187.png)
4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式!
![4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!](https://file1.elecfans.com/web2/M00/C4/8A/wKgZomX0EhWACv8DAAAUet8ikhs451.png)
恩智浦半导体正式加入RT-Thread全球合作伙伴计划!
![恩智浦半导体正式加入<b class='flag-5'>RT-Thread</b>全球合作伙伴计划!](https://file1.elecfans.com/web2/M00/C5/31/wKgaomXyY_eAHGhqAAAkpBZI8PE901.png)
《RT-Thread设备驱动开发指南》基础篇--以先楫bsp的hwtimer设备为例
![《<b class='flag-5'>RT-Thread</b>设备驱动开发指南》基础篇--以先楫bsp的hwtimer设备为例](https://file.elecfans.com/web2/M00/37/D7/pYYBAGI9l9uAOwALAAAmFmqVYdg094.png)
RT-Thread设备驱动开发指南基础篇—以先楫bsp的hwtimer设备为例
![<b class='flag-5'>RT-Thread</b>设备驱动开发指南基础篇—以先楫bsp的hwtimer设备为例](https://file1.elecfans.com/web2/M00/C1/40/wKgaomXUXOGAMhesAAAk3OUcwHA076.png)
评论