2440的lcd控制器能夠驅動 STN LCD 和 TFT LCD 本文以較爲常見的 TFT LCD 來說解使用方法:
數組
首先, 咱們來看下2440的 lcd controller 組成框圖:函數
REGBANK: LCD控制器的寄存器組, 含有17個寄存器及一塊256x16的調色板spa
LCDCDMA: LCD控制器的專用DMA通道, 能夠自動從系統總線上獲取圖像數據, 顯示圖像時不須要cpu core的參與指針
TIMEGEN / LPC3600: 產生控制時序, 如: VSYNC, HSYNC, VCLK, VDEN, 而這些信號又與REGBANK中的LCDCON1/2/3/4的配置密切相關, 經過不一樣的配置產生不一樣的控制信號. 而後從VIDEO MUX中傳遞給液晶屏(LPC3600爲STN屏專用)code
VIDPRCS: 接收LCDCDMA 的數據, 而後轉換爲合適的數據格式, 好比 4 bit單掃 / 8ibt單掃 /4bit雙掃, 而後由 VD[23:0]來顯示索引
其次, 咱們來分析一下lcd controller的時序:
接口
VSYNC/VFRAME/STV:垂直同步信號(TFT)/幀同步信號(STN)/SEC TFT信號 HSYNC/VLINE/CPV: 水平同步信號(TFT)/行同步脈衝信號(STN)/SEC TFT信號 VCLK/LCD_HCLK: 像素時鐘信號(TFT/STN)/SEC TFT信號 VD[23:0]: LCD像素數據輸出端口(TFT/STN/SEC TFT) VDEN/VM/TP: 數據使能信號(TFT)/LCD驅動交流偏置信號(STN)/SEC TFT 信號 LEND/STH: 行結束信號(TFT)/SEC TFT信號 LCD_LPCOE: SEC TFT OE信號 LCD_LPCREV: SEC TFT REV信號 LCD_LPCREVB: SEC TFT REVB信號
全部顯示器顯示圖像的原理都是從上到下,從左到右的。這是什麼意思呢?這麼說吧,一副圖像能夠看作是一個矩形,由不少排列整齊的點一行一行組成,這些點稱之爲像素。那麼這幅圖在LCD上的顯示原理就是:圖片
A:顯示指針從矩形左上角的第一行第一個點開始,一個點一個點的在LCD上顯示,在上面的時序圖上表示爲VCLK,咱們稱之爲像素時鐘信號 B:當顯示指針一直顯示到矩形的右邊就結束這一行,那麼這一行的動做在上面的時序圖中就稱之爲1 Line C:接下來顯示指針又回到矩形的左邊從第二行開始顯示,注意,顯示指針在從第一行的右邊回到第二行的左邊是須要必定的時間的,咱們稱之爲行切換 D:如此類推,顯示指針就這樣一行一行的顯示至矩形的右下角才把一副圖顯示完成。行的顯示在時序圖上看就是HSYNC E:然而,LCD要顯示多個圖片就要一幅一幅的切換, 那麼這每一幅圖像就稱之爲幀,在時序圖上就表示爲1 Frame,所以從時序圖上能夠看出1 Line只是1 Frame中的一行 F:一樣的,在幀與幀切換之間也是須要必定的時間的,咱們稱之爲幀切換,那麼LCD整個顯示的過程在時間線上看,就可表示爲時序圖上的VSYNC
上面時序圖上各時鐘延時參數的含義以下:(這些參數的值,LCD產生廠商會提供相應的數據手冊)內存
VBPD(vertical back porch):表示在一幀圖像開始時,垂直同步信號之後的無效的行數,對應驅動中的upper_margin VFBD(vertical front porch):表示在一幀圖像結束後,垂直同步信號之前的無效的行數,對應驅動中的lower_margin VSPW(vertical sync pulse width):表示垂直同步脈衝的寬度,用行數計算,對應驅動中的vsync_len HBPD(horizontal back porch):表示從水平同步信號開始到一行的有效數據開始之間的VCLK的個數,對應驅動中的left_margin HFPD(horizontal front porth):表示一行的有效數據結束到下一個水平同步信號開始之間的VCLK的個數,對應驅動中的right_margin HSPW(horizontal sync pulse width):表示水平同步信號的寬度,用VCLK計算,對應驅動中的hsync_len
使用LCD的步驟:同步
Lcd_Port_Init(); // 設置LCD引腳 Tft_Lcd_Init(MODE_TFT_16BIT_240320); // 初始化LCD控制器, 這裏配置了液晶的顯示模式, 如: 分辨率 240x320 顏色深度 16bit Lcd_PowerEnable(0, 1); // 設置LCD_PWREN有效,它用於打開LCD的電源 Lcd_EnvidOnOff(1); // 使能LCD控制器輸出信號 ClearScr(0x0); // 清屏,黑色
GPCUP = 0xffffffff; // 禁止內部上拉 GPCCON = 0xaaaaaaaa; // GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND GPDUP = 0xffffffff; // 禁止內部上拉 GPDCON = 0xaaaaaaaa; // GPIO管腳用於VD[23:8] GPBCON &= ~(GPB0_MSK); // Power enable pin GPBCON |= GPB0_out; // GPB0控制液晶的背光ic輸出使能 GPBDAT &= ~(1<<0); // Power off printf("Initializing GPIO ports..........\n");
#define CLKVAL_TFT_640480 (1) #define LCDTYPE_TFT 0x3 #define BPPMODE_16BPP 0xC #define ENVID_DISABLE 0 LCDCON1 = (CLKVAL_TFT_640480<<8) | (LCDTYPE_TFT<<5) | (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0); /* 設置VCLK的頻率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2] 選擇LCD類型: TFT LCD 設置顯示模式: 16BPP 先禁止LCD信號輸出 */
#define VBPD_640480 ((33-1)&0xff) #define LINEVAL_TFT_640480 (LCD_YSIZE_TFT_640480-1) #define VFPD_640480 ((10-1)&0xff) #define VSPW_640480 ((2-1) &0x3f) LCDCON2 = (VBPD_640480<<24) | (LINEVAL_TFT_640480<<14) | (VFPD_640480<<6) | (VSPW_640480);
#define HBPD_640480 ((48-1)&0x7f) #define HOZVAL_TFT_640480 (LCD_XSIZE_TFT_640480-1) #define HFPD_640480 ((16-1)&0xff) LCDCON3 = (HBPD_640480<<19) | (HOZVAL_TFT_640480<<8) | (HFPD_640480);
#define HSPW_640480 ((96-1)&0xff) LCDCON4 = HSPW_640480;
#define FORMAT8BPP_565 1 #define HSYNC_INV 1 #define VSYNC_INV 1 #define HWSWP 1 LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | (HWSWP<<1); /* 16bpp 565 設置HSYNC、VSYNC脈衝的極性(這須要參考具體LCD的接口信號): 反轉 半字(2字節)交換使能 */
#define LCDFRAMEBUFFER 0x30400000 #define LOWER21BITS(n) ((n) & 0x1fffff) LCDSADDR1 = ((LCDFRAMEBUFFER>>22)<<21) | LOWER21BITS(LCDFRAMEBUFFER>>1); /* 0x30400000的[30:22]值爲LCDSADDR1[29:21]的值, 因此0x30400000>>22以後再左移21位 0x30400000的[21:1]值爲LCDSADDR1[20:0]的值, 因此0x30400000>>1 爲應該配置的值, 這個值只保留低21位, 因此 又與 0x1fffff相與 */
#define HOZVAL_TFT_640480 (LCD_XSIZE_TFT_640480-1) #define LINEVAL_TFT_640480 (LCD_YSIZE_TFT_640480-1) LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+(LINEVAL_TFT_640480+1)*(HOZVAL_TFT_640480+1)*2)>>1); /* 這裏是計算幀緩衝的結束地址, 本例中佔用緩衝區大小爲: (LINEVAL_TFT_640480+1) * 640 * 480 * 2, 這裏 *2 是由於16bpp, 若是是8bpp則應 *1. 這個大小再加上起始地址LCDFRAMEBUFFER就獲得告終束地址 */
#define LCD_XSIZE_TFT_640480 (640) #define LCD_YSIZE_TFT_640480 (480) LCDSADDR3 = (0<<11) | (LCD_XSIZE_TFT_640480*2/2);
若是須要禁止調色板:
/* 禁止臨時調色板寄存器 */ TPAL = 0;
幀地址:
unsigned int fb_base_addr; unsigned int bpp; unsigned int xsize; unsigned int ysize; fb_base_addr = LCDFRAMEBUFFER; bpp = 16; xsize = 640; ysize = 480;
* 設置是否輸出LCD電源開關信號LCD_PWREN * 輸入參數: * invpwren: 0 - LCD_PWREN有效時爲正常極性 * 1 - LCD_PWREN有效時爲反轉極性 * pwren: 0 - LCD_PWREN輸出有效 * 1 - LCD_PWREN輸出無效 */ void Lcd_PowerEnable(int invpwren, int pwren) { GPGCON = (GPGCON & (~(3<<8))) | (3<<8); // GPG4用做LCD_PWREN GPGUP = (GPGUP & (~(1<<4))) | (1<<4); // 禁止內部上拉 LCDCON5 = (LCDCON5 & (~(1<<5))) | (invpwren<<5); // 設置LCD_PWREN的極性: 正常/反轉 LCDCON5 = (LCDCON5 & (~(1<<3))) | (pwren<<3); // 設置是否輸出LCD_PWREN }
/* * 設置LCD控制器是否輸出信號 * 輸入參數: * onoff: * 0 : 關閉 * 1 : 打開 */ void Lcd_EnvidOnOff(int onoff) { if (onoff == 1) { LCDCON1 |= 1; // ENVID ON GPBDAT |= (1<<0); // Power on 背光 } else { LCDCON1 &= 0x3fffe; // ENVID Off GPBDAT &= ~(1<<0); // Power off 背光 } }
屏幕上任何寫操做都是由寫一個個的點來組合完成的, 寫點函數以下:
/* * 畫點 * 輸入參數: * x、y : 象素座標 * color: 顏色值 * 對於16BPP: color的格式爲0xAARRGGBB (AA = 透明度), * 須要轉換爲5:6:5格式 * 對於8BPP: color爲調色板中的索引值, * 其顏色取決於調色板中的數值 */ void PutPixel(UINT32 x, UINT32 y, UINT32 color) { UINT8 red,green,blue; switch (bpp){ case 16: { UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x); //這裏fb_base_addr已經指向了幀內存首地址, 還有幀內存與視圖雖然類比爲窗口同樣的形狀, 可是在內存裏只有線性結構, 就如同二維數組照樣是線性存儲的. 因此這裏能夠這樣尋址每一個點的內存中的位置 red = (color >> 19) & 0x1f; green = (color >> 10) & 0x3f; blue = (color >> 3) & 0x1f; color = (red << 11) | (green << 5) | blue; // 格式5:6:5 *addr = (UINT16) color; break; } case 8: { UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x); *addr = (UINT8) color; break; } default: break; } }