裸機系列--s3c2400之LCD

平臺                   mini2440

編譯工具          ads1.2

lcd型號             索尼3.5寸TFT液晶屏

 

開始lcd的驅動是在上個學期末,那時候快要期末了,要準備考試什麼的,大概看了一下沒看一下的,感覺學習效率很低,考試完後家裏面要幫忙幹農活,就回家呆了半個月,回到學校已經是8月份了,本來在回來後不久把lcd驅動了弄了一下,也準備總結一下的,但是後面的linux計劃,也不知道是什麼刺激 了我的神經,就感覺要加快進度學習了,後來拖着就到了今天,突然感覺這個學習要踏實,總結對於學習來說是非常重要的,有利於自己把知識歸類,劃分出重點,然後提高分析總結能力,同時總結的時候還能看到一些問題和不足。基於以上的一些原因,我還是繼續在回顧一些裸機系列的最後的lcd和觸摸屏倆個驅動,然後在做個系列小結吧!

1.0 LCD液晶屏與觸摸屏

首先要區分這倆個,很多買的液晶屏都帶了觸摸屏,但是就只有一塊屏,我一開始一直沒有分清楚這倆個屏,其實這裏有倆個屏,是分開的,獨立的,我做觸 摸屏的實驗的時候我一開始就不明白這個屏亮都不亮(就是lcd沒有驅動)可以驅動觸摸屏嗎,事實上是可以的,他們的接口是分開的,觸摸屏是和AD放在一起的,主要是因爲觸摸屏的實現主要運用AD計算位置。

1.1 lcd的單色 灰度 16bpp 24bpp 調色板

lcd用於顯示設備是一個一個像素點來顯示的,回想一起的RTC電視機也是一個一個的點來顯示的。所以顯示一個像素點可以用單色或者用彩色來顯示,就和黑白電視和彩電 一樣,單色就是沒有彩色只有黑白,一個像素點用一位來表示,要麼黑要麼白,對於美術我們不是很瞭解,但是我們都知道美術的素描,就用鉛筆畫畫,這個我們就是素描一幅畫的時候有的地方有點地方濃一些有的地方淡一些,這樣就能看到一些立體感,同樣,對於顯示我們也可以把黑與白之間分爲幾個層次,比如四級灰度, 這樣一來一個像素點需要用倆位來表示,要表示黑白之間的四種,同時我們用bpp表示每一位像素點用多少位表示,四級灰度同時也是2bpp。當然灰度再多也是黑白的,彩色看起來才鮮豔,這裏首先有需要了解一下,自然界的顏色都可以用red green blue三種顏色混合而成(這麼說應該是不對的,因爲這只是人眼的視覺效應而已,並不是說三種顏色混合後就變成其他顏色了,不是這樣的,只是眼睛看起來變成其他顏色了),所以對於16bpp就是每一位像素16位,這樣就可以用5,6,5 或者5,5,5,1 分別表示紅綠藍佔的位數,後面的1就用來表示透 明度。24bpp的話就紅綠藍各佔8位,這樣一來使用不同的搭配就可以表示各種顏色了,但是對於8bpp來說使用8位,紅綠藍使用不到3位來表示,這樣顏色表示的不豐富而且顯示能力太弱,這樣就引入了調色板,調試板其實是一塊內存,mini2440 的是256x 16,256作爲8bpp的索引,每一個單元16位表示一個像素點,這樣就在lcd上顯示出來的是16bpp的顏色。

2.0幀頻率,行頻率,像素時鐘

理解lcd的關鍵在於對lcd工作方式的瞭解,首先一個很重要也很簡單的事實是lcd顯示器都是從左到右,從上到下像我們寫一頁本子一樣,突然想到一起的作文本和這樣很像,真的,那這個來說明比較好,一個一個像素顯示出來。顯示一頁的頻率就是幀頻率,也叫場頻率,垂直頻率和我們比較熟悉的顯示器頻率(就是我們設置的那個顯示器頻率,記得我們大一的時候,學校電腦好爛,那個計算機老師和我們說,要我們去上機的時候把顯示器頻率調到75吧,不記得了反正說調到最高,要不然我們就會看到那個顯示器上有波紋),然後是行頻率,就是顯示 一行的頻率,液晶水平頻率,最後就是像素頻率啦,每個像素點的頻率。

