LCD驱动移植(一)

一、种类
LCD的种类比较多,有以下四种,不过现在我们用的大多是TFT液晶显示屏
1、TN型显示器
2、STN型彩色显示器
3、DSTN型显示器
4、TFT显示器
二、LCD参数
(1)尺寸
    一般液晶屏使用对角线的长度表示屏幕的大小,单位是英寸.
(2)分辨率(Resolution)
    一幅图像被称作一帧(frame),每帧有若干行、列的像素数组成,常见的分辨率如下:
           320*240(QVGA)     640*480(VGA)      800*480(WVGA)
           800*600(SVGA)     1280*800(WXGA)
(3)PPI
    pixels per inch.在图像中,每英寸所包含的像素数,也是描述一个图片信息的重要属性.图像的ppi值越高,画面的细节就越丰富.常见的ppi值有72ppi、180ppi、300ppi.
(4)色深(色位)
    每个像素的颜色使用若干位的二进制数据来表示,单位是BPP(bit per pixel).常见色深有:
    <1>8BPP : 2^8=256色,每个像素颜色使用8bit二进制数表示,即一个像素点有256种颜色.但对与三种原色平均下来,每种原色只能使用不到3位的数据来表示,即每个原色最多不过8个级别,这不足以表示更丰富的色彩.为了解决该问题,需要调用调色板.每个像素对应的8位数据不再表示RGB三种原色,而它在调色板中的索引值.要显示某像素时,使用该索引值从调色板中取得RGB颜色.
    <2>16BPP : 2^16=65k色,称作伪彩色,每个像素颜色使用16bit二进制数表示,即一个像素点有65k种颜色.每个像素点的颜色使用两个字节来表示,这两个字节的数据格式又分为两种: 5:6:5、5:5:5:1,前者使用高5位表示红色,中间6位表示绿色,低5位表示蓝色;后者的高15位从高到低分成3个5位来表示红色、绿色、蓝色,最低位表示透明度.故5:5:5:1的数据格式又称作RGBA格式(A:Alpha,表示透明度).
    <3>24BPP : 2^24=16M色,称作真彩色,每个像素颜色使用24bit的二进制数表示,即一个像素点有16M种颜色.每个像素点的颜色使用三个字节来表示,每种原色使用8bit,LCD控制器通过VD[23:0]数据线发送给LCD.为了方便DMA传输,在内存中使用4个字节来表示一个像素,其中3个字节从高到低分别表示红、绿、蓝,剩余一个字节无效.是最低字节还是最高字节无效,这是可以选择的.   
  【attention】
      LCD一帧数据的字节数,或者说一幅图片的字节数=像素点数*色深
      如16BPP的320*240的LCD,其一帧数据的字节数=320*240*2=153600
(5)点位图
    点位图法是把一幅图像分成许许多多的像素,每个像素用若干个二进制位来指定该像素的颜色、亮度和属性.因此一幅图像由许许多多描述每个像素的数据组成,这些数据通常称为图像数据,而这些数据通常是作为一个文件来存储的,这种文件又称为图像文件.
(6)基色
    根据色彩学原理,现实世界上的任何颜色可由红绿蓝三种颜色的不同强度混合而成,这三种颜色称为基色或原色.
(7)灰度级
    灰度也可认为是亮度,特指每种基色的发光亮度.将基色的发光亮度按强度大小划分,就是灰度级.显示屏能产生的灰度级越高,显示的颜色就越多,但造价也更高.例如:
        4级(=2^2)灰度的基色,每种基色使用2bit来表示,能显示的颜色为(2^2)*(2^2)*(2^2)=64种
      16级(=2^4)灰度的基色,每种基色使用4bit来表示,能显示的颜色为(2^4)*(2^4)*(2^4)=4096种
    256级(=2^8)灰度的基色,每种基色使用8bit来表示,能显示的颜色为(2^8)*(2^8)*(2^8)=16777216种
     可以根据颜色的浓烈程度将三原色都分成256个级别(0-255),则可以使用255级的红色、255级的蓝色、255级的绿色组合成白色;可以使用0级的红色、0级的蓝色、0级的绿色组合成黑色.
