# STM32H7 双 DMA 协同:总线仲裁优先级配置陷阱深度解析 ## 一、背景与问题引入 STM32H7 系列(如 H743/H750)拥有强大的双 DMA 架构:**MDMA**(Master DMA)挂载在 AXI 总线,负责大块内存间搬运;**LPDMA**(Low-Power DMA)挂载在 AHB 总线,用于外设与内存间的低功耗传输。两者协同可构建流水线式数据处理(如 ADC 采集→LPDMA→SRAM→MDMA→外部 SDRAM)。 然而,当 MDMA 和 LPDMA 同时访问共享资源(如 AXI SRAM、Flash 或 SDRAM)时,总线仲裁策略将决定谁先获得总线控制权。若配置不当,轻则传输效率骤降,重则产生数据一致性错误(如搬运的数据被覆盖)或总线死锁。 ## 二、总线仲裁机制原理 ### 1. STM32H7 总线矩阵架构 - **AXI 总线**:连接 MDMA、D2-to-D1 桥、AXI SRAM 等,支持 128 位带宽,具有独立的读写通道。 - **AHB 总线**:连接 LPDMA、GPIO、定时器等外设,带宽 32/64 位,通过总线矩阵与 AXI 交互。 - **总线矩阵**:负责仲裁多个主设备(如 CPU、MDMA、LPDMA)对从设备(如 SRAM、Flash)的访问请求。 ### 2. 仲裁优先级配置 总线矩阵为每个主设备提供可编程的优先级(高/中/低),通过 `AHB1/AHB2/AHB3` 等寄存器的 `PRICTL` 字段设置。当多个主设备同时请求同一从设备时,高优先级者先获得总线。 **关键陷阱**:MDMA 和 LPDMA 的优先级是独立配置的,但它们的请求可能指向同一从设备(如 D2 域的 SRAM)。若 LPDMA 优先级高于 MDMA,且 LPDMA 频繁访问,则 MDMA 可能被“饿死”,导致大块搬运停滞。反之,若 MDMA 优先级过高,LPDMA 的实时性(如 ADC 数据采集)可能受影响。 ## 三、常见配置陷阱 ### 陷阱 1:默认优先级导致 MDMA 饥饿 - 默认情况下,MDMA 优先级为低,LPDMA 为高。当 LPDMA 持续搬运外设数据(如 DAC 输出)时,MDMA 的 AXI 请求会被无限期延迟,造成 CPU 等待 MDMA 完成标志超时。 ### 陷阱 2:忽略 AXI 与 AHB 的桥接延迟 - MDMA 访问 AHB 外设(如通过桥接)时,需要额外的等待周期。若 LPDMA 同时访问同一桥,优先级配置不当会加剧延迟,导致 MDMA 传输效率下降。 ### 陷阱 3:缓存一致性未考虑 - 当 MDMA 和 LPDMA 共同操作同一内存区域(如双缓冲),若未启用 Cache 维护(如 SCB_CleanDCache),优先级冲突可能导致数据错乱。 ## 四、配置步骤(基于 CubeMX) ### 1. 初始化 MDMA 和 LPDMA 在 CubeMX 中,分别添加 MDMA 和 LPDMA 通道,设置数据宽度、突发大小等。 ### 2. 配置总线优先级 - 打开 `System Core > RCC`,在 `AHB` 选项卡中,找到 `MDMA` 和 `LPDMA` 的优先级设置。 - 推荐策略: - 若 LPDMA 用于实时外设(如 ADC),且数据量小,将 LPDMA 设为**高**,MDMA 设为**中**,并启用 MDMA 的 `Burst` 模式以减少总线占用。 - 若 MDMA 负责大块数据(如图像处理),且 LPDMA 仅偶尔触发,将 MDMA 设为**高**,LPDMA 设为**低**。 ### 3. 代码示例(HAL 库) ```c // 配置 MDMA 优先级为高,LPDMA 为低(示例) void BusPriority_Config(void) { // 假设使用 AHB1 总线矩阵 __HAL_RCC_AHB1_CLK_ENABLE(); // 设置 MDMA 优先级为高(PRICTL 寄存器,位 0-1) MODIFY_REG(AHB1->PRICTL, AHB_PRICTL_MDMA_PRI_Msk, AHB_PRICTL_MDMA_PRI_HIGH); // 设置 LPDMA 优先级为低(位 2-3) MODIFY_REG(AHB1->PRICTL, AHB_PRICTL_LPDMA_PRI_Msk, AHB_PRICTL_LPDMA_PRI_LOW); } // MDMA 传输完成回调 void MDMA_IRQHandler(void) { HAL_MDMA_IRQHandler(&MDMA_Handle); } void HAL_MDMA_MemToMem_CompleteCallback(MDMA_HandleTypeDef *hmdma) { // 处理完成事件 } // 启动 MDMA 搬运(从 SRC 到 DST) void MDMA_Transfer(uint32_t *src, uint32_t *dst, uint32_t size) { HAL_MDMA_Start_IT(&MDMA_Handle, (uint32_t)src, (uint32_t)dst, size); } ``` ### 4. 完整配置示例(CubeMX 生成代码基础上修改) ```c // main.c 中初始化 int main(void) { HAL_Init(); SystemClock_Config(); // 配置总线优先级(必须在 DMA 初始化前) BusPriority_Config(); // 初始化 MDMA 和 LPDMA MX_MDMA_Init(); MX_LPDMA_Init(); // 启动传输 MDMA_Transfer((uint32_t*)src_buf, (uint32_t*)dst_buf, 1024); while (1) {} } ``` ## 五、调试与验证技巧 - 使用逻辑分析仪或 STM32CubeMonitor 观察总线占用率,确认无饥饿现象。 - 在 MDMA 和 LPDMA 中断中记录时间戳,分析传输延迟。 - 若出现数据错误,检查是否启用 Cache 维护: ```c SCB_CleanDCache(); // 在 MDMA 写后,LPDMA 读前调用 SCB_InvalidateDCache(); // 在 LPDMA 写后,MDMA 读前调用 ``` ## 六、注意事项 1. **优先级配置时机**:必须在 DMA 使能前配置,否则可能无效。 2. **突发传输**:MDMA 使用突发模式(如 INCR8)可减少总线仲裁次数,提高效率。 3. **内存域隔离**:尽量让 MDMA 访问 AXI SRAM,LPDMA 访问 AHB SRAM,避免冲突。 4. **中断优先级**:DMA 中断优先级应低于系统时钟中断,防止死锁。 5. **参考手册**:仔细阅读 RM0433 中“总线矩阵”章节,不同型号(H7A3/H7B0)寄存器略有差异。 ## 七、总结 MDMA 与 LPDMA 协同是 STM32H7 高性能应用的关键,但总线仲裁优先级配置不当会引发隐蔽问题。通过理解总线矩阵原理,合理设置优先级,并配合缓存维护,可确保数据传输高效稳定。建议在项目初期进行带宽评估,避免后期调试的泥潭。