當然還有一個很重要的問題是,每一幀數據開始前都要有準備的時間,一行之後也有一些時間然後要回到下一行開始的時間,還有顯示最後一個像素後也有一些時間不可以顯示像素點,這就像我們的作文本一樣,上下左右都有一些地方是沒有寫東西的,我們寫東西的只有中間那一些方格。

而真正需要用到程序中的就是設置lcd的時鐘頻率,而這些時鐘頻率就需要考慮到這些不能用的時間和我們需要顯示用的時間一起,而這些不能用的時間詳細看2440手冊上的TFT LCD時序圖,那個圖要看懂,那個時序圖包含倆部分,上面是一幀的時序,下面是一幀當中的一個小週期就是一行的時序,具體對應的參數像 VSPW,VBPD, VFPD,HSPW,HBPD ,HFPD這些參數要看懂,還有LINEVAL HOZVAL這倆個分別是有效的行數和一行有效的點數,當然這裏的參數手冊上都是對應要加1的,這裏要注意,比如我們屏寬240,這裏一行有效的像素點就是LINEVAL + 1 = 240 ,所以LINEVAL 的取值就是239 。

最後知道屏的寬和高,然後參考lcd手冊上的那些不能顯示的時間參數,最後就可以計算出lcd的幀頻 Frame Rate了,當然這裏還涉及一個時鐘的基準信號,就是lcd使用的VCLK是從系統的HCLK分頻過來的,所以還有注意設置這個分頻的參數。

3.0寄存器的設置

lcd的寄存器比較多共有17個,不過我們使用TFT液晶屏是需要設置的寄存器並不多,主要是lcd的控制寄存器, LCDCON1~5 和 lcd幀內存地址寄存器LCDSADDR1~3 。具體設置根據需要來,主要參考手冊還有提供的參考程序。

4.0 遇到的幾個問題

1  我買的是索尼3.5寸lcd,友善之臂稱爲X-35,我手上的這款屏的手冊不是很好,而且網上也沒有很多這款屏的資料,感覺手冊寫成那樣子,不像是索尼這樣公司的作風,懷疑不是原廠的手冊,那手冊上找不到上面所說的那些無用的時間的參數,試着看時序圖看能不能得出結果也是不能,不知道是不是我水平太低沒看 到了,要是有人知道怎麼得出這些參數一定要記得告訴我,非常感謝。後面沒辦法,網上搜,還有論壇裏面問,也沒找到,後面突然想到不是有參考程序的嗎,趕緊去看,裏面果然有這麼參數的值,不過你需要對着一個一個的看,因爲命名的關係,不過還是很好了。

2.寫好程序後液晶屏沒有反應,因爲程序開始比較簡單,而且我還是依照參考程序和趙老師博客來寫的,所以我的第一感覺就是這個問題應該是:要麼是時鐘的問題要麼就是內存地址設置的問題,果然查看代碼時是幀內存地址錯誤。

3.液晶屏出現條紋,沒有圖像,這個要檢查一下lcd信號的極性,關於寄存器LCDCON5中信號的極性具體應該參考相應數據手冊,但是我的手冊上也沒找到,所以我就參考的參考代碼的,這裏INVVDEN信號的極性一定要反轉,這裏是針對我的X-35屏來說,要不然會出現前面的液晶屏條紋。當然出現條紋可能還有其他的原因。

代碼:

/*************************************************
file name 	lcd.c
function	無系統驅動TFT液晶屏
			顯示sunflower圖片
硬件參數	mini2440學習板
			索尼3.5寸TFT液晶屏 X35
驅動參數	寬和高 240 x 320
			16bpp非調試板模式
			沒有使用虛擬屏
程序結構	前面部分是串口程序,用於控制和調試用
			後面部分是lcd驅動部分
完成時間	2011-08-06
作者		周茂夫
problem		暫無
修改		暫無
*************************************************/
#define	GLOBAL_CLK		1

#include <stdlib.h>
#include <string.h>
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"
#include "profile.h"
#include "memtest.h"

#define baudrate 115200 

#define	LCD_WIDTH	240
#define	LCD_HEIGHT	320
//#define LCD_CLKCAL	17	//這個我計算出來是17參考程序給的是4 測試倆個都可以
							//影響不大 測試25 30 都還可以
#define LCD_CLKCAL	17		

#define	LCD_RIGHT_MARGIN 25
#define	LCD_LEFT_MARGIN  0
#define	LCD_HSYNC_LEN    4

#define	LCD_UPPER_MARGIN 0
#define	LCD_LOWER_MARGIN 4
#define	LCD_VSYNC_LEN 9

