3、帧缓冲区(FrameBuffer)详解:显存映射、颜色深度(RGB565/RGB888)与数据格式

帧缓冲区(FrameBuffer)是 LCD 显示驱动中连接 CPU/GPU 与显示面板的核心内存区域。它本质上是一块连续的物理内存,用于存储一帧完整的图像数据。显示控制器通过 DMA 或专用硬件接口,以固定刷新率(如 60Hz)从该区域逐像素读取数据,并转换为 LCD 面板所需的时序信号。理解帧缓冲区的内存映射、颜色深度与数据排列格式,是优化显示数据搬运效率的前提。

3.1 显存映射:物理地址与虚拟地址

在嵌入式系统中,帧缓冲区通常位于系统的 SDRAM 或专用的 SRAM 区域。CPU 通过内存管理单元(MMU)将物理地址映射到进程的虚拟地址空间,使得应用程序可以像操作普通数组一样读写显存。

关键概念: 帧缓冲区的物理地址必须连续,且起始地址通常需要按 4KB 或更大的页对齐,以满足 DMA 控制器的地址对齐要求。
帧缓冲区内存映射架构 CPU 虚拟地址空间 0xB000 0000 (虚拟地址) 0xB010 0000 (虚拟地址) MMU 映射表 MMU 页表 转换 物理内存 (SDRAM) 0x8000 0000 (物理地址) 0x8010 0000 (物理地址) DMA 直接访问 DMA 控制器 使用物理地址 绕过 MMU 直接搬运
优化提示: 在 Linux 中,可通过 dma_alloc_coherent() 或 ioremap_wc() 分配连续物理内存并建立映射,确保 CPU 与 DMA 对同一物理地址的访问一致性。

3.2 颜色深度:RGB565 与 RGB888

颜色深度决定了每个像素占用的位数,直接影响帧缓冲区的大小与数据传输带宽。常见的 LCD 面板支持 RGB565(16位)或 RGB888(24位/32位)格式。

颜色深度 每像素位数 颜色总数 典型帧缓冲区大小(1024×600) 带宽需求(60Hz)
RGB565 16 bpp 65,536 1024 × 600 × 2 = 1,228,800 字节 ≈ 1.17 MB 1.17 MB × 60 ≈ 70.3 MB/s
RGB888 24 bpp 16,777,216 1024 × 600 × 3 = 1,843,200 字节 ≈ 1.76 MB 1.76 MB × 60 ≈ 105.5 MB/s
ARGB8888 32 bpp 16,777,216 + Alpha 1024 × 600 × 4 = 2,457,600 字节 ≈ 2.34 MB 2.34 MB × 60 ≈ 140.6 MB/s
带宽瓶颈: 对于高分辨率面板(如 1920×1080),RGB888 格式的原始带宽需求高达 1920×1080×3×60 ≈ 373 MB/s。若系统总线或 DMA 通道无法满足,必须考虑压缩、双缓冲或局部刷新策略。

3.3 数据格式:像素排列与字节序

帧缓冲区中的像素数据必须按照 LCD 控制器期望的格式排列。常见的排列方式包括:

  • RGB565 小端模式: 每个像素占用 2 字节,低字节存储 G[2:0] + R[4:0],高字节存储 B[4:0] + G[5:3]。
  • RGB888 连续模式: 每个像素占用 3 字节,按 R、G、B 顺序连续排列。
  • ARGB8888 对齐模式: 每个像素占用 4 字节(Alpha、R、G、B),便于 32 位总线对齐访问。
RGB565 与 RGB888 像素数据格式对比 RGB565 (16-bit): R[4:0] G[5:3] B[4:0] ← 高字节 (Byte 1) G[2:0] ← 低字节 (Byte 0) RGB888 (24-bit): R[7:0] G[7:0] B[7:0] ← 连续 3 字节 字节序注意事项 • RGB565 小端:低地址存低位 • RGB888 大端:R 在最低地址 • ARGB8888:Alpha 在最高字节 • DMA 传输时需保持字节序一致 • 部分 LCD 控制器支持硬件转换

3.4 帧缓冲区与 DMA 传输的配合

DMA 控制器在搬运帧缓冲区数据时,通常以行为单位进行传输。每行像素数据在内存中连续排列,行与行之间可能存在 行间距(stride/pitch),用于对齐或预留边界。

行间距优化: 若 LCD 宽度为 1024 像素,RGB565 格式每行需要 2048 字节。但为了满足 DMA 的 32 字节突发传输对齐,实际分配的行间距可能为 2048 或 2048 + padding。合理设置行间距可避免 DMA 传输时的地址未对齐惩罚。
/* 帧缓冲区分配示例:RGB565 格式,1024x600,行间距 2048 字节 */
#define LCD_WIDTH  1024
#define LCD_HEIGHT 600
#define BPP        2          /* RGB565: 2 bytes per pixel */
#define STRIDE     2048       /* 行间距,对齐到 32 字节 */

