2. 嵌入式GUI基础
做嵌入式GUI,说白了就是在跟一块内存打交道。这块内存,就是显存(VRAM)。而帧缓冲(Framebuffer),则是我们操作这块内存的“视图”。
我记得刚入行那会儿,总以为GUI就是画点、画线、画圆。后来被一个闪烁的界面折磨了三天,才明白——显存和帧缓冲才是GUI的根基。不懂它们,你连画面为什么闪都搞不清楚。
显存(VRAM)是什么?
显存,全称Video Random Access Memory。你可以把它想象成一块专用的画板。
CPU把要显示的数据写进去,显示控制器(Display Controller)定时从里面读出来,转换成屏幕上的像素点。这个过程是硬件自动完成的,不需要CPU干预。
核心要点:显存是CPU和显示设备之间的“共享缓冲区”。CPU写,显示控制器读,两者并行工作。
我做过一个项目,MCU只有64KB RAM,但屏幕分辨率是320×240,颜色深度16位。算一下:320×240×2 = 153,600字节,约150KB。64KB根本装不下!
怎么办?要么换MCU,要么用外部SRAM或SDRAM。这就是显存带来的第一个现实问题——容量。
帧缓冲(Framebuffer)的概念
帧缓冲,是显存在软件层面的抽象。它就是一个内存区域,里面按顺序存放着每个像素的颜色值。
举个例子:一个320×240、16位色的屏幕,帧缓冲就是一块320×240×2 = 153,600字节的连续内存。
像素的排列方式通常是从左到右、从上到下。第0个字节对应屏幕左上角,第319个字节对应第一行最右边,以此类推。
我的习惯:在项目初期,我会先画一张“帧缓冲布局图”。把每个像素的偏移地址标出来,这样写驱动时心里有底,不会搞错坐标映射。
帧缓冲的核心作用就两个:
- CPU写入:应用程序修改帧缓冲中的数据
- 硬件读取:显示控制器按固定频率扫描帧缓冲,刷新屏幕
这两个操作是异步的。如果CPU在写入时,显示控制器正好在读同一块区域,就会出现“撕裂”——上半屏是旧画面,下半屏是新画面。
避坑指南:我曾经在一个工控项目里忽略了撕裂问题。结果客户反馈屏幕偶尔闪一下,查了三天才发现是帧缓冲读写冲突。后来加了双缓冲才解决。
像素格式与颜色深度
像素格式,决定了每个像素用多少位来表示。颜色深度,就是这些位的数量。
常见的颜色深度有:
| 颜色深度 | 每像素位数 | 颜色数 | 典型格式 | 适用场景 |
|---|---|---|---|---|
| 1位 | 1 | 2 | 黑白 | 电子纸、低端LCD |
| 8位 | 8 | 256 | 索引色(调色板) | 低端彩屏、资源受限系统 |
| 16位 | 16 | 65536 | RGB565 / RGB555 | 嵌入式GUI最常用 |
| 24位 | 24 | 1677万 | RGB888 | 高端显示、HMI |
| 32位 | 32 | 1677万+Alpha | ARGB8888 | 带透明效果的GUI |
嵌入式里最常用的是RGB565。为什么?
- 16位,每个像素占2字节,内存占用适中
- 颜色数65536,人眼基本看不出色阶
- 硬件支持广泛,大部分LCD控制器原生支持
RGB565的位布局:
位: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| R4 R3 R2 R1 R0 | G5 G4 G3 G2 G1 G0 | B4 B3 B2 B1 B0 |
红色5位 绿色6位 蓝色5位
你想想看,红色和蓝色各5位,绿色6位。为什么绿色多1位?因为人眼对绿色最敏感。这是硬件设计者的小心思。
实际经验:我在一个项目里用过RGB888(24位),效果确实好,但帧缓冲大小是RGB565的1.5倍。MCU的RAM吃紧,DMA传输时间也变长。后来权衡了一下,还是换回了RGB565。除非你的应用需要显示照片级图片,否则RGB565足够了。
帧缓冲与像素格式的关系
帧缓冲的大小,由分辨率 × 每像素字节数决定。
举个例子:
- 分辨率:320×240
- 像素格式:RGB565(2字节/像素)
- 帧缓冲大小:320 × 240 × 2 = 153,600字节
如果换成RGB888(3字节/像素):
- 帧缓冲大小:320 × 240 × 3 = 230,400字节
差了76.8KB。在嵌入式系统里,这可能是半个MCU的RAM总量。
我的建议:选像素格式时,先算帧缓冲大小,再对比MCU的RAM容量。留出至少20%的余量给堆栈和变量。否则后期优化会非常痛苦。
显存与帧缓冲的典型架构
下面这张图,是我做项目时经常参考的结构。它把显存、帧缓冲、显示控制器之间的关系画清楚了。
从这张图可以看得很清楚:
- CPU 负责往帧缓冲里写数据
- 显示控制器 负责从帧缓冲里读数据
- 屏幕 只是被动接收显示控制器的信号
帧缓冲是CPU和显示控制器之间的桥梁。这座桥的设计好坏,直接决定了GUI的流畅度和稳定性。
实际项目中的选择策略
我总结了一套选型思路,分享给你:
- 先定分辨率:根据屏幕硬件确定
- 再算RAM:分辨率 × 每像素字节数 = 帧缓冲大小
- 对比MCU:帧缓冲大小不能超过MCU可用RAM的70%
- 选像素格式:在RAM允许范围内,选颜色深度最高的
- 考虑双缓冲:如果需要防撕裂,帧缓冲大小要翻倍
我曾经踩过的坑:在一个项目里,我选了RGB565,但没注意MCU的DMA传输宽度。DMA只支持32位传输,而RGB565是16位。结果每次传输都要做位对齐,效率直接砍半。后来我换成了RGB888(24位),用32位DMA传输,虽然帧缓冲大了,但速度反而更快。
嗯,这里要注意:硬件特性会影响软件选择。选像素格式时,不光要看RAM,还要看DMA、总线宽度、显示控制器的支持情况。
显存和帧缓冲,是嵌入式GUI的基石。搞懂了它们,你就能理解为什么有些界面会闪、为什么有些颜色不对、为什么内存总是不够用。下一层,我们就要开始动手操作帧缓冲了。