ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32H7 SAI到DTCM数据搬运失败?HPDMA配置与MPU排查指南

STM32H7 SAI到DTCM数据搬运失败?HPDMA配置与MPU排查指南 1. 项目概述与问题场景最近在调一块用 STM32H7 系列做音频采集的板子遇到一个挺典型的问题SAI 通过 HPDMA 往 DTCM 里搬数据怎么配都不工作。现象很统一——HPDMA 的传输完成中断永远不触发状态寄存器里挂着超时或者总线错误SAI 侧倒是正常出帧同步可数据就是过不去。这个组合SAI HPDMA DTCM在 H7 系列上其实很常见尤其是想做低延迟音频处理的场景。SAI 负责收音频数据HPDMA 负责把数据从外设搬到内存DTCM 作为最终存储区域为的是让 CPU 能以零等待状态直接访问数据。理论上这套链路很流畅实际配起来却有不少暗坑。先说结论问题大概率出在 HPDMA 对 DTCM 的访问路径、MPU 配置、以及 SAI 与 DMA 之间的触发方式这三处。这篇博文会把 HPDMA 和 DTCM 的配合逻辑、常见配置错误、排查方法完整梳理一遍还把最关键的“为什么不工作”几个原因逐一拆开。这篇文章适合正在用 STM32H7 系列做音频、高速 ADC 采集、或者任何需要外设 DMA 直通 DTCM 的朋友参考。即使你用的是 G4 或者 F4 系列里面关于 DMA 与内存区域匹配的原理也是通用的。2. 理解 HPDMA、DTCM 和 SAI 的角色定位2.1 HPDMA 到底是什么和普通 DMA 有什么区别STM32H7 系列里的 HPDMA 是新一代 DMA 控制器和老的 DMA1/DMA2 对比主要体现在几个方面通道数更多、支持 8 字节突发传输、可以处理 32-bit 甚至 64-bit 位宽的数据搬运还支持 scatter-gather 模式也就是链表传输。HPDMA 的定位是“高性能”它挂在 AXI 总线上理论带宽比传统 DMA 高很多。在音频场景里采样率 48kHz、32bit 双通道数据量大概是 384KB/s这个量级老 DMA 也能扛住但 HPDMA 的触发延迟更低、中断开销更小更适合长时间不间断的音频流。不过 HPDMA 的灵活性也带来了配置复杂度。它的每个通道有独立的控制寄存器、状态寄存器和中断标志配置出错后的表现也和老 DMA 不太一样——老 DMA 配错了直接不进中断或者数据错位HPDMA 配错了常常表现为总线错误Bus error或者传输超时Timeout排查难度反而更高。2.2 DTCM 的特性为什么选择它作为音频数据存储区DTCMData Tightly Coupled Memory是 Cortex-M7 内核特有的内存区域和内核通过专用总线连接访问延迟极低而且是零等待状态。这一点在音频处理中非常关键——如果 SAI 数据到达后 CPU 要频繁读取处理放在 DTCM 里可以显著降低 CPU 等待周期。但 DTCM 有个比较尴尬的特性它不是所有总线主设备都能访问。Cortex-M7 内核本身可以直接读写 DTCM但 DMA 控制器呢H7 系列上 DMA 是否能访问 DTCM取决于总线互联矩阵的具体设计。实测下来HPDMA 是能够通过 AXI 总线访问 DTCM 的但必须满足一个前提——DTCM 的基地址在系统地址映射中处于正确位置同时相关的 MPU 区域配置必须允许 DMA 访问。这里补充一个非常容易忽略的细节DTCM 和 ITCM 一样在 H7 系列中有“别名映射”机制。在默认的地址映射里DTCM 出现在 0x20000000 区域但如果你在链接脚本或者系统配置里改了别名映射DMA 侧的地址和 CPU 侧的地址可能就不一致了。这个是“DMA 搬运不到 DTCM”的一个隐藏原因。2.3 SAI 到 DMA 的数据路径整个链路怎么走SAI 外设Serial Audio Interface在 H7 上支持同步和异步收发数据通过内部 FIFO 接收后会产生 DMA 请求信号。这个请求信号要经过外设互联矩阵DMAMUX后映射到 HPDMA 的某个通道上。完整链路是SAI 接收 FIFO 半满/非空 → 触发 DMA 请求 → DMAMUX 映射到 HPDMA 通道 → HPDMA 从 SAI 数据寄存器读取数据 → 写入目标内存地址DTCM。这条链路里任何一环断了都会导致传输不工作。比较常见的断点是DMAMUX 映射没配对SAI 的请求没有连到目标 HPDMA 通道SAI 的 DMA 请求条件没开启比如没有使能 FIFO 的 DMA 请求输出HPDMA 通道的源地址配错了没有指向 SAI 的数据寄存器目标地址DTCM访问权限不足所以排查的时候不要一上来就怀疑 HPDMA 寄存器配置先理清楚整个链路里最容易被忽略的那几环。3. 核心问题拆解为什么 HPDMA 到 DTCM 会失败3.1 原因一DTCM 区域被 MPU 配置为不可访问或权限受限Cortex-M7 内置的 MPUMemory Protection Unit可以配置内存区域的访问权限、缓存策略和共享属性。H7 系列上电默认的 MPU 配置里DTCM 区域是正常可读写的CPU 能访问不等于 DMA 能访问。但如果你的代码里自行配置了 MPU很多 RTOS 或安全相关代码会这么做把 DTCM 区域配置为“只允许特权模式访问”或者把共享属性设为“不可缓存”但同时又开了某种缓存一致性策略DMA 访问就可能被拒绝。我在实际调试中遇到过一次MPU 把 0x20000000 区域配置为 32KB 粒度、只读属性CPU 侧写数据没问题因为内核配置的是可写但 HPDMA 要向这个地址写数据时总线层面直接返回错误。HPDMA 的状态寄存器会显示BUSY1, BUSERR1中断标志里也有TETransfer Error。排查方式在调试器里读出 MPU 相关寄存器MPU_RBAR、MPU_RLAR确认 0x20000000 附近的区域配置是否正确。如果发现权限位有问题要么修改 MPU 配置要么把目标地址换到 AXI SRAM 区域0x24000000 起。3.2 原因二DTCM 的地址映射和链接脚本不匹配这里的坑比 MPU 更隐蔽。H7 系列的内存映射里DTCM 默认在 0x20000000AXI SRAM 在 0x24000000SRAM1/2/3 在 0x30000000 区域。但很多工程模板会把 DTCM 的地址改到别的区域比如为了给 ITCM 让路或者为了配合 bootloader 的加载地址。如果链接脚本把音频缓冲区所在的段定义在了 DTCM 的“逻辑地址”区域但实际运行时总线互联矩阵对这个地址的映射和你的预期不一致DMA 写入就会失败。典型的错误是在链接脚本里写了类似.audio_buffer (NOLOAD) : { *(.audio_buffer) } DTCMRAM但芯片实际的 DTCM 物理基地址和你链接脚本里定义的内存区域名称不对应。排查方式编译后查看 map 文件确认音频缓冲区的实际链接地址是多少然后和参考手册里的 DTCM 基地址对照。如果地址明显不在 0x20000000 附近查一下 startup 文件和链接脚本里对内存区域的划分是否正确。3.3 原因三HPDMA 的 FIFO 和突发配置与 SAI 数据宽度不匹配SAI 的数据寄存器宽度可以配置为 8-bit、16-bit、32-bit。HPDMA 外设端的数据宽度也要和 SAI 保持一致。如果两者不匹配会出现数据错位、传输卡死、或者只搬了部分数据就停住。举个例子SAI 配置为 32-bit 数据宽度HPDMA 的 PSIZE 也配置为 32-bit这两个是匹配的。但如果 HPDMA 的 MSIZE内存端数据宽度配置为 8-bit并且没有开启 FIFO 模式内存端逐字节写入会严重拉低传输效率极端情况下 DMA 控制器会进入忙等状态导致看起来像卡住了。更隐蔽的是 FIFO 阈值设置。HPDMA 在直连模式Direct mode下要求外设端和内存端的数据宽度严格一致。只有开启 FIFO 模式后才允许外设端和内存端宽度不同。我的建议是在 SAI HPDMA 场景里把 HPDMA 配成 FIFO 模式外设端宽度跟随 SAI内存端宽度设为 32-bit如果目标是 DTCM 或 AXI SRAM 的话FIFO 阈值设为 1/2 或 1/4。3.4 原因四SAI 的 DMA 请求条件和 HPDMA 的触发方式配置不一致SAI 的 DMA 请求有两种模式一种是在 FIFO 达到编程阈值时产生请求一种是在 FIFO 为空时产生请求。HPDMA 的触发可以是电平触发Level-sensitive或边沿触发Edge-sensitive。如果 SAI 设置为“FIFO 半满时产生 DMA 请求”而 HPDMA 的触发方式配置成了边沿触发有可能出现FIFO 半满的状态保持时间不足以让 HPDMA 采样到边沿导致 HPDMA 一直不启动传输。这个问题的诡异之处在于你单独看 SAI 寄存器FIFO 里确实有数据单独看 HPDMA 寄存器通道也已经 enable 了但两者就是没“握手”上。设置建议HPDMA 外设端请求设置为电平触发Level-sensitiveSAI 的 FIFO 阈值设置为 1/2。这是音频场景里实测最稳的组合。如果你用了类似LL_DMA_SetTriggerMode的 API确认传入的触发模式参数是电平触发而不是边沿触发。4. 实操过程从零配置一套可用的 SAI 到 DTCM 链路4.1 工程准备与 CubeMX 基础配置我用的是 STM32H743 CubeMX 6.x 生成的工程LL 库开发。SAI 接了一个外部音频 Codec主时钟由 SAI 提供接收通路数据需要通过 HPDMA 搬运到 DTCM。CubeMX 里需要配这几样SAI1 Block A 配为接收模式主时钟输出数据格式 32-bit/通道帧长 64-bitDMAMUX1 把 SAI1_A 的 DMA 请求映射到 HPDMA1 Channel 0HPDMA1 Channel 0 配为外设到内存传输外设地址固定SAI1_A 数据寄存器内存地址递增内存区域选 DTCM0x20000000 区域缓冲区定义在 DTCM 段里CubeMX 生成的初始化代码里HPDMA 的配置函数已经写好了但有个地方需要手动改一下生成的代码默认把 DMA 目标地址设在一个内部数组上而这个数组可能被链接脚本放在了 AXI SRAM 而非 DTCM。所以要么在代码里手动定义 DTCM 段的缓冲区要么改链接脚本。4.2 链接脚本里划分 DTCM 缓冲区在 STM32H743 的链接脚本中默认有一个DTCMRAM区域起始地址 0x20000000大小 128KB。如果把音频缓冲区分到这个区域里用起来最直接__attribute__((section(.audio_buf))) uint32_t audio_data[2048];然后在链接脚本里加上.audio_buf (NOLOAD) : { *(.audio_buf) } DTCMRAM编译后从 map 文件确认audio_data确实在 0x20000000 区域然后 MPU 检查它的区域配置是允许读写、允许 DMA 访问共享属性建议配置为 Write-BackRead-Write Allocate。4.3 HPDMA 通道寄存器配置要点如果不想依赖 CubeMX 生成的 HAL 代码自己用 LL 库配置 HPDMA 也可以。核心代码如下LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_0, LL_DMAMUX1_REQ_SAI1_A); LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY); LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_0, LL_DMA_MODE_CIRCULAR); LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_0, LL_DMA_PERIPH_NOINCREMENT); LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_0, LL_DMA_MEMORY_INCREMENT); LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_0, LL_DMA_PDATAALIGN_WORD); LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_0, LL_DMA_MDATAALIGN_WORD); LL_DMA_SetFIFOMode(DMA1, LL_DMA_CHANNEL_0, LL_DMA_FIFO_ENABLE_1_2); LL_DMA_SetTriggerMode(DMA1, LL_DMA_CHANNEL_0, LL_DMA_TRIGGER_LEVEL); LL_DMA_SetStreamPriority(DMA1, LL_DMA_CHANNEL_0, LL_DMA_PRIORITY_HIGH);有几个关键点必须说清楚LL_DMA_SetPeriphRequest里的第二个参数如果是 H7 系列要区分 DMA1 和 DMA2其实是两个 HPDMA 实例映射号来自 DMAMUX1 的请求映射表LL_DMA_SetFIFOMode设置的是 FIFO 阈值 1/2这个在 SAI 32-bit 数据宽度下理论可以保证稳定传输LL_DMA_TRIGGER_LEVEL就是前面说的电平触发千万不要配成LL_DMA_TRIGGER_EDGE配置完通道后使能传输完成中断LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_0); LL_DMA_EnableStream(DMA1, LL_DMA_CHANNEL_0);注意LL_DMA_EnableStream之后HPDMA 不是立刻开始搬运的它在等外设请求。这个外设请求由 SAI 在 FIFO 达到阈值时产生所以时序上 SAI 要先启动。4.4 启动顺序先开 DMA 还是先开 SAI这其实是一个很容易踩的坑。我个人的实践结论是先在 DMA 侧使能通道并等待请求再启动 SAI 接收。原因是如果 SAI 先启动FIFO 可能已经积压了几帧数据DMA 通道还没建立好此时 SA 侧会产生 overrun 错误ROVR 标志并且会清掉 FIFO 里的数据。正确的启动顺序配置好 SAI 的采样率、帧格式、数据宽度但不要使能 SAI配置好 HPDMA 的通道参数使能中断使能通道让它挂在那里等最后使能 SAI 的接收使能位在 HPDMA 的传输完成中断里做数据消费这个顺序能最大程度避免时序竞争。我遇到过几次启动顺序不对导致的偶发丢数据调整顺序后稳定了很多。4.5 中断处理传输完成中断里应该做什么HPDMA 的传输完成中断触发时代表指定长度的数据已经从 SAI FIFO 搬到了 DTCM 缓冲区。实际代码里建议做这几件事void DMA1_Channel0_IRQHandler(void) { if (LL_DMA_IsActiveFlag_TC0(DMA1)) { LL_DMA_ClearFlag_TC0(DMA1); /* 此时 audio_data 里的数据是有效的 */ process_audio_data(audio_data); } }如果你用的是循环模式Circular mode传输完成中断会周期性地触发。每段数据的长度由LL_DMA_SetDataLength指定。在音频流里这个值建议设置为一次 DMA 中断搬运的采样块大小比如 256 个采样32-bit这样中断频率在 48kHz 采样率下约 187HzCPU 负载很轻。有一个点需要特别提一下HPDMA 传输完成中断标志需要在读数据之前清掉这是防止中断标志被重复触发的最简单方式。如果你不清标志就出中断下次中断永远不会来。5. 常见问题与排查技巧实录5.1 问题一HPDMA 一直不启动状态寄存器显示 IDLE排查思路先确认 SAI 有没有产生 DMA 请求——读 SAI 的STAT寄存器看有没有 FIFO 阈值触发标志。如果没有说明 SAI 侧没产生请求问题不在 DMA确认 DMAMUX 映射是否正确——读 DMAMUX1_C0CR 寄存器它的DMAREQ_ID字段值要对应 SAI1_A 的请求号确认 HPDMA 通道是否使能——看CCR寄存器的 EN 位是否置 1还要确认没有在使能后又立刻被别的代码禁用实测中相当一部分“DMA 不工作”的现象最终都定位到 DMAMUX 映射错了或者是 CubeMX 生成的初始化代码里用了默认的映射而不是 SAI 的请求号。5.2 问题二HPDMA 传输到一半卡死状态寄存器显示 BUSY这种情况通常不是 DMA 本身的问题而是总线访问失败。优先查这三处目标地址是否在系统总线上可以访问的内存区域。DTCM 在部分 DMA 配置下可能访问受限如果确认是这个问题换到 AXI SRAM0x24000000再试MPU 缓存策略。如果 MPU 把目标区域配置成了 Write-Through 或者不可缓存但 DMA 又需要某种缓存一致性保证可能会出现 DMA 写完后从 CPU 读到的还是旧数据看起来像“没搬”内存端地址对齐。HPDMA 要求内存端地址按数据宽度对齐如果目标地址奇数对齐且数据宽度是 32-bit会触发对齐错误5.3 问题三数据偶尔错位或者首尾有杂音这种情况和 SAI 的帧长度、槽位配置有关DMA 本身是正常的。SAI 接收数据时每帧的第一个槽位如果是有效数据就应该从帧头开始搬运。如果你的 SAI 配置的槽位数量和实际音频 Codec 输出的不一致DMA 搬到的数据会出现移位。排查方式用调试器看一次 DMA 搬回的数据前几个字节和 SAI 输入端的预期数据做对比。如果前面多了一两个 0说明帧长配置有问题。也可以在 SAI 寄存器里打开FRCR的 Frame Length 设置确保它和 Codec 输出的帧长完全一致。5.4 问题四HPDMA 中断触发频率异常高或异常低先算一下理论中断频率。比如音频采样率 48kHz、通道数 2、位深 32bitSAI 每个采样帧产生 2 个 32-bit 数据。HPDMA 一次搬运 N 个 32-bit 数据然后中断一次。那么中断频率就是 48000 * 2 / N Hz。如果实际中断频率和预期差距很大多半是LL_DMA_SetDataLength配置的长度不是按数据宽度换算的。HPDMA 的数据长度单位是“次传输”不是字节。如果配置长度时没有除以 432-bit 宽度实际中断会提前或延后。5.5 问题五用 memset 或 memcpy 操作 DTCM 缓冲区时程序跑飞这不是 DMA 的锅是 DTCM 的访问权限问题。部分 H7 芯片上DTCM 区域只有在特权模式下才能由 CPU 用普通 load/store 指令访问。如果你跑的是非特权线程直接用memcpy访问 DTCM 缓冲区会触发 HardFault。解决办法是要么把缓冲区挪到 AXI SRAM要么在 MPU 配置里把 DTCM 区域设为“可被非特权访问”。不过更推荐前者——不要把关键音频缓冲区放在 DTCM 里放在 AXI SRAM 反而更省心DTCM 留给 CPU 的栈和局部变量。6. 正确配置 MPU让 DMA 和 CPU 都能正常访问6.1 为 DTCM 缓冲区配置 MPU 区域如果你确实想用 DTCM 作为 DMA 的目标内存MPU 配置不能省。参考配置如下MPU_Region_InitTypeDef MPU_InitStruct {0}; HAL_MPU_Disable(); MPU_InitStruct.Enable MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress 0x20000000; MPU_InitStruct.Size MPU_REGION_SIZE_64KB; MPU_InitStruct.SubRegionDisable 0x00; MPU_InitStruct.TypeExtField MPU_TEX_LEVEL1; MPU_InitStruct.AccessPermission MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec MPU_INSTRUCTION_ACCESS_ENABLE; MPU_InitStruct.IsShareable MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsBufferable MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(MPU_InitStruct); HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);特别说明TypeExtField和IsCacheable这几个字段组合起来决定了内存的缓存策略。MPU_TEX_LEVEL1 CACHEABLE BUFFERABLE组合对应 Write-Back 缓存策略这是数据缓冲区最常用的配置。如果 DMA 和 CPU 之间需要强一致性可以改成IsBufferable MPU_ACCESS_NOT_BUFFERABLE但会牺牲一点性能。6.2 一个更稳的替代方案把目标内存放到 AXI SRAM很多时候音频缓冲区真的没必要放在 DTCM。AXI SRAM0x24000000 起始在 H7 上同样接在 AXI 总线上HPDMA 访问它没有任何权限问题CPU 访问它虽然比 DTCM 慢几个周期但对于音频处理这种中等实时性需求来说完全够用。实测对比同样的一路 48kHz 音频放在 DTCM 和放在 AXI SRAM 的 CPU 占有率差异不到 2%但配置复杂度差了一个量级。所以我的建议很直接——如果不是要做极低延迟的高保真音频处理就别纠结 DTCM直接 AXI SRAM。7. 一步步复现一篇可运行的 Demo 流程7.1 Demo 功能描述做一个最小验证SAI1_A 接收外部输入的方波信号通过 HPDMA 把 64 个 32-bit 数据搬到 DTCM 缓冲区如果你决定用 AXI SRAM改个地址就行每 64 个采样触发一次 DMA 传输完成中断在中断里翻转一次 GPIO用逻辑分析仪观察中断频率。7.2 代码结构拆解整个 demo 分三层初始化层配置时钟、GPIO、SAI、HPDMA、NVIC、MPU业务层定义音频缓冲区、设置 DMA 目标地址中断层处理传输完成中断初始化层的核心代码上文已经给出。业务层的代码更简单#define AUDIO_BUF_LEN 64 __attribute__((section(.audio_buf))) uint32_t audio_buf[AUDIO_BUF_LEN]; void audio_dma_init(void) { LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_0, (uint32_t)audio_buf); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_0, AUDIO_BUF_LEN); LL_DMA_EnableStream(DMA1, LL_DMA_CHANNEL_0); }中断层的核心代码在 4.5 里已经给出只需要在中断里加一个 GPIO 翻转void DMA1_Channel0_IRQHandler(void) { if (LL_DMA_IsActiveFlag_TC0(DMA1)) { LL_DMA_ClearFlag_TC0(DMA1); LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_0); } }7.3 验证步骤和预期结果正常情况下逻辑分析仪应该能看到 GPIOB PIN 0 以固定的频率翻转。48kHz 采样率、每次搬 64 个 32-bit 采样理论中断频率是 750Hz。如果测到的频率不对回头检查 SAI 的采样率配置和 DMA 的数据长度。7.4 验证失败时如何缩小问题范围加一个“手动触发”测试在代码里把 HPDMA 的LL_DMA_EnableStream之后临时用软件写一次LL_DMA_SetSWRequest或者触发一次软件请求如果 HPDMA 能搬到数据说明 DMA 到内存的通路是好的问题出在 SAI 的 DMA 请求侧。如果连软件触发都搬不了就要查 DMA 配置、地址、MPU。这个方法能把“SAI 不产生请求”和“HPDMA 根本搬不了”两个问题快速分离开。实测排查效率非常高。8. 常见配置速查表与避坑清单为了节省大家反复翻参考手册的时间我把这套配置里最容易出错的参数整理成了一张速查表建议截图保存。配置项推荐值错误示例后果HPDMA 触发模式电平触发边沿触发SAI 请求可能不被识别DMA 不启动HPDMA 数据宽度32-bit对齐 SAI 数据宽度8-bit数据错位效率骤降HPDMA FIFO 模式使能阈值 1/2直连模式宽度不匹配时导致总线错误DMAMUX 请求号SAI1_A 对应请求号默认请求号 0DMA 收不到外设请求目标内存区域AXI SRAM 或正确配置的 DTCM任意未配置区域总线错误或 HardFaultMPU 缓存策略Write-BackRead/Write Allocate不可缓存且无一致性保障数据读到旧值启动顺序先 DMA 后 SAI先 SAI 后 DMA偶发 overrun丢数据速查表里最核心的三条是HPDMA 用电平触发、DMAMUX 映射务必核对、MPU 缓存策略要落实。这三条做到位绝大多数 “SAI to DTCM not working” 的场景都能解决。9. 经验总结与性能实测补充从这次问题排查到现在稳定运行几个直观感受和建议如下。第一H7 系列的 DMA 链路虽然功能强大但配置入口比 F4 系列多了不少。遇到问题不要只盯着 HPDMA 的寄存器DMAMUX、MPU、外设请求条件一个都不能漏。我这次真正找到问题的关键是先把工程里所有涉及内存访问的配置链接脚本、MPU、DMA 地址全部拉出来核对了一遍。第二性能方面实测用 HPDMA 把一路 48kHz/32bit 音频流从 SAI 搬到 AXI SRAMCPU 负载几乎可以忽略中断频率大约 750Hz 的时候每次中断处理消耗约 20 个 CPU 周期只是简单的置标志位。如果把缓冲区放在 DTCM中断处理时间会再低一点点但对整体系统影响很小。所以普通音频场景直接把缓冲区放 AXI SRAM 就好。第三关于 DTCM 和 DMA 配合我个人的结论是DTCM 适合 CPU 高频访问的小块数据但不太适合作为外设 DMA 的长缓冲区。因为 DMA 访问 DTCM 虽然能工作但需要额外确认 MPU、地址映射、总线仲裁这些细节收益与投入不成正比。如果你的应用对延迟极其敏感可以预留 DTCM 的一小段比如 8KB专门给实时信号处理用同时用 HPDMA 先把数据搬到 AXI SRAM再在中断里把需要的那一小段拷贝到 DTCM。这样既利用了 DTCM 的低延迟又避开了直接 DMA 到 DTCM 的配置复杂度。最后分享一个小技巧调试这个类型的问题时可以在初始化阶段把 HPDMA 的传输完成中断打开然后在中断里放一个static计数变量用调试器挂在中断里观察。只要中断能进DMA 通路就通了剩下的就是业务逻辑的调整。这套方法帮我节省了很多时间。遇到问题别急着改代码先把链路每一段的“信号”确认到位问题自然水落石出。
返回列表