13.2 TFT LCD顯示實例 13.2.1 程式設計 本實例的目的是從串口輸出一個菜單,從中選擇各種方法進行測試,比如畫線、 畫圓、顯示單色、使用調色板等。 13.2.2代碼詳解 本實例源碼在/work/hardware/lcd目錄下,與LCD相關的代碼有3個文件:lcddrv.c、 frame ...
13.2 TFT LCD顯示實例 13.2.1 程式設計 本實例的目的是從串口輸出一個菜單,從中選擇各種方法進行測試,比如畫線、 畫圓、顯示單色、使用調色板等。 13.2.2代碼詳解 本實例源碼在/work/hardware/lcd目錄下,與LCD相關的代碼有3個文件:lcddrv.c、 framebuffer.c和lcdlib.c(及相應的頭文件)。 (1)lcddrv.c封裝了對LCD控制器、調色板的訪問函數,可以設置LCD的顯示模式、 開啟/關閉LCD、設置調色板等。 (2)framebuffer.c直接操作幀緩衝區,實現畫點、畫線、畫同心圓、清屏等函數。 (3)lcdlib.c調用前兩個文件提供的函數在LCD上進行各種操作。 程式的結構如圖13.8所示。 1.main.c main.c的代碼很簡單,其主體如下:
1 c = getc(); 2 printf("%c\n\r", c); 3 switch(c) 4 { 5 case '1': 6 { 7 Test_Lcd_Tft_8Bit_240320(); 8 break; 9 } 10 11 case '2': 12 { 13 Test_Lcd_Tft_16Bit_240320(); 14 break; 15 } 16 17 case '3': 18 { 19 Test_Lcd_Tft_8Bit_640480(); 20 break; 21 } 22 23 case '4': 24 { 25 Test_Lcd_Tft_16Bit_640480(); 26 break; 27 } 28 }main.c主體代碼 它根據串口的輸入選擇是以哪種顯示模式操作LCD,所調用的4個函數都在lcdlib.c中實現。 2.lcdlib.c 8BPP模式將用到調色板,其操作比16BPP模式稍複雜,但大部分仍相似。下麵以 Test_Lcd_Tft_8Bit_240320為例進行說明。
1 行號 2 11行/* 3 12行 *以240x320、8BPP的顯示模式測試TFT LCD 4 13行 */ 5 14行void Test_Lcd_Tft_8Bit_240320(void) 6 15行{ 7 16行 Lcd_Port_Init(); //設置LCD引腳 8 17行 Tft_Lcd_Init(MODE_TFT_8BIT_240320); //初始化LCD控制器 9 18行 Lcd_PowerEnable(0, 1); //設置LCD_PWREN有效,它用於打開LCD的電源 10 19行 Lcd_EnvidOnOff(1); //使能LCD控制器輸出信號lcdlib.c->Test_Lcd_Tft_8bit_240320()_1 第16行設置所涉及的GPIO引腳用於LCD功能。 第17行調用Tft_Lcd_Init函數初始化LCD控制器,即設置各個控制信號的時間特性、 LCD顯示模式、幀緩衝區的地址等,它是lcddrv.c中最複雜的函數,在後面會詳細分析這個 函數。 進行第16、17行的初始化之後,只要打開lcd,幀緩衝區中的數據就會被LCD控制器 自動地發送到LCD上去顯示。打開操作由18、19行完成。 第18行發出LCD_PWREN信號。對於有電源開關控制引腳的LCD,可以使用其打開過關 閉LCD。LCD_PWREN信號的極性可以設置。 第19行使能LCD控制器輸出信號。這時,幀緩衝區中數據就開始在LCD上顯示出來了。 接下來就是按照設定的流程進行各類操作了,比如畫線、清屏等,代碼如下:
1 Lcd_Palette8Bit_Init(); //初始化調色板 2 ClearScr(0x0); //清屏 3 printf("[TFT 64K COLOR(16bpp) LCD TEST]\n"); 4 5 printf("1. Press any key to draw line\n"); 6 get(); 7 DrawLine(0 , 0 , 239, 0 , 0); //顏色為DEMO256pal[0] 8 DrawLine(0 , 0 , 0 , 319, 1); //顏色為DEM0256pal[1] 9 DrawLine(239, 0 , 239, 319, 2); //... 10 DrawLine(0 , 319, 239, 319, 4); 11 DrawLine(0 , 0 , 239, 319, 8); 12 DrawLine(239, 0 , 0 , 319, 16); 13 DrawLine(120, 0 , 120, 319, 32); 14 DrawLine(0 , 160, 239, 160, 64); 15 16 printf("2. Press any key to draw circles\n"); 17 getc(); 18 Mire(); 19 20 printf("3. Press any key to fill the screem with one color\n"); 21 getc(); 22 ClearScr(128); //輸出單色圖像,顏色值等於DEMO256pal[128] 23 24 printf("4. Press any key to fill the screem by temporary palette\n"); 25 getc(); 26 ClearScrWithTmpPlt(0x0000ff); //輸出單色圖像,顏色為藍色 27 28 printf("5. Press any key to fill the screem by palette\n"); 29 getc(); 30 DisableTmpPlt(); //關閉臨時調色板寄存器 31 ChangePalette(0xffff00); //改變整個調色板為黃色,輸出單色圖像 32 33 printf("6. Press any key to stop the testing\n"); 34 getc(); 35 Lcd_EnvidOnOff(0); 36 }lcdlib.c->Test_Lcd_Tft_8bit_240320()_2 將上面的函數分成3類: (1)清屏函數ClearScr、畫線函數DrawLine,都是通過framebuffer.c中的PutPixel函數 來設置幀緩衝區的數據,以像素為單位修改顏色來實現的。 (2)Lcd_Palette8Bit_Init函數:設置調色板,ChangePalette函數:通過設置調色板來 實現清屏功能,不涉及幀緩衝區,它在lcddrv.c中實現。 (3)ClearScrWithTmpPlt函數:通過臨時調色板寄存器來快速地輸出單色的圖像,也 不涉及幀緩衝區,它在lcddrv.c中實現 lcddrv.c、framebuffer.c文件中各個函數才是本實例的關鍵。可以認為lcddrv.c是對操作 各寄存器的封裝,framebuffer.c則是對操作圖像數據的封裝。先看lcddrv.c文件 3.lcddrv.c 這個文件中函數的重點在於Tft_Lcd_Init、Lcd_Palette8Bit_Init。 (1)Lcd_Port_Init函數。 設置所涉及的GPIO引腳用於LCD功能。 (2)Tft_Lcd_Init函數。 初始化LCD控制器,即設置各個控制信號的時間特性、LCD的顯示模式、幀緩衝區的地址等。 首先是對5個控制寄存器LCDCON1~5的設置,代碼如下:
1 /* 2 *初始化LCD控制器 3 *輸入參數: 4 *type:顯示模式 5 * MODE_TFT_8BIT_240320:240*320 8bpp的TFT LCD 6 * MODE_TFT_16BIT_240320:240*320 16bpp的TFT LCD 7 * MODE_TFT_8BIT_640480:640*480 8bpp的TFT LCD 8 * MODE_TFT_16BIT_640480:640*480 16bpp的TFT LCD 9 */ 10 void Tft_Lcd_Init(int type) 11 { 12 switch(type) 13 { 14 case MODE_TFT_8BIT_240320: 15 /* 16 *設置LCD控制器的控制寄存器LCDCON1~5 17 *1.LCDCON1 18 * 設置VCLK的頻率:VCLK(Hz) = HCLK/[(CLKVAL+1) x 2] 19 * 選擇LCD類型:TFT LCD 20 * 設置顯示模式:8BPP 21 * 先禁止LCD信號輸出 22 *2.LCDCON2/3/4 23 * 設置控制信號的時間參數 24 * 設置解析度,即行數和列數 25 *現在,可以根據公式算出顯示器的解析度 26 *當HCLK = 100MHz時, 27 *Frame Rate = 1/[{(VSPW+1) + (VBPD+1) + (LIINEVAL+1) + (VFPD+1)} x 28 * {(HSPW+1) + (HBPD+1) + (HFPD+1) + (HOZVAL+1)} x 29 * {(2x(CLKVAL+1)/(HCLK))}] 30 * = 60Hz 31 *3.LCDCON5 32 * 設置顯示模式為8BPP時,調色板中的數據格式為5:6:5 33 * 設置HSYNC、VSYNC脈衝的極性(這需要參考具體的LCD的介面信號):反轉位元組交換使能 34 */ 35 LCDCON1 = (CLKVAL_TFT_240320 << 8) | (LCDTYPE_TFT << 5) | \ 36 (BPPMODE_8BPP << 1) | (ENVID_DISABLE << 0); 37 LCDCON2 = (VBPD_240320 << 24) | (LINEVAL_TFT_240320 << 14) | \ 38 (VFPD_240320 << 6) | (VSPW_240320); 39 LCDCON3 = (HBPD_240320 << 19) | (HOZVAL_TFT_240320 << 8) | (HFPD_240320); 40 LCDCON4 = HSPW_240320; 41 LCDCON5 = (FORMAT8BPP_565 << 11) | (HSYNC_INV << 9) | (VSYNC_INV << 8) | \ 42 (BSWP << 1);lcddrv.c->Tft_Lcd_Init() 時間參數VSPW、VBPD、VFPD、HSPW、HBPD、HFPD、CLKVAL的設置可以 從LCD數據手冊瞭解到,或使用經驗值,或自行調整,並根據上面的公式確認顯示頻 率在60Hz左右或之上。 接下來是地址寄存器LCDSADDR1~3的設置,請參考圖13.7幀記憶體與視圖的位置關 系。在本程式中,幀記憶體與視圖吻合,即圖中的OFFSIZE為0,LCDBANK、LCDBASEU 指向同一個地址(它們是同一個地址的不同位)。 需要註意的是,8BPP的顯示模式要用到調色板,幀緩衝區中的數據不是顏色值,而 是調色板中的索引值,真正的顏色值在調色板中。
1 行號 2 78行 /* 3 79行 *設置LCD控制器的地址寄存器:LCDSADDR1~3 4 80行 *幀記憶體與視口(view point)完全吻合 5 81行 *圖像數據格式如下(8BPP時,幀緩衝區中的數據為調色板中的索引值): 6 82行 * |--------- PAGEWIDTH ----------| 7 83行 * y/x 0 1 2 239 8 84行 * 0 idx idx idx ... idx 9 85行 * 1 idx idx idx ... idx 10 86行 *1.LCDSADDR1 11 87行 * 設置LCDBANK、LCDBASEU 12 88行 *2.LCDSADDR2 13 89行 * 設置LCDBASEL:幀緩衝區的結束地址A[21:1] 14 90行 *3.LCDSADDR3 15 91行 * OFFSIZE等於0,PAGEWIDTH等於(240/2) 16 92行 */ 17 93行 LCDSADDR1 = ((LCDFRAMEBUFFER >> 22) << 21) | LOWER21BITS (LCDFRAMEBUFFER >> 1); 18 94行 LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ \ 19 95行 (LINEVAL_TFT_240320 + 1) x (HOZVAL_TFT_240320 + 1) x 1) >> 1); 20 96行 LCDSADDR3 = (0 << 11) | (LCD_XSIZE_TFT_240320/2); 21 97行設置LCD控制器的地址寄存器 第93行將幀緩衝區的開始地址寫入LCDSADDR1寄存器。 第94行先計算幀緩衝區的結束地址,再取其位[21:1]存入LCDSADDR2中。這個地址值 在本實例中即是“LCDFRAMEBUFFER+320x240x1”,其中的“x1”表示在8BPP中一個像素 使用1個位元組表示(對於16BPP,就是“x2”)。 在設置寄存器的最後,禁止臨時調色板寄存器,現在還沒用到它。
行號 98行 /*禁止臨時調色板寄存器*/ 99行 TPAL = 0; 100行最後,將顯示模式的主要參數記錄下來,在framebuffer.c中需要用到。
101行 fb_base_addr = LCDFRAMEBUFFER; 102行 bpp = 8; 103行 xsize = 240; 104行 ysize = 320; 105行其他顯示模式的寄存器設置非常相似,不再贅述。 需要說明的是,顯示模式為8BPP時,LCDCON5中BSWAP位設為1,表示“位元組交換 使能”,這時幀緩衝區中的數據與屏幕上的像素位置關係如圖13.6所示; 顯示模式為16BPP時,LCDCON5中HWSWAP位設為1,表示“半字交換使能”,這時 幀緩衝區中的數據與屏幕上的像素位置關係如圖13.5所示。它們都是“低地址的數據”對 應“位置靠前”的像素。 (3)Lcd_Palette8Bit_Init函數。 設置調色板上的數據:調色板大小為256x16,而8BPP模式中每個像素的索引值占據8 位,剛好有256個索引值。代碼如下:
1 行號 2 296行 /* 3 297行 *設置調色板 4 298行 */ 5 299行 void Lcd_Palette8Bit_Init(void) 6 300行 { 7 301行 int i; 8 302行 volatile unsigned int *palette; 9 303行 10 304行 LCDCON5 |= (FORMAT8BPP_565 << 11); //設置調色板中數據格式為:5:6:5 11 305行 12 306行 palette = (volatile unsigned int *)PALETTE; 13 307行 for(i = 0; i < 256; i++) 14 308行 *palette++ = DEMO256pal[i]; 15 309行 } 16 310行Lcd_Palette8Bit_Init() 調色板中用16BPP的格式表示顏色。 第307、308行將數組DEMO256pal中數據寫入調色板。這個數組中的數據沒有 什麼特別之處,讀者可以自行構造。 (4)ChangePalette函數。 以給定的顏色值填充整個調色板,代碼如下:
1 行號 2 311行 /* 3 312行 *改變調色板為一種顏色 4 313行 *輸入參數: 5 314行 * color:顏色值,格式為0xRRGGBB 6 315行 */ 7 316行 void ChangePalette(UINT32 color) 8 317行 { 9 318行 int i; 10 319行 unsigned char red, green, blue; 11 320行 UINT32 *palette; 12 321行 13 322行 palette = (UINT32 *)PALETTE; 14 323行 for(i = 0; i < 256; i++) 15 324行 { 16 325行 red = (color >> 19) & 0xff; 17 326行 green = (color >> 10) & 0xff; 18 327行 blue = (color >> 3) & 0xff; 19 328行 color = (red << 11) | (green << 5) | blue; //格式:5:6:5 20 329行 21 330行 while((LCDCON5 >> 16) == 2); //等待直到VSTATUS不為“有效” 22 331行 *palette++ = color; 23 332行 } 24 333行 } 25 334行ChangePalette() 第330行檢測當前VSYNC信號的狀態,如果它處於有效的狀態,則等待。前面說過, 讀寫調色板時,VSTATUS、HSTATUS不能處於有效狀態。這裡當VSTATUS不是“有效” 狀態時,HSTATUS也不可能是“有效”狀態。 (5)Lcd_PowerEnable函數。 用於控制是否發出LCD_PWREN信號。對於有電源開關控制引腳的LCD,可以使用 LCD_PWREN來打開或關閉LCD。LCD_PWREN信號的極性可以設置。代碼如下:
1 /* 2 *設置是否輸出LCD電源開關信號LCD_PWREN 3 *輸入參數: 4 * invpwren:0表示LCD_PWREN有效時為正常極性 5 * 1表示................反轉極性 6 * pwren :0表示LCD_PWREN輸出有效 7 * 1表示LCD_PWREN輸出無效 8 */ 9 void Lcd_PowerEnable(int invpwren, int pwren) 10 { 11 GPGCON = (GPGCON & (~(3 << 8))) | (3 << 8); //GPG4用於LCD_PWREN 12 GPGUP = (GPGUP & (~(1 << 4))) | (1 << 4); //禁止內部上拉 13 14 LCDCON5 = (LCDCON5 & (~(1 << 5))) | (invpwren << 5); //設置LCD_PWREN的極性:正常/反轉 15 LCDCON5 = (LCDCON5 & (~(1 << 3))) | (pwren << 3); //設置是否輸出LCD_PWREN 16 }Lcd_PowerEnable() (6)Lcd_EnvidOnOff函數 用於控制是否使能LCD控制器輸出各個LCD信號,當設置如控制寄存器、地址寄存器 之後,即可調用此函數輸出各個LCD信號,這樣,幀緩衝區中的數據即發送給LCD。代碼如下:
1 /* 2 *設置LCD控制器是否輸出信號 3 *輸入參數: 4 *onoff: 5 * 0:關閉 6 * 1:打開 7 */ 8 void Lcd_EnvidOnOff(int onoff) 9 { 10 if(onoff == 1) 11 LCDCON1 |= 1; //ENVID ON 12 else 13 LCDCON1 &= 0x3fffe; //ENVID OFF 14 }Lcd_EnvidOnOff (7)ClearScrWithTmpPlt、DisableTmpPlt函數。 參考13.13TPAL寄存器格式,ClearScrWithTmpPlt函數設置顏色值並使能TPAL寄 存器,這使得LCD上顯示單一顏色圖像。DisableTmpPlt函數停止TPAL寄存器的功能, 繼續輸出幀緩衝區的圖像。它們的代碼如下:
1 /* 2 *使用臨時調色板寄存器輸出單色圖像 3 *輸入參數: 4 * color:顏色值,格式為0xRRGGBB 5 */ 6 void ClearScrWithTmpPlt(UINT32 color) 7 { 8 TPAL = (1 << 24) | ((color & 0xffffff) << 0); 9 } 10 11 /* 12 *停止使用臨時調色板寄存器 13 */ 14 void DisableTmpPlt(void) 15 { 16 TPAL = 0; 17 }ClearScrWithTmpPlt()和DisableTmpPlt() 4.framebuffer.c 此文件有4個函數:畫點PutPixel、畫線DrawLine、繪製同心圓Mire、清屏ClearScr, 後3個函數都是基於PutPixel函數實現的。畫點函數時framebuffer.c文件的核心,它在 幀緩衝區中找到給定坐標的像素的記憶體,然後修改它的值,代碼如下:
1 行號 2 8行 extern unsigned int fb_base_addr; 3 9行 extern unsigned int bpp; 4 10行 extern unsigned int xsize; 5 11行 extern unsigned int ysize; 6 12行 7 13行 /* 8 14行 *畫點 9 15行 *輸入參數: 10 16行 * x、y:像素坐標 11 17行 * color:顏色值 12 18行 * 對於16BPP:color的格式為0xAARRGGBB(AA = 透明度), 13 19行 * 需要轉換為5:6:5格式 14 20行 * 對於8BPP:color為調色板中索引值, 15 21行 * 其顏色取決於調色板中的數值 16 22行 */ 17 23行 void PutPixel(UINT32 x, UINT32 y, UINT32 color) 18 24行 { 19 25行 UINT8 red, green, blue; 20 26行 21 27行 switch(bpp){ 22 28行 case 16: 23 29行 { 24 30行 UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x); 25 31行 red = (color >> 19) & 0xff; 26 32行 green = (color >> 10) & 0xff; 27 33行 blue = (color >> 3) & 0xff; 28 34行 color = (red << 11) | (green << 5) | blue; //格式:5:6:5 29 35行 *addr = (UINT16)color; 30 36行 break; 31 37行 } 32 38行 33 39行 case 8: 34 40行 { 35 41行 UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x); 36 42行 *addr = (UINT8)color; 37 43行 break; 38 44行 } 39 45行 40 46行 default: 41 47行 break; 42 48行 } 43 49行 } 44 50行PutPixel 第8~11行的4個變數在lcddrv.c中的Tft_Lcd_Init函數中設置,PutPixel函數根據它們 來確定給定坐標的像素在幀緩衝區中的地址。 對於16BPP模式,每個像素占2位元組;對於8BPP模式,每個像素占1位元組。 對於16BPP模式,第31~34行從0xAARRGGBB格式的color變數中,提取8位紅色值 的高5位、8位綠色值的高6位、8位藍色值的高5位組成5:6:5格式的16BPP顏色值。 最後,第35、42行將顏色值(對於8BPP模式,為調色板的索引值)寫入幀緩衝區中, 這樣,下一次顯示時,新顏色即可顯示出來。 13.2.3 實例測試 本程式在main函數中通過串口輸出一個菜單,用於選擇LCD的顯示模式進行測試。 實驗步驟如下: (1)使用串口連接開發板和PC,打開PC上串口工具並設置為115200、8N1. (2)在LCD目錄下執行make命令生成lcd可執行程式,燒入NAND Flash後運行。 (3)在PC串口工具上,可以看到如下菜單: #### Test TFT LCD #### [1] TFT240320 8Bit [2] TFT240320 16Bit [3] TFT640480 8Bit [4] TFT640480 16Bit Enter your selection: (4)可以輸入1、2、3或4,然後按照提示輸入任意鍵可一步一步地觀察到LCD中圖像 的變化。 (5)最後又會出現第(3)步驟的菜單,可以再次選擇。 附:代碼: 鏈接: https://pan.baidu.com/s/1kV24a9L 密碼: tfab