LVGL从SD卡读取图片并显示到MCU LCD屏详细笔记教程

1、前言

        在上一篇LVGL专题文章中已经讲解了如何将LVGL与FatFs链接起来,实现LVGL对SD卡中的数据进行读写操作。本文在上一文的基础上,将实现LVGL读取文件系统中的图片文件(BMP、PNG、JPG),并显示到MCU设备中的LCD屏中。

        LVGL配置FatFs文件系统,实现对STM32的SD卡数据读写-CSDN博客

        不得不吐槽一下,在MCU这类资源受限的设备上,需要刷一张图片,可谓是一波三折,属实头大。在试错,踩坑好几回后,总计归纳了这篇文章所呈现的笔记内容。如果本文讲解的方法有不合适的地方,欢迎在评论区或者私信交流探讨。

2、LVGL图片类型

(1)、解码器

        在LVGL中,支持如下4种格式的图片类型,分别是BMPPNGJPGGIF

        打开LVGL官网的文档,根据使用的LVGL选择了对应的文档版本后,点击3rd party libraries。可以看到官方对于使用这些第三方库资源内容的说明。

        BMP decoder — LVGL documentation

        除了使用本地图片解码器,LVGL官网也提供了在线图像转换器:Image Converter — LVGL

(2)、BMP

        BMP(Bitmap Image File)是一种图像文件格式,广泛应用于Windows操作系统中,用于存储数字图像。BMP图片是没有经过压缩的RGB图像数据。在使用的时候,不需要特定的解码器,直接读取文件的内容,简单处理后,就能进行显示。但因为其没有压缩,所以占用的内存空间较大。接下来所说的LVGL BMP解码器是指读取BMP文件处理像素的操作。

        而较多的MCU设备LCD显示屏仅支持16位RGB565格式的BMP图像显示。因此,对于目标设备LCD仅支持RGB565时,需要将24位BMP位图进行格式转换后,才能正常显示到LCD中。于是在这一篇文章中,将BMP的原理进行了深度的解析,同时也提供了位图格式转换的参考代码,要是有这个问题的困扰,可以参考一下。

        BMP位图原理深度解析及编程实现RGB565图片格式转换-CSDN博客

        在LVGL的BMP解码器中,像素是按需读取的(不是整个图像被加载),因此使用 BMP 图像需要很少的 RAM。

        在 lv_conf.h 中通过 LV_USE_BMP 可启用BMP解码器,并将 BMP 文件直接用作图像源。

(3)、PNG

        PNG(Portable Network Graphics)是一种广泛使用的图像文件格式,它采用无损压缩算法,旨在提供高质量的图像显示,同时保持文件大小相对较小。

        PNG图像解码时,其解码期间的RAM为: 图像宽度 x 图像高度 x 4字节

        在 lv_conf.h 中通过 LV_USE_PNG 可启用PNG解码器,并将 PNG 文件直接用作图像源。

(4)、JPG

        JPG文件,全称为JPEG(Joint Photographic Experts Group),是一种广泛使用的图像文件格式,它基于JPEG标准,该标准定义了一种有损压缩算法,用于减少图像文件的大小,同时尽可能保持图像质量。

        对于JPG图片的处理,LVGL提供的解码JPG库的实际上是SJPG,这是基于“普通”JPG 的自定义格式,专门用于LVGL。

        在LVGL中,解码普通 JPG 会消耗整个未压缩图像大小的 RAM,因此LVGL官方仅推荐用于具有更多 RAM 的设备。并且LVGL提供的JPG解码器,仅解码 JPG 和 SJPG 图像所需的部分,因此在使用过程中,无法对图片进行缩放或旋转。

        默认未修改的情况下,sjpg 图像占用的缓存空间为:图像宽度 * 2 * 16 字节

        在 lv_conf.h 中通过 LV_USE_SJPG 可启用JPG解码器,并将 JPG 文件直接用作图像源。

(5)、GIF

        GIF文件,全称Graphics Interchange Format,是一种无损压缩的图像文件格式,主要用于创建简单且颜色较少的图像,尤其是具有动画效果的图像。

        要解码和显示 GIF 动画,需要以下 RAM 量:

LV_COLOR_DEPTH 8: 3 x 图像宽度 x 图像高度

LV_COLOR_DEPTH 16:4 x 图像宽度 x 图像高度

LV_COLOR_DEPTH 32:5 x 图像宽度 x 图像高度

        在 lv_conf.h 中通过 LV_USE_GIF 中启用GIF解码器,并且可将GIF文件直接作为图像源。

