# STM32H7 使用 MDMA 与 LTDC 联动实现无 CPU 干预的图层混合刷新 ## 一、为什么需要 MDMA 与 LTDC 联动? 在 STM32H7 系列(如 STM32H743/H750)中,LTDC(LCD TFT Display Controller)负责将显存(Frame Buffer)中的像素数据实时扫描输出到 RGB 接口的 LCD 屏。LTDC 支持最多 2 个图层(Layer0/Layer1),每个图层可独立配置像素格式、透明度(Alpha)和混合模式。 传统做法是:CPU 或普通 DMA 将图像数据从外部 Flash/SRAM 搬运到显存,然后 LTDC 自动刷新。但若需要动态更新图层(如动画、视频帧),CPU 频繁搬运数据会占用大量内核时间,导致系统响应变慢。 **MDMA(Master DMA)** 是 STM32H7 中的高性能 DMA 控制器,支持: - 二维传输(Block Transfer),可配置源/目标地址偏移,适合图像矩形区域搬运。 - 突发传输(Burst),提高总线利用率。 - 与 LTDC 的同步机制:通过 LTDC 的 Line Interrupt 或 Vsync 信号触发 MDMA 传输,确保在屏幕刷新间隙更新显存,避免撕裂(Tearing)。 **联动方案**:MDMA 在 LTDC 的垂直消隐(VBlank)期间,自动将新图像数据从源地址(如 SDRAM 中的另一缓冲区)搬运到 LTDC 的当前显存地址,同时可配置 Alpha 混合参数(通过修改图层控制寄存器),整个过程无需 CPU 干预,实现流畅的图层混合刷新。 ## 二、硬件与软件准备 - **硬件**:STM32H743 开发板(或任意 H7 系列),RGB LCD 屏(如 480x272),SDRAM(可选,用于大容量缓冲)。 - **软件**:STM32CubeIDE(或 Keil),STM32CubeH7 固件库,STM32CubeMX 用于初始化。 ## 三、原理详解 ### 1. LTDC 图层混合机制 LTDC 的每个图层有独立的控制寄存器(LxCR)、窗口位置(LxWHPCR/LxWVPCR)、像素格式(LxPFCR)和 Alpha 值(LxCACR)。混合公式为: `Output = (Layer1 * Alpha1 + Layer0 * Alpha0) / (Alpha1 + Alpha0)`,其中 Alpha 范围 0-255。 要实现动态混合,只需在刷新时更新图层的 Alpha 值或替换图层数据。 ### 2. MDMA 二维传输 MDMA 的传输由 `MDMA_CxTCR`(传输配置寄存器)定义,包括源/目标地址、块大小(Block Size)、块内偏移(Source/Dest Offset)等。二维传输允许你指定源和目标地址的步进(如每行偏移),适合搬运矩形图像区域。 ### 3. 同步机制 LTDC 产生中断(如 Line Interrupt 或 Vsync)时,可触发 MDMA 的请求。通过配置 MDMA 的请求源(Request Generator)为 LTDC 的特定事件,MDMA 会在事件发生时自动启动传输。 ## 四、CubeMX 配置步骤 1. **配置 LTDC**: - 在 Pinout 中启用 LTDC,设置时序参数(Hsync/Vsync/HBP/VBP 等,根据 LCD 手册)。 - 启用 Layer0 和 Layer1,设置像素格式(如 RGB565),分配显存地址(如 0xC0000000 和 0xC0100000)。 2. **配置 MDMA**: - 在 DMA 设置中添加 MDMA,选择请求源为 `LTDC_LINE_INT`(或 `LTDC_VSYNC`)。 - 设置传输模式:内存到内存(Memory-to-Memory),但触发源为外部事件。 - 配置二维参数:源地址为图像数据缓冲区,目标地址为显存,块大小 = 图像宽度 * 高度 * 像素字节数,行偏移 = 每行字节数。 3. **配置中断**: - 使能 LTDC 的 Line Interrupt(在消隐区触发),并设置 MDMA 传输完成中断(可选,用于通知 CPU)。 4. **生成代码**:在 CubeMX 中生成初始化代码,然后添加用户逻辑。 ## 五、完整代码示例 以下代码基于 STM32CubeH7 库,实现 MDMA 在 LTDC 垂直消隐期间自动搬运新图像到 Layer0,并动态调整 Alpha 值。 ```c // 定义图像缓冲区(假设在 SDRAM) #define IMG_WIDTH 480 #define IMG_HEIGHT 272 #define IMG_PIXEL_SIZE 2 // RGB565 #define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT * IMG_PIXEL_SIZE) uint8_t image_buffer[IMG_SIZE] __attribute__((section(".sdram"))); // 新图像数据 uint8_t ltdc_layer0_addr[IMG_SIZE] __attribute__((section(".sdram"))); // LTDC 显存 // 初始化 MDMA 传输 void MDMA_Init(void) { MDMA_HandleTypeDef hmdma; hmdma.Instance = MDMA_Channel0; hmdma.Init.Request = MDMA_REQUEST_LTDC_LINE_INT; // 由 LTDC 行中断触发 hmdma.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; // 块传输 hmdma.Init.Priority = MDMA_PRIORITY_HIGH; hmdma.Init.Endianness = MDMA_LITTLE_ENDIAN; hmdma.Init.SourceInc = MDMA_SRC_INC_WORD; // 源地址递增(按字) hmdma.Init.DestInc = 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 = IMG_SIZE / 4; // 以字为单位 hmdma.Init.SourceBurst = MDMA_SOURCE_BURST_32BEATS; hmdma.Init.DestBurst = MDMA_DEST_BURST_32BEATS; hmdma.Init.SourceBlockAddressOffset = 0; hmdma.Init.DestBlockAddressOffset = 0; hmdma.Init.SourceBlockDataSize = IMG_WIDTH * IMG_PIXEL_SIZE; // 每行字节数 hmdma.Init.DestBlockDataSize = IMG_WIDTH * IMG_PIXEL_SIZE; hmdma.Init.SourceBlockDataStride = 0; // 连续 hmdma.Init.DestBlockDataStride = 0; HAL_MDMA_Init(&hmdma); } // 启动 MDMA 传输(在 LTDC 中断中调用) void Start_MDMA_Transfer(void) { HAL_MDMA_Start(&hmdma, (uint32_t)image_buffer, (uint32_t)ltdc_layer0_addr, IMG_SIZE/4, 1); } // LTDC 行中断回调(在 HAL_LTDC_LineEventCallback 中) void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef *hltdc) { // 在消隐区启动 MDMA 传输 Start_MDMA_Transfer(); } // 主函数中初始化并启动 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_LTDC_Init(); MX_MDMA_Init(); // 填充图像数据(示例) for (int i = 0; i < IMG_SIZE; i++) image_buffer[i] = i & 0xFF; // 使能 LTDC 行中断 HAL_LTDC_EnableLineEvent(&hltdc, LTDC_IT_LI); HAL_LTDC_LineEventCallback(&hltdc); // 首次启动 while (1) { // 主循环可做其他任务,无需干预刷新 } } ``` **动态 Alpha 混合**:在需要改变透明度时,只需修改 LTDC 图层控制寄存器(如 `LTDC_Layer1->CACR = alpha_value`),LTDC 会自动应用新混合效果,无需重启传输。 ## 六、注意事项 - **显存对齐**:MDMA 要求源和目标地址按 4 字节对齐,且传输长度以字为单位。若图像宽度不是 4 的倍数,需调整 `SourceBlockDataSize` 和 `DestBlockDataSize` 为实际字节数,并确保地址对齐。 - **同步时序**:务必在 LTDC 垂直消隐(VBlank)期间启动 MDMA,否则可能产生撕裂。可通过配置 Line Interrupt 在消隐区触发,或使用 Vsync 中断。 - **缓存一致性**:STM32H7 的 D-Cache 可能导致 MDMA 读取到旧数据。在更新图像缓冲区后,需调用 `SCB_CleanDCache()` 或使用非缓存内存(如 MPU 配置)。 - **MDMA 通道冲突**:确保 MDMA 通道未被其他外设占用,且优先级设置合理,避免与 LTDC 总线访问冲突。 - **性能优化**:使用突发传输(Burst)可提高带宽利用率,但需确保总线仲裁支持。对于大图像,可考虑双缓冲(Ping-Pong)机制,进一步避免撕裂。 ## 七、总结 通过 MDMA 与 LTDC 的联动,STM32H7 可以在无 CPU 干预的情况下自动完成图层数据搬运和混合刷新,极大提升图形界面性能。本文从原理到代码实践,展示了如何配置 MDMA 的二维传输、利用 LTDC 中断触发同步,并给出了关键注意事项。掌握这一技术,你可以在嵌入式 GUI 开发中实现更流畅的动画和特效,同时释放 CPU 资源处理其他任务。