三、TFT的时序
1、帧同步信号(VSYNC):用高电平(或者低电平)表示每一帧扫描的开始。
2、行同步信号(HSYNC):用高电平(或者低电平)表示每一行扫描的开始。
3、时钟(VCLK):在上升沿或者下降沿将数据写入液晶屏。
4、数据有效控制(VDEN):表示是否开启TFT输出。
5、数据信号(VD):表示每个点的颜色,通常有16位,18位,24位等模式。
注:对这些参数的理解可以更好的认清TFT的工作时序,在编程上会比较直观,下午再找个时序图发上来
LCD驱动移植(一)_第1张图片
时序图中上半部是以行为单位的扫描图.
     (1)VSYNC信号有效时,表示一帧数据的开始.一帧的数据,实际上是一屏的数据的开始
     (2)VSPW表示VSYNC信号的脉冲宽度为(VSPW+1)个HSYNC信号周期.即(VSPW+1)行的数据无效.
     (3)还要再经过(VBPD+1)个HSYNC信号周期,有效的行数据才出现.因此,在VSYNC信号有效之后,总共还要经过(VSPW+1+VBPD+1) 个无效的行,然后第一个有效的行才出现(VDEN信号置位时,才是有效数据,否则视为无效数据).
     (4)随后连续发出(LINEVAL+1)行的有效数据.
     (5)最后是(VFPD+1)个无效的行.最终完整的一帧结束,紧接着是下一帧数据了.即下一个VSYNC信号. 
时序图中下半部是以每行中的像素为单位的扫描图.
     (1)HSYNC信号有效时,表示一行数据的开始.每一个脉冲对应的一行的数据开始
     (2)HSPW表示HSYNC信号的脉冲宽度为(HSPW+1)个VCLK信号周期.即这(HSPW+1)个像素数据无效.
     (3)还要再经过(HBPD+1)个VCLK信号周期,有效的像素数据才出现.因此,在HSYNC信号有效之后,总共还要经过(HSPW+1+HBPD+1)个无效的像素,然后第一个有效的像素才出现(VDEN信号置位时,才是有效像素,否则视为无效像素).
     (4)随后连续发出(HOZVAL+1)行的有效数据.
     (5)最后是(HFPD+1)个无效的像素.最终完整的一行结束,紧接着是下一行数据了.即下一个HSYNC信号.

     (6)VCLK 像素同步信号,数据对应的是一个一个的点

     (7)LEND 行结束信号
【attention】
      通过TFT_LCD时序图可知,在工作中的显示器上,在四周看到黑色的边框.上方的黑框是因为当发出VSYNC信号时,需要经过若干行之后第一行数据才有效.下方的黑框是因为显示完所有行的数据时,VSYNC信号还没有发出,此时数据已无效.左边的黑框是因为当发出HSYNC信号时,需要经过若干像素之后第一列数据才有效.右边的黑框是因为显示完一行的数据时,显示器还没有扫描到最右边(HSYNC信号还没有发出),这时数据已经无效.注意,显示器只会根据VSYNC、HSYNC信号来取得、显示数据,并不理会该数据是否有效,何时发出有效数据则由显卡决定.
      VSYNC信号出现的频率表示1s内能显示多少帧图像,称作垂直频率或场频率,这就是常说的显示器的频率.HSYNC信号出现的频率称作水平频率.
      有效数据的行数、列数,即分辨率,它与VSYNC、HSYNC信号之间的距离可以设置,这些工作由显卡来完成.


LCD驱动移植(一)_第2张图片


1、LCD驱动器一般是跟屏做在一起

2、LCD控制器一般的嵌入式处理器上面都会有

3、显示缓存类似于电脑的显卡,也有有些电脑没有显卡,然后直接开辟一部分内存作为显示缓存

下面帖出LCD驱动的裸机程序,相信如果能理解裸机程序了,再看系统上的驱动程序的话会有一定的帮助的

#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 LCD_N35 //NEC 256K色240*320/3.5英寸TFT真彩液晶屏,每一条水平线上包含240个像素点,共有320条这样的线

#if defined(LCD_N35)

#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 4

#define LCD_RIGHT_MARGIN 39
#define LCD_LEFT_MARGIN 16
#define LCD_HSYNC_LEN 5

#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1

#endif

void TFT_LCD_Test(void);

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

volatile static unsigned short LCD_BUFFER[SCR_YSIZE][SCR_XSIZE];  //定义320行,240列的数组,用于存放显示数据

extern unsigned char sunflower_240x320[];

#define	M5D(n)	((n)&0x1fffff)
#define LCD_ADDR ((U32)LCD_BUFFER)

#define ADC_FREQ 2500000

volatile U32 preScaler;

static void cal_cpu_bus_clk(void);
void Set_Clk(void);

