s3c2440具有LCD控制器,可以很方便驱动各种LCD.下面以TFT型LCD为例,讲一讲LCD控制器的用法:
LCD控制器方框图:
可以看到s3c2440中LCD控制器的外部接口信号有33个,包括24个数据位和9个控制位。
那么这些数据位控制位有什么作用呢,首先那看看TFT屏的工作时序:
VSYNC为帧同步信号,每发出一个脉冲表示新的一屏图像数据开始传输。计算方法:VSF=HSF÷[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]
HSYNC为行同步信号,每发出一个脉冲表示新的一行图像数据开始传输。计算方法:HSF=VCLK÷[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]
VCLK为像素同步信号,每发出一个脉冲表示新的一个点图像数据开始传输。计算方法:VCLK=HCLK÷[(CLKVAL+1)×2]
LCD显示是一帧一帧(一个画面)的,每一帧里显示又是从上到下一行一行的,每一行显示又是从左到右一个点一个点的。
而VSYNC,HSYNC,VCLK这些决定了他们的显示速度。
图中的VSPW,HSPW等决定相应脉冲的宽度,VBPD,HBPD决定了延时时间。
这些参数的设置都是由LCDCONn决定的。
具体功能可以参见数据手册。
来看具体的编程流程:
需要设置的寄存器:
LCDCON1:LCD控制寄存器1
LCDCON2:LCD控制寄存器2
LCDCON3:LCD控制寄存器3
LCDCON4:LCD控制寄存器4
LCDCON5:LCD控制寄存器5
LCDSADDR1:帧缓冲器开始地址1寄存器
LCDSADDR2:帧缓冲器开始地址2寄存器
LCDSADDR3:帧缓冲器开始地址3寄存器
一,初始化。
初始化包括端口初始化,显示模式初始化,帧缓冲初始化。
1,端口初始化:
主要是初始化相应端口为第二功能,这些脚将会与LCD连接。
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]
主要是设置显示的相关参数,通过LCDCON1~LCDCON5来完成。
具体功能可以参见数据手册:
#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
#define LCD_XSIZE LCD_WIDTH #define LCD_YSIZE LCD_HEIGHT #define SCR_XSIZE LCD_WIDTH #define SCR_YSIZE LCD_HEIGHT
用到的寄存器及设置的说明:
LCDCON1: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);
rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);LCDCON3: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);LCDCON4:bit[15:8](13:MVAL,只有当LCDCON1 bit[7]MMODE=1才有效);bit[7:0](5:HSPW)
rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0);LCDCON5: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);3,帧缓冲初始化:
LCDSADDR1~3来告诉LCD控制器要从从显示缓存的那个地址开始取数据,要去多少数据等。
LCDBANK: 视频帧缓冲区内存地址30-22位
LCDBASEU: 视频帧缓冲区的开始地址21-1位
LCDBASEL: 视频帧缓冲区的结束地址21-1位
PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行字节数
OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0
/*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); /*bit[21:11]:OFFSIZE; bit[10:0]:PAGEWIDTH*/ rLCDSADDR3 = LCD_WIDTH;二,初始化好了以后,就可以向LCD缓冲区里面写数据了。
LCD控制器就会不断地自动从你设定的缓冲区地址以设定的方式读取数据并显示在LCD上了。
/*显示一副图片在屏幕上*/ Pait_Bmp(0, 0, 240, 320, sunflower_240x320);
/*在屏幕上画图*/ 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 ; } } }LCD.c源代码:实现在MINI2440上测试 三原色和显示图片
#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); }