# STM32H7 的 MDMA 与 DMA2D 协同实现双缓冲图形加速的配置要点 ## 引言 在嵌入式图形系统中,CPU 直接搬运像素数据会严重占用处理周期。STM32H7 系列内置的 DMA2D(Chrom-ART Accelerator)和 MDMA(Master DMA)提供了硬件级的数据搬运能力。DMA2D 擅长像素格式转换、混合和填充,而 MDMA 则能高效地在内存区域间搬移大块数据。将两者结合,可以构建一条从帧缓冲区到显示控制器的自动流水线,实现双缓冲的无缝切换,大幅提升图形刷新率。本文面向有 STM32 开发经验的工程师,聚焦配置要点与代码实现。 ## 原理:双缓冲与硬件加速 双缓冲的核心是:后台缓冲区用于绘制新帧,前台缓冲区用于显示。当后台绘制完成后,需要快速交换指针或复制数据。在 STM32H7 上,更高效的方式是使用 DMA2D 将后台缓冲区的像素数据搬运到 LTDC(LCD 控制器)的当前帧地址,或者直接切换 LTDC 的帧地址(但需注意时序)。而 MDMA 可以在此过程中扮演两个角色: - **预加载**:将外部 SDRAM 中的图像数据块预先搬运到内部 SRAM(如 D1 域),供 DMA2D 快速处理。 - **后处理**:将 DMA2D 处理完的数据快速搬移到 LTDC 的帧缓冲区(可能位于 SDRAM)。 关键点在于,MDMA 和 DMA2D 都挂载在 AXI 总线上,它们可以并行工作,但需要避免访问冲突。通过合理配置 MDMA 的通道优先级和 DMA2D 的 FIFO 阈值,可以最大化吞吐量。 ## 配置步骤 ### 1. 时钟与总线使能 STM32H7 的 MDMA 和 DMA2D 均位于 AXI 总线域,需确保 RCC 中对应时钟已使能。 ```c __HAL_RCC_MDMA_CLK_ENABLE(); __HAL_RCC_DMA2D_CLK_ENABLE(); ``` 注意:MDMA 的时钟源可选 AXI 或 AHB,通常使用 AXI(默认),频率可达 240MHz。 ### 2. 内存区域规划 双缓冲需要至少两个缓冲区,建议分配在 D1 域(AXI SRAM)或外部 SDRAM,并确保 32 字节对齐(MDMA 要求)。 ```c #define BUFFER_SIZE (800*480*2) // RGB565, 800x480 __attribute__((section(".ARM.__at_0x24000000"))) uint8_t buf_back[BUFFER_SIZE]; __attribute__((section(".ARM.__at_0x24000000+BUFFER_SIZE"))) uint8_t buf_front[BUFFER_SIZE]; ``` ### 3. MDMA 配置(用于搬运图像块到 SRAM) MDMA 支持 Linked List 模式,可链式执行多个传输。以下配置一个通道,用于从 SDRAM 搬运 32KB 数据到内部 SRAM。 ```c MDMA_HandleTypeDef hmdma; void MDMA_Init(void) { hmdma.Instance = MDMA_Channel0; hmdma.Init.Request = MDMA_REQUEST_SW; // 软件触发 hmdma.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER; hmdma.Init.Priority = MDMA_PRIORITY_HIGH; hmdma.Init.Endianness = MDMA_LITTLE_ENDIAN; hmdma.Init.SourceInc = MDMA_SRC_INC_WORD; hmdma.Init.DestinationInc = MDMA_DST_INC_WORD; hmdma.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD; hmdma.Init.DestDataSize = MDMA_DST_DATASIZE_WORD; hmdma.Init.DataAlignment = MDMA_DATAALIGN_PACK; hmdma.Init.BufferTransferLength = 32*1024/4; // 以 word 为单位 hmdma.Init.SourceBurst = MDMA_SOURCE_BURST_32BEATS; hmdma.Init.DestBurst = MDMA_DEST_BURST_32BEATS; hmdma.Init.SourceAddress = (uint32_t)src_addr; hmdma.Init.DestAddress = (uint32_t)buf_back; HAL_MDMA_Init(&hmdma); } ``` ### 4. DMA2D 配置(用于像素格式转换与搬运) DMA2D 可以执行内存到内存的搬运,并支持 RGB565 到 RGB888 等转换。以下配置将后台缓冲区内容复制到前台缓冲区(模拟双缓冲切换)。 ```c DMA2D_HandleTypeDef hdma2d; void DMA2D_Init(void) { hdma2d.Instance = DMA2D; hdma2d.Init.Mode = DMA2D_M2M; // 内存到内存 hdma2d.Init.ColorMode = DMA2D_RGB565; hdma2d.Init.OutputOffset = 0; hdma2d.LayerCfg[0].InputOffset = 0; hdma2d.LayerCfg[0].InputColorMode = DMA2D_RGB565; hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[0].InputAlpha = 0xFF; HAL_DMA2D_Init(&hdma2d); } void DMA2D_CopyBuffer(uint32_t src, uint32_t dst, uint16_t width, uint16_t height) { HAL_DMA2D_Start(&hdma2d, src, dst, width, height); HAL_DMA2D_PollForTransfer(&hdma2d, 100); } ``` ### 5. 协同工作流程 实际应用中,MDMA 和 DMA2D 可以流水线化:MDMA 从 SDRAM 预加载下一帧图像到 SRAM,同时 DMA2D 处理当前帧。以下是一个简单示例: ```c void Start_Frame_Transfer(void) { // 1. 启动 MDMA 搬运下一帧到后台缓冲区 HAL_MDMA_Start_IT(&hmdma, (uint32_t)next_frame_sdram, (uint32_t)buf_back, 32*1024/4); // 2. 同时启动 DMA2D 将后台缓冲区内容复制到前台(或直接处理) DMA2D_CopyBuffer((uint32_t)buf_back, (uint32_t)buf_front, 800, 480); // 3. 等待 MDMA 完成,然后更新 LTDC 帧地址(可选) } ``` 注意:MDMA 和 DMA2D 的完成中断应分别处理,避免资源竞争。 ## 完整代码示例(简化) ```c // 中断回调 void HAL_MDMA_XferCpltCallback(MDMA_HandleTypeDef *hmdma) { // 通知主循环可以安全使用 buf_back } void HAL_DMA2D_XferCpltCallback(DMA2D_HandleTypeDef *hdma2d) { // 切换 LTDC 帧地址到 buf_front LTDC->Layer1->CFBAR = (uint32_t)buf_front; } int main(void) { HAL_Init(); SystemClock_Config(); // 初始化 LTDC、SDRAM 等 MDMA_Init(); DMA2D_Init(); // 注册回调 HAL_MDMA_RegisterCallback(&hmdma, HAL_MDMA_XFER_CPLT_CB_ID, MDMA_XferCpltCallback); HAL_DMA2D_RegisterCallback(&hdma2d, HAL_DMA2D_XFER_CPLT_CB_ID, DMA2D_XferCpltCallback); // 启动第一次传输 Start_Frame_Transfer(); while(1) { // 主循环处理其他任务 } } ``` ## 注意事项 - **内存对齐**:MDMA 要求源和目标地址按传输宽度对齐(如 32 位对齐),否则会触发错误。建议使用 32 字节对齐。 - **缓存一致性**:STM32H7 的 D-Cache 可能导致 DMA 看到的数据不一致。需在 DMA 操作前调用 `SCB_CleanDCache()` 或使用 MPU 配置为非缓存区域。 - **中断优先级**:MDMA 和 DMA2D 中断应设置为较高优先级,且避免在回调中执行耗时操作。 - **总线仲裁**:当 MDMA 和 DMA2D 同时访问 SDRAM 时,可能产生带宽竞争。可通过调整 MDMA 的 burst 大小和 DMA2D 的 FIFO 阈值来优化。 - **描述符链**:对于复杂传输,使用 MDMA 的 Linked List 模式可以避免 CPU 干预,但需注意描述符本身的内存放置(建议放在 D2 域 SRAM)。 ## 结语 MDMA 与 DMA2D 的协同可以显著降低图形系统的 CPU 负载,实现高效的双缓冲刷新。关键在于合理规划内存、配置正确的触发模式,并处理好缓存一致性问题。本文提供的配置要点和代码框架可作为开发起点,实际项目中还需根据具体屏幕分辨率和刷新率调整参数。掌握这两者,你的 STM32H7 图形性能将得到质的飞跃。