0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

RT-Thread SPI作为从模式接收数据的使用方法

冬至子 来源:DCUU_8834 作者:DCUU_8834 2023-10-17 14:45 次阅读

最近遇到了如下需求:

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

    文章

    1122

    浏览量

    52643
  • 中断处理
    +关注

    关注

    0

    文章

    92

    浏览量

    10891
  • SPI接口
    +关注

    关注

    0

    文章

    251

    浏览量

    33938
  • RT-Thread
    +关注

    关注

    31

    文章

    1149

    浏览量

    38900
  • MCU芯片
    +关注

    关注

    3

    文章

    221

    浏览量

    11144
收藏 人收藏

    评论

    相关推荐

    4月25日北京站RT-Thread线下workshop,探索RT-Thread混合部署新模式

    4月25日,下午我们将在北京举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深
    的头像 发表于 04-16 08:35 91次阅读
    4月25日北京站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>

    4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式

    4月10日我们将在深圳福田举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深嵌入式软件工程师农晓明老师为您讲
    的头像 发表于 03-27 11:36 438次阅读
    4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!

    4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式

    4月10日我们将在深圳福田举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深
    的头像 发表于 03-27 08:34 154次阅读
    4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新<b class='flag-5'>模式</b>!

    RT-Thread Nano入门:串口接收与消息队列

    本文主要介绍怎么用RT-Thread Nano的消息队列方式实现串口数据接收,结合串口接收中断和空闲中断,接收上位机发来的一帧
    的头像 发表于 11-22 11:07 1025次阅读
    <b class='flag-5'>RT-Thread</b> Nano入门:串口<b class='flag-5'>接收</b>与消息队列

    基于rt-thread的socket通信设计

    最近再研究 rt-thread 的通信 ,想设计出 eps8266(多个) rt-thread(作为中控) 服务器的通信框架,使用的开发板是 潘多拉
    的头像 发表于 10-13 15:02 697次阅读
    基于<b class='flag-5'>rt-thread</b>的socket通信设计

    RT-Thread中mymqtt软件包的使用方法

    在上一篇文章 RT-Thread中Lan8720和lwip协议栈的使用的工程基础上添加mymqtt软件包。 使能mqtt example和mqtt test,保存,等待下载更新软件包。
    的头像 发表于 10-13 10:44 523次阅读
    <b class='flag-5'>RT-Thread</b>中mymqtt软件包的<b class='flag-5'>使用方法</b>

    试用RT-Thread Studio(VSCode)

    想尝试RT-Thread studio (VSCode),先下载安装VSCode,再搜索RT-Thread
    的头像 发表于 10-12 10:58 573次阅读
    试用<b class='flag-5'>RT-Thread</b> Studio(VSCode)

    RT-Thread中Agile Modbus软件包的使用方法

    开发环境:野火的stm32f407,rt-thread studio版本是版本: 2.2.6,stm32f4的资源包为0.2.2,Agile Modbus软件包版本为v1.1.2。工程使用上一篇 RT-Thread中RS485驱动包的使用 工程为基础。
    的头像 发表于 10-11 15:37 655次阅读
    <b class='flag-5'>RT-Thread</b>中Agile Modbus软件包的<b class='flag-5'>使用方法</b>

    RT-Thread v5.0.2 发布

    RT-Thread 代码仓库地址: ●  https://github.com/RT-Thread/rt-thread RT-Thread 5.0.2 版本发布日志详情: ●  htt
    的头像 发表于 10-10 18:45 780次阅读
    <b class='flag-5'>RT-Thread</b> v5.0.2 发布

    新书上架|嵌入式系统原理及应用——基于STM32和RT-Thread

    人间九月,山河已秋, RT-Thread又上新了!自2018年RT-Thread首本书籍问世后,越来越多的创作者因为RT-Thread的自主可控、稳定可靠、友好生态,把RT-Thread
    的头像 发表于 09-25 18:25 542次阅读
    新书上架|嵌入式系统原理及应用——基于STM32和<b class='flag-5'>RT-Thread</b>

    基于 RT-Thread 的 RoboMaster 电控框架(一)

    。但也正是因为这些优点的覆盖面较广,很多初学者会觉得无从下手,但只要步入 RT-Thread 的大门,你就发现她的美好。这系列文档将作为本人基于 RT-Thread 开发 RoboMaster 电控框架
    的头像 发表于 09-19 19:55 420次阅读

    新书上架|RT-Thread带你解密“芯”世界

    夏木荫荫,人间八月,RT-Thread又上新了!自2018年RT-Thread首本书籍问世后,越来越多的创作者因为RT-Thread的自主可控、稳定可靠、友好生态,把RT-Thread
    的头像 发表于 08-22 20:10 456次阅读
    新书上架|<b class='flag-5'>RT-Thread</b>带你解密“芯”世界

    RT-Thread源码分析之网卡数据接收和发送

    ethernetif.c是lwip的网卡接口,在该接口中处理网卡的数据接收和发送,rt-thread在该接口文件中抽象了一个eth_device,管理网络数据的收发和向内核的netde
    的头像 发表于 08-11 16:49 1016次阅读

    RT-Thread最小系统的移植方法

    目标为进一步研究RT-Thread,掌握系统移植的基本方法
    发表于 06-02 09:47 304次阅读
    <b class='flag-5'>RT-Thread</b>最小系统的移植<b class='flag-5'>方法</b>

    基于RT-Thread Studio学习

    前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。
    的头像 发表于 05-15 11:00 2594次阅读
    基于<b class='flag-5'>RT-Thread</b> Studio学习