找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5690|回復: 1
收起左側

2440裸機編程之九 LCD液晶顯示器

[復制鏈接]
ID:68618 發(fā)表于 2014-11-12 13:47 | 顯示全部樓層 |閱讀模式
S3C2440A 內(nèi)部含有一個LCD 驅動控制器.能自動產(chǎn)生LCD 驅動控制所需的控制信號,因此S3C2440A 可以與諸如黑白灰度、STN 型彩色、TFT 型彩色等LCD 屏直接接口,而不需要另外加LCD 控制器。在這種接口方式下,LCD 顯示緩沖區(qū)映射在系統(tǒng)的存儲器空間上(DMA),程序只需像素點內(nèi)容寫入存儲器對應地址就可以實現(xiàn)對應LCD屏上像素點顏色的顯示,十分方便。
這里以S3C2440A 與一個TFT 型640 像素×480 行的彩色液晶顯示屏接口為例,介紹如何在LCD 上顯示某種色彩及如何繪制簡單圖形等。
2440的LCD 驅動控制器如下:



要想正確使用LCD,必須注意兩點:1、時序;2、顯示緩存區(qū)。
1、時序
LCD 一般需要三個時序信號:VSYNC、HSYNC 和VCLK。VSYNC 是垂直同步信號,在每進行一個幀(即一個屏)的掃描之前,該信號就有效一次,由該信號可以確定LCD 的場頻,即每秒屏幕刷新的次數(shù)(單位Hz)。HSYNC 是水平同步信號,在每進行一行的掃描之前,該信號就有效一次,由該信號可以確定LCD 的行頻,即每秒屏幕從左到右掃描一行的次數(shù)(單位Hz)。VCLK 是像素時鐘信號。s3c2440 處理LCD 的時鐘源是HCLK,通過寄存器LCDCON1 中的CLKVAL 可以調(diào)整VCLK 頻率大小,它的公式為:VCLK=HCLK÷[(CLKVAL+1)×2]
例如,HCLK 的頻率為100MHz,要想驅動像素時鐘信號為6.4MHz 的LCD 屏,則通過上式計算CLKVAL 值,結果CLKVAL 為6.8,取整后(值為6)放入寄存器LCDCON1 中相應的位置即可。由于CLKVAL 進行了取整,因此我們把取整后的值代入上式,重新計算VCLK,得到VCLK=7.1MHz。
按理說,對于一個已知尺寸(即水平顯示尺寸HOZVAL 和垂直顯示尺寸LINEVAL 已知)的LCD 屏,只要確定了VCLK 值,行頻和場頻就應該知道了。但這樣還不行的,因為在每一幀時鐘信號中,還會有一些與屏顯示無關的時鐘出現(xiàn),這就給確定行頻和場頻帶來了一定的復雜性。如在HSYNC 信號先后會有水平同步信號前肩(HFPD)和水平同步信號后肩(HBPD)出現(xiàn),在VSYNC 信號先后會有垂直同步信號前肩(VFPD)和垂直同步信號后肩(VBPD)出現(xiàn),在這些信號時序內(nèi),不會有有效像素信號出現(xiàn),另外HSYNC 和VSYNC信號有效時,其電平要保持一定的時間,它們分別叫做水平同步信號脈寬HSPW 和垂直同步信號脈寬VSPW,這段時間也不能有像素信號。因此計算行頻和場頻時,一定要包括這些信號。HBPD、HFPD 和HSPW 的單位是一個VCLK 的時間,而VSPW、VFPD 和VBPD 的單位是掃描一行所用的時間。在s3c2440 中,所有的這些信號(VSPW、VFPD、VBPD、LINEVAL、HBPD、HFPD、HSPW 和HOZVAL)都是實際值減1 的結果。這些值是通過寄存器LCDCON2、LCDCON3 和LCDCON4 來配置,只要把這些值配置成與所要驅動的LCD中相關內(nèi)容的數(shù)據(jù)一致即可。例如,我們所要顯示的LCD 屏大小為320×240,因此HOZVAL=320-1,LINEVAL=240-1。水平同步信號的脈寬、前肩和后肩分別為30、20 和38,則HSPW=30-1,HFPD=20-1,HBPD=38-1;垂直同步信號的脈寬、前肩和后肩分別為3、12 和15,則VSPW=3-1,VFPD=12-1,VBPD=15-1。
下面我們就具體計算一下行頻(HSF)和場頻(VSF):
HSF=VCLK÷[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]=7.1÷408=17.5kHz
VSF=HSF÷[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]=17.5÷270=64.8Hz
在有些情況下,s3c2440 的LCD 時鐘信號的默認極性與所控制的LCD 時鐘信號的極性相反,這時可以通過寄存器LCDCON5 的相關位來改變某些時鐘信號的極性。
2、顯示緩存區(qū)
只要把所要顯示的數(shù)據(jù)放入顯示緩存區(qū)內(nèi),就可以在屏幕上呈現(xiàn)內(nèi)容。該緩存區(qū)是我們自己編程時開辟的一段內(nèi)存區(qū)。一般我們是通過定義一個與屏幕尺寸大小相同的二維數(shù)組來開辟該空間的,這樣控制屏幕內(nèi)容會方便一些,如當屏幕的尺寸為640×480 時,可以定義該緩存區(qū)為LCD_BUFFER[480][640]。由于s3c2440 支持16 位和24 位的非調(diào)色板真彩色的TFT 型LCD 模式,而24 位顏色模式是用32 位數(shù)據(jù)來表示的,所以前面定義的那個二維數(shù)據(jù)的數(shù)據(jù)類型應該是半字整型或全字整型的。例如,在24 位顏色模式下,我們想要在尺寸大小為640×480 屏幕的中心處設置為白色像素,則:LCD_BUFFER[240][320]=0xffffffff。在s3c2440 中,寄存器LCDSADDR1 和LCDSADDR2 用于設置顯示緩存區(qū),即把們定義的那個二維數(shù)組告訴s3c2440。其中LCDBANK 的9 位數(shù)據(jù)指定LCD 的BANK,即顯示緩存區(qū)的第30 位到第22 位地址LCDBASEU 的21 位數(shù)據(jù)指定了LCD 的基址,即顯示緩存區(qū)開始地址的第21 位到第1 位;LCDBASEL 的21 位數(shù)據(jù)指定了LCD 的尾址,即顯示緩存區(qū)結束地址的第21 位到第1 位。例如,我們想要在尺寸為320×240 的屏幕上顯示24 位顏色, 定義的顯示緩存區(qū)數(shù)組為LCD_BUFFER[480][640], 則LCDBANK 等于LCD_BUFFER 的第30 位到第22 位數(shù)據(jù)值(因為LCD_BUFFER 表示的就是數(shù)組的首地址),LCDBASEU 等于LCD_BUFFER 的第21 位到第1 位數(shù)據(jù)值,由于是用32 位數(shù)據(jù)表示24 為顏色,因此每個像素值是4 個字節(jié),所以LCDBASEL 等于(LCD_BUFFER+(640×480×4))
結果的第21 位到第1 位的數(shù)據(jù)值。另外寄存器LCDSADDR3 有兩個內(nèi)容:OFFSIZE 和PAGEWIDTH。OFFSIZE 用于虛擬屏幕的偏移長度,如果我們不使用虛擬屏幕,就把它置為0;PAGEWIDTH 定義了視口的寬,單位是半字,如在上面的例子中,PAGEWIDTH 應該為640×32÷16。


