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从接收就基本完成了。这些只是一个大体的思路,也可以使用自己喜欢的方式,或者添加其他的功能。如果大家有更好的思路,欢迎分享出来。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接收机
    +关注

    关注

    9

    文章

    1225

    浏览量

    54902
  • 中断处理
    +关注

    关注

    0

    文章

    94

    浏览量

    11297
  • SPI接口
    +关注

    关注

    0

    文章

    267

    浏览量

    35755
  • RT-Thread
    +关注

    关注

    32

    文章

    1447

    浏览量

    42382
  • MCU芯片
    +关注

    关注

    3

    文章

    257

    浏览量

    12206
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【好书推荐】RT-Thread第20本相关书籍!《嵌入式实时操作系统RT-Thread原理与应用》| 技术集结

    知识与实际应用。本书内容丰富、循序渐进,涵盖RT-Thread的基础理论到高级应用的完整知识体系,并通过大量实践案例进行讲解。全书共15章,内容包括RT-Thre
    的头像 发表于 08-24 10:05 ?167次阅读
    【好书推荐】<b class='flag-5'>RT-Thread</b>第20本相关书籍!《嵌入式实时操作系统<b class='flag-5'>RT-Thread</b>原理与应用》| 技术集结

    SPI玩转WiFi,RT-Thread ESP-Hosted驱动深度适配指南 | 技术集结

    还在为MCU的WiFi连接方案发愁?RT-Thread社区开源ESP-Hosted驱动,通过标准SPI接口即可实现,并且该仓库已整理成RT-Thread软件包。RT
    的头像 发表于 07-09 19:03 ?728次阅读
    用<b class='flag-5'>SPI</b>玩转WiFi,<b class='flag-5'>RT-Thread</b> ESP-Hosted驱动深度适配指南 | 技术集结

    RT-Thread荣获2025优秀开源项目 | 新闻速递

    贡献,荣获年度“优秀开源项目奖”。RT-Thread睿赛德荣获2025优秀开源项目奖目前,开源已从软件领域延展至硬件、数据、算法、标准、内容等多领域。作为国内开源
    的头像 发表于 07-04 09:04 ?1993次阅读
    <b class='flag-5'>RT-Thread</b>荣获2025优秀开源项目 | 新闻速递

    揭秘RT-Thread上的AUTOSAR CP系统

    本文探讨了RT-Thread与AUTOSARCP的融合,解决车载ECU开发中实时性、安全性与灵活性的平衡问题。通过分层安全内核(rt-safetyos/autoos)和工具链整合,兼容AUTOSAR
    的头像 发表于 06-23 20:22 ?2471次阅读
    揭秘<b class='flag-5'>RT-Thread</b>上的AUTOSAR CP系统

    玄铁加入RT-Thread 高级会员合作伙伴 | 战略新篇

    。深化合作,共建RISC-V生态RT-Thread作为国内领先的嵌入式操作系统,与玄铁团队在技术适配、生态共建方面已建立长期紧密的合作关系。目前,RT-Thread
    的头像 发表于 06-23 20:22 ?611次阅读
    玄铁加入<b class='flag-5'>RT-Thread</b> 高级会员合作伙伴 | 战略新篇

    RT-Thread 上如何实现 SLAAC?

    大佬们,本菜鸟有一些网络上的问题需要帮助: RT-Thread 上如何实现 SLAAC(无状态地址自动分配),给连接到我的板子的设备分配 IPv6 地址; RT-Thread 如何发送以太网报文,要求报文头开始都是我自己组,
    发表于 05-27 07:21

    RT-Thread审核团招募: 深度参与开源RTOS社区治理与演进

    全球开发者招募:RT-Thread审核团(ReviewTeam)正式开放申请!在开源的世界里,代码审查(CodeReview)是保证软件质量、促进技术交流的关键环节。RT-Thread作为全球领先
    的头像 发表于 05-21 18:02 ?781次阅读
    <b class='flag-5'>RT-Thread</b>审核团招募: 深度参与开源RTOS社区治理与演进

    如何将RT-Thread移植到NXP MCUXPressoIDE上

    RT-Thread默认支持的IDE只有IAR 和 Keil, 那如何将RT-Thread移植到NXP MCUXPressoIDE上呢?本文内容比较简单但稍有琐碎,希望对有需要的小伙伴有所帮助。
    的头像 发表于 02-13 10:37 ?2068次阅读
    如何将<b class='flag-5'>RT-Thread</b>移植到NXP MCUXPressoIDE上

    开源中来到开源中去-RT-Thread北京社区篇

    RT-Thread今年全域推行不同区域社区网,通过建立区域社区网,构建更加紧密联系的开源社区,实现更广泛的开源资源共享,让更多的RT-Thread开发者受益。今年8月份RT-Thread正式启动北京
    的头像 发表于 11-29 01:05 ?567次阅读
    <b class='flag-5'>从</b>开源中来到开源中去-<b class='flag-5'>RT-Thread</b>北京社区篇

    【S32K146 RT-Thread】之 使用SFUD组件驱动spi flash

    【S32K146 RT-Thread】之 使用SFUD组件驱动spi flash
    的头像 发表于 11-21 01:05 ?1280次阅读
    【S32K146 <b class='flag-5'>RT-Thread</b>】之 使用SFUD组件驱动<b class='flag-5'>spi</b> flash

    恩智浦FRDM-MCXA153 RT-Thread实践指南 实现和优化RT-ThreadSPI驱动

    串行外设接口(SPI)是一种广泛使用的同步串行通信接口,在嵌入式系统中扮演着重要角色。本文将深入探讨如何在NXP MCXA153 MCU上实现和优化RT-ThreadSPI驱动,同时介绍NXP提供
    的头像 发表于 11-14 08:58 ?1969次阅读
    恩智浦FRDM-MCXA153 <b class='flag-5'>RT-Thread</b>实践指南 实现和优化<b class='flag-5'>RT-Thread</b>的<b class='flag-5'>SPI</b>驱动

    RT-Thread上CAN实践

    开箱测试RT-Thread官方已完成了对英飞凌XMC7200EVK的移植,通过shell可以看到做好了uart3的console。本文将介绍如何进行RT-ThreadCan移植。接下来我们要完成CAN_FD的驱动移植,并正常启动RT-T
    的头像 发表于 11-13 01:03 ?2327次阅读
    <b class='flag-5'>RT-Thread</b>上CAN实践

    【S32K146 RT-thread】之 SPI驱动适配

    概述RT-ThreadSPI总线的驱动,抽象出了spibus的设备驱动,我们基于S32K146的硬件学习spibus设备驱动。
    的头像 发表于 11-01 08:11 ?1435次阅读
    【S32K146 <b class='flag-5'>RT-thread</b>】之 <b class='flag-5'>SPI</b>驱动适配

    开源共生 商业共赢 | RT-Thread 2024开发者大会报名启动!

    亲爱的RT-Thread开发者我们很高兴地宣布,一年一度的RDC(RT-ThreadDeveloperConference,RT-Thread开发者大会)正式启动报名!2024RT-Threa
    的头像 发表于 10-29 08:06 ?1041次阅读
    开源共生 商业共赢 | <b class='flag-5'>RT-Thread</b> 2024开发者大会报名启动!

    【大连】9月7日RT-Thread巡回线下培训-OpenMV机器视觉

    亲爱的RT-Thread社区成员们:2024年RT-Thread全球开发者线下培训拉开帷幕啦!24年全球巡回培训将覆盖超10座城市及国家,为开发者提供一个深入学习RT-Thread嵌入式开发的绝佳
    的头像 发表于 09-01 08:02 ?4210次阅读
    【大连】9月7日<b class='flag-5'>RT-Thread</b>巡回线下培训-OpenMV机器视觉