Mini2440裸機開發之LCD程式設計
在上一節我們介紹了LCD的硬體基礎只是、以及S3C2440 LCD控制器相關的暫存器。這一節我們將會動手在LCD上顯示一幅日落的圖片。
一、LCD初始化程式設計步驟
1.1初始化GPIO,引腳複用
在上一節我們介紹了S3C2440這些引腳對應的LCD TFT上的引腳。這裡就不在重複介紹了。我們需要配置Port C和Port D為LCD功能。
埠C相關暫存器的相關資訊。
暫存器 | 地址 | R/W | 描述 | 復位值 |
GPCCON | 0x56000020 | R/W | 配置埠C的引腳 | 0x00 |
GPCDAT | 0x56000024 | R/W | 配置C的資料暫存器 | - |
GPCUP | 0x56000028 | R/W | 埠C的上拉使能暫存器 | 0x00 |
保留 | 0x5600002C |
- | 保留 | - |
(1) GPCCON
GPCCON | 位 | 描述 | 初始狀態 |
GPC15 | [31:30] | 00 = 輸入 01 = 輸出 10 = VD[7] 11 = 保留 | 0 |
GPC14 | [29:28] | 00 = 輸入 01 = 輸出 10 = VD[6] 11 = 保留 | 0 |
GPC13 | [27:26] | 00 = 輸入 01 = 輸出 10 = VD[5] 11 = 保留 | 0 |
GPC12 | [25:24] | 00 = 輸入 01 = 輸出 10 = VD[4] 11 = 保留 | 0 |
GPC11 | [23:22] | 00 = 輸入 01 = 輸出 10 = VD[3] 11 = 保留 | 0 |
GPC10 | [21:20] | 00 = 輸入 01 = 輸出 10 = VD[2] 11 = 保留 |
0 |
GPC9 | [19:18] | 00 = 輸入 01 = 輸出 10 = VD[1] 11 = 保留 | 0 |
GPC8 | [17:16] | 00 = 輸入 01 = 輸出 10 = VD[0] 11 = 保留 | 0 |
GPC7 | [15:14] | 00 = 輸入 01 = 輸出 10 = LCD_LPCREVB 11 = 保留 | 0 |
GPC6 | [13:12] | 00 = 輸入 01 = 輸出 10 = LCD_LPCREV 11 = 保留 | 0 |
GPC5 | [11:10] | 00 = 輸入 01 = 輸出 10 = LCD_LPCOE 11 = 保留 | 0 |
GPC4 | [9:8] | 00 = 輸入 01 = 輸出 10 = VM 11 = 保留 | 0 |
GPC3 | [7:6] |
00 = 輸入 01 = 輸出 10 = VFRAME 11 = 保留 | 0 |
GPC2 | [5:4] | 00 = 輸入 01 = 輸出 10 = VLINE 11 = 保留 | 0 |
GPC1 | [3:2] | 00 = 輸入 01 = 輸出 10 = VCLK 11 = 保留 | 0 |
GPC0 | [1:0] | 00 = 輸入 01 = 輸出 10 = LEND 11 = 保留 | 0 |
由上表可知,B埠的控制暫存器可以將每個引腳配置為四種模式:
- 00:輸入模式
- 01:輸出模式
- 10:功能擴充套件模式
- 11:保留模式
配置埠C功能複用為LCD:
GPCCON=0xaaaaaaaa
(2) GPCDAT
GPCDAT | 位 | 描述 |
GPC[15:0] | [15:0] |
當埠配置為輸入埠時,相應位為引腳狀態。當埠配置為輸出埠時,引腳狀態將與相應位相同。當埠配置為功能引腳,將讀取到未定義值。 |
(3) GPCUP
GPCUP | 位 | 描述 |
GPC[15:0] | [15:0] |
0:使能附加上拉功能到相應埠引腳 1:禁止附加上拉功能到相應埠引腳 |
禁止上拉:
GPCUP = 0xffffffff;
埠D相關暫存器的相關資訊。
暫存器 | 地址 | R/W | 描述 | 復位值 |
GPDCON | 0x56000030 | R/W | 配置埠D的引腳 | 0x00 |
GPDDAT | 0x56000034 | R/W | 配置D的資料暫存器 | - |
GPDUP | 0x56000038 | R/W | 埠D的上拉使能暫存器 | 0xF000 |
保留 | 0x5600003C | - | 保留 | - |
(1) GPDCON
GPDCON | 位 | 描述 | 初始狀態 |
GPD15 | [31:30] | 00 = 輸入 01 = 輸出 10 = VD[23] 11 = 保留 | 0 |
GPD14 | [29:28] | 00 = 輸入 01 = 輸出 10 = VD[22] 11 = 保留 | 0 |
GPD13 | [27:26] | 00 = 輸入 01 = 輸出 10 = VD[21] 11 = 保留 | 0 |
GPD12 | [25:24] | 00 = 輸入 01 = 輸出 10 = VD[20] 11 = 保留 | 0 |
GPD11 | [23:22] | 00 = 輸入 01 = 輸出 10 = VD[19] 11 = 保留 | 0 |
GPD10 | [21:20] | 00 = 輸入 01 = 輸出 10 = VD[18] 11 = 保留 | 0 |
GPD9 | [19:18] | 00 = 輸入 01 = 輸出 10 = VD[17] 11 = 保留 | 0 |
GPD8 | [17:16] | 00 = 輸入 01 = 輸出 10 = VD[16] 11 = 保留 | 0 |
GPD7 | [15:14] | 00 = 輸入 01 = 輸出 10 = VD[15] 11 = 保留 | 0 |
GPD6 | [13:12] | 00 = 輸入 01 = 輸出 10 = VD[14] 11 = 保留 | 0 |
GPD5 | [11:10] | 00 = 輸入 01 = 輸出 10 =VD[13] 11 = 保留 | 0 |
GPD4 | [9:8] | 00 = 輸入 01 = 輸出 10 = VD[12] 11 = 保留 | 0 |
GPD3 | [7:6] | 00 = 輸入 01 = 輸出 10 =VD[11] 11 = 保留 | 0 |
GPD2 | [5:4] | 00 = 輸入 01 = 輸出 10 = VD[10] 11 = 保留 | 0 |
GPD1 | [3:2] | 00 = 輸入 01 = 輸出 10 =VD[9] 11 = 保留 | 0 |
GPD0 | [1:0] | 00 = 輸入 01 = 輸出 10 =VD[8] 11 = 保留 | 0 |
由上表可知,B埠的控制暫存器可以將每個引腳配置為四種模式:
- 00:輸入模式
- 01:輸出模式
- 10:功能擴充套件模式
- 11:保留模式
配置埠D功能複用為LCD:
GPDCON=0xaaaaaaaa
(2) GPDDAT
GPCDAT | 位 | 描述 |
GPD[15:0] | [15:0] |
當埠配置為輸入埠時,相應位為引腳狀態。當埠配置為輸出埠時,引腳狀態將與相應位相同。當埠配置為功能引腳,將讀取到未定義值。 |
(3) GPDUP
GPCUP | 位 | 描述 |
GPD[15:0] | [15:0] |
0:使能附加上拉功能到相應埠引腳 1:禁止附加上拉功能到相應埠引腳 |
禁止上拉:
GPDUP = 0xffffffff;
總結下來:
void _lcd_gpio_init() { GPCUP = 0xffffffff; /* Disable Pull - up register */ GPCCON = 0xaaaaaaaa; /* Initialize VD[7:0], VM, VFRAME, VLINE, VCLK */ GPDUP = 0xffffffff; /* Disable Pull - up register */ GPDCON = 0xaaaaaaaa; /* Initialize VD[15:8] */ }
1.2開啟LCD電源
LCD_PWR對應的引腳,所以設定GPG4就可以控制LED背光電源了。(GPGCON:9-8寫入11),這時候LCD電源的開啟/關閉可以通過LCDCON5位3來控制:
LCD_PWREN 輸出訊號使能/禁止:0 = 禁止PWREN 訊號 1 = 允許PWREN訊號:
void _lcd_power_on() { /* 開啟LCD電源 */ GPGUP |= (1 << 4); /* Pull - up disable */ GPGCON |= (3 << 8); /* 設定GPG4引腳為LCD_PWRDN模式 */ LCDCON5 |= (1 << 3); /* LCD_PWREN輸出訊號使能 */ }
1.3設定其他訊號線
其他訊號線包括VD0-VD23和VFRAME、VLINE、VCLK等,分別在GPCCON,GPDCON中選擇相應功能。這些在上一節已經介紹過。這裡就稍微提一下
1) 設定VLCK、BPPMODE、PNRMODE
(VCLK)LCD的Datasheet上一般會寫有一個推薦的頻率,上一節已經介紹過我使用的螢幕推薦頻率為6.4M,我們通過計算得到的CLKVAL=7比較合適。寫入LCDCON1位17-8;
位4-1選擇BPP(位每畫素)模式 1100 = TFT 的16 BPP;
位6-5選擇顯示模式 11 = TFT LCD 面板
2) 設定其他相關引數
LCD相關的引數主要還有這幾個:LINEVAL: LCD水平畫素-1,如240-1 = 239;
HOZVAL: LCD垂直畫素-1,如320-1 = 319;
HFPD: 行開始前的VCLK時鐘數(LCD螢幕的Datasheet一般有推薦值);
HBPD: 行結束後的VCLK時鐘數(LCD螢幕的Datasheet一般有推薦值);
HSPW: 行之間水平同步的無效VCLK時鐘數(LCD螢幕的Datasheet一般有推薦值);
VFPD: 幀資料開始前的空白行數(LCD螢幕的Datasheet一般有推薦值);
VBPD: 幀資料結束後的空白行數(LCD螢幕的Datasheet一般有推薦值);
VSPW: 幀之間垂直同步的無效行數(LCD螢幕的Datasheet一般有推薦值);
(相關暫存器LCDCON2, LCDCON3, LCDCON4);
3) 設定視訊緩衝區的地址
S3C2440支援虛擬螢幕,可以通過改變LCD暫存器實現螢幕快速移動;
PAGEWIDTH:虛擬螢幕一行的位元組數,如果不使用虛擬螢幕,設定為實際螢幕的行半字數,如16位寬320畫素,設為320 ;
OFFSIZE:虛擬螢幕左側偏移的位元組數,如果不使用虛擬螢幕,設定為0;
LCDBANK: 視訊幀緩衝區記憶體地址30-22位;
LCDBASEU: 視訊幀緩衝區的開始地址21-1位;
LCDBASEL: 視訊幀緩衝區的結束地址21-1位;
(相關暫存器LCDSADDR1,LCDSADDR2,LCDSADDR3);
4) 確定訊號的極性
S3C2440的LCD控制器允許設定VCLK、VLINE、VFRAME等訊號的極性(上升沿有效還是下降沿有效),需要對照LCD的Datasheet一一確認。
(相關暫存器LCDCON5)
5) 關中斷
設定LCDINTMSK位1-0。
6) 禁止LPC3600/LCC3600模式!
如果不是使用三星LPC3600/LCC3600 LCD,必須禁止LPC3600/LCC3600模式(寫入0到TCONSEL位4、0)。
7) 關閉臨時調色盤
TPAL位24設定為0。
總體設定程式碼如下:
void _lcd_control_init() { /* [17:8] CLKVAL * [6:5] PNRMODE;選擇顯示模式 * 00 = 4 位雙掃描顯示模式 01 = 4 位單掃描顯示模式(STN) * 10 = 8 位單掃描顯示模式 11 = TFT LCD 面板 * [4:1] BPPMODE 選擇BPP(位每畫素)模式 1100 = TFT 的16 BPP * [0] ENVID LCD 視訊輸出和邏輯使能/禁止。 * 0 = 禁止視訊輸出和LCD 控制訊號 1 = 允許視訊輸出和LCD 控制訊號 */ LCDCON1 = (CLKVAL<<8)| (3<<5)|(0xC<<1); /* 16 bpp for TFT */ /* [31:24] VBPD:幀同步訊號的後肩 * [23:14] LINEVAL:LCD面板的垂直尺寸 * [13:6] VFPD:幀同步訊號的前肩 * [5:0] VSPW:同步訊號的脈寬 */ LCDCON2 = (VBPD<<24)|(LINEVAL<<14)|(VFPD<<6)|(VSPW); /* [25:19] HBPD: 行同步訊號的後肩 * [18:8] HOZVAL: LCD面板的水平尺寸 * [7:0] HFPD: 行同步訊號的前肩 */ LCDCON3 = (HBPD<<19)|(HOZVAL<<8)|(HFPD); LCDCON4 = (HSPW); /* [11] FRM565: 此位選擇16 BPP 輸出視訊資料的格式 0 = 5:5:5:1 格式 1= 5:6:5 格式 * [10] STN/TFT: 此位控制VCLK 有效沿的極性 * [9] INVVLINE: STN/TFT:此位表明VLINE/HSYNC 脈衝極性 0 = 正常 1 = 反轉 * [8] INVVFRAME: STN/TFT:此位表明VFRAME/VSYNC 脈衝極性 0 = 正常 1 = 反轉 * VLINE/HSYNC 脈衝極性、VFRAME/VSYNC 脈衝極性反轉(LCD型號決定) * [0] HWSWP: STN/TFT:半位元組交換控制位 0 = 交換禁止 1 = 交換使能 */ LCDCON5 = ((1<<11) | (1<<10) | (1 << 9) | (1 << 8) | (1 << 0)); #define M5D(n) ((n)&0x1fffff) #define LCD_ADDR ((u32)LCD_BUFFER) /* FrameBuffer地址 */ /* [29:21] LCDBANK:存放幀緩衝起始地址的[30:22] * [20:0] LCDBASEU: 存放幀緩衝起始地址的[21:1] */ LCDSADDR1 = ((LCD_ADDR >> 22) << 21) | (M5D(LCD_ADDR >> 1)) ; /* 存放幀結束地址[21:1] */ LCDSADDR2 = M5D((LCD_ADDR + (LCD_WIDTH * LCD_HEIGHT * 2 )) >> 1); /* [21:11] OFFSIZE:表示虛擬屏偏移尺寸 即上一行最後畫素點到下一行第一個畫素點之間間隔多少個畫素點 * [10:0] PAGEWIDTH:表示行的寬度(半字為單位 16bit) */ LCDSADDR3 = LCD_WIDTH; LCDINTMSK |= 3; TCONSEL &= ~((1 << 4) | 1); TPAL = 0x00; /* 關閉臨時調色盤 */ }
1.4 開啟視訊輸出
ENVID設為1 (LCDCON1:0寫入1)。
/* LCD使能 */ LCDCON1 |= 1;
到此LCD初始化步驟就介紹完成了,整體程式碼如下:
void lcd_init() { _lcd_gpio_init(); _lcd_power_on(); _lcd_control_init(); /* LCD使能 */ LCDCON1 |= 1; }
二、顯示BMP圖片
如果想在LCD上顯示一幅圖片,只需要向LCD_BUFFER緩衝區寫入圖片資料即可。
void lcd_draw_bmp(u16 x0,u16 y0,u16 width,u16 height,const u8 *bmp) { u16 x,y; u16 c; u32 p = 0; /* 繪製每一行 */ for( y = 0 ; y < height; y++ ) { /* 繪製每一點 */ for( x = 0 ; x < width; x++ ) { c = bmp[p + 1] | (bmp[p] << 8); /* bmp.c中的陣列元素大小是8bit,螢幕畫素設定16bit */ if (((x0 + x) < LCD_WIDTH) && ( (y0 + y) < LCD_HEIGHT)) /* LCD尺寸範圍,根據實際LCD設定 */ { LCD_BUFFER[y0 + y][x0 + x] = c; } p = p + 2 ; /* 螢幕畫素設定16bit,所以要+2 */ } } }
在Ecllispe除錯模式下,將程式碼直接下載SDRAM地址0x30000000執行,圖片顯示正常:
但是通過MiniTools下載到SDRAM地址0x30000000執行,圖片缺顯示正常:
目前原因還未排查清楚。
三、顯示字元
3.1 字元顯示原理
3.2 字元製作