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)關注正點原子公眾號,獲取最新資料
第十九章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位時使用調色板中顏色的索引值。
各個部分的大小如下圖所示:
圖 19.1.1 BMP文件各部分及其大小
我們一般見到的圖像以24位圖像為主,即R、G、B三種顏色各用8個bit來表示,這樣的圖像我們稱之為真彩色。在這種情況下是不需要調色板的,位圖信息頭后面緊跟的就是位圖數據了。也就是說,位圖文件從文件頭開始偏移54個字節就是圖像數據了。在這里我們就以一幅24位BMP圖片為例,如圖 19.1.2所示,詳細介紹其文件結構。
圖 19.1.2 示例圖片:24位BMP圖片
首先我們來看一下該圖片在Window中的屬性信息,如下圖所示:
圖 19.1.3 示例圖片屬性
圖 19.1.3中包括BMP圖片的文件屬性以及其圖像屬性,文件大小為1.09MB,圖像分辨率為800*480,每個像素點的顏色使用24位表示。
接下來,我們使用Notepad++以十六進制格式打開該BMP文件,如下圖所示:
圖 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文件頭數據結構
我們將表 19.1.1中橙色區域與下圖矩形區域中的數據一一對應:
圖 19.1.5 BMP文件頭
對比后可得到如下結果:
1、bf_Size:位圖文件的大小為0x119436,即1152054字節(1.09MB),與示例圖片屬性一致。需要注意的是,在BMP文件中,如果一個數據需要用幾個字節來表示的話,那么該數據的低字節存放在低地址,高字節存放在高地址;
2、bfOffBits:文件頭到圖像數據之間的偏移量為0x36,即54字節。這個偏移量非常有用,我們可以利用它快速定位BMP文件中的圖像數據的位置。
接下來是BMP信息頭的數據結構,如下表所示:
表 19.1.2 BMP信息頭數據結構
同樣,將中橙色區域與下圖矩形區域中的數據一一對應:
圖 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硬件設計
根據實驗任務我們可以畫出本次實驗的系統框圖,如下圖所示:
圖 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 #include
- 2 #include
- 3 #include
- 4 #include "xil_types.h"
- 5 #include "xil_cache.h"
- 6 #include "xparameters.h"
- 7 #include "xgpio.h"
- 8 #include "xaxivdma.h"
- 9 #include "xaxivdma_i.h"
- 10 #include "display_ctrl/display_ctrl.h"
- 11 #include "vdma_api/vdma_api.h"
- 12 #include "ff.h"
- 13
- 14 //宏定義
- 15 #define BYTES_PIXEL 3 //像素字節數,RGB888占3個字節
- 16 #define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //動態時鐘基地址
- 17 #define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
- 18 #define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
- 19 #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL端 AXI GPIO 0(lcd_id)器件ID
- 20 #define AXI_GPIO_0_CHANEL 1 //PL按鍵使用AXI GPIO(lcd_id)通道1
- 21
- 22 //函數聲明
- 23 void load_sd_bmp(u8 *frame);
- 24
- 25 //全局變量
- 26 XAxiVdma vdma;
- 27 DisplayCtrl dispCtrl;
- 28 XGpio axi_gpio_inst; //PL端 AXI GPIO 驅動實例
- 29 VideoMode vd_mode;
- 30 //frame buffer的起始地址
- 31 unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000);
- 32 unsigned int lcd_id=0; //LCD ID
- 33
- 34 int main(void)
- 35 {
- 36 //獲取LCD的ID
- 37 XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID);
- 38 lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
- 39 xil_printf("LCD ID: %x",lcd_id);
- 40
- 41 //根據獲取的LCD的ID號來進行video參數的選擇
- 42 switch(lcd_id){
- 43 case 0x4342 : vd_mode = VMODE_480x272; break; //4.3寸屏,480*272分辨率
- 44 case 0x4384 : vd_mode = VMODE_800x480; break; //4.3寸屏,800*480分辨率
- 45 case 0x7084 : vd_mode = VMODE_800x480; break; //7寸屏,800*480分辨率
- 46 case 0x7016 : vd_mode = VMODE_1024x600; break; //7寸屏,1024*600分辨率
- 47 case 0x1018 : vd_mode = VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
- 48 default : vd_mode = VMODE_800x480; break;
- 49 }
- 50
- 51 //配置VDMA
- 52 run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
- 53 frame_buffer_addr,0, 0,ONLY_READ);
- 54
- 55 //初始化Display controller
- 56 DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
- 57 //設置VideoMode
- 58 DisplaySetMode(&dispCtrl, &vd_mode);
- 59 DisplayStart(&dispCtrl);
- 60
- 61 //讀取SD卡圖片并顯示
- 62 load_sd_bmp((u8*)frame_buffer_addr);
- 63
- 64 return 0;
- 65 }
- 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格式的圖片,并將其寫入圖片顯存所對應的地址空間中,其代碼如下所示:
- 67 //從SD卡中讀取BMP圖片
- 68 void load_sd_bmp(u8 *frame)
- 69 {
- 70 static FATFS fatfs;
- 71 FIL fil;
- 72 u8 bmp_head[54];
- 73 UINT *bmp_width,*bmp_height,*bmp_size;
- 74 UINT br;
- 75 int i;
- 76
- 77 //掛載文件系統
- 78 f_mount(&fatfs,"",1);
- 79
- 80 //打開文件
- 81 f_open(&fil,"fengjing.bmp",FA_READ);
- 82
- 83 //移動文件讀寫指針到文件開頭
- 84 f_lseek(&fil,0);
- 85
- 86 //讀取BMP文件頭
- 87 f_read(&fil,bmp_head,54,&br);
- 88 xil_printf("fengjing.bmp head: ");
- 89 for(i=0;i<54;i++)
- 90 xil_printf(" %x",bmp_head);
- 91
- 92 //打印BMP圖片分辨率和大小
- 93 bmp_width = (UINT *)(bmp_head + 0x12);
- 94 bmp_height = (UINT *)(bmp_head + 0x16);
- 95 bmp_size = (UINT *)(bmp_head + 0x22);
- 96 xil_printf(" width = %d, height = %d, size = %d bytes ",
- 97 *bmp_width,*bmp_height,*bmp_size);
- 98
- 99 //讀出圖片,寫入DDR
- 100 for(i=*bmp_height-1;i>=0;i--){
- 101 f_read(&fil,frame+i*(*bmp_width)*3,(*bmp_width)*3,&br);
- 102 }
- 103
- 104 //關閉文件
- 105 f_close(&fil);
- 106
- 107 Xil_DCacheFlush(); //刷新Cache,數據更新至DDR3中
- 108 xil_printf("show bmp");
- 109 }
- 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屏幕分辨率保持一致。我們在工程目錄下新建了一個名為“風景圖片”的文件夾,里面有四種不同分辨率的圖片,如下圖所示:
圖 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中可以看到應用程序打印的信息,如下圖所示:
圖 19.5.2 串口打印信息
圖 19.5.2中打印出了BMP圖片的文件頭和信息頭等信息,與圖 19.1.4中的數據保持一致。同時從數據中計算出BMP圖片的寬度為800,高度為480。另外根據讀出的LCD屏幕ID看出,所使用的LCD屏幕分辨率為800*480,與Micro SD卡中的BMP圖片分辨率一致。如果不一致,那么屏幕上就無法正常顯示圖片。
LCD屏幕上顯示的圖片如下圖所示,說明本次實驗在領航者ZYNQ開發板上面下載驗證成功。
圖 19.5.3 下載驗證
總結
以上是生活随笔為你收集整理的bmp文件头_「正点原子FPGA连载」第十九章SD卡读BMP图片LCD显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python二级多少分过_python考
- 下一篇: sklearn中的xgboost_xgb