/*演示函数*/
void delay(int times)
{
    int i,j;
    for(i=0;i<times;i++)
       for(j=0;j<400;j++);
}

/*在屏幕上画图*/
static void Pait_Bmp(int x0,int y0,int h,int l,const unsigned char *bmp)
{
    int x,y;
    U32 c;
    int p = 0;
	
    for( y = 0 ; y < l ; y++ )
    {
    	for( x = 0 ; x < h ; x++ )
    	{
    	    c = bmp[p+1] | (bmp[p]<<8) ;

	    if ( ( (x0+x) < SCR_XSIZE) && ( (y0+y) < SCR_YSIZE) )
	        LCD_BUFFER[y0+y][x0+x] = c ;

    	    p = p + 2 ;
    	}
    }
}


/*填充全屏为某一颜色*/
static void Lcd_ClearScr( 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 ;
    	}
    }
}


/*LCD开关*/
static void Lcd_EnvidOnOff(int onoff)
{
    if(onoff==1)
	rLCDCON1|=1; // ENVID=ON
    else
	rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off
}


/*端口初始化*/
static void Lcd_Port_Init( void )
{
    rGPCUP=0xffffffff; // Disable Pull-up register
    rGPCCON=0xaaaa02a8; //Initialize VD[7:0],VM,VFRAME,VLINE,VCLK

    rGPDUP=0xffffffff; // Disable Pull-up register
    rGPDCON=0xaaaaaaaa; //Initialize VD[15:8]
}

/*LCD初始化*/
static void LCD_Init(void)
{
	Lcd_Port_Init();

	/*显示模式初始化*/
	/*bit[17:8](4:CLKVAL);bit[6:5](11:TFT LCD panel);bit[4:1](1100:16 bpp for TFT)*/
	rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 <<  5) | (12 << 1);
	
	/*bit[31:24](1:VBPD);bit[23:14](320-1:行数);bit[13:6](5:VFPD);bit[5:0](1:VSPW)*/
   	rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);
   	
   	/*bit[25:19](36:HBPD);bit[18:8](240-1:列数);bit[7:0](19:HFPD)*/
   	rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH  - 1) <<  8) | (LCD_LEFT_MARGIN << 0);
   	
   	/*bit[15:8](13:MVAL,只有当LCDCON1 bit[7]MMODE=1才有效);bit[7:0](5:HSPW)*/
   	rLCDCON4 = (13 <<  8) | (LCD_HSYNC_LEN << 0);
   	
   	/*bit[11](5:6:5 Format);bit[9](VLINE/HSYNC polarity inverted);bit[8](VFRAME/VSYNC inverted)
	  bit[3](Enalbe PWERN signal),bit[1](half-word swap control bit)*/
	rLCDCON5   =  (1<<11) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 0);
	
	
	

        /*帧缓冲地址初始化*/
	/*
	LCDBANK: 视频帧缓冲区内存地址30-22位
	LCDBASEU: 视频帧缓冲区的开始地址21-1位
	LCDBASEL: 视频帧缓冲区的结束地址21-1位
	*/
	/*bit[29:21]:LCDBANK,bit[20:0]:LCDBASEU*/
    	rLCDSADDR1 = ((LCD_ADDR >> 22) << 21) | ((M5D(LCD_ADDR >> 1)) <<  0);
    
    	/*bit[20:0]:LCDBASEL*/
    	rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);
    
    
    	/*PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行字节数
	  OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0
	*/
	/*bit[21:11]:OFFSIZE; bit[10:0]:PAGEWIDTH*/
    	rLCDSADDR3 = LCD_WIDTH;        

	/*屏蔽中断*/
    	rLCDINTMSK |= 3;
  	rTCONSEL   &= (~7);
 
	/*disable调色板*/
   	rTPAL = 0x0;
   	
   	/*禁止LPC3600/LCC3600模式*/
   	rTCONSEL &= ~((1<<4) | 1); 
   	
   	/*打开LCD*/
   	Lcd_EnvidOnOff(1);	 
}


void TFT_LCD_Show(void)
{	
    /*红(255:0:0);绿(0:255:0);蓝(0:0:255);黑(0:0:0);白(255,255,255)*/
	
    /*在屏幕上显示三基色*/
	
    Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x00)  )  ;		//clear screen black
    delay(10000);
		
    Lcd_ClearScr((0x1f<<11) | (0x00<<5) | (0x00));			//red
    delay(10000);
		
    Lcd_ClearScr((0x00<<11) | (0x3f<<5) | (0x00));			//green
    delay(10000);
    
    Lcd_ClearScr((0x00<<11) | (0x00<<5) | (0x1f));			//blue
    delay(10000);

    Lcd_ClearScr( (0x1f<<11) | (0x3f<<5) | (0x1f)  )  ;		//clear screen white
    delay(10000);
	
    /*显示一副图片在屏幕上*/
    Pait_Bmp(0, 0, 240, 320, sunflower_240x320);
}
   