下面是實驗程序:
//********************************************************************
#define CLKVAL (6)  //VCLK=HCLK÷[(CLKVAL+1)×2]
#define PNRMODE (3)  //TFT LCD panel
#define BPPMODE (12) //16 bpp for TFT
#define ENVID (1)  //輸出和控制 有效
#define VBPD (29)  //垂直同步信號后肩
#define LINEVAL (480) //垂直尺寸
#define VFPD (13)  //垂直同步信號前肩
#define VSPW (3)  //垂直同步信號脈寬
#define HBPD (40)  //水平同步信號后肩
#define HOZVAL (800) //水平尺寸
#define HFPD (40)  //水平同步信號前肩
#define HSPW (48)  //水平同步信號脈寬
#define FRM565 (1)  //565格式
#define PWREN (1)  //GPG供電使能(用于掉電模式)
#define BSWP (0)     //字節(jié)不交換:
//#define HWSWP (1)     //半字交換 16位用不到

#define OFFSIZE (0)  //若不用虛擬屏幕,則為0
#define PAGEWIDTH (HOZVAL)//虛擬屏幕的寬 單位半字 若不用虛擬屏幕,則和實際一致

volatile unsigned short pixel[LINEVAL][HOZVAL]={0};      //緩沖區(qū)


void Main(void)
{     
    int i;
    ……硬件初始化……

Uart_Printf("LCD實驗\n\n");
lcd_init();

while(1)
{
  Uart_Printf("input x1 :");
  x1=Uart_GetIntNum();
  Uart_Printf("x1 = %d\n",x1);
  
  Uart_Printf("input x2 :");
  x2=Uart_GetIntNum();
  Uart_Printf("x2 = %d\n",x2);
  
  Uart_Printf("input y1 :");
  y1=Uart_GetIntNum();
  Uart_Printf("y1 = %d\n",y1);
  
  Uart_Printf("input y2 :");
  y2=Uart_GetIntNum();
  Uart_Printf("y2 = %d\n",y2);
  
  Uart_Printf("input color :");
  color=Uart_GetIntNum();
  Uart_Printf("color = %d\n",color);
  
  rectangle(x1,x2,y1,y2,color);//畫一個矩形
  Uart_Printf("paint over\n");
}
}
void lcd_init(void)
{
rGPCCON = 0xAAAAAAAA;         //LCD功能
rGPDCON = 0xAAAAAAAA;         //LCD功能
rGPGCON = rGPGCON & ~(3<<8) | 3<<8 ;     //LCD電源功能

rLCDCON1 = rLCDCON1 & ~(0x3ff<<8) | CLKVAL<<8 ;   //LCD頻率
rLCDCON1 = rLCDCON1 & ~(0x3<<5) | PNRMODE<<5 ;   //顯示模式
rLCDCON1 = rLCDCON1 & ~(0xf<<1) | BPPMODE<<1 ;   //BPP模式(每個像素用幾位表示)
rLCDCON1 = rLCDCON1 & ~(1) | 0 ;     //關閉輸出

rLCDCON2 = rLCDCON2 & ~(0xff<<24) | VBPD<<24 ;   //與LCD屬性一致
rLCDCON2 = rLCDCON2 & ~(0x3ff<<14) | (LINEVAL-1)<<14 ; //垂直尺寸
rLCDCON2 = rLCDCON2 & ~(0xff<<6) | VFPD<<6 ;   //與LCD屬性一致
rLCDCON2 = rLCDCON2 & ~(0x3f) | VSPW ;     //與LCD屬性一致

rLCDCON3 = rLCDCON3 & ~(0x7f<<19) | HBPD<<19 ;   //與LCD屬性一致
rLCDCON3 = rLCDCON3 & ~(0x7ff<<8) | (HOZVAL-1)<<8 ;  //水平尺寸
rLCDCON3 = rLCDCON3 & ~(0xff) | HFPD ;     //與LCD屬性一致

rLCDCON4 = rLCDCON4 & ~(0xff) | HSPW ;     //與LCD屬性一致

rLCDCON5 = rLCDCON5 & ~(1<<11) | FRM565<<11 ;   //模式:565或5551
rLCDCON5 = rLCDCON5 & ~(1<<3) | PWREN<<3 ;    //供電引腳使能 ( GPG4 )
rLCDCON5 = rLCDCON5 & ~(1<<1) | BSWP ;     //字節(jié)是否交換

rLCDSADDR1 = rLCDSADDR1 & ~(0x1ff<<21) | ( ( (U32)pixel>>22 )&0x1ff )<<21 ;       //緩存區(qū)首地址高位[30:22]->rLCDSADDR1[29:21]
rLCDSADDR1 = rLCDSADDR1 & ~(0x1fffff) | ( (U32)pixel>>1 )&0x1fffff ;        //緩存區(qū)首地址低位[21:1]->rLCDSADDR1[20:0]

rLCDSADDR2 = rLCDSADDR2 & ~(0x1fffff) | ( ((U32)pixel+LINEVAL*HOZVAL*2)>>1 )&0x1fffff ;    //緩存區(qū)(尾地址+1)低位[21:1]->rLCDSADDR2[20:0]

rLCDSADDR3 = rLCDSADDR3 & ~(0x7ff<<11) | OFFSIZE<<11 ;            //虛擬屏幕偏移長度
rLCDSADDR3 = rLCDSADDR3 & ~(0x7ff) | PAGEWIDTH ;             //虛擬屏幕寬度
rLCDCON1 = rLCDCON1 & ~(1) | ENVID ;                //開啟輸出,這個要最后做,不然有問題
}
U8 make_pixel(U16 x,U16 y,U32 color)
{
if(x<HOZVAL && y<LINEVAL)
{
  pixel[y][x] = color;
  return 1;
}
else return 0;
}
  
  

void rectangle(U16 x1,U16 x2,U16 y1,U16 y2,U32 color)
{
U16 x;
for(;y1<=y2;y1++)
  for(x=x1;x<=x2;x++)
   make_pixel(x,y1,color);
}
//*******************************************************************

實驗結果如下:

回復

使用道具 舉報

ID:45457 發(fā)表于 2015-9-11 19:09 | 顯示全部樓層
2440裸機編程之九 LCD液晶顯示器
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表