搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《DNESP32S3使用指南-IDF版_V1.6》第四十章 图片显示实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1
发表于 2024-7-30 18:23:54 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 正点原子 于 2024-7-30 18:23 编辑


1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第四十章 图片显示实验


       在开发产品的时候,很多时候,我们都会用到图片解码,在本章中,我们将向大家介绍如何通过ESP32-S3来解码BMP/JPG/JPEG/PNG/GIF等图片,并在SPILCD上显示出来。
       本章分为如下几个小节:
       40.1 图片格式简介
       40.2 硬件设计
       40.3 程序设计
       40.4 下载验证

       40.1 图片格式介绍
       我们常用的图片格式有很多,一般最常用的有三种:JPEG(或JPG)、BMP、PNG和GIF。其中JPEG(或JPG)、PNG和BMP是静态图片,而GIF则是可以实现动态图片。下面,我们简单介绍一下这三种图片格式。

       40.1.1 BMP编码简介
       我们常用的图片格式有很多,一般最常用的有三种:JPEG(或JPG)、BMP和GIF。其中JPEG(或JPG)和BMP是静态图片,而GIF则是可以实现动态图片。下面,我们简单介绍一下这三种图片格式。
       首先,我们来看看BMP图片格式。BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,文件后缀名为“.bmp”,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大,但是没有失真。BMP文件的图像深度可选lbit、4bit、8bit、16bit、24bit及32bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。
       典型的BMP图像文件由四部分组成:

       ①:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;

       ②:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息

       ③:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

       ④:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

       关于BMP的详细介绍,请参考光盘的《BMP图片文件详解.pdf》。

       40.1.2 JPEG编码简介
       JPEG是Joint Photographic Experts Group(联合图像专家组)的缩写,文件后辍名为“.jpg”或“.jpeg”,是最常用的图像文件格式,由一个软件开发联合会组织制定,同BMP格式不同,JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤(BMP不会,但是BMP占用空间大)。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量明显降低,如果追求高品质图像,不宜采用过高压缩比例。但是JPEG压缩技术十分先进,它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。而且JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,压缩比率通常在10:1到40:1之间,压缩比越大,品质就越低;相反地,压缩比越小,品质就越好。比如可以把1.37Mb的BMP位图文件压缩至20.3KB。当然也可以在图像质量和文件尺寸之间找到平衡点。JPEG格式压缩的主要是高频信息,对色彩的信息保留较好,适合应用于互联网,可减少图像的传输时间,可以支持24bit真彩色,也普遍应用于需要连续色调的图像。
       JPEG/JPG的解码过程可以简单的概述为如下几个部分:

       ①:从文件头读出文件的相关信息。
       JPEG文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本、长宽、采样因子、量化表、哈夫曼表等重要信息。所以解码前必须将文件头信息读出,以备图像数据解码过程之用。

       ②:从图像数据流读取一个最小编码单元(MCU),并提取出里边的各个颜色分量单元。

       ③:将颜色分量单元从数据流恢复成矩阵数据。
       使用文件头给出的哈夫曼表,对分割出来的颜色分量单元进行解码,把其恢复成8×8的数据矩阵。

       ④:8×8的数据矩阵进一步解码。
       此部分解码工作以8×8的数据矩阵为单位,其中包括相邻矩阵的直流系数差分解码、使用文件头给出的量化表反量化数据、反Zig-zag编码、隔行正负纠正、反向离散余弦变换等5个步骤,最终输出仍然是一个8×8的数据矩阵。

       ⑤:颜色系统YCrCb向RGB转换。
       将一个MCU的各个颜色分量单元解码结果整合起来,将图像颜色系统从YCrCb向RGB转换。

       ⑥:排列整合各个MCU的解码数据。
       不断读取数据流中的MCU并对其解码,直至读完所有MCU为止,将各MCU解码后的数据正确排列成完整的图像。JPEG的解码本身是比较复杂的,这里FATFS的作者,提供了一个轻量级的JPG/JPEG解码库:TjpgDec,最少仅需3KB的RAM和3.5KB的FLASH即可实现JPG/JPEG解码,本例程采用TjpgDec作为JPG/JPEG的解码库,关于TjpgDec的详细使用,请参考“A盘4,软件资料图片编解码TjpgDec技术手册”这个文档。

       40.1.3 PNG编码简介
       PNG(Portable Network Graphics)是一种无损的位图图像格式,旨在替代GIF格式并增加一些GIF文件格式所不具备的特性。PNG图像使用一种称为DEFLATE的无损数据压缩算法来减小文件大小,不会损失图像质量。这种压缩算法结合了LZ77算法和哈夫曼编码,能够有效地压缩数据并减小文件大小。
       在PNG编码过程中,预滤器编码格式被用来先对图像数据进行预处理,以便更好地利用Deflate算法进行压缩。PNG定义了五种不同的预滤器,分别是None、Sub、Up、Average和Paeth。这些预滤器根据图像像素的特性对每一行的像素进行编码,以更好地利用Deflate算法进行压缩。
       PNG还支持多种颜色模式,包括8位灰度图像、索引彩色图像和24位真彩色图像,并且可以支持Alpha通道透明度,这意味着可以在图像中创建半透明的效果。此外,PNG还支持多层图像,可以将多个图像组合在一起,每个图像可以具有自己的透明度和颜色。
       典型的PNG图像文件由以下几部分组成:

       ①:文件署名域(File Signature):这是文件的开头部分,由8个字节组成,用于标识该文件是一个PNG文件。其值固定为"89 50 4E 47 0D 0A 1A 0A"。

       ②:关键数据块(Critical Chunk):这是PNG文件必须包含的数据块,包括IHDR、IDAT、IEND等。IHDR块包含了图像的基本信息,如宽度、高度、像素格式等;IDAT块包含了图像的实际数据;IEND块标记了图像数据的结束。

       ③:辅助数据块(Ancillary Chunk):这是可选的数据块,用于存储与图像相关的其他信息,如文本注释、时间戳等。这些数据块对于解码图像是可选的,但如果存在,解码器应当对其进行解析。

       40.1.4 GIF编码简介
       GIF(Graphics Interchange Format)是CompuServe公司开发的图像文件存储格式,1987年开发的GIF文件格式版本号是GIF87a,1989年进行了扩充,扩充后的版本号定义为GIF89a。GIF图像文件以数据块(block)为单位来存储图像的相关信息。一个GIF文件由表示图形/图像的数据块、数据子块以及显示图形/图像的控制信息块组成,称为GIF数据流(DataStream)。数据流中的所有控制信息块和数据块都必须在文件头(Header)和文件结束块(Trailer)之间。
       GIF文件格式采用了LZW(Lempel-ZivWalch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明(transparency)属性。此外,GIF文件格式可在一个文件中存放多幅彩色图形/图像。如果在GIF文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
       一个GIF文件的结构可分为文件头(FileHeader)、GIF数据流(GIFDataStream)和文件终结器(Trailer)三个部分。文件头包含GIF文件署名(Signature)和版本号(Version);GIF数据流由控制标识符、图象块(ImageBlock)和其他的一些扩展块组成;文件终结器只有一个值为0x3B的字符(';')表示文件结束。
       关于GIF的详细介绍,请参考光盘GIF解码相关资料。图片格式简介,我们就介绍到这里。

       40.2 硬件设计

       40.2.1 例程功能
       开机的时候先检测字库,然后检测SD卡是否存在,如果SD卡存在,则开始查找SD卡根目录下的PICTURE文件夹,如果找到则显示该文件夹下面的图片文件(支持bmp、jpg、jpeg、png和gif格式),循环显示,通过按KEY0和KEY2可以快速浏览下一张和上一张,KEY_UP按键用于暂停/继续播放,DS1用于指示当前是否处于暂停状态。如果未找到PICTURE文件夹/任何图片文件,则提示错误。同样我们也是用DS0来指示程序正在运行。

       40.2.2 硬件资源

       1.LED
              LED-IO1

       2.XL9555
              IIC_SDA-IO41
              IIC_SCL-IO42

       3.SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       4.SD
              CS-IO2
              SCK-IO12
              MOSI-IO11
              MISO-IO13

       40.3 程序设计

       40.3.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图40.3.1.1 图片显示实验程序流程图

       40.3.2 图片显示函数解析
       正点原子提供的PICTURE驱动源码包括以下文件,并且已经针对正点原子ESP32-S3软硬件进行了移植适配,用户在使用时,仅需将这以下文件添加到自己的工程中即可,如下图所示:

图40.3.2.1 正点原子PICTURE驱动源码文件

       其中:
       bmp.c和bmp.h用于实现对bmp文件的解码;
       tjpgd.c和tjpgd.h用于实现对jpeg/jpg文件的解码;
       gif.c和gif.h用于实现对gif文件的解码;
       这几个代码太长了,而且也有规定的标准,需要结合各个图片编码的格式来编写,所以我们在这里不贴出来,大家查看光盘中的源码的实现过程即可。

       40.3.3 图片显示函数驱动解析
       在IDF版的29_pitures例程中,作者在29_pitures \components\BSP路径下并未添加新的内容,而是在29_pitures \main\APP路径下面,新增了一个APP文件,我们将详细解析这四个文件的实现内容。

       1,解码库的控制句柄_pic_phy和_pic_info
       我们使用这个接口,把解码后的图形数据与LCD的实际操作对应起来。为了方便去显示图片,我们需要将图片的信息与我们的LCD联系上。这里我们定义了_pic_phy和_pic_info分别用于定义图片解码库的LCD操作和存放解码后的图片尺寸颜色信息。它们的定义如下:
  1. /* 图片显示物理层接口 */
  2. /* 在移植的时候,必须由用户自己实现这几个函数 */
  3. typedef struct
  4. {
  5.     /* 画点函数 */
  6.     void(*draw_point)(uint16_t, uint16_t, uint16_t);
  7.    
  8.     /* 单色填充函数 */
  9.     void(*fill)(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t);
  10.    
  11.     /* 画水平线函数 */
  12.     void(*draw_hline)(uint16_t, uint16_t, uint16_t, uint16_t);
  13.    
  14.     /*多点填充 */
  15.     void(*multicolor)(uint16_t, uint16_t, uint16_t, uint16_t *);
  16. } _pic_phy;

  17. /* 图像信息 */
  18. typedef struct
  19. {
  20.     uint16_t lcdwidth;                  /* LCD的宽度 */
  21.     uint16_t lcdheight;                   /* LCD的高度 */
  22. } _pic_info;
复制代码
       在piclib.c文件中,我们用上述类型定义了两个结构体,声明如下:
  1. _pic_info picinfo;      /* 图片信息 */
  2. _pic_phy pic_phy;       /* 图片显示物理接口 */
复制代码

       2,piclib_init函数
       piclib_init函数,该函数用于初始化图片解码的相关信息,用于定义解码后的LCD操作。 具体定义如下:
  1. /**
  2. * @brief       画图初始化
  3. *   @note      在画图之前,必须先调用此函数, 指定相关函数
  4. * @param       无
  5. * @retval      无
  6. */
  7. void piclib_init(void)
  8. {
  9.     pic_phy.draw_point = lcd_draw_pixel;    /* 画点函数实现,仅GIF需要 */
  10.     pic_phy.fill = lcd_fill;                /* 填充函数实现,仅GIF需要 */
  11.     pic_phy.draw_hline = lcd_draw_hline;    /* 画线函数实现,仅GIF需要 */
  12.     pic_phy.multicolor = piclib_multi_color;/* 颜色填充函数实现,JPEG、BMP、PNG */

  13.     picinfo.lcdwidth = lcd_self.width;      /* 得到LCD的宽度像素 */
  14.     picinfo.lcdheight = lcd_self.height;    /* 得到LCD的高度像素 */
  15. }
复制代码
       初始化图片解码的相关信息,这些函数必须由用户在外部实现。我们使用之前LCD的操作函数对这个结构体中的绘制操作:画点、画线、画圆等定义与我们的LCD操作对应起来。具体这些操作可以查看SPILCD一节的描述。
       该函数的形参描述,如下表所示:

表40.3.1.1 函数piclib_init ()形参描述

       该函数的返回值描述,如下表所示:

表40.3.1.2 函数piclib_init ()返回值描述

       3,piclib_ai_load_picfile函数
       piclib_ai_load_picfile帮助我们得到需要显示的图片信息并助于下一步的绘制。本函数需要结合文件系统来操作,图片根据后缀区分并且在文件夹在保存是我们在PC下的习分类,也是我们处理和分类图片的最方便的方式。
  1. /**
  2. * @brief       智能画图
  3. * @note        图片仅在x,y和width, height限定的区域内显示.
  4. *
  5. * @param       filename      : 包含路径的文件名(.bmp/.jpg/.jpeg/.png/.gif等)
  6. * @param       x, y          : 起始坐标
  7. * @param       width, height : 显示区域
  8. * @param       fast          : 使能快速解码
  9. * @arg                         0, 不使能
  10. * @arg                         1, 使能
  11. * @note                        图片尺寸小于等于液晶分辨率,才支持快速解码
  12. * @retval      无
  13. */
  14. uint8_t piclib_ai_load_picfile(char *filename,
  15.                                      uint16_t x,
  16.                                      uint16_t y,
  17.                                      uint16_t width,
  18.                                      uint16_t height)
  19. {
  20.     uint8_t res = 0;/* 返回值 */
  21.     uint8_t temp;

  22.     if ((x + width) > picinfo.lcdwidth)return PIC_WINDOW_ERR;  /* x坐标超范围了 */

  23.     if ((y + height) > picinfo.lcdheight)return PIC_WINDOW_ERR;/* y坐标超范围了 */

  24.     /* 得到显示方框大小 */
  25.     if (width == 0 || height == 0)return PIC_WINDOW_ERR;        /* 窗口设定错误 */

  26.     /* 文件名传递 */
  27.     temp = exfuns_file_type(filename);                                                   /* 得到文件的类型 */

  28.     switch (temp)
  29.     {
  30.         case T_BMP:
  31.             res = bmp_decode(filename,width, height);                   /* 解码BMP */
  32.             break;

  33.         case T_JPG:
  34.         case T_JPEG:
  35.             res = jpeg_decode(filename,width, height);         /* 解码JPG/JPEG */
  36.             break;

  37.         case T_GIF:
  38.             res = gif_decode(filename, x, y, width, height);            /* 解码gif */
  39.             break;

  40.         case T_PNG:
  41.             res = png_decode(filename, width, height);                  /* 解码PNG */
  42.             break;

  43.         default:
  44.             res = PIC_FORMAT_ERR;                                               /* 非图片格式!!! */
  45.             break;
  46.     }

  47.     return res;
  48. }
复制代码
       该函数的形参描述,如下表所示:

表40.3.1.3 函数piclib_ai_load_picfile ()形参描述

       该函数的返回值描述,如下表所示:

表40.3.1.4 函数piclib_ai_load_picfile ()返回值描述

       piclib_ai_load_picfile函数,整个图片显示的对外接口,外部程序,通过调用该函数,可以实现bmp、jpg/jpeg、png和gif的显示,该函数根据输入文件的后缀名,判断文件格式,然后交给相应的解码程序(bmp解码/jpeg解码/gif解码/png解码),执行解码,完成图片显示。
       这里用到的exfuns_file_type()函数是我们用来判断文件类型,方便我们进行程序设计。由于图片显示需要用到大内存,我们使用动态内存分配来实现,我们仍使用我们自定的内存管理函数来管理程序内存。申请内存函数piclib_mem_malloc()和内存释放函数piclib_mem_free()的实现就比较简单了,大家参考光盘的源码即可。

       40.3.4 CMakeLists.txt文件
       打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
  1. set(src_dirs
  2.             IIC
  3.             LCD
  4.             LED
  5.             SPI
  6.             XL9555)

  7. set(include_dirs
  8.             IIC
  9.             LCD
  10.             LED
  11.             SPI
  12.             XL9555)

  13. set(requires
  14.             driver
  15.             fatfs)

  16. idf_component_register(SRC_DIRS ${src_dirs}
  17. INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  18. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
       上述的红色fatfs依赖库需要由开发者自行添加,以确保图片显示驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了图片显示驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
       打开本实验main文件下的CMakeLists.txt文件,其内容如下所示:
  1. idf_component_register(
  2.     SRC_DIRS
  3.         "."
  4.         "APP"
  5.     INCLUDE_DIRS
  6.         "."
  7.         "APP")
复制代码
       上述的红色APP驱动需要由开发者自行添加,在此便不做赘述了。

       40.3.5 实验应用代码
       打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
  1. i2c_obj_t i2c0_master;

  2. /**
  3. * @brief       程序入口
  4. * @param       无
  5. * @retval      无
  6. */
  7. void app_main(void)
  8. {
  9.     esp_err_t ret = 0;
  10.     uint8_t res = 0;
  11.     FF_DIR picdir;                                              /* 图片目录 */
  12.     FILINFO *picfileinfo;                                       /* 文件信息 */
  13.     char *pname;                                                /* 带路径的文件名 */
  14.     uint16_t totpicnum;                                         /* 图片文件总数 */
  15.     uint16_t curindex = 0;                                      /* 图片当前索引 */
  16.     uint8_t key = 0;                                            /* 键值 */
  17.     uint8_t pause = 0;                                          /* 暂停标记 */
  18.     uint8_t t;
  19.     uint16_t temp;
  20.     uint32_t *picoffsettbl;                             /* 图片文件offset索引表 */

  21.     ret = nvs_flash_init();                             /* 初始化NVS */

  22. if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
  23.             ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  24.     {
  25.         ESP_ERROR_CHECK(nvs_flash_erase());
  26.         ret = nvs_flash_init();
  27.     }

  28.     led_init();                                                 /* 初始化LED */
  29.     i2c0_master = iic_init(I2C_NUM_0);                              /* 初始化IIC0 */
  30.     spi2_init();                                                /* 初始化SPI */
  31.     xl9555_init(i2c0_master);                                  /* 初始化IO扩展芯片 */  
  32.     lcd_init();                                                 /* 初始化LCD */

  33.     while (sd_spi_init())                                       /* 检测不到SD卡 */
  34.     {
  35.         lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);
  36.         vTaskDelay(500);
  37.         lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
  38.         vTaskDelay(500);
  39.     }

  40.     res = exfuns_init();        /* 为fatfs相关变量申请内存 */

  41.     while (fonts_init())                                        /* 检查字库 */
  42.     {
  43.         lcd_clear(WHITE);                                       /* 清屏 */
  44.         lcd_show_string(30, 30, 200, 16, 16, "ESP32-S3", RED);
  45.         
  46.         key = fonts_update_font(30, 50, 16, (uint8_t *)"0:", RED);  /* 更新字库 */

  47.         while (key)                                            /* 更新失败 */
  48.         {
  49.             lcd_show_string(30, 50, 200, 16, 16, "Font Update Failed!", RED);
  50.             vTaskDelay(200);
  51.             lcd_fill(20, 50, 200 + 20, 90 + 16, WHITE);
  52.             vTaskDelay(200);
  53.         }

  54.         lcd_show_string(30, 50, 200, 16, 16, "Font Update Success!   ", RED);
  55.         vTaskDelay(1500);
  56.         lcd_clear(WHITE);                                      /* 清屏 */
  57.     }

  58.     text_show_string(30, 50, 200, 16, "正点原子ESP32开发板",16,0, RED);
  59.     text_show_string(30, 70, 200, 16, "图片显示 实验", 16, 0, RED);
  60.     text_show_string(30, 90, 200, 16, "正点原子@ALIENTEK", 16, 0, RED);
  61.     text_show_string(30, 110, 200, 16, "KEY0:NEXT KEY1:PREV", 16, 0, RED);
  62.     text_show_string(30, 130, 200, 16, "KEY_UP:PAUSE:", 16, 0, RED);

  63.     while (f_opendir(&picdir, "0:/PICTURE"))                  /* 打开图片文件夹 */
  64.     {
  65.         text_show_string(30, 150, 240, 16, "PICTURE文件夹错误!", 16, 0, RED);
  66.         vTaskDelay(200);
  67.         lcd_fill(30, 150, 240, 186, WHITE);                  /* 清除显示 */
  68.         vTaskDelay(200);
  69.     }

  70.     totpicnum = pic_get_tnum("0:/PICTURE");                        /* 得到总有效文件数 */

  71.     while (totpicnum == NULL)                                 /* 图片文件为0 */
  72.     {
  73.         text_show_string(30, 150, 240, 16, "没有图片文件!", 16, 0, RED);
  74.         vTaskDelay(200);
  75.         lcd_fill(30, 150, 240, 186, WHITE);             /* 清除显示 */
  76.         vTaskDelay(200);
  77.     }

  78.     picfileinfo = (FILINFO *)malloc(sizeof(FILINFO));/* 申请内存 */
  79. pname = malloc(255 * 2 + 1);                      /* 为带路径的文件名分配内存 */

  80. /* 申请4*totpicnum个字节的内存,用于存放图片索引 */
  81.     picoffsettbl = malloc(4 * totpicnum);

  82.     while (!picfileinfo || !pname || !picoffsettbl)/* 内存分配出错 */
  83.     {
  84.         text_show_string(30, 150, 240, 16, "内存分配失败!", 16, 0, RED);
  85.         vTaskDelay(200);
  86.         lcd_fill(30, 150, 240, 186, WHITE);              /* 清除显示 */
  87.         vTaskDelay(200);
  88.     }

  89.     /* 记录索引 */
  90.     res = f_opendir(&picdir, "0:/PICTURE");                /* 打开目录 */

  91.     if (res == FR_OK)
  92.     {
  93.         curindex = 0;                                     /* 当前索引为0 */

  94.         while (1)                                         /* 全部查询一遍 */
  95.         {
  96.             temp = picdir.dptr;                          /* 记录当前dptr偏移 */
  97.             res = f_readdir(&picdir, picfileinfo);        /* 读取目录下的一个文件 */
  98.                   
  99.                    /* 错误了/到末尾了,退出 */
  100.             if (res != FR_OK || picfileinfo->fname[0] == 0)break;   
  101.             res = exfuns_file_type(picfileinfo->fname);

  102.             if ((res & 0X0F) != 0X00)                /* 取高四位,看看是不是图片文件 */
  103.             {
  104.                 picoffsettbl[curindex] = temp;        /* 记录索引 */
  105.                 curindex++;
  106.             }
  107.         }
  108.     }

  109.     text_show_string(30, 150, 240, 16, "开始显示...", 16, 0, RED);
  110.     vTaskDelay(1500);
  111.    
  112.     /* 初始化画图 */
  113.     piclib_init();
  114.    
  115.     /* 从0开始显示 */
  116.     curindex = 0;
  117.    
  118.     /* 打开目录 */
  119.     res = f_opendir(&picdir, (const TCHAR *)"0:/PICTURE");
  120.    
  121.     /* 打开成功 */
  122.     while (res == FR_OK)
  123.     {
  124.         /* 改变当前目录索引 */
  125.         dir_sdi(&picdir, picoffsettbl[curindex]);
  126.         
  127.         /* 读取目录下的一个文件 */
  128.         res = f_readdir(&picdir, picfileinfo);
  129.    
  130.         /* 错误了/到末尾了,退出 */
  131.         if (res != FR_OK || picfileinfo->fname[0] == 0)break;
  132.         
  133.         /* 复制路径(目录) */
  134.         strcpy((char *)pname, "0:/PICTURE/");
  135.         
  136.         /* 将文件名接在后面 */
  137.         strcat((char *)pname, (const char *)picfileinfo->fname);
  138.         lcd_clear(BLACK);
  139.         
  140.         /* 显示图片 */
  141.         piclib_ai_load_picfile(pname, 0, 0, lcd_self.width, lcd_self.height);
  142.         
  143.         /* 显示图片名字 */
  144.         text_show_string(2, 2, lcd_self.width, 16, (char *)pname, 16, 0, RED);
  145.         t = 0;

  146.         while (1)
  147.         {
  148.             key = xl9555_key_scan(0);           /* 扫描按键 */

  149.             if (t > 250)key = 1;                /* 模拟一次按下KEY0 */

  150.             if ((t % 20) == 0)
  151.             {
  152.                 LED_TOGGLE();                   /* LED闪烁,提示程序正在运行. */
  153.             }

  154.             if (key == KEY1_PRES)               /* 上一张 */
  155.             {
  156.                 if (curindex)
  157.                 {
  158.                     curindex--;
  159.                 }
  160.                 else
  161.                 {
  162.                     curindex = totpicnum - 1;
  163.                 }
  164.                
  165.                 break;
  166.             }
  167.             else if (key == KEY0_PRES)        /* 下一张 */
  168.             {
  169.                 curindex++;

  170.                 if (curindex >= totpicnum)
  171.                 {
  172.                     curindex = 0;           /* 到末尾的时候,自动从头开始 */
  173.                 }

  174.                 break;
  175.             }
  176.             else if (key == KEY3_PRES)
  177.             {
  178.                 pause = !pause;
  179.                 LED(pause);                 /* 暂停的时候LED1亮. */
  180.             }

  181.             if (pause == 0)t++;

  182.             vTaskDelay(10);
  183.         }

  184.         res = 0;
  185.     }

  186.     free(picfileinfo);                                    /* 释放内存 */
  187.     free(pname);                                          /* 释放内存 */
  188.     free(picoffsettbl);                                   /* 释放内存 */
  189. }
复制代码
       可以看到整个设计思路是跟据图片解码库来设计的,piclib_ai_load_picfile()是这套代码的核心,其它的交互是围绕它和图片解码后的图片信息作的显示。大家再仔细对照光盘中的源码进一步了解整个设置思路。另外,我们的程序中只分配了4个文件索引,故更多数量的图片无法直接在本程序下演示,大家根据自己的需要再进行修改即可。

       40.4 下载验证
       在代码编译成功之后,我们下载代码到开发板上,可以看到LCD开始显示图片(假设SD卡及文件都准备好了,即:在SD卡根目录新建:PICTURE文件夹,并存放一些图片文件(.bmp/.jpg/.gif/.png)在该文件夹内),如图40.4.1所示:

图40.4.1图片显示实验显示效果

       按KEY0和KEY2可以快速切换到下一张或上一张,KEY_UP按键可以暂停自动播放,同时DS1亮,指示处于暂停状态,再按一次KEY_UP则继续播放。同时,由于我们的代码支持gif格式的图片显示(注意尺寸不能超过LCD屏幕尺寸),所以可以放一些gif图片到PICTURE文件夹,来看动画了。

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-25 06:22

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表