一、种类
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的工作时序,在编程上会比较直观,下午再找个时序图发上来
时序图中上半部是以行为单位的扫描图.
(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信号之间的距离可以设置,这些工作由显卡来完成.
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
}