天天看點

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

1)摘自【正點原子】領航者 ZYNQ 之嵌入式開發指南

2)實驗平台:正點原子領航者ZYNQ開發闆

3)平台購買位址:https://item.taobao.com/item.htm?&id=606160108761

4)全套實驗源碼+手冊+視訊下載下傳:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html

5)對正點原子FPGA感興趣的同學可以加群讨論:876744900

6)關注正點原子公衆号,擷取最新資料

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

第十九章SD卡讀BMP圖檔LCD顯示實驗

在“SD卡讀寫TXT文本實驗”中,我們利用FATFS在SD卡中實作了TXT文本的建立、寫入與讀取。在本次實驗中,我們将學習如何從SD卡中讀取BMP圖檔,并将其顯示在LCD上。

本章包括以下幾個部分:

1919.1簡介

19.2實驗任務

19.3硬體設計

19.4軟體設計

19.5下載下傳驗證

19.1簡介

我們常用的圖檔格式有很多,一般最常用的有三種:JPEG(或JPG)、BMP和GIF。其中JPEG(或JPG)和BMP是靜态圖檔,而GIF則是可以實作動态圖檔。在本次實驗中,我們選擇使用BMP圖檔格式。

BMP(全稱Bitmap)是Window作業系統中的标準圖像檔案格式,檔案字尾名為“.bmp”,使用非常廣。它采用位映射存儲格式,除了圖像深度可選以外,不采用其他任何壓縮,是以,BMP檔案所占用的空間很大,但是沒有失真。BMP檔案的圖像深度可選lbit、4bit、8bit、16bit、24bit及32bit。BMP檔案存儲資料時,圖像的掃描方式是按從下到上、從左到右的順序。

典型的BMP圖像檔案由四部分組成:   

1、BMP檔案頭,它包含BMP圖像檔案的類型、大小等資訊;

2、BMP資訊頭,它包含有BMP圖像的寬、高、壓縮方法,以及定義顔色等資訊;

3、調色闆,這個部分是可選的,如果使用索引來表示圖像,調色闆就是索引與其對應顔色的映射表;   

4、位圖資料,即圖像資料,在位深度為24位時直接使用RGB格式,而小于24位時使用調色闆中顔色的索引值。

各個部分的大小如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.1 BMP檔案各部分及其大小

我們一般見到的圖像以24位圖像為主,即R、G、B三種顔色各用8個bit來表示,這樣的圖像我們稱之為真彩色。在這種情況下是不需要調色闆的,位圖資訊頭後面緊跟的就是位圖資料了。也就是說,位圖檔案從檔案頭開始偏移54個位元組就是圖像資料了。在這裡我們就以一幅24位BMP圖檔為例,如圖 19.1.2所示,詳細介紹其檔案結構。

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.2 示例圖檔:24位BMP圖檔

首先我們來看一下該圖檔在Window中的屬性資訊,如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.3 示例圖檔屬性

圖 19.1.3中包括BMP圖檔的檔案屬性以及其圖像屬性,檔案大小為1.09MB,圖像分辨率為800*480,每個像素點的顔色使用24位表示。

接下來,我們使用Notepad++以十六進制格式打開該BMP檔案,如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.4 示例圖檔16進制資料

圖 19.1.4中紅色矩形區域為BMP檔案頭,共14位元組;藍色區域為BMP資訊頭,共40位元組;剩餘部分為圖像資料。左下角紅色橢圓區域表明整個BMP檔案共1152054個位元組,除去檔案頭和資訊頭所占的54個位元組,圖像資料為1152000位元組。由于示例圖檔每個像素點使用3個位元組表示顔色,是以我們可以計算出圖像資料的大小為800*480*3 = 1152000位元組,與Notepad++中計算得到的結果一緻。

首先來了解一下BMP檔案頭的資料結構,如下表所示:

表 19.1.1 BMP檔案頭資料結構

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

我們将表 19.1.1中橙色區域與下圖矩形區域中的資料一一對應:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.5 BMP檔案頭

對比後可得到如下結果:

1、bf_Size:位圖檔案的大小為0x119436,即1152054位元組(1.09MB),與示例圖檔屬性一緻。需要注意的是,在BMP檔案中,如果一個資料需要用幾個位元組來表示的話,那麼該資料的低位元組存放在低位址,高位元組存放在高位址;

