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 启动下一轮搬运。
时序对齐策略
- 双缓冲机制:使用两个帧缓冲(Buffer A 和 Buffer B),MDMA 将新数据写入 Buffer B,同时 DMA2D 从 Buffer A 读取并显示。当 MDMA 完成写入后,交换角色。
- 中断同步:MDMA 传输完成中断(TCIF)触发 DMA2D 启动;DMA2D 完成中断(TCIF)触发 MDMA 启动下一轮。
- 优先级设置: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。
// 定义缓冲结构
#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 图形应用性能更上一层楼。