痞子衡嵌入式:在i.MXRTxxx下使能DMA链式传输可达到SPI从设备接收速率上限50Mbps

news/2025/10/29 17:14:51/文章来源:https://www.cnblogs.com/henjay724/p/19174935

  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT下使能DMA链式传输可达到SPI从设备接收速率上限50Mbps

  最近痞子衡在帮一个 RT600 的 AR 眼镜客户优化 SPI 从设备接收数据的速率,我们知道 SPI 从设备接收数据方法一般有三种:1) 轮询模式,2) 中断模式,3) DMA 模式。前两种模式都会受到 CPU 性能的限制,而 DMA 模式则可以最大程度地降低 CPU 负载,提高数据传输效率以及速率。

  然而使用 DMA 传输也会有潜在问题,单次 DMA 传输数据长度有上限(受限于 DMA 通道缓冲区长度),如果在一次 DMA 传输结束之后才开始手动启动下一次 DMA 传输,中间的延迟则有可能导致漏收数据,这时我们就需要使用 DMA 链式传输(Linked Transfer)来解决潜在漏收数据问题。这便是今天我们要讨论的话题:

  • Note1:本文方法主要针对 RT500/600 上的 DMA(又称LPC_DMA),其来自于恩智浦 LPC 系列。
  • Note2:RT700/RT4digits 上的 eDMA 与 LPC_DMA 完全不同,其来自于原飞思卡尔 Kinetis 系列(KL25 DMA是第一代,K60 eDMA算第二代)。

一、Flexcomm SPI速率

  在讨论这个话题之前,我们先来看一下 RT500/600 上的 SPI 外设本身速率。我们知道 RT3digits 上有一个非常神奇的外设 Flexcomm(与之对应的是 RT4digits 上的 FlexIO),这个外设可以按照用户需求被配置成 USART, SPI, I2C 或者 I2S 外设功能之一。

  RT3digits 内部一般会有多个 Flexcomm,而其本身又分为普通和专用两种类型:普通 Flexcomm 内部结构复杂,因为外设功能配置灵活性而稍稍放弃了一点传输性能;专用 Flexcomm 则限定用于特定外设功能,放弃了灵活性,但是传输性能更高。下面是芯片数据手册里找到的 SPI 速率:

芯片系列普通SPI
Master/Slave:TX/RX - 25Mbps
高速SPI
Master:TX/RX - 50Mbps
Slave:RX - 50Mbps, TX - 35Mbps
RT500Flexcomm 0-8, 10-12专用 Flexcomm 14,16
RT600Flexcomm 0-7专用 Flexcomm 14

二、为什么必须要用DMA?

  下图是 Flexcomm 模块简图,其内部对于收发均配置了一个深度为 8 entries 的 FIFO(对于 SPI frame 长度硬件上能直接支持 4-16 bits,所以这里 entries 是以 frame 长度为单位。如果配置为最常用的 8bits frame,那 FIFO 就能缓存 8bytes 数据),有一定的数据缓存能力,不至于因 CPU 响应不及而立即漏数据。

  文章开头说了 SPI 从设备接收数据方法有三种,我们来一一具体分析:

  • 轮询方式: CPU 每隔一段时间就来读一次 SPI RX FIFO 状态寄存器,一旦有数据就立刻取走,这种方法能够达到速率上限 50Mbps,但是代价是 275/300MHz 的 CPU 需要付出相当大的负载地在这轮询 SPI RX FIFO 寄存器状态,这对于应用程序设计太不友好,稍稍不慎就会漏数据,显得匆匆忙忙,一般不会这么用。
  • 中断方式: 预先设置一下 SPI RX FIFO 的 level trigger point(1-8 entries),当 RX FIFO 中数据达到这个水平时就触发中断,在 ISR 里把数据取走。这种方法可以降低 CPU 负载,但是由于 Cortex-M33 中断延迟较大,再加上 ISR 代码执行时间消耗导致可能达不到速率上限 50Mbps(理想情况下需要 FIFO 触发设 4 entries,然后一次 ISR 读取 4 个 entries 数据),ISR 加点代码都需要谨慎,显得小心翼翼,因此也不太推荐。
  • DMA方式: 利用 DMA 来自动搬运 SPI RX FIFO 中的数据到指定 Buffer 中(最长 1024 个 SPI frame),完全不需要 CPU 参与,这种方式可以最大程度地降低 CPU 负载,并且可以轻松达到 50Mbps 的速率上限,此时才算是游刃有余,唯一需要注意的是,单次 DMA 传输长度有上限,需要使用 DMA 链式传输来避免数据漏收。