2、bfOffBits:檔案頭到圖像資料之間的偏移量為0x36,即54位元組。這個偏移量非常有用,我們可以利用它快速定位BMP檔案中的圖像資料的位置。

接下來是BMP資訊頭的資料結構,如下表所示:

表 19.1.2 BMP資訊頭資料結構

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

同樣,将中橙色區域與下圖矩形區域中的資料一一對應:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.1.6 BMP資訊頭

對比後可得到如下結果:

1、biWidth:圖像的寬度為0x320,即800像素;

2、biHeight:圖像的高度為0x1e0,即480像素;

3、biBitCount:像素的位深度為0x18,即24位;

4、biSizeImage:圖像的大小為0x119400,即1152000位元組。

19.2實驗任務

本章的實驗任務是使用領航者ZYNQ開發闆讀取SD卡中存放的BMP格式圖檔,分辨率為800*480,并将其顯示在LCD上。

19.3硬體設計

根據實驗任務我們可以畫出本次實驗的系統框圖,如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.3.1 系統框圖

圖 19.3.1與“PS通過VDMA驅動LCD顯示實驗”中的系統框圖基本相同,其中各個子產品的功能介紹請大家參考相應的章節。唯一不同的地方是,在“PS通過VDMA驅動LCD顯示實驗”中我們顯示在LCD上的是CPU在DDR3記憶體中繪制的彩條圖案。而在本次實驗中,我們需要從SD卡中讀取BMP圖檔,并将其寫到DDR3相應的記憶體空間,用于LCD顯示。

本次實驗的硬體環境可以在“PS通過VDMA驅動LCD顯示實驗”的基礎上搭建。由于需要讀SD卡的功能,是以在配置ZYNQ7 PS子產品時,需要使能SD卡控制器外設,具體方法請參考“SD卡讀寫TXT文本實驗”中的硬體設計部分。

19.4軟體設計

為了能夠在軟體中讀取SD卡中的檔案,我們需要設定BSP工程,添加并設定FATFS庫。具體的方法請參考“SD卡讀寫TXT文本實驗”軟體設計部分。

接下來修改main.c檔案中的代碼,修改完成後代碼的主體部分如下所示:

  1. 1 #include
  2. 2 #include
  3. 3 #include
  4. 4 #include "xil_types.h"
  5. 5 #include "xil_cache.h"
  6. 6 #include "xparameters.h"
  7. 7 #include "xgpio.h"
  8. 8 #include "xaxivdma.h"
  9. 9 #include "xaxivdma_i.h"
  10. 10 #include "display_ctrl/display_ctrl.h"
  11. 11 #include "vdma_api/vdma_api.h"
  12. 12 #include "ff.h"
  13. 13
  14. 14 //宏定義
  15. 15 #define BYTES_PIXEL 3 //像素位元組數,RGB888占3個位元組
  16. 16 #define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //動态時鐘基位址
  17. 17 #define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
  18. 18 #define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
  19. 19 #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL端 AXI GPIO 0(lcd_id)器件ID
  20. 20 #define AXI_GPIO_0_CHANEL 1 //PL按鍵使用AXI GPIO(lcd_id)通道1
  21. 21
  22. 22 //函數聲明
  23. 23 void load_sd_bmp(u8 *frame);
  24. 24
  25. 25 //全局變量
  26. 26 XAxiVdma vdma;
  27. 27 DisplayCtrl dispCtrl;
  28. 28 XGpio axi_gpio_inst; //PL端 AXI GPIO 驅動執行個體
  29. 29 VideoMode vd_mode;
  30. 30 //frame buffer的起始位址
  31. 31 unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000);
  32. 32 unsigned int lcd_id=0; //LCD ID
  33. 33
  34. 34 int main(void)
  35. 35 {
  36. 36 //擷取LCD的ID
  37. 37 XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID);
  38. 38 lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
  39. 39 xil_printf("LCD ID: %x",lcd_id);
  40. 40
  41. 41 //根據擷取的LCD的ID号來進行video參數的選擇
  42. 42 switch(lcd_id){
  43. 43 case 0x4342 : vd_mode = VMODE_480x272; break; //4.3寸屏,480*272分辨率
  44. 44 case 0x4384 : vd_mode = VMODE_800x480; break; //4.3寸屏,800*480分辨率
  45. 45 case 0x7084 : vd_mode = VMODE_800x480; break; //7寸屏,800*480分辨率
  46. 46 case 0x7016 : vd_mode = VMODE_1024x600; break; //7寸屏,1024*600分辨率
  47. 47 case 0x1018 : vd_mode = VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
  48. 48 default : vd_mode = VMODE_800x480; break;
  49. 49 }
  50. 50
  51. 51 //配置VDMA
  52. 52 run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
  53. 53 frame_buffer_addr,0, 0,ONLY_READ);
  54. 54
  55. 55 //初始化Display controller
  56. 56 DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
  57. 57 //設定VideoMode
  58. 58 DisplaySetMode(&dispCtrl, &vd_mode);
  59. 59 DisplayStart(&dispCtrl);
  60. 60
  61. 61 //讀取SD卡圖檔并顯示
  62. 62 load_sd_bmp((u8*)frame_buffer_addr);
  63. 63
  64. 64 return 0;
  65. 65 }
  66. 66

