# STM32H7 帧缓冲搬运的时序对齐:MDMA 与 DMA2D 协同优化实战 ## 为什么需要 MDMA 与 DMA2D 协同? 在 STM32H7 系列中,DMA2D 是专用的 2D 图形加速器,可执行像素格式转换、混合、填充等操作,但它的源地址和目的地址通常位于 AHB 总线域。而 MDMA(Master DMA)则能访问所有存储器映射区域,包括 AXI SRAM、SDRAM 等,且支持链表模式,可自动执行复杂的数据搬运。 当我们需要将摄像头采集的 RGB565 帧数据搬运到 SDRAM 中的帧缓冲,再通过 DMA2D 进行格式转换或混合后显示到 LCD 时,若简单串联使用,会因总线仲裁和 DMA 优先级差异导致时序错位,出现画面撕裂。 **核心目标**:让 MDMA 完成大块数据搬运,DMA2D 在数据就绪后立即处理,两者通过中断和标志位精确同步,避免数据竞争。 ## 硬件特性与时序对齐原理 ### MDMA 的关键特性 - 支持 8/16/32/64 位数据宽度,突发传输可达 128 字节。 - 拥有 4 个 FIFO,可缓存数据,降低总线占用。 - 支持链接列表(Linked List),可连续执行多个传输块,无需 CPU 干预。 ### DMA2D 的关键特性 - 支持前景/背景层,可执行颜色填充、混合、格式转换。 - 操作基于像素,需确保源数据完整。 - 完成中断可触发 MDMA 启动下一轮搬运。 ### 时序对齐策略 1. **双缓冲机制**:使用两个帧缓冲(Buffer A 和 Buffer B),MDMA 将新数据写入 Buffer B,同时 DMA2D 从 Buffer A 读取并显示。当 MDMA 完成写入后,交换角色。 2. **中断同步**:MDMA 传输完成中断(TCIF)触发 DMA2D 启动;DMA2D 完成中断(TCIF)触发 MDMA 启动下一轮。 3. **优先级设置**:MDMA 优先级设为高,DMA2D 设为中,确保搬运不被显示操作阻塞。 ## 配置步骤 ### 1. 初始化 MDMA - 使能 MDMA 时钟(RCC->AHB3ENR |= RCC_AHB3ENR_MDMAEN)。 - 配置 MDMA 通道,设置源地址、目的地址、数据块大小(以 32 位字为单位)。 - 启用传输完成中断,并设置优先级。 ### 2. 初始化 DMA2D - 使能 DMA2D 时钟(RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN)。 - 配置 DMA2D 为内存到内存模式,设置源地址(帧缓冲)、目的地址(LCD 显存)。 - 设置像素格式(如 ARGB8888)、行偏移等。 - 启用完成中断。 ### 3. 中断服务函数 - 在 MDMA 中断中,启动 DMA2D 传输。 - 在 DMA2D 中断中,切换缓冲角色,并启动 MDMA 搬运下一帧。 ## 完整代码示例 以下代码基于 STM32H743,使用 HAL 库,假设帧缓冲位于 AXI SRAM,LCD 显存位于 SDRAM。 ```c // 定义缓冲结构 #define FRAME_WIDTH 480 #define FRAME_HEIGHT 272 #define PIXEL_SIZE 2 // RGB565 #define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT * PIXEL_SIZE) uint8_t buffer_A[FRAME_SIZE] __attribute__((section(".RAM_D1"))); uint8_t buffer_B[FRAME_SIZE] __attribute__((section(".RAM_D1"))); uint8_t *active_buffer = buffer_A; uint8_t *display_buffer = buffer_B; // MDMA 句柄 MDMA_HandleTypeDef hmdma; // DMA2D 句柄 DMA2D_HandleTypeDef hdma2d; // MDMA 初始化 void MDMA_Init(void) { __HAL_RCC_MDMA_CLK_ENABLE(); hmdma.Instance = MDMA_Channel0; hmdma.Init.Request = MDMA_REQUEST_MEM2MEM; hmdma.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER; hmdma.Init.Priority = MDMA_PRIORITY_HIGH; hmdma.Init.Endianness = MDMA_LITTLE_ENDIAN_PRESERVE; hmdma.Init.SourceInc = MDMA_SRC_INC_WORD; hmdma.Init.DestinationInc = MDMA_DEST_INC_WORD; hmdma.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD; hmdma.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD; hmdma.Init.DataAlignment = MDMA_DATAALIGN_PACK; hmdma.Init.BufferTransferLength = FRAME_SIZE / 4; // 32位字数量 HAL_MDMA_Init(&hmdma); HAL_NVIC_SetPriority(MDMA_IRQn, 1, 0); HAL_NVIC_EnableIRQ(MDMA_IRQn); } // DMA2D 初始化 void DMA2D_Init(void) { __HAL_RCC_DMA2D_CLK_ENABLE(); 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); HAL_NVIC_SetPriority(DMA2D_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA2D_IRQn); } // 启动 MDMA 搬运(从源到目标) void MDMA_StartTransfer(uint8_t *src, uint8_t *dst) { HAL_MDMA_Start_IT(&hmdma, (uint32_t)src, (uint32_t)dst, FRAME_SIZE / 4, 0); } // 启动 DMA2D 转换(从帧缓冲到显存) void DMA2D_StartTransfer(uint8_t *src) { HAL_DMA2D_Start_IT(&hdma2d, (uint32_t)src, (uint32_t)LCD_FB_ADDR, FRAME_WIDTH, FRAME_HEIGHT); } // MDMA 中断回调 void HAL_MDMA_TC_Callback(MDMA_HandleTypeDef *hmdma) { // 数据搬运完成,启动 DMA2D 显示 DMA2D_StartTransfer(active_buffer); } // DMA2D 中断回调 void HAL_DMA2D_TC_Callback(DMA2D_HandleTypeDef *hdma2d) { // 显示完成,交换缓冲并启动下一轮搬运 uint8_t *tmp = active_buffer; active_buffer = display_buffer; display_buffer = tmp; // 假设新数据已由外部填充到 active_buffer MDMA_StartTransfer(source_data, active_buffer); } // 主程序启动 int main(void) { HAL_Init(); SystemClock_Config(); MDMA_Init(); DMA2D_Init(); // 首次启动 MDMA_StartTransfer(source_data, active_buffer); while(1) { // 主循环可处理其他任务 } } ``` ## 注意事项 - **内存对齐**:帧缓冲地址必须按 32 字节对齐,否则 MDMA 可能产生总线错误。 - **缓存一致性**:如果使用 AXI SRAM,需注意 CPU 缓存与 DMA 的一致性,必要时使用 `SCB_CleanDCache()` 或 `SCB_InvalidateDCache()`。 - **中断优先级**:MDMA 中断优先级应高于 DMA2D,避免 DMA2D 完成中断被 MDMA 中断抢占导致时序错乱。 - **传输长度单位**:MDMA 的 `BufferTransferLength` 以 32 位字为单位,计算时务必除以 4。 - **调试建议**:使用逻辑分析仪或示波器观察帧同步信号,验证时序是否对齐。 ## 总结 通过合理配置 MDMA 与 DMA2D 的中断同步和双缓冲机制,可以高效实现帧缓冲搬运与显示的流水线操作,大幅降低 CPU 负载并避免画面撕裂。本文提供的代码框架可直接应用于实际项目,但需根据具体硬件调整地址和参数。掌握这一技巧,将让你的 STM32H7 图形应用性能更上一层楼。