DMA2D 传输模式:四种核心玩法
DMA2D 这玩意儿,说白了就是个专门干图像搬运和处理的硬件加速器。我刚开始接触的时候,总觉得它跟普通 DMA 差不多,后来才发现完全不是一回事。它内置了四种传输模式,每种模式解决一类特定问题。
嗯,咱们一个一个来看。
模式一:寄存器到内存(Register-to-Memory)
这个模式最简单,也最容易被忽略。它的作用是用一个固定的颜色值去填充一整块内存区域。
你可能会问:「这跟 memset 有什么区别?」
区别大了。memset 是 CPU 逐字节写,DMA2D 是硬件流水线操作。我在一个 800×480 的屏幕上做清屏测试,memset 大概要 3ms,DMA2D 寄存器到内存模式只需要 0.5ms。帧率一下子就上来了。
配置要点:
- 源地址:直接填
NULL,因为数据来自寄存器 - 前景色寄存器(FGPFCCR):设置你要填充的 ARGB 颜色值
- 目标地址:指向帧缓冲区的起始位置
- 传输大小:按像素数计算,不是字节数
// 寄存器到内存模式配置示例
DMA2D->CR = 0x00030000; // 模式选择:寄存器到内存
DMA2D->FGPFCCR = 0xFF0000FF; // 前景色:蓝色(ARGB)
DMA2D->OMAR = (uint32_t)fb_addr; // 目标地址:帧缓冲
DMA2D->NLR = (height << 16) | width; // 行数 | 每行像素数
DMA2D->CR |= 0x00000001; // 启动传输
模式二:内存到内存(Memory-to-Memory)
这个模式就是纯粹的图像搬运工。把一块内存区域的数据,原封不动地复制到另一块区域。
听起来跟 memcpy 一样?没错,功能上确实一样。但 DMA2D 的优势在于:
- 不占用 CPU 周期,CPU 可以去做别的事
- 传输速度比 CPU 快,尤其是大块数据
- 支持硬件双缓冲切换
我在做 LVGL 双缓冲显示时,就用这个模式来切换前后台缓冲。前台显示完了,后台画好了,一个 DMA2D 传输就把后台内容搬到前台,CPU 全程不用管。
// 内存到内存模式配置示例
DMA2D->CR = 0x00000000; // 模式选择:内存到内存
DMA2D->FGMAR = (uint32_t)src; // 源地址
DMA2D->OMAR = (uint32_t)dst; // 目标地址
DMA2D->FGOR = 0; // 源偏移量(行尾到下一行首的像素数)
DMA2D->OOR = 0; // 目标偏移量
DMA2D->NLR = (height << 16) | width;
DMA2D->CR |= 0x00000001; // 启动
模式三:内存到内存带像素格式转换(Memory-to-Memory with PFC)
这个模式是 DMA2D 的核心价值所在。它能在搬运图像的同时,完成像素格式的转换。
你想想看,LVGL 从外部存储(比如 SD 卡、Flash)加载图片时,图片可能是 RGB565 格式的,但你的帧缓冲是 ARGB8888 格式的。如果没有 DMA2D,你得用 CPU 逐像素转换,那效率... 嗯,画面卡顿是必然的。
DMA2D 的 PFC(Pixel Format Converter)模块,可以在一次传输中完成:
- 像素格式转换(RGB565 → ARGB8888、RGB888 → RGB565 等)
- Alpha 通道处理(固定 Alpha、每像素 Alpha)
- 颜色空间转换(YUV ↔ RGB)
- 抖动处理(减少色彩带)
| 源格式 | 目标格式 | 典型场景 |
|---|---|---|
| RGB565 | ARGB8888 | SD 卡图片加载到帧缓冲 |
| RGB888 | RGB565 | 节省帧缓冲空间 |
| ARGB8888 | RGB565 | 低端屏幕显示 |
| L8(8位索引色) | ARGB8888 | 字体/图标渲染 |
// 内存到内存带格式转换配置示例
DMA2D->CR = 0x00010000; // 模式选择:内存到内存带PFC
DMA2D->FGMAR = (uint32_t)src; // 源地址(RGB565图片)
DMA2D->OMAR = (uint32_t)dst; // 目标地址(ARGB8888帧缓冲)
// 配置源格式:RGB565
DMA2D->FGPFCCR = (0x0C << 0); // 像素格式:RGB565
DMA2D->FGOR = 0; // 源行偏移
// 配置目标格式:ARGB8888
DMA2D->OPFCCR = (0x00 << 0); // 像素格式:ARGB8888
DMA2D->OOR = 0; // 目标行偏移
// 设置Alpha值(透明度)
DMA2D->FGPFCCR |= (0xFF << 24); // 固定Alpha = 255(不透明)
DMA2D->NLR = (height << 16) | width;
DMA2D->CR |= 0x00000001; // 启动
CPU 逐像素转换:约 12ms
DMA2D 硬件转换:约 1.2ms
性能提升:10 倍
模式四:内存到内存带颜色填充(Memory-to-Memory with Fill)
这个模式是模式一和模式二的结合体。它可以把一块内存区域的数据,叠加到一个固定的颜色背景上。
举个例子:你要在屏幕上显示一个带透明度的图标。图标本身是 ARGB 格式的,背景是纯色。DMA2D 可以在搬运图标数据的同时,把背景色填充进去,并且处理 Alpha 混合。
说白了,这就是硬件加速的「图像合成」操作。LVGL 的图层混合、窗口透明效果,底层就是靠这个模式实现的。
// 内存到内存带颜色填充配置示例
DMA2D->CR = 0x00020000; // 模式选择:内存到内存带填充
DMA2D->FGMAR = (uint32_t)src; // 源地址(图标数据)
DMA2D->BGMAR = (uint32_t)bg; // 背景地址(纯色背景)
DMA2D->OMAR = (uint32_t)dst; // 目标地址(合成结果)
// 配置前景(图标)
DMA2D->FGPFCCR = (0x00 << 0); // ARGB8888
DMA2D->FGOR = 0;
// 配置背景(纯色)
DMA2D->BGPFCCR = (0x00 << 0); // ARGB8888
DMA2D->BGOR = 0;
// 配置目标
DMA2D->OPFCCR = (0x00 << 0); // ARGB8888
DMA2D->OOR = 0;
DMA2D->NLR = (height << 16) | width;
DMA2D->CR |= 0x00000001; // 启动
四种模式对比总结
| 模式 | 功能 | 典型用途 | 性能等级 |
|---|---|---|---|
| 寄存器→内存 | 纯色填充 | 清屏、背景 | ★★★★★ |
| 内存→内存 | 数据搬运 | 缓冲切换、图像复制 | ★★★★☆ |
| 内存→内存+PFC | 搬运+格式转换 | 图片加载、格式适配 | ★★★★★ |
| 内存→内存+填充 | 搬运+合成 | 图标渲染、图层混合 | ★★★★☆ |
嗯,四种模式讲完了。你可能会觉得有点多,但实际项目中常用的就两种:寄存器到内存(清屏)和内存到内存带 PFC(图片加载)。另外两个模式,等你需要做更复杂的图像处理时自然会用到。