2、LVGL图片加载基础:lv_img_dsc_t结构体、内置图片与外部图片的区别
好,咱们正式开始聊图片加载。很多新手朋友一上来就急着调缓存、搞DMA,结果连最基础的 lv_img_dsc_t 都没搞明白。我当年也犯过这个错——项目都快交付了,发现图片显示全是花屏,排查了两天才发现是结构体里的 header.always_zero 字段没清零。嗯,这种坑踩一次就够了。
2.1 lv_img_dsc_t:图片描述符的核心
在LVGL里,任何一张图片,不管它是存在内部Flash还是外部存储卡,最终都要通过 lv_img_dsc_t 这个结构体来“描述”自己。说白了,它就是图片的身份证加户口本。
typedef struct {
lv_img_header_t header; // 图片头信息
uint32_t data_size; // 数据总字节数
const uint8_t *data; // 指向像素数据的指针
lv_img_cf_t cf; // 颜色格式(已废弃,保留兼容)
} lv_img_dsc_t;
这里面最关键的三个字段:
- header:包含宽、高、颜色格式、是否启用缓存等标志位。我习惯在初始化时用
lv_img_header_t的默认值先清零,再逐个赋值,避免遗留脏数据。 - data_size:注意,这个值是 像素数据的总字节数,不是文件大小。比如一张 100x100 的 RGB565 图片,data_size = 100 * 100 * 2 = 20000 字节。
- data:指向实际像素数据的指针。内置图片指向Flash地址,外部图片指向RAM中的缓存区或文件系统读取后的内存块。
lv_img_dsc_t 变量时,总是用 = {0} 初始化。曾经遇到过因为结构体未清零,导致 header.always_zero 字段非零,LVGL直接拒绝加载图片。这个字段必须为0,否则内部校验会失败。
2.2 内置图片 vs 外部图片:本质区别
你想想看,为什么LVGL要区分这两种加载方式?其实核心就一句话:数据源的位置不同。
| 对比维度 | 内置图片 | 外部图片 |
|---|---|---|
| 数据存储位置 | MCU内部Flash(代码段) | 外部存储(SD卡、SPI Flash、文件系统) |
| 加载方式 | 编译时链接,直接引用地址 | 运行时从文件系统读取到RAM |
| lv_img_dsc_t.data 指向 | Flash中的常量数组地址 | RAM中的动态分配缓冲区 |
| 内存占用 | 不占用RAM(直接解码显示) | 需要RAM缓存整张图片或分块加载 |
| 修改灵活性 | 需重新编译固件 | 可动态替换文件 |
| 典型场景 | 图标、Logo、固定背景 | 用户照片、地图瓦片、动态素材 |
我记得有一次做智能家居面板,客户要求开机动画能通过SD卡更换。如果全用内置图片,每次换图都得远程OTA升级固件,太折腾了。后来改成外部加载,用户直接把新图片拷进SD卡就行。这就是外部图片的灵活之处。
2.3 内置图片的加载流程
内置图片其实没什么好说的——LVGL在编译时就把图片数据转成C数组,链接到固件里。你调用 lv_img_set_src(img_obj, &my_image) 时,LVGL直接拿着 lv_img_dsc_t 里的 data 指针去Flash里读像素。
流程非常简单:
- 使用LVGL的图片转换工具(如
LVGL Image Converter)将PNG/JPG转成C数组。 - 把生成的
.c文件加入工程编译。 - 在代码中
extern声明该lv_img_dsc_t变量。 - 直接赋值给
lv_img对象的src属性。
2.4 外部图片的加载流程
外部图片就复杂多了。LVGL本身不直接操作文件系统,它通过 lv_fs 驱动层来读写文件。你需要先注册一个文件系统驱动(比如FatFS、LittleFS),然后LVGL才能通过路径字符串打开图片文件。
基本步骤:
- 初始化文件系统驱动(挂载SD卡、初始化SPI Flash等)。
- 调用
lv_fs_drv_register()注册你的文件系统驱动。 - 使用
lv_img_set_src(img_obj, "S:/images/photo.bin")加载外部图片。 - LVGL内部通过
lv_fs_open()读取文件,解析图片头,分配RAM缓冲区,将像素数据读入内存。
2.5 两种加载方式的内存对比
咱们用一张图来直观感受一下区别。下面这个SVG展示了内置和外部图片在内存布局上的差异:
从这张图可以看得很清楚:内置图片的 data 指针直接指向Flash地址,CPU读Flash的速度虽然比RAM慢一点,但胜在完全不占RAM。而外部图片需要先把文件从存储介质读到RAM里,LVGL再从这个RAM缓冲区解码显示。这意味着你的RAM里必须留出至少一张图片大小的空间。
2.6 什么时候该用哪种?
我个人总结了一个简单的选择原则:
- 小于 32KB 的图标、按钮背景 → 内置图片。这点Flash空间还是舍得给的。
- 大于 64KB 的全屏背景、照片 → 外部图片。RAM不够用,而且用户可能需要换图。
- 中间尺寸(32KB~64KB) → 看你的Flash剩余空间。如果Flash还够,内置更省心;如果Flash紧张,就外置。