3、LVGL显示图片

(1)、图片显示方案

        在LVGL中,将图片显示到LCD设备中,主要如下3种方法:

1、将图片转码成源代码,编译时,一起编译到程序代码中

2、将图片转码成二进制bin文件,将图片拷贝到SD卡中,程序运行时进行读取显示

3、将图片直接拷贝到SD卡中,通过LVGL的解码库解析显示图片

        在本文中,讲解的是,如何配置MCU工程及LVGL,实现利用LVGL的解码库,将各类图片解析出来,显示到LCD屏幕中。

        提醒:在显示BMP图片时,如果是直接将BMP原图片拷贝到SD卡中去显示时。需要对图片进行转码操作,否则BMP图片颜色深度位数不一致会无法显示

[Warn]    (0.484, +484)     decoder_open: LV_COLOR_DEPTH == 16 but bpp is 24 (should be 16)     (in lv_bmp.c line #160)
[Warn]    (0.495, +11)     _lv_img_cache_open: Image draw cannot open the image resource     (in lv_img_cache.c line #125)
[Warn]    (0.506, +11)     lv_draw_img: Image draw error     (in lv_draw_img.c line #84)
[Warn]    (0.515, +9)     decoder_open: LV_COLOR_DEPTH == 16 but bpp is 24 (should be 16)     (in lv_bmp.c line #160)
[Warn]    (0.525, +10)     _lv_img_cache_open: Image draw cannot open the image resource     (in lv_img_cache.c line #125)
[Warn]    (0.536, +11)     lv_draw_img: Image draw error     (in lv_draw_img.c line #84)

        BMP位图原理深度解析及编程实现RGB565图片格式转换-CSDN博客

(2)、MCU工程配置

①、修改内存大小

        根据自己所用开发的芯片,找到其启动文件,修改其堆、栈空间。如果堆栈空间不足,运行LVGL程序时,会出现各种莫名其妙的问题,因此,在能芯片容量范围内的情况下,能多大就多大。

        打开lv_conf.h文件,需要为LVGL分配多一些的内存空间,找到LV_MEM_SIZE这个宏,将其值设置的大一些。

        如下所示为在程序代码调试过程中,堆栈空间值过小,打开图片失败时,产生的错误提示,将启动文件的堆栈空间和LV_MEM_SIZE设置到合适值时,如下所示的错误消失,图片正常显示。

[Warn]    (0.693, +693)     _lv_img_cache_open: Image draw cannot open the image resource     (in lv_img_cache.c line #125)
[Warn]    (0.704, +11)     lv_draw_img: Image draw error     (in lv_draw_img.c line #84)
[Warn]    (0.715, +11)     _lv_img_cache_open: Image draw cannot open the image resource     (in lv_img_cache.c line #125)
[Warn]    (0.726, +11)     lv_draw_img: Image draw error     (in lv_draw_img.c line #84)
[Warn]    (0.737, +11)     _lv_img_cache_open: Image draw cannot open the image resource     (in lv_img_cache.c line #125)
[Warn]    (0.748, +11)     lv_draw_img: Image draw error     (in lv_draw_img.c line #84)

        如果MCU设备的内存空间告急严重,可以尝试使用微库,这也能节省一定的内存空间。如下所示是开启微库前后,编译出来的程序内存变化情况。

②、修改LVGL配置文件

        在LVGL中,如果需要读取SD卡中的原始图片文件,并显示到LCD中,需要开启宏定义配置。

        打开lv_conf.h文件,找到文件系统部分的配置,检查LV_USE_FS_FATFS是否开启,如果还未配置LVGL的文件系统,请查看本文开始部分的内容,进行文件系统的适配。

        找到如下图所示的LV_USE_PNG、LV_USE_BMP、LV_USE_SJPG、LV_USE_GIF,根据实际的使用需要,对这些宏定义开关进行配置。

(3)、参考程序源码

#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"
#include "lv_conf.h"

#include "diskio.h"
#include "ff.h"
#include "ffconf.h"

void FatFs_Init(void)
{
    FATFS fs;                        
    FRESULT res_sd;
    
    while(SD_Init())
    {
        LED_RED_ON;
        delay_ms(500);
        LED_RED_OFF;
        delay_ms(500);
    }
    

    res_sd = f_mount(&fs,"0:", 1);
    if (res_sd!=FR_OK)
    {
        printf("SD Mount FatFs Failed! %d\r\n",res_sd);
        while (1);
    } else {
        printf("SD Mount FatFs Success!\r\n");
    }
}

void LVGL_Show_Images(void)
{
    FatFs_Init();//初始化文件系统,并且将SD卡挂载
    
    lv_init(); 
    lv_port_disp_init();
    lv_port_indev_init();
            
    lv_obj_t *label_1 = lv_label_create(lv_scr_act());
    lv_obj_t *label_2 = lv_label_create(lv_scr_act());
    lv_obj_t *label_3 = lv_label_create(lv_scr_act());
    const lv_font_t *font_txt;
    font_txt = &lv_font_montserrat_14;
    
    lv_obj_set_style_text_font(label_1, font_txt, LV_PART_MAIN);
    lv_obj_set_style_text_font(label_2, font_txt, LV_PART_MAIN);
    lv_obj_set_style_text_font(label_3, font_txt, LV_PART_MAIN);
    
    lv_label_set_text(label_1, "PNG");
    lv_label_set_text(label_2, "BMP");
    lv_label_set_text(label_3, "JPG");
        
    lv_obj_t *img1 = lv_img_create(lv_scr_act());
    lv_img_set_src(img1, "0:/images/1.png");//jpg 11.2K以内,png 11.2K以内
    lv_obj_center(img1);
    lv_obj_align_to(label_1 ,img1, LV_ALIGN_OUT_LEFT_MID, -10, 0);
        
    
    lv_obj_t *img2 = lv_img_create(lv_scr_act());
    lv_img_set_src(img2, "0:/images/2.bmp");
    lv_obj_align_to(img2, img1, LV_ALIGN_OUT_TOP_MID, 0, -40);
    lv_obj_align_to(label_2 ,img2, LV_ALIGN_OUT_LEFT_MID, -10, 0);
    
    lv_obj_t *img3 = lv_img_create(lv_scr_act());
    lv_img_set_src(img3, "0:/images/3.jpg");
    lv_obj_align_to(img3, img1, LV_ALIGN_OUT_BOTTOM_MID, 0, 40);
    lv_obj_align_to(label_3 ,img3, LV_ALIGN_OUT_LEFT_MID, -10, 0);
}

(4)、LCD显示效果

以下是一个用于读取SD卡图片显示LCD上的代码示例: ``` #include <SD.h> #include <Adafruit_ILI9341.h> #include <Adafruit_GFX.h> #define TFT_CS 10 #define TFT_RST 9 #define TFT_DC 8 Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST); void setup() { // 初始化SD卡 if (!SD.begin(4)) { tft.println("SD卡初始化失败"); return; } // 初始化LCD tft.begin(); tft.fillScreen(ILI9341_BLACK); tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); tft.setTextSize(2); tft.setCursor(10, 10); tft.println("正在读取图片..."); // 打开图片文件 File imageFile = SD.open("image.bmp"); if (!imageFile) { tft.println("打开图片失败"); return; } // 读取图片文件头信息 uint32_t imageSize = imageFile.size(); uint32_t imageOffset = (uint32_t)imageFile.position(); uint32_t imageStart = imageOffset + 54; // 假设图片文件头为54字节 // 将文件指针移到图片数据开始处 imageFile.seek(imageStart); // 读取图片数据并在LCD显示 uint16_t imageColor; for (uint32_t i = imageStart; i < imageSize; i += 2) { uint8_t colorBuf[2] = {0}; imageFile.read(colorBuf, 2); imageColor = makeColor(colorBuf[0], colorBuf[1]); // 自定义函数用于将两个字节转换为16位颜色值 tft.drawPixel(x, y, imageColor); // 更新坐标 x++; if (x >= tft.width()) { x = 0; y++; } } // 关闭文件 imageFile.close(); tft.println("图片读取完成"); } void loop() { // 程序循环主体 } uint16_t makeColor(uint8_t r, uint8_t g, uint8_t b) { return tft.color565(r, g, b); } ``` 以上代码使用了Arduino的SD库和Adafruit公司的ILI9341库来实现对SD卡图片的读取以及在LCD上的显示。首先,代码需要初始化SD卡LCD。然后,它会打开指定的图片文件,并读取文件头信息以确定图片数据的起始位置。接下来,代码通过读取图片数据并使用`drawPixel`函数将每个像素点显示LCD上。最后,文件被关闭并在LCD显示读取完成的提示信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值