第四十章圖片顯示實驗
1)實驗平台:正點原子DNESP32S3開發板
2)章節摘自【正點原子】ESP32-S3使用指南—IDF版 V1.6
3)產品鏈接:https://detail.tmall.com/item.htm?&id=768499342659
4)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3.html
5)正點原子官方B站:https://space.bilibili.com/394620890
6)正點原子DNESP32S3開發板技術交流羣: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(PortableNetwork 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文件。其值固定為"8950 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操作和存放解碼後的圖片尺寸顏色信息。它們的定義如下:
/* 圖片顯示物理層接口 */
/* 在移植的時候,必須由用户自己實現這幾個函數 */
typedef struct
{
/* 畫點函數 */
void(*draw_point)(uint16_t, uint16_t, uint16_t);
/* 單色填充函數 */
void(*fill)(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t);
/* 畫水平線函數 */
void(*draw_hline)(uint16_t, uint16_t, uint16_t, uint16_t);
/*多點填充 */
void(*multicolor)(uint16_t, uint16_t, uint16_t, uint16_t *);
}_pic_phy;
/* 圖像信息 */
typedef struct
{
uint16_t lcdwidth; /*LCD的寬度 */
uint16_t lcdheight; /* LCD的高度 */
}_pic_info;
在piclib.c文件中,我們用上述類型定義了兩個結構體,聲明如下:
_pic_info picinfo; /* 圖片信息 */
_pic_phy pic_phy; /* 圖片顯示物理接口 */
2,piclib_init函數
piclib_init函數,該函數用於初始化圖片解碼的相關信息,用於定義解碼後的LCD操作。 具體定義如下:
/**
* @brief 畫圖初始化
* @note 在畫圖之前,必須先調用此函數, 指定相關函數
* @param 無
* @retval 無
*/
voidpiclib_init(void)
{
pic_phy.draw_point= lcd_draw_pixel; /* 畫點函數實現,僅GIF需要 */
pic_phy.fill =lcd_fill; /* 填充函數實現,僅GIF需要 */
pic_phy.draw_hline= lcd_draw_hline; /* 畫線函數實現,僅GIF需要 */
pic_phy.multicolor= piclib_multi_color;/* 顏色填充函數實現,JPEG、BMP、PNG */
picinfo.lcdwidth= lcd_self.width; /* 得到LCD的寬度像素 */
picinfo.lcdheight= lcd_self.height; /* 得到LCD的高度像素 */
}
初始化圖片解碼的相關信息,這些函數必須由用户在外部實現。我們使用之前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下的習分類,也是我們處理和分類圖片的最方便的方式。
/**
* @brief 智能畫圖
* @note 圖片僅在x,y和width, height限定的區域內顯示.
*
* @param filename : 包含路徑的文件名(.bmp/.jpg/.jpeg/.png/.gif等)
* @param x, y : 起始座標
* @param width, height : 顯示區域
* @param fast : 使能快速解碼
* @arg 0, 不使能
* @arg 1, 使能
* @note 圖片尺寸小於等於液晶分辨率,才支持快速解碼
* @retval 無
*/
uint8_tpiclib_ai_load_picfile(char *filename,
uint16_t x,
uint16_t y,
uint16_t width,
uint16_theight)
{
uint8_t res = 0;/* 返回值 */
uint8_t temp;
if ((x + width) >picinfo.lcdwidth)return PIC_WINDOW_ERR; /* x座標超範圍了 */
if ((y +height) > picinfo.lcdheight)returnPIC_WINDOW_ERR;/* y座標超範圍了 */
/* 得到顯示方框大小 */
if (width == 0 ||height == 0)return PIC_WINDOW_ERR; /* 窗口設定錯誤 */
/* 文件名傳遞 */
temp =exfuns_file_type(filename); /* 得到文件的類型 */
switch (temp)
{
case T_BMP:
res =bmp_decode(filename,width,height); /* 解碼BMP */
break;
case T_JPG:
caseT_JPEG:
res =jpeg_decode(filename,width,height); /* 解碼JPG/JPEG */
break;
case T_GIF:
res =gif_decode(filename, x, y, width,height); /* 解碼gif */
break;
case T_PNG:
res =png_decode(filename, width,height); /* 解碼PNG */
break;
default:
res =PIC_FORMAT_ERR; /* 非圖片格式!!! */
break;
}
return res;
}
該函數的形參描述,如下表所示:
表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文件,其內容如下所示:
set(src_dirs
IIC
LCD
LED
SPI
XL9555)
set(include_dirs
IIC
LCD
LED
SPI
XL9555)
set(requires
driver
fatfs)
idf_component_register(SRC_DIRS${src_dirs}
INCLUDE_DIRS ${include_dirs}REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的紅色fatfs依賴庫需要由開發者自行添加,以確保圖片顯示驅動能夠順利集成到構建系統中。這一步驟是必不可少的,它確保了圖片顯示驅動的正確性和可用性,為後續的開發工作提供了堅實的基礎。
打開本實驗main文件下的CMakeLists.txt文件,其內容如下所示:
idf_component_register(
SRC_DIRS
"."
"APP"
INCLUDE_DIRS
"."
"APP")
上述的紅色APP驅動需要由開發者自行添加,在此便不做贅述了。
40.3.5 實驗應用代碼
打開main/main.c文件,該文件定義了工程入口函數,名為app_main。該函數代碼如下。
i2c_obj_ti2c0_master;
/**
* @brief 程序入口
* @param 無
* @retval 無
*/
voidapp_main(void)
{
esp_err_t ret = 0;
uint8_t res = 0;
FF_DIR picdir; /* 圖片目錄 */
FILINFO *picfileinfo; /* 文件信息 */
char *pname; /* 帶路徑的文件名 */
uint16_t totpicnum; /* 圖片文件總數 */
uint16_t curindex = 0; /* 圖片當前索引 */
uint8_t key = 0; /* 鍵值 */
uint8_t pause = 0; /* 暫停標記 */
uint8_t t;
uint16_t temp;
uint32_t *picoffsettbl; /* 圖片文件offset索引表 */
ret =nvs_flash_init(); /* 初始化NVS */
if (ret ==ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret =nvs_flash_init();
}
led_init(); /* 初始化LED */
i2c0_master =iic_init(I2C_NUM_0); /* 初始化IIC0 */
spi2_init(); /* 初始化SPI */
xl9555_init(i2c0_master); /* 初始化IO擴展芯片 */
lcd_init(); /* 初始化LCD */
while (sd_spi_init()) /* 檢測不到SD卡 */
{
lcd_show_string(30, 110, 200, 16, 16, "SDCard Error!", RED);
vTaskDelay(500);
lcd_show_string(30, 130, 200, 16, 16, "PleaseCheck! ", RED);
vTaskDelay(500);
}
res =exfuns_init(); /* 為fatfs相關變量申請內存 */
while (fonts_init()) /* 檢查字庫 */
{
lcd_clear(WHITE); /* 清屏 */
lcd_show_string(30, 30, 200, 16, 16, "ESP32-S3", RED);
key =fonts_update_font(30, 50, 16, (uint8_t *)"0:", RED); /* 更新字庫 */
while (key) /* 更新失敗 */
{
lcd_show_string(30, 50, 200, 16, 16, "FontUpdate Failed!", RED);
vTaskDelay(200);
lcd_fill(20, 50, 200 + 20, 90 + 16, WHITE);
vTaskDelay(200);
}
lcd_show_string(30, 50, 200, 16, 16, "FontUpdate Success! ", RED);
vTaskDelay(1500);
lcd_clear(WHITE); /* 清屏 */
}
text_show_string(30, 50, 200, 16, "正點原子ESP32開發板",16,0, RED);
text_show_string(30, 70, 200, 16, "圖片顯示 實驗", 16, 0, RED);
text_show_string(30, 90, 200, 16, "正點原子@ALIENTEK", 16, 0, RED);
text_show_string(30, 110, 200, 16, "KEY0:NEXTKEY1:PREV", 16, 0, RED);
text_show_string(30, 130, 200, 16, "KEY_UP:PAUSE:", 16, 0, RED);
while (f_opendir(&picdir, "0:/PICTURE")) /* 打開圖片文件夾 */
{
text_show_string(30, 150, 240, 16, "PICTURE文件夾錯誤!", 16, 0, RED);
vTaskDelay(200);
lcd_fill(30, 150, 240, 186, WHITE); /* 清除顯示 */
vTaskDelay(200);
}
totpicnum =pic_get_tnum("0:/PICTURE"); /* 得到總有效文件數 */
while (totpicnum== NULL) /* 圖片文件為0 */
{
text_show_string(30, 150, 240, 16, "沒有圖片文件!", 16, 0, RED);
vTaskDelay(200);
lcd_fill(30, 150, 240, 186, WHITE); /* 清除顯示 */
vTaskDelay(200);
}
picfileinfo = (FILINFO*)malloc(sizeof(FILINFO));/* 申請內存 */
pname =malloc(255 * 2 + 1); /* 為帶路徑的文件名分配內存 */
/* 申請4*totpicnum個字節的內存,用於存放圖片索引 */
picoffsettbl =malloc(4 * totpicnum);
while (!picfileinfo|| !pname || !picoffsettbl)/* 內存分配出錯 */
{
text_show_string(30, 150, 240, 16, "內存分配失敗!", 16, 0, RED);
vTaskDelay(200);
lcd_fill(30, 150, 240, 186, WHITE); /* 清除顯示 */
vTaskDelay(200);
}
/* 記錄索引 */
res =f_opendir(&picdir, "0:/PICTURE"); /* 打開目錄 */
if (res == FR_OK)
{
curindex = 0; /* 當前索引為0 */
while (1) /* 全部查詢一遍 */
{
temp =picdir.dptr; /* 記錄當前dptr偏移 */
res =f_readdir(&picdir, picfileinfo); /* 讀取目錄下的一個文件 */
/* 錯誤了/到末尾了,退出 */
if (res != FR_OK|| picfileinfo->fname[0] == 0)break;
res =exfuns_file_type(picfileinfo->fname);
if ((res & 0X0F) != 0X00) /* 取高四位,看看是不是圖片文件 */
{
picoffsettbl[curindex] = temp; /* 記錄索引 */
curindex++;
}
}
}
text_show_string(30, 150, 240, 16, "開始顯示...", 16, 0, RED);
vTaskDelay(1500);
/* 初始化畫圖 */
piclib_init();
/* 從0開始顯示 */
curindex = 0;
/* 打開目錄 */
res =f_opendir(&picdir, (const TCHAR*)"0:/PICTURE");
/* 打開成功 */
while (res == FR_OK)
{
/* 改變當前目錄索引 */
dir_sdi(&picdir,picoffsettbl[curindex]);
/* 讀取目錄下的一個文件 */
res =f_readdir(&picdir, picfileinfo);
/* 錯誤了/到末尾了,退出 */
if (res != FR_OK|| picfileinfo->fname[0] == 0)break;
/* 複製路徑(目錄) */
strcpy((char *)pname, "0:/PICTURE/");
/* 將文件名接在後面 */
strcat((char *)pname, (const char *)picfileinfo->fname);
lcd_clear(BLACK);
/* 顯示圖片 */
piclib_ai_load_picfile(pname, 0, 0,lcd_self.width, lcd_self.height);
/* 顯示圖片名字 */
text_show_string(2, 2,lcd_self.width, 16, (char *)pname, 16, 0, RED);
t = 0;
while (1)
{
key =xl9555_key_scan(0); /* 掃描按鍵 */
if (t > 250)key = 1; /* 模擬一次按下KEY0 */
if ((t % 20) == 0)
{
LED_TOGGLE(); /*LED閃爍,提示程序正在運行. */
}
if (key ==KEY1_PRES) /* 上一張 */
{
if (curindex)
{
curindex--;
}
else
{
curindex =totpicnum - 1;
}
break;
}
else if (key ==KEY0_PRES) /* 下一張 */
{
curindex++;
if (curindex>= totpicnum)
{
curindex = 0; /* 到末尾的時候,自動從頭開始 */
}
break;
}
else if (key ==KEY3_PRES)
{
pause = !pause;
LED(pause); /* 暫停的時候LED1亮. */
}
if (pause == 0)t++;
vTaskDelay(10);
}
res = 0;
}
free(picfileinfo); /* 釋放內存 */
free(pname); /* 釋放內存 */
free(picoffsettbl); /* 釋放內存 */
}
可以看到整個設計思路是跟據圖片解碼庫來設計的,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文件夾,來看動畫了。