1. 课程导论:为什么需要外部存储加载图片?

做嵌入式 GUI 开发的朋友,十有八九都会碰到一个尴尬的局面——UI 做漂亮了,但芯片内部 Flash 装不下。尤其是 LVGL 这种现代 GUI 框架,图片资源稍微丰富一点,内部存储就捉襟见肘。

我最早接手的一个智能家居项目,屏幕是 480×320 的 RGB565 屏,UI 设计师一口气给了 30 多张背景图和图标。当时我心想,LVGL 内置的图片加载方式挺成熟的,直接转成 C 数组烧进去不就完了?结果编译完一看,固件体积直接飙到 4MB 多,而那颗 STM32F429 的内部 Flash 只有 2MB。嗯,这条路走不通。

LVGL 内置图片加载方式的局限性

LVGL 官方提供了几种图片加载方式,咱们先捋一捋,看看它们各自的问题在哪。

加载方式 存储位置 典型问题
C 数组(const uint8_t) 内部 Flash 占用代码段空间,大图直接撑爆 Flash
文件系统(lv_fs) 外部存储 需要实现驱动接口,官方示例偏简单
PNG/JPEG 解码 RAM + 解码库 解码慢,内存占用高,小 RAM 设备扛不住
二进制字体/图片 外部 Flash 需要专用烧录工具,更新麻烦

说白了,LVGL 内置的图片加载机制,默认是假设你的图片资源都放在内部存储里的。但实际项目里,内部 Flash 就那么点空间,还要放代码、放字库、放配置参数,哪还有位置给图片?

核心矛盾:LVGL 的图片加载 API 设计得很优雅,但它不负责帮你解决「图片存在哪」的问题。存储介质的选择和驱动封装,得开发者自己搞定。

内部 Flash 的「容量焦虑」

我给大家算笔账。一张 480×272 的 RGB565 全屏背景图,体积是:

480 × 272 × 2 = 261,120 字节 ≈ 255 KB

一张 100×100 的 ARGB8888 图标,体积是:

100 × 100 × 4 = 40,000 字节 ≈ 39 KB

假设你的 UI 有 5 张全屏背景 + 20 个图标,光图片就得吃掉:

5 × 255 KB + 20 × 39 KB = 1,275 KB + 780 KB = 2,055 KB ≈ 2 MB

一颗主流 MCU 的内部 Flash 也就 512KB 到 2MB 之间。你想想看,代码、RTOS 内核、LVGL 库本身、字库、配置文件……这些加起来,留给图片的空间还剩多少?

我曾经踩过的坑:有个项目为了省 Flash,把所有图片都压缩成 JPEG,然后在 LVGL 里实时解码。结果界面切换时卡得不行,解码一张 320×240 的 JPEG 要 200ms 以上,用户体验直接崩了。后来换成外部 SPI Flash 存储 RGB565 裸图,加载速度提升了 10 倍。

外部存储的优势在哪里?

把图片放到外部存储,说白了就是「用空间换时间,用接口换容量」。常见的方案有这几种:

  • SPI Flash / QSPI Flash:容量 8MB~128MB,接口简单,适合存 RGB565 裸图或压缩图
  • SD 卡 / eMMC:容量 GB 级,适合存大量 JPEG/PNG 图片,但需要文件系统
  • NAND Flash:容量大,但需要坏块管理和 ECC,驱动复杂度高
  • 外部 PSRAM:容量 8MB~64MB,适合做解码缓冲区或帧缓冲

我个人习惯把图片按使用频率分层:高频图标放内部 Flash,低频背景图放外部 SPI Flash,用户自定义图片放 SD 卡。这样既保证了响应速度,又解决了容量问题。

LVGL 外部存储加载的核心挑战

把图片放到外部存储,听起来简单,但实际落地时你会遇到几个绕不开的问题:

  1. 驱动接口不统一——SPI Flash、SD 卡、NAND Flash,每种介质的读写时序都不一样
  2. LVGL 的 fs 驱动需要自己实现——官方只给了模板,真正的读写、寻址、缓存策略都得自己写
  3. 图片格式转换麻烦——外部存储里存的是 RGB565 裸图还是 PNG?解码器要不要集成?
  4. 加载速度与内存的平衡——一次性加载整张大图会爆 RAM,分块加载又需要设计缓存策略

我的建议:不要一上来就想着「万能驱动」。先搞清楚你的项目用哪种外部存储,再针对性地封装接口。本课程后面会手把手带你实现一套可复用的驱动框架,从底层 SPI 读写到上层 LVGL 图片显示,一条龙讲清楚。

本章知识体系

为了让大家对后续内容有个整体认知,我画了一张结构图,把外部存储加载图片的核心链路梳理了一下:

外部存储加载图片核心链路 外部存储加载图片核心链路 图片资源 存储介质(SPI Flash / SD卡 / NAND) 驱动接口封装(读/写/寻址/擦除) LVGL fs 驱动(lv_fs_drv_t) LVGL 图片显示 常见问题 • 内部 Flash 容量不足 • 解码速度慢 • 驱动接口不统一 本课程目标 • 封装通用驱动接口 • 实现 LVGL fs 对接 • 可复用的代码框架

从这张图可以看得很清楚:图片资源经过存储介质、驱动接口封装、LVGL fs 驱动三层转换,最终才能显示在屏幕上。每一层都有它的技术细节和坑点,这也是本课程要逐一攻破的内容。

小结

这一章我们聊了为什么需要外部存储加载图片,以及 LVGL 内置方式的局限性。说白了,内部 Flash 容量是硬伤,外部存储是绕不开的选择。但外部存储不是插上就能用,驱动接口的封装和复用才是真正的技术活。

接下来的章节,我会从最底层的 SPI Flash 驱动开始,一步步带你搭建一套完整的、可复用的外部存储图片加载方案。你在项目中遇到的那些「图片加载慢」「Flash 不够用」「驱动写不好」的问题,咱们一个一个解决。