三、LPC_DMA功能介绍

  看起来这种情况下 DMA 是必须要用了,那我们就来简单了解一下 LPC_DMA 功能,下图是其原理框图。首先我们要知道一个 DMA 会包含多个 channel(RT600 上是 33 个,RT500上是 37 个),各 channel 均可以独立工作(当然也可以合作)。对于每个 channel 独立工作,我们需要重点了解四个最基本的概念:

  • src/dest data: DMA 本质上是数据搬运,从源地址到目的地址,源/目的地址既可以是一般内存,也可以是外设寄存器,因此从类型上就分为:内存->内存、内存->外设、外设->内存、外设->外设(这种类型一般需要特殊设计,RT3digits 不直接支持)。
  • XFER Count: 一次 DMA 传输搬运的数据量,是可配置的,RT500/600 里上限均是 1024 个 units(每个 unit 长度 8/16/32bits 可配)。
  • DMA requests: 指 DMA 数据搬运涉及外设寄存器时,源/目的地址对应哪种外设,这里每个 channel 设计是定死的,比如 RT600 上 DMA0 channel 26 仅能接收 Flexcomm 14 SPI 的 RX 请求。
  • DMA triggers: 指触发 DMA 开始工作的条件,每个 channel 相同,除了最基本的软件直接触发之外,均可以配置不同硬件触发条件(RT600 上是 25 个,RT500 上是 27 个),这些硬件触发条件包含各种外设中断、其他 DMA channel trigger output 等。

  了解了 DMA 基本概念,我们还需要再进一步了解下 DMA 数据传输的几种方式(这里均针对单 channel 而言):

  • Single buffer: 这是最基础的单次 DMA 传输方式(一般用于内存到内存),并且源/目的地址均是线性不间隔增长。
  • Linked transfers: 这是将多次 DMA 传输串起来(数量不限,仅受限于内存容量,即只要你有足够的 Buffer),一次传输结束自动转到下一次传输。这里包含了一个非常常用的场景:当仅串 2 次 DMA 传输时(利用双 Buffer 循环工作),这叫乒乓传输(Ping-pong Transfer)。
  • Interleaved transfers: 这是一种特殊的 DMA 传输方式,可建立在 Linked transfers 基础之上,但是源/目标地址增长可按一定步长,适用于预处理音频/图像数据场合(比如二维图像数据里仅提取每行/列数据,多通道音频数据里仅提取单通道数据)。

  有了上面的铺垫,现在我们很自然地想到可以把多个 DMA channel 串起来一起工作,channel A 完成触发 channel B 继续工作,那就是所谓的 Channel chaining 模式。此外,虽然原则上多个 channel 可以并行工作,但是如果涉及到总线带宽限制或者内存访问冲突,我们还可以为其设置响应优先级,RT500/600 上一共支持 8 档优先级(每次仲裁时始终从最高优先级 channel 开始检查,并选择第一个处于激活状态的 channel 进行服务)。

四、使能SPI DMA链式传输方法

  关于 DMA 本身的链式传输示例可直接参考 \SDK_25_09_00_EVK-MIMXRT685\boards\evkmimxrt685\driver_examples\dma\linked_transfer 例程,这个例程设计非常简单清晰,代码过程一目了然,实现的功能就是将 s_srcBuffer1 和 s_srcBuffer2 数据往 s_destBuffer 里搬,如果 DMA_Callback 里不做任何处理,那么将会一直循环搬移。