uint8_t *framebuffer = (uint8_t *)malloc(STRIDE * LCD_HEIGHT);

/* 像素访问宏 */
#define PIXEL(x, y) (*(uint16_t *)(framebuffer + (y) * STRIDE + (x) * BPP))

/* 设置像素颜色 (RGB565) */
void set_pixel_rgb565(int x, int y, uint16_t color) {
    PIXEL(x, y) = color;
}

/* DMA 传输配置:源地址 = framebuffer,目的地址 = LCD 控制器 FIFO */
dma_config_t dma_cfg = {
    .src_addr      = (uint32_t)framebuffer,
    .dst_addr      = (uint32_t)LCD_FIFO_ADDR,
    .transfer_size = STRIDE * LCD_HEIGHT,  /* 总字节数 */
    .burst_size    = 32,                    /* 32 字节突发 */
    .src_inc       = DMA_ADDR_INC,          /* 源地址递增 */
    .dst_inc       = DMA_ADDR_FIXED,        /* 目的地址固定 */
};

3.5 多缓冲与双缓冲机制

为避免屏幕撕裂(tearing)和闪烁,现代显示驱动通常采用 双缓冲(Double Buffering) 或 三缓冲(Triple Buffering) 机制。CPU 在后缓冲区(Back Buffer)绘制,DMA 从前缓冲区(Front Buffer)读取并发送到 LCD。

缓冲策略 内存占用 撕裂风险 适用场景
单缓冲 1 × FB 高 低刷新率、静态图像
双缓冲(Page Flip) 2 × FB 低(需 VSync 同步) 视频播放、UI 动画
三缓冲 3 × FB 极低 高帧率游戏、实时渲染
双缓冲工作流程(Page Flip) 前缓冲区 (Front) DMA 读取 → LCD 后缓冲区 (Back) CPU 绘制更新 VSync 中断 交换指针 双缓冲优势 • 消除撕裂现象 • 提高画面流畅度 • 代价:2 倍显存占用 • 需 VSync 同步信号 • DMA 地址切换无延迟
工程实践: 在实现双缓冲时,DMA 的源地址寄存器应在 VSync 消隐期(VBlank)内更新,避免在扫描线输出过程中切换缓冲区导致画面撕裂。部分 LCD 控制器支持自动地址切换(Auto Page Flip),可进一步降低 CPU 干预。

3.6 帧缓冲区对齐与缓存一致性

CPU 与 DMA 同时访问帧缓冲区时,必须处理 缓存一致性(Cache Coherency) 问题。CPU 的写操作可能仅更新 Cache,而 DMA 直接从物理内存读取,导致数据不一致。

  • 方案一: 将帧缓冲区内存区域设置为 非缓存(Non-cacheable) 或 写合并(Write-combining),避免 Cache 干扰。
  • 方案二: 在 CPU 写入后手动执行 Cache 刷新(Clean/Flush),在 DMA 读取前执行 Cache 失效(Invalidate)。
  • 方案三: 使用硬件 I/O 一致性(I/O Coherency) 总线,如 ARM 的 ACE/CHI 协议,自动维护一致性。
/* Linux 帧缓冲区缓存一致性处理示例 */
#include <linux/dma-mapping.h>

/* 分配一致性内存(非缓存) */
dma_addr_t phys_addr;
void *virt_addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);

/* 或使用普通内存 + 手动刷新 */
void *virt_addr = kmalloc(size, GFP_KERNEL);
dma_addr_t phys_addr = virt_to_phys(virt_addr);

/* CPU 写入后刷新 Cache */
dma_sync_single_for_device(dev, phys_addr, size, DMA_TO_DEVICE);

/* DMA 读取后使 Cache 失效 */
dma_sync_single_for_cpu(dev, phys_addr, size, DMA_FROM_DEVICE);
性能陷阱: 频繁调用 Cache 刷新函数会引入较大开销。对于高帧率显示(如 60fps),建议优先使用一致性内存分配,或将帧缓冲区放在专用的非缓存内存区域。

3.7 本章小结

帧缓冲区是 LCD 显示驱动的数据中枢,其内存映射方式、颜色深度选择、数据排列格式以及缓存一致性策略,直接决定了显示系统的性能与图像质量。在实际项目中,应根据 LCD 面板规格、系统总线带宽和 CPU 处理能力,综合权衡 RGB565 与 RGB888 的选用,并合理设计双缓冲与 DMA 传输的配合机制。