拓冰建站拓冰建站
首页 / 资讯中心 / 正文

06 - DMA2D 硬件加速刷屏:M2M 搬运替代 CPU memcpy + 性能实测

06 - DMA2D 硬件加速刷屏M2M 搬运替代 CPU memcpy 性能实测系列导航一STM32F767 RT-Thread 图形栈总览【系列导读】二RT-Thread Studio 工程创建与 CubeMX 基础配置三FMC 驱动 SDRAM 配置与读写验证四LTDC 点亮 800×480 RGB 屏与帧缓冲踩坑五LVGL v8.3.11 移植与显示适配六DMA2D 硬件加速刷屏与性能实测 ←本文七工程源码说明与进阶优化本篇目标启用 STM32F767 的 DMA2DChroma-Art 图形加速器用寄存器到寄存器的 M2M 搬运替代drv_lcd.c中 CPU 逐行 memcpy 的刷屏路径并给出前后实测数据对比。背景问题第 05 篇的 LVGL 采用full_refresh 1全屏刷新每次 flush 要搬运 800×480×4 1.5MB数据。CPU 逐行 memcpy 时这段搬运是纯阻塞式体力活——期间 CPU 无法渲染下一帧、也无法跑业务逻辑。DMA2D 就是让硬件干这活。1. DMA2D 是什么、能干什么DMA2D业内俗称 Chroma-Art是 F4/F7/H7 系列的二维图形加速 DMA四种工作模式模式功能典型用途寄存器到存储器 (R2M)用单一颜色填充矩形区域清屏、画纯色背景存储器到存储器 (M2M)纯搬运不做格式转换本篇draw_buf → 帧缓冲存储器到存储器并带 PFC (M2MPFC)搬运同时做像素格式转换RGB565 显示 ARGB8888 素材存储器到存储器并带混合 (M2MBlend)两路源混合后输出硬件 Alpha 叠加、图层合成本篇只用最简单的M2M 模式源LVGL draw_buf和目的LCD front/back_buf都是 ARGB8888搬运即可无需转换。PFC/混合是后续进阶方向见第 07 篇。DMA2D 寄存器细节NLR/OLR/FGOR/BGOR/FGPFCCR 等参考RM0410 第 10 章 DMA2D野火《STM32库开发实战指南》DMA2D 章节有入门级讲解。2. CubeMX 配置 DMA2D2.1 基础参数Computing → DMA2DMode 勾选Activated配置项取值Transfer ModeMemory to MemoryColor Mode输出ARGB8888Output Offset0代码中按刷新矩形动态改ForegroundInput Color ModeARGB8888ForegroundALPHA MODENo modification of the alpha channel valueForegroundInput Offset0代码中动态改Red and Blue swapRegular mode输入/输出格式都设为 ARGB8888DMA2D 走纯搬运路径效率最高。若你的 LVGL 与屏幕像素格式不同此处需要 PFC。2.2 中断NVIC 中开启DMA2D global interrupt抢占 0 / 子优先级 0完成后 GENERATE CODE。3. 代码实现3.1 DMA2D 初始化与中断服务lv_port_disp.c由于工程外设初始化由 RT-Thread 驱动体系接管我们在lv_port_disp.c中直接完成 DMA2D 的句柄初始化DMA2D_HandleTypeDef hdma2d;voidDMA2D_IRQHandler(void){HAL_DMA2D_IRQHandler(hdma2d);}staticvoidMX_DMA2D_Init(void){hdma2d.InstanceDMA2D;hdma2d.Init.ModeDMA2D_M2M;/* 存储器到存储器 */hdma2d.Init.ColorModeDMA2D_OUTPUT_ARGB8888;hdma2d.Init.OutputOffset0;/* 后面按矩形动态设置 */hdma2d.LayerCfg[1].InputOffset0;hdma2d.LayerCfg[1].InputColorModeDMA2D_INPUT_ARGB8888;hdma2d.LayerCfg[1].AlphaModeDMA2D_NO_MODIF_ALPHA;hdma2d.LayerCfg[1].InputAlpha0;hdma2d.LayerCfg[1].AlphaInvertedDMA2D_REGULAR_ALPHA;hdma2d.LayerCfg[1].RedBlueSwapDMA2D_RB_REGULAR;HAL_DMA2D_Init(hdma2d);HAL_DMA2D_ConfigLayer(hdma2d,1);}3.2 功能开关宏lcd_port.h在drivers/include/lcd_port.h顶部加总开关方便 A/B 对比测试#defineUSE_DMA2D_TRANSFER/* 注释掉则回退 CPU memcpy 版本 */3.3 刷屏路径改造drv_lcd.c核心变化在RTGRAPHIC_CTRL_RECT_UPDATE分支用#ifdef USE_DMA2D_TRANSFER包裹两个版本#ifdefUSE_DMA2D_TRANSFERexternDMA2D_HandleTypeDef hdma2d;caseRTGRAPHIC_CTRL_RECT_UPDATE:{structlcd_rect_info*rect(structlcd_rect_info*)args;uint32_tline_bytelcd-lcd_info.width*lcd-lcd_info.bits_per_pixel/8;uint32_trow_sizerect-rect.width*(lcd-lcd_info.bits_per_pixel/8);/* DMA2D 的行偏移单位是像素不是字节 偏移 屏幕总宽 - 本次刷新区域宽 */uint32_toffset_pixelslcd-lcd_info.width-rect-rect.width;/* Cortex-M7 有 D-CacheDMA2D 直接读内存前需保证数据已落盘 本工程显存为非 Cacheable 的 FMC 区域时可不维护见 3.4 节讨论 */if(_lcd.cur_buf){uint8_t*dstlcd-front_bufrect-rect.y*line_byterect-rect.x*(lcd-lcd_info.bits_per_pixel/8);/* ★ DMA2D 替代 rt_memcpy */hdma2d.Init.OutputOffsetoffset_pixels;HAL_DMA2D_ConfigLayer(hdma2d,1);HAL_DMA2D_Start_IT(hdma2d,(uint32_t)rect-pixels,(uint32_t)dst,rect-rect.width,rect-rect.height);LTDC_LAYER(LtdcHandle,0)-CFBAR(uint32_t)(_lcd.front_buf);_lcd.cur_buf0;}else{/* 对称路径dst 换 back_buf切换显示 */...}rt_sem_take(_lcd.lcd_lock,RT_TICK_PER_SECOND/20);HAL_LTDC_Reload(LtdcHandle,LTDC_SRCR_VBR);}break;#endif四个关键点OutputOffset的单位是像素不是字节。DMA2D 逐行搬运width个像素后目的地址指针要跳过本行剩余部分才到下一行行首——即屏幕宽 - 矩形宽个像素。这里用字节算会直接花屏每个 ARGB8888 像素 4 字节误差 4 倍。HAL_DMA2D_Start_IT的参数是宽、高像素数不是字节数库内部按输出像素格式换算。配置顺序改OutputOffset后要重新HAL_DMA2D_ConfigLayer再Start_IT。#else分支保留 CPU memcpy 路径宏一开一关即可实测对比。3.4 D-Cache 一致性问题重要原理值得单列Cortex-M7 带 D-Cache。CPU 写完 draw_buf 后数据可能还停留在 Cache 里没有写入 SDRAM而 DMA2D 直接读 SDRAM 物理内存——读到的是旧数据造成偶发花屏/图像残影。标准做法是在启动 DMA2D 前清理 CacheSCB_CleanDCache_by_Addr((uint32_t*)rect-pixels,row_size*rect-rect.height);本工程实测未开启该调用也能正常显示原因是本工程的 MPU 未把 SDRAM 配置为 CacheableRT-Thread 默认 MPU 配置下 FMC 外部内存区域不缓存Cache 与 SDRAM 之间不存在不一致。但如果你的工程开启了 SDRAM Cacheable性能更好就必须在 DMA2D 启动前 Clean、目的端读回前 Invalidate。这也是drv_lcd.c中保留被注释掉的SCB_CleanDCache_by_Addr调用的原因——为 Cacheable 配置预留。Cache 一致性与 MPU 配置的深入讨论参考《ARM Cortex-M7 Processor Technical Reference Manual》以及野火/正点原子 F7 Cache 专题文章。4. 编译与实测4.1 编译Build Finished: 0 errors, 1 warning Flash: 397516 B (388.20 KB) RAM: 6156648 B (6012.35 KB)RAM 统计含.sdram段实际片内占用不变。4.2 运行日志与性能数据下载后串口输出disp_monitor回调打印每帧刷新统计[D/drv.lcd] LTDC init success, double buffer: front 0xd0000000, back 0xd0177000 [D/lvgl] lvgl init success, use double draw buf: frame_buf_1 0xd02ee000, frame_buf_2 0xd0465000, each 1536000 bytes ... 每帧刷新统计 pixels 384000, bytes 1536000, time 161ms ← 首帧含初始化开销 pixels 384000, bytes 1536000, time 100~151ms ← 稳态区间实物运行效果图表滚动明显更顺滑4.3 性能对比与解读方案全屏搬运 1.5MB 方式现象CPU memcpy第 05 篇逐行 rt_memcpyCPU 全程阻塞动画偏卡CPU 占用高DMA2D M2M本篇硬件搬运CPU 仅做寄存器配置动画流畅度改善CPU 解放几点解读单帧 100~161ms 是全屏刷新路径的固有成本full_refresh 模式下每帧都要搬运整屏 1.5MB理论刷新率约 6~10fps。提升空间主要不在搬运DMA2D FMC 带宽固定而在减少搬运量——即关闭 full_flush 回归局部刷新脏矩形 DMA2D 行偏移机制天然支持非全宽矩形搬运DMA2D 的收益不只是速度搬运期间 CPU 完全空闲可以并行渲染下一帧或跑业务逻辑进一步优化局部刷新、异步化、PFC/混合在第 07 篇进阶方向中汇总。提示HAL_DMA2D_Start_IT启动后当前代码仍同步等待 LTDC 重载信号量属于半异步。极致优化应把 CFBAR 切换 VBR 重载移到 DMA2D 传输完成回调中执行实现真正流水线化——代码中已留注释说明改造位置。5. 常见问题速查现象原因一开 DMA2D 就花屏OutputOffset 单位错了字节 vs 像素输入/输出像素格式与实际不符偶发残影/花屏SDRAM 开了 Cacheable 但没做 Clean/Invalidate见 3.4屏幕局部错位刷新矩形 width 与 offset_pixels 计算 containing 屏宽硬件卡死DMA2D 中断未使能却用了 Start_IT或中断函数没调用 HAL_DMA2D_IRQHandler编译找不到 hdma2dextern 声明与定义分属 drv_lcd.c / lv_port_disp.c注意头文件包含关系6. 本篇小结DMA2D M2M 模式三步走CubeMX 使能 中断、MX_DMA2D_InitARGB8888 进出、M2M、刷屏路径替换HAL_DMA2D_Start_IT两个高频坑OutputOffset 单位是像素M7 D-Cache 与 DMA 的一致性本工程 MPU 未缓存 SDRAM 故可跳过通用场景必须处理USE_DMA2D_TRANSFER宏保留双路径方便随时 A/B 对比与回退。至此SDRAM → LTDC → LVGL → DMA2D完整图形栈全部打通。最后一篇讲工程源码结构、已知遗留问题与进阶优化路线07 - 工程源码说明与进阶优化。参考资源《RM0410 参考手册》第 10 章 DMA2D (Chroma-Art Accelerator)、《ARM Cortex-M7 TRM》Cache 章节、ST AN4861LCD-TFT 显示优化应用笔记
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门