/*************************************************
Function name: Set_Clk()
Parameter    : void
Description	 : 设置CPU的时钟频率
Return		 : void
Argument     : void
Autor & date : Daniel
**************************************************/
void Set_Clk(void)
{
	int i;
	U8 key;
	U32 mpll_val = 0 ;
	i = 2 ;	             //don't use 100M!
		                 //boot_params.cpu_clk.val = 3;
	switch ( i ) {
	case 0:	//200
		key = 12;
		mpll_val = (92<<12)|(4<<4)|(1);
		break;
	case 1:	//300
		key = 13;
		mpll_val = (67<<12)|(1<<4)|(1);
		break;
	case 2:	//400
		key = 14;
		mpll_val = (92<<12)|(1<<4)|(1);
		break;
	case 3:	//440!!!
		key = 14;
		mpll_val = (102<<12)|(1<<4)|(1);
		break;
	default:
		key = 14;
		mpll_val = (92<<12)|(1<<4)|(1);
		break;
	}
	
	//init FCLK=400M, so change MPLL first
	ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3);   //set the register--rMPLLCON
	ChangeClockDivider(key, 12);    //the result of rCLKDIVN [0:1:0:1] 3-0 bit
	cal_cpu_bus_clk();    //HCLK=100M   PCLK=50M
}
/*************************************************
Function name: cal_cpu_bus_clk
Parameter    : void
Description	 : 设置PCLK\HCLK\FCLK的频率
Return		 : void
Argument     : void
Autor & date : Daniel
**************************************************/
static void cal_cpu_bus_clk(void)
{
	static U32 cpu_freq;
    static U32 UPLL;
	
	U32 val;
	U8 m, p, s;
	
	val = rMPLLCON;
	m = (val>>12)&0xff;
	p = (val>>4)&0x3f;
	s = val&3;

	//(m+8)*FIN*2 不要超出32位数!
	FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100;     //FCLK=400M  FIN=12000000
	
	val = rCLKDIVN;
	m = (val>>1)&3;
	p = val&1;	
	val = rCAMDIVN;
	s = val>>8;
	
	switch (m) {
	case 0:
		HCLK = FCLK;
		break;
	case 1:
		HCLK = FCLK>>1;
		break;
	case 2:
		if(s&2)
			HCLK = FCLK>>3;
		else
			HCLK = FCLK>>2;
		break;
	case 3:
		if(s&1)
			HCLK = FCLK/6;
		else
			HCLK = FCLK/3;
		break;
	}
	
	if(p)
		PCLK = HCLK>>1;
	else
		PCLK = HCLK;
	
	if(s&0x10)
		cpu_freq = HCLK;
	else
		cpu_freq = FCLK;
		
	val = rUPLLCON;
	m = (val>>12)&0xff;
	p = (val>>4)&0x3f;
	s = val&3;
	UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
	UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
	Uart_Printf("\nFCLK:%dMHz,HCLK:%dMHz,PCLK=%dMHZ\n",FCLK/1000/1000,HCLK/1000/1000,PCLK/1000/1000);
}

/*主函数*/
int Main(void)
{	
    Set_Clk();
    
    LCD_Init();
    
    TFT_LCD_Show();
    
    return 0;
}


volatile static unsigned short LCD_BUFFER[SCR_YSIZE][SCR_XSIZE]; //定义320行,240列的数组,用于存放显示数据
LCD_BUFFER这个二维数组就是帧缓冲的大小,它是一个二维数组,实际上帧缓冲的大小是:
SCR_YSIZE X SCR_XSIZE X sizeof(unsigned short),它的大小就是这个屏的每一个像素点(每个像素点是unsigned short)之和.

因为配置了寄存器,只有帧缓冲里的数据有改变,显示就会发生变化,当然要起动显示
/*LCD开关*/
static void Lcd_EnvidOnOff(int onoff)
{
if(onoff==1)
rLCDCON1|=1; // ENVID=ON
else
rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off
}


你可能感兴趣的:(LCD驱动移植(一))