#define	LCD_XSIZE	LCD_WIDTH
#define	LCD_YSIZE	LCD_HEIGHT
#define	SCR_XSIZE	LCD_WIDTH
#define	SCR_YSIZE	LCD_HEIGHT

extern const unsigned char sunflower_240x320[] ;

volatile static unsigned short LCD_BUFFER[SCR_YSIZE][SCR_XSIZE] ; //LCD BUFFER

#define M5D(n)		((n)&0x1fffff)  //設置顯示緩存區時取地址的低21位
#define	LCD_ADDR	((U32)(LCD_BUFFER))	
/**********************************
void delay(int times)
{
	int i = 1000 ;
	while(times--)
	{
		for(; i>0; --i)
			;
	}
}
************************************/
/***********************************
UART_int初始化led IO端口GPBCON5-8
初始化GPBHCON爲串口通信
配置串口通信寄存器
配置中斷寄存器
************************************/
void UART_int_init(void)
{
	/********configuration LED IO port**********/
	rGPBCON &= ~(0xff<<10) ;
	rGPBCON |= 0x55<<10 ;
	
	/*******configuration GPHCON to UART*******/
	rGPHCON &= ~(0xf<<4) ;
	rGPHCON |=  0xa<<4 ;
	
	/****configuration UART0 communication register******/
	rULCON0 = 0x03 ;						//8-bits,1 stop bit, no parity
	rUCON0  = 0x05 ;
	rUBRDIV0= (int)(PCLK/baudrate/16) -1 ;	//configuration UART baudrate
	
	/*****clean interrupt bit clea RX_INT******/
	rSUBSRCPND |= 0x1 ;
	rSRCPND |= 1<<28 ;
	rINTPND |= 1<<28 ;

	/******open UART interrupt*********/
	rINTSUBMSK &= ~(0x1) ;
	rINTMSK &= ~(0x1<<28) ;	
}

//UART send byte
void UART_send_byte(char Tx_data)
{
	while(!(rUTRSTAT0&0x2)) ;//wait Tx empty
	if(Tx_data == '\n')		//Tx '\n'
	{
		rUTXH0 = 0x0d ;
		while(!(rUTRSTAT0&0x2)) ;
		rUTXH0 = 0x0a ;
	}
	else
	{
		rUTXH0 = Tx_data ;
	}
}
//UART send string
void UART_send_string(const char *str) 
{
	while(*str)
	{
		UART_send_byte(*str) ;
		str++ ;
	}
}
//UART receive byte
void UART_receive_byte(void) 
{
	char temp ;
	
	while(!(rUTRSTAT0&0x1)) ;	//wait RX ready
	
	temp = rURXH0 ;
	
	switch(temp)			//測試發送單個字符
	{
		case 's': rGPBDAT &= ~(0xf<<5) ; break ;
		case 'p': rGPBDAT |= (0xf<<5) ; break ;
	}
	UART_send_byte(temp) ;
}
/*******************************************
中斷處理函數
置1清除中斷,注意順序,先子中斷後父中斷
點亮led燈
********************************************/
void __irq UART0_interrupt(void)
{
	/******clean interrupt bit*************/
	rSUBSRCPND |= 0x1 ;
	rSRCPND |= 1<<28 ;
	rINTPND |= 1<<28 ;
	
	rGPBDAT &= ~(0xf<<5) ;	//lighten led
	UART_receive_byte(); 
}
/****************************************************************
function	initialize LCD IO port VD[0:15] VM VLINE VCLK VFREAM
input 		void
return		void
*****************************************************************/
static void Lcd_port_init(void)
{
	rGPCUP	= 0xffffffff ; //Disable Pull-up register
	rGPCCON	= 0xaaaa02a8 ; //Initialize VD[7:0],VM,VFREAM,VLINE,VCLK
	
	rGPDUP	= 0xffffffff ; //Disable Pull-up register
	rGPDCON	= 0xaaaaaaaa ; //Initialize VD[15:8]
	
}
/****************************************************************
function	configarution LCDCON1-5 LCDSADDR1-3 LCD INTERRUPT TPAL
			etc register TFT 16bpp
input 		void
return		void
*****************************************************************/
static void Lcd_init(void)
{
	rLCDCON1 = (LCD_CLKCAL<<8) | (3<<5) | (12<<1) ;	
   	rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);
   	rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH  - 1) <<  8) | (LCD_LEFT_MARGIN << 0);
   	rLCDCON4 = (LCD_HSYNC_LEN << 0);	
   	rLCDCON5 = (1<<11) | (1 << 9) | (1 << 8) | (1<<6) | (1 << 3) | (1 << 0) ;
	
	rLCDSADDR1	= ((LCD_ADDR>>22)<<21) | (M5D(LCD_ADDR>>1)) ;
	//LCDBASEL OFFSIZE=0,PAGEWIDTH=LCD_WIDTH, x2的原因 16bpp 每個像素點2個字節,>>1見地址對應關係 16bpp
	rLCDSADDR2	= M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2)>>1) ;
 	rLCDSADDR3	= LCD_WIDTH ;
 	
 	rLCDINTMSK	|= 3 ; //屏蔽中斷
 	rTCONSEL	= 0 ;  //LPC3600 LCC3600 無效
 	rTPAL		= 0 ;  //禁止臨時調色板
 }
