4、显存优化:双缓冲与单缓冲、帧缓冲压缩、局部刷新策略、颜色深度选择

显存优化,说白了就是跟内存要效率。做嵌入式GUI,最头疼的就是内存不够用。我见过太多项目,UI功能都写好了,一跑起来内存直接爆掉。嗯,今天咱们就把显存这块掰开揉碎了讲清楚。

4.1 双缓冲 vs 单缓冲:别盲目跟风

先说说最基础的概念。单缓冲就是一块显存,CPU往里画,LCD控制器往外读。双缓冲就是两块,一块给CPU画,一块给LCD读,画完再交换。

你可能会问:「双缓冲不是更好吗?没有撕裂啊。」

对,但代价是内存翻倍。我做过一个智能手表项目,屏幕分辨率是240×240,RGB565格式。单缓冲只需要240×240×2 = 115KB。双缓冲直接干到230KB。对于只有256KB RAM的MCU来说,这几乎要了命。

核心原则:

  • MCU内存小于256KB → 优先考虑单缓冲 + 局部刷新
  • MCU内存大于512KB → 双缓冲更省心
  • 中间地带 → 看你的刷新频率要求

我个人习惯是:如果屏幕刷新率超过30fps,我才会考虑双缓冲。低于这个值,单缓冲配合垂直同步,效果其实差不多。

避坑指南:我曾经在一个项目里强行上了双缓冲,结果内存不够,不得不把字体库和图标库全部压缩。后来发现,其实单缓冲+局部刷新完全够用。白白浪费了两周时间。

4.2 帧缓冲压缩:能省则省

帧缓冲压缩,说白了就是让显存「缩水」。常用的方法有几种:

4.2.1 RLE压缩

适合大面积纯色背景。比如一个蓝色背景的界面,RLE可以把连续相同的像素压缩成「颜色+长度」。我做过测试,纯色背景的压缩率能达到90%以上。

// RLE压缩示例
typedef struct {
    uint16_t color;  // RGB565颜色
    uint16_t count;  // 连续像素数
} RLE_Pixel;

// 压缩前:100个蓝色像素 = 200字节
// 压缩后:1个RLE_Pixel = 4字节
// 压缩率:98%

4.2.2 调色板压缩

如果界面颜色种类少(比如少于256色),用调色板能省一半内存。RGB565每个像素2字节,调色板模式下每个像素1字节甚至更少。

注意:调色板压缩不适合照片类图片。我见过有人把一张风景图用调色板压缩,结果颜色断层严重,用户直接投诉。

4.2.3 差值压缩

只存储相邻像素的差值。适合渐变背景、图表等连续色调的场景。压缩率一般在30%-50%。

4.3 局部刷新策略:别整屏重绘

这是我最想强调的一点。很多新手一上来就整屏刷新,显存带宽直接拉满。你想想看,一个320×240的屏幕,RGB565格式,整屏刷新一次要传输153.6KB数据。如果只是更新一个按钮,这太浪费了。

局部刷新的核心思路:只更新变化区域。

// 局部刷新示例
void lv_refresh_area(lv_area_t *area) {
    // 只刷新脏区域
    uint16_t width = area->x2 - area->x1 + 1;
    uint16_t height = area->y2 - area->y1 + 1;
    uint32_t bytes = width * height * 2;  // RGB565
    
    // 只传输这部分数据到LCD
    lcd_send_data(area->x1, area->y1, width, height, framebuffer);
}

我做过一个工业仪表项目,界面有十几个仪表盘和数字显示。如果整屏刷新,每秒要传输2MB数据。用了局部刷新后,每秒只传输200KB左右。CPU占用率从80%降到了15%。

经验之谈:局部刷新要配合脏区域合并。比如两个按钮同时更新,别分别刷新两次,合并成一个矩形区域一次刷新。这样能减少LCD的通信开销。

4.4 颜色深度选择:RGB565 vs RGB888

这个问题其实很简单:看你的屏幕和内存。

参数 RGB565 RGB888
每像素字节 2字节 3字节
颜色数量 65536色 1677万色
320×240显存 153.6KB 230.4KB
适合场景 MCU、低内存 MPU、高内存

我个人建议:除非你的屏幕是高清屏(比如800×480以上)且需要显示照片级图片,否则RGB565完全够用。人眼对颜色的敏感度其实没那么高,65536色在大多数嵌入式场景下已经足够了。

我记得有一次,客户非要RGB888,说「颜色更丰富」。结果内存不够,不得不外挂SDRAM,成本增加了30%。后来我拿RGB565和RGB888的对比图给他看,他根本分不出来区别。

警告:RGB565在渐变和抗锯齿场景下会有颜色断层。如果你做的是医疗影像或者专业绘图工具,还是老老实实用RGB888吧。

4.5 综合优化策略

好了,咱们把前面讲的串起来。一个典型的显存优化流程是这样的:

  1. 评估需求:屏幕分辨率、刷新率、颜色要求
  2. 选择颜色深度:默认RGB565,除非有特殊需求
  3. 确定缓冲方案:内存够用就双缓冲,不够就单缓冲+局部刷新
  4. 应用压缩:静态界面用RLE,动态界面用差值压缩
  5. 实现局部刷新:脏区域追踪+合并

我做过一个最极致的优化案例:一个128×64的单色屏,只有1KB显存。我用RLE压缩+局部刷新,硬是在一个8KB RAM的MCU上跑出了流畅的动画效果。说白了,优化就是跟资源较劲,谁更了解硬件,谁就能赢。

最后说一句:显存优化没有银弹。每个项目都要具体分析。我的建议是:先跑起来,再优化。别一开始就想着省内存,结果功能都做不全。