#include "fsl_dma.h"
static dma_handle_t s_DMA_Handle;
SDK_ALIGN(dma_descriptor_t s_dma_table[2], 16U);
SDK_ALIGN(uint32_t s_srcBuffer1[4], sizeof(uint32_t));
SDK_ALIGN(uint32_t s_srcBuffer2[4], sizeof(uint32_t));
SDK_ALIGN(uint32_t s_destBuffer[8], sizeof(uint32_t));
// 一次 DMA 传输结束用户回调(对应一个 DMA 传输描述符里的工作)
void DMA_Callback(dma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{// Do someting
}
// 初始化 DMA0 通道 0
DMA_Init(DMA0);
DMA_CreateHandle(&s_DMA_Handle, DMA0, 0);
DMA_EnableChannel(DMA0, 0);
DMA_SetCallback(&s_DMA_Handle, DMA_Callback, NULL);
// DMA 传输属性配置(uint大小为4bytes,源和目标地址均按1个unit自增,一次传输16bytes,使能reload特性和INTB)
uint32_t xferCfg = DMA_SetChannelXferConfig(true, false, false, true, 4U, kDMA_AddressInterleave1xWidth, kDMA_AddressInterleave1xWidth, 16U);
// 初始化两个 DMA 传输描述符,并且将其互相链接
DMA_SetupDescriptor(&(s_dma_table[0]), xferCfg, s_srcBuffer1, &s_destBuffer[0], &(s_dma_table[1]));
DMA_SetupDescriptor(&(s_dma_table[1]), xferCfg, s_srcBuffer2, &s_destBuffer[4], &(s_dma_table[0]));
// 将第一个 DMA 传输描述符赋给 DMA0 通道 0
DMA_SubmitChannelDescriptor(&s_DMA_Handle, &(s_dma_table[0]));
// 软件触发 DMA0 通道 0 开始工作
DMA_StartTransfer(&s_DMA_Handle);

  但是很遗憾的是 SDK 中并没有现成的 SPI RX DMA 链式传输例程,在仅有的 \SDK_25_09_00_EVK-MIMXRT685\boards\evkmimxrt685\driver_examples\spi\dma_b2b_transfer\slave\ 例程里,它也仅是启动单次传输,查看其代码,我们发现根本原因是 fsl_spi_dma.c 驱动(V2.2.2)里从设计上就不支持链式传输。

SPI_SlaveTransferDMA() -> SPI_MasterTransferDMA() ->SPI_TransferSetupRxContextDMA(handle, xfer);SPI_EnableRxDMA(base, true);SPI_TransferSubmitNextRxDMA(base, handle);  // 问题出在这个函数设计上handle->rxInProgress = true;DMA_StartTransfer(handle->rxHandle);

  在 SPI_TransferSubmitNextRxDMA() 函数里,其默认使用内部 s_dma_descriptor_table 描述符,每次只提交单次 DMA 传输,禁止了 reload 功能,这个函数显然是为了被多次调用去连续顺序数据传输而设计的,因此要想实现 DMA 链式传输,我们必须改造这个函数。

  具体改造过程,痞子衡就不一一赘述了,大家直接看下面代码吧,只是改造过程中还是有一些坑需要注意的,这些都是痞子衡调试时的血泪教训。

https://github.com/JayHeng/perf-rt600-fcspi-dma-linked-transfer/blob/main/devices/MIMXRT685S/drivers/fsl_spi_dma.c坑1:链式传输时 DMA_SubmitTransfer() 函数里不能判断 DMA_ChannelIsActive(),否则初始化提交第二个 DMA 传输描述符时会直接返回 busy
坑2:链式传输时 SPI_TransferRxHandlerDMA() 中断处理要重新设计,不要根据 rxInProgress、rxRemainingBytes 状态来决定是否调用用户回调函数
坑3:链式传输时 spiHandle->state 状态不要在中断里改变成 kSPI_Idle 状态,否则 SPI_MasterTransferGetCountDMA() 函数会失效

  至此,i.MXRT下使能DMA链式传输可达到SPI从设备接收速率上限50Mbps痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/950084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国产LTCC低通滤波器HT-LFCG-530+实测:完美替代LFCG-530+,5G/WiFi6/车规全场景

国产LTCC低通滤波器HT-LFCG-530+实测:完美替代LFCG-530+,5G/WiFi6/车规全场景封装对比 ① 尺寸:5.03.01.1 mm,与原版游标卡尺测量完全一致,焊盘0.450.65 mm,0.65 mm间距,8Pin,钢网不用改。 ② 重量:16.3 mg v…

C语言 打印菱形图案

先画图,找关系,搞明白关系就会做题了//数量关系 // 3.空数 4.星数 1.val:辅助值 2.层数// * 3=4-1 1 4=mid 1// *** 2=5-3 3 5=mid+1 …

Java并发编程利器:从ConcurrentHashMap到Fork/Join的奇幻之旅

上篇讲了Lock锁、AQS相关的内容,本篇讲一下线程安全的类,拿来即用无需其他操作就能达到线程安全的效果,省力又省心 ~ ~你是否曾为多线程编程中的各种坑而头疼?本文将用生动比喻和实用代码,带你轻松掌握Java并发容…

mysql报错many connections errors

mysql报错many connections errors如上图所示 首先使用可以访问数据库的主机进入数据库,我的mysql在linux系统中直接从Linux系统进入然后重新使用可视化工具进行连接即可!

2025薪酬管理系统推荐:6大主流系统全面对比与选型指南

引言:复杂多变的薪酬挑战与数字化破局之道 在 “降本增效”成为主旋律的2025年,中国企业面临的薪酬管理挑战愈发复杂和严峻。薪酬,作为企业人力资源管理的核心环节,不仅关乎员工的切身利益和组织士气,更是企业合规…

Solon (可替换 SpringBoot)集成 Docker 实战:30分钟搞定轻量级应用容器化部署

本文介绍了如何将Solon框架与Docker快速集成,突出了Solon在启动速度、内存占用和容器化方面的优势。文章从环境准备入手,详细说明了两种Docker打包方式(Maven插件和Dockerfile),提供了完整的配置示例和操作命令,…

vue2 组件封装 el-date-picker 日期

vue2 组件封装 el-date-picker 日期 基本使用都满足包括常用的:时间格式 :type [date,datetime,daterange ....]快捷方式 :日期左侧:如 今天,昨天,一个月前日期日期禁用:禁用日期段其它相关属性custom-date-pi…

我使用FHQ写了线段树2

虽然并没有任何的意义,但是我觉得很有意思,所以记录一下: #include <bits/stdc++.h> #define int long long using namespace std; const int MN=1e6+116; struct Node{int lc, rc, siz, rnd;int val, sum, a…

092_尚硅谷_for循环注意事项和细节(1)

092_尚硅谷_for循环注意事项和细节(1)1.for循环的第二种写法2.for循环的第三种写法_死循环3.for循环的第三种写法_死循环使用break终止循环

详细介绍:【网络通讯安全认证的理解:从密钥签名、数字证书到 HTTPS/TLS 流程】

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

VK36N5D 工作电压 2.2-5.5V 触摸芯片抗干扰5键触摸触控 5路触摸检测IC

VK36N5D具有5个触摸按键,可用来检测外部触摸按键上人手的触摸动作。该芯片具有较 高的集成度,仅需极少的外部组件便可实现触摸按键的检测。 提供了5个1对1输出脚,1个触摸状态输出脚,可通过IO脚选择上电输出电平和输…

魔兽争霸3冰封王座修改器 下载安装教程(图文步骤 + 功能详解)

魔兽争霸3冰封王座修改器下载与安装教程(适配1.24e~1.26版本),完整讲解安装方法、功能用法、快捷键操作、兼容性优化与常见问题解决方案。支持Windows 10 / 11系统,安全稳定。魔兽争霸3冰封王座修改器 下载安装教程…

Softmax回归模型

这段代码是一个完整的 Softmax回归模型 实现,用于解决 Fashion-MNIST数据集的图像分类问题。简单来说,它的作用是:让计算机通过学习大量衣服、鞋子等服饰图片,学会识别新的服饰图片属于哪一类(比如T恤、裤子、运动…

Oracle的connect by level在MySQL中的华丽变身 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

handsontable实现新增删除行(双行)

handsontable实现新增删除行(双行)// 配置方法const tableSettings = computed(() => {return {...hotTableParams,nestedHeaders: false,filters: false,columnSorting: false,height: 358,rowHeaders: false,co…

2025年国产角接触球轴承厂家推荐 一文了解轴承厂家选择标准

角接触球轴承在机械设备中的应用广泛,特别是精密机床、高速电机、电主轴等运行要求高的场合,更需要质量品质好一点的角接触轴承。想要轴承用的好,就得找到合适的生产厂家,下面就来推荐下2025年值得信任的国产角接触…

vxe-table 树形表格显示连接线的方式

vxe-table 树形表格显示连接线的方式 完整连接线 通过 tree-config.showLine 来启用是否显示连接线<template><div><vxe-grid v-bind="gridOptions"></vxe-grid></div> </…

2025年上海衣帽间定制机构权威推荐榜单:衣帽间设计/衣帽间十大品牌/衣帽间装修源头公司精选

在上海,一个规划合理的衣帽间正成为新兴住宅的标配。数据显示,2024年中国家装行业市场规模已突破860亿元,其中定制家具份额持续增长,而衣帽间作为定制家具的重要组成部分,正受到越来越多消费者的青睐。 01 行业趋…

在Web应用开发中状态到底是什么?

在计算机科学中,“状态”(State)这个词经常出现在讨论有状态(Stateful)和无状态(Stateless)系统、服务或组件时。要理解“状态”到底是什么,我们可以从最基本的层面来解释。一、什么是“状态”? 简单来说,“…

前后端不分离的springboot应用,静态文件修改了不更新的问题

当然,还有不依赖idea的解决方案,就是静态文件通过nginx来代理,直接将js和css这些文件代理到我们的代码目录,这样我们修改了代码目录后,配置就立马生效了。这样可以不依赖idea的版本,假如你的idea怎么设置热更新都…