/****************************************************************
function	Envid turn on or off
input 		onoff	1,Envid turn on
return		void
*****************************************************************/
static void Lcd_EnvidOnOff(int onoff)
{
 	if(onoff==1)
 		rLCDCON1 |= 1 ; //ENVID ON
 	else
 		rLCDCON1 &= ~(1<<0) ; //ENBID OFF
}
/****************************************************************
function	LCD power enable
input 		pwren	1, enable lcd power
return		void
*****************************************************************/
static void Lcd_PowerEnable(int pwren)
{
 	rGPGUP	|= (1<<4) ; //Pull-up Disable
 	rGPGCON	|= (3<<8) ; //GPG4 is LCD_PWREN
 	
 	rLCDCON5&= ~(1<<5) ;	//invpwren 正常極性
 	rLCDCON5 = rLCDCON5 & ~(1<<3)|(pwren<<3) ;	//PWREN 使能
}
/****************************************************************
function	Filling sole colour into LCD background
input 		c 		colour
return		void
*****************************************************************/ 
static void Lcd_FillCor(U16 c)
{
 	unsigned int x,y ;
 	for(y=0; y<SCR_YSIZE; y++)
 	{
 		for(x=0; x<SCR_XSIZE; x++)
 		{
 			LCD_BUFFER[y][x] = c ;		
 		}
 	}
}
/****************************************************************
function	Paint picture
input 		x0 y0			assign start bit
			level vertical	the wide and high of picture
			*bmp			the picture string
return		void
*****************************************************************/ 
static void Paint_BMP(int x0, int y0, int level, int vertical, const unsigned char *bmp) 
{
 	int x, y ;
 	U32 col ;
 	int p = 0 ;
 	
 	for(y=0; y<vertical; y++)
 	{
 		for(x=0; x<level; x++)
 		{
 			col = bmp[p+1] | (bmp[p]<<8) ;	//16bpp 一個像素點用倆個字節
 			if( ((x0+x)<SCR_XSIZE) && ((y0+y)<SCR_YSIZE))
 				LCD_BUFFER[y0+y][x0+x] = col ;
 			p += 2 ;
 		}
 	}
}
 
/*******************************************************************
串口UART是掛在PCLK總線上的,需要設置PCLK時鐘,即需要設置2440時鐘。
首先需要設置PLLCON寄存器設置CPU時鐘(FCLK),然後設置FCLK HCLK PCLK的
分頻比,確定好PCLK時鐘。
*****************************************************************/
int Main(void)
{
	/*****************【CLOCK】******************/
	MMU_Init();
	ChangeMPllValue(127,2,1);		//405MHZ
	ChangeClockDivider(13,12);        //1:3:6	
	
	/**************【UART】**********************/
	UART_int_init() ;
	UART_send_string("wo zhen de hen yun si \n") ;	
	pISR_UART0 = (U32)UART0_interrupt ;
	/*************【LCD】************************/
	Lcd_port_init() ;
	Lcd_init() ;
	Lcd_PowerEnable(1) ;
	Lcd_EnvidOnOff(1) ;
	UART_send_string("LCD initial \n") ;
	while(1) 
	{
		//Lcd_FillCor((0x00<<11) | (0x00<<5) | (0x00<<0)) ; 	//clear screen
		//Paint_BMP(0,0,240,320,sunflower_240x320) ;			//filling picture sunflower
		Lcd_FillCor(0xffff) ;
	}
	return 0 ;
}


 


發佈了53 篇原創文章 · 獲贊 21 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章