主函數的代碼與“PS通過VDMA驅動LCD顯示實驗”幾乎完全相同,我們隻需要将其中寫彩條的函數修改為函數load_sd_bmp(u8 *frame)即可,如程式第23行和第62行所示。另外,由于對SD卡進行操作用到了FAT檔案系統相關的函數,是以在程式的第12行還包含了ff.h頭檔案。

有關這部分代碼更詳細的介紹,請大家參考“PS通過VDMA驅動LCD顯示實驗”軟體設計部分。

load_sd_bmp(u8 *frame)函數負責從SD卡中讀取BMP格式的圖檔,并将其寫入圖檔顯存所對應的位址空間中,其代碼如下所示:

  1. 67 //從SD卡中讀取BMP圖檔
  2. 68 void load_sd_bmp(u8 *frame)
  3. 69 {
  4. 70 static FATFS fatfs;
  5. 71 FIL fil;
  6. 72 u8 bmp_head[54];
  7. 73 UINT *bmp_width,*bmp_height,*bmp_size;
  8. 74 UINT br;
  9. 75 int i;
  10. 76
  11. 77 //挂載檔案系統
  12. 78 f_mount(&fatfs,"",1);
  13. 79
  14. 80 //打開檔案
  15. 81 f_open(&fil,"fengjing.bmp",FA_READ);
  16. 82
  17. 83 //移動檔案讀寫指針到檔案開頭
  18. 84 f_lseek(&fil,0);
  19. 85
  20. 86 //讀取BMP檔案頭
  21. 87 f_read(&fil,bmp_head,54,&br);
  22. 88 xil_printf("fengjing.bmp head: ");
  23. 89 for(i=0;i<54;i++)
  24. 90 xil_printf(" %x",bmp_head);
  25. 91
  26. 92 //列印BMP圖檔分辨率和大小
  27. 93 bmp_width = (UINT *)(bmp_head + 0x12);
  28. 94 bmp_height = (UINT *)(bmp_head + 0x16);
  29. 95 bmp_size = (UINT *)(bmp_head + 0x22);
  30. 96 xil_printf(" width = %d, height = %d, size = %d bytes ",
  31. 97 *bmp_width,*bmp_height,*bmp_size);
  32. 98
  33. 99 //讀出圖檔,寫入DDR
  34. 100 for(i=*bmp_height-1;i>=0;i--){
  35. 101 f_read(&fil,frame+i*(*bmp_width)*3,(*bmp_width)*3,&br);
  36. 102 }
  37. 103
  38. 104 //關閉檔案
  39. 105 f_close(&fil);
  40. 106
  41. 107 Xil_DCacheFlush(); //重新整理Cache,資料更新至DDR3中
  42. 108 xil_printf("show bmp");
  43. 109 }
  44. 110

在上面的程式中,主要是通過調用FATFS庫函數來讀取SD卡中的BMP圖檔檔案。本章的簡介部分詳細介紹了BMP圖檔的資料格式,我們首先要讀取BMP檔案前面54個位元組的資料,如程式第87行所示,其中包含了BMP圖檔的分辨率等資訊。然後在程式的92至97行,我們根據BMP資訊頭中各資料的偏移位址,找到并列印出圖像的分辨率和大小等資訊。

在讀取BMP檔案之前,我們先通過調用f_lseek(&fil,0)函數将檔案的讀寫指針移動到檔案開頭。然後在讀取54個位元組的資料之後,讀寫指針便移動到了BMP檔案中圖像資料的起始位置。接下來就可以通過f_read()函數繼續讀取下面的圖像資料。

我們需要注意的是程式的第99至102行。由于BMP檔案存儲資料時,圖像的掃描方式是按從下到上、從左到右的順序,是以如果我們直接将一整幅圖檔存入DDR顯存,那麼最終顯示出來的将是一個上下颠倒的圖檔。在這裡我們通過一個for循環來讀取一整幅BMP圖檔,從上到下每次讀取一行,然後把先讀出來的資料放到了圖檔顯存後面的位置。也就是說,讀出來的第一行資料實際上是BMP圖檔的最後一行圖像,是以要把它放在顯存的最後一行。通過這個for循環,我們就可以将上下颠倒的圖像給反過來。

最後通過調用Xil_DCacheFlush( )函數将緩存在DataCache中的資料重新整理到DDR3中,供PL中的子產品讀取并在LCD上顯示。

到這裡本次實驗的軟體設計就介紹完了,如果大家對FATFS庫函數的使用方法不熟悉的話,請參考“SD卡讀寫TXT文本實驗”中的軟體設計部分。

19.5下載下傳驗證

首先我們将下載下傳器與領航者底闆上的JTAG接口連接配接,下載下傳器另外一端與電腦連接配接。然後使用Mini USB連接配接線将開發闆左側的USB_UART接口與電腦連接配接,用于序列槽通信。

接下來使用FPC排線将正點原子的RGB LCD螢幕連接配接到領航者底闆上的LCD接口。然後把本章簡介部分所給出的示例圖檔重命名為“fengjing.bmp”,并拷貝到SD卡的根目錄下。最後将Micro SD卡插入領航者底闆背面的卡槽中。

需要注意的是,拷貝到Micro SD卡中的BMP圖檔分辨率應與開發闆所連接配接的LCD螢幕分辨率保持一緻。我們在工程目錄下建立了一個名為“風景圖檔”的檔案夾,裡面有四種不同分辨率的圖檔,如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.5.1 不同分辨率的BMP圖檔

比如本次實驗使用的LCD螢幕分辨率為800*480,那麼就将上圖中名為“fengjing_800x480.bmp”的圖檔拷貝到Micro SD卡根目錄下。在拷貝完成後,千萬不要忘記将Micro SD中圖檔的名稱修改為“fengjing.bmp”。

另外本次實驗要求使用的Micro SD卡為FAT32格式,如果不是,那麼在使用前需要将其格式化為FAT32格式。

準備工作完成之後,接下來連接配接開發闆的電源,并打開電源開關。

在SDK軟體下方的SDK Terminal視窗中點選右上角的加号來設定并連接配接序列槽。然後下載下傳本次實驗硬體設計過程中所生成的BIT檔案,來對PL進行配置。最後下載下傳軟體程式,下載下傳完成後,在下方的SDK Terminal中可以看到應用程式列印的資訊,如下圖所示:

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.5.2 序列槽列印資訊

圖 19.5.2中列印出了BMP圖檔的檔案頭和資訊頭等資訊,與圖 19.1.4中的資料保持一緻。同時從資料中計算出BMP圖檔的寬度為800,高度為480。另外根據讀出的LCD螢幕ID看出,所使用的LCD螢幕分辨率為800*480,與Micro SD卡中的BMP圖檔分辨率一緻。如果不一緻,那麼螢幕上就無法正常顯示圖檔。

LCD螢幕上顯示的圖檔如下圖所示,說明本次實驗在領航者ZYNQ開發闆上面下載下傳驗證成功。

bmp檔案頭_「正點原子FPGA連載」第十九章SD卡讀BMP圖檔LCD顯示

圖 19.5.3 下載下傳驗證

繼續閱讀