S3C2440 TFTLCD驱动详解
S3C2440自带有LCD控制器,该控制器主要有以下接口
仅仅说TFT显示器,TFT显示器的时序如下
TFT显示器的驱动是以行列的形式逐点扫描过来的,驱动时钟有三种,一种是行时钟,一种是列时钟,还有一个点时钟, VSYNC低电平期间需要扫描完一列数据, HSYNC低电平期间要写完一个点的数据,vclk负责一个点的每一位数据写入,实际上就是说,如果有a列,b行,一个点需c个VCLK时钟,那么扫描完成需要的中vclk为a*b*c(近似,中间还有一些延时时间)
可以将这个过程看作是1602的刷新过程, VSYNC为低选择一列, HSYNC为低选择一行,然后写入显示数据还需要一个数据时钟,写完之后指针自动增长,中间的等待时间是设备响应时间,在TFT上叫做同步时间,用于时钟的同步,防止时钟混乱
那么现在就有这些参数需要设置
VBPD: VSYNC与VCLK的开始同步时间
VSPW: VHYNC脉冲的高电平宽度
LINEVAL LCD面板的垂直尺寸:
VFPD: VSYNC与VCLK的结束同步时间
HBPD: HSYNC与VCLK的开始同步时间
HFPD: HSYNC与VCLK的结束同步时间
HOZVAL: 决定了LCD面板的水平尺寸
HSPW: HSYNC脉冲的高电平宽度
配置这些参数一般都依靠显示器的数据手册进行配置,我使用群创4.3寸屏幕,数据手册上显示
也就是说,确定一个DCLK时间,剩下的都可以确定了.(配置之前注意IO口功能配置GPIOC和D)
根据这张表,驱动S3C2440的步骤分为以下几步
1. 设置DCLK频率,像素比以及信号输出
2. 确定VSYC的开始同步结束同步以及高电平宽度还有尺寸
3. 3.设置HSYC的开始同步结束同步以及高电平宽度还有尺寸
4. 设置HSPW时间
5. 对于输出的图像格式进行一些选择
6. 设置LCD显示缓冲区相关的数据
然后,CPU系统就会去自动刷新屏幕(使用缓冲区数据),而我们的读出写入都针对于缓冲区,再由显示器接口写入显示器
具体设置请查看程序:
Lcd.c
#include "tftlcd.h" volatile static unsigned short LCD_BUFFER[SCR_YSIZE_TFT][SCR_XSIZE_TFT]; //定义显示缓存区 /************************************************************** TFT LCD功能模块初始化 **************************************************************/ void LCDInit(void) { //配置引脚 rGPCUP = 0x00000000; rGPCCON = 0xaaaa02a9; rGPDUP = 0x00000000; rGPDCON= 0xaaaaaaaa; //Initialize VD[15:8] // TFT LCD panel,16bpp TFT,ENVID=off rLCDCON1=(CLKVAL_TFT<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0; rLCDCON2=(VBPD<<24)|(LINEVAL_TFT<<14)|(VFPD<<6)|(VSPW); rLCDCON3=(HBPD<<19)|(HOZVAL_TFT<<8)|(HFPD); rLCDCON4= HSPW; rLCDCON5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (0<<7) | (0<<6) | (1<<3) |(BSWP<<1) | (HWSWP); //16位输出格式565,VCLK下降沿取数据等 (看数据手册)这里指定目的地址 rLCDSADDR1=(((u32)LCD_BUFFER>>22)<<21)|M5D((u32)LCD_BUFFER>>1); //单扫描 rLCDSADDR2=M5D( ((u32)LCD_BUFFER+(SCR_XSIZE_TFT*LCD_YSIZE_TFT*2))>>1 ); //LCD_WIDTH×16/16;由于是选择的16位模式, //如果是24位模式,每个像素4字节则为LCD_WIDTH×32/16 //(LCD_WIDTH在此为LCD_XSIZE_TFT) rLCDSADDR3=(((SCR_XSIZE_TFT-LCD_XSIZE_TFT)/1)<<11)|(LCD_XSIZE_TFT/1); rLCDINTMSK|=(3); // MASK LCD Sub Interrupt rTCONSEL &= (~7) ; // Disable LPC3600 rTPAL=0; // 禁止临时调色板寄存器 } /************************************************************** * LCD视频和控制信号输出或者停止,1开启视频输出 **************************************************************/ void LCDEnvidOnOff(int onoff) { if(onoff==1) rLCDCON1|=1; // ENVID=ON else rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off } /************************************************************** * TFT LCD 电源控制引脚使能 * pwren=1时,允许PWREN信号 * pwren=0时,禁止PWREN信号 * invpwre=1,PWREN信号极性反转 * invpwre=0,PWREN信号极性正常 **************************************************************/ void LCDPowerEnable(int invpwren,int pwren) { //GPG4 is setted as LCD_PWREN rGPGUP |= (1<<4);//GPG4上拉电阻无效 rGPGCON |= (3<<8); //GPG4=LCD_PWREN rGPGDAT |= 1<<4 ; //GPG4置1 //invpwren=pwren; //Enable LCD POWER ENABLE Function if(pwren)rLCDCON5 |= 1<<3; else rLCDCON5 &= ~(1<<3); if(invpwren) rLCDCON5 |= 1<<5; else rLCDCON5 &= ~(1<<5); } /************************************************************** TFT LCD单个象素的显示数据输出 **************************************************************/ void LCDPutPixel(u32 x,u32 y, u32 color ) { if ( (x < SCR_XSIZE_TFT) && (y < SCR_YSIZE_TFT) ) LCD_BUFFER[(y)][(x)] = color; } /************************************************************** TFT LCD全屏填充特定颜色单元或清屏 **************************************************************/ void LcdClearScr( u32 color) { unsigned int x,y ; for( y = 0 ; y < SCR_YSIZE_TFT ; y++ ) { for( x = 0 ; x < SCR_XSIZE_TFT ; x++ ) { LCD_BUFFER[y][x] = color ; } } } /************************************************************** 在LCD屏幕上指定坐标点画一个指定大小的图片 **************************************************************/ void LCDPaintBmp(u16 x0,u16 y0,u16 h,u16 l,const unsigned char bmp[]) { int x,y; u32 c; int p = 0; for( y = y0 ; y < l ; y++ ) { for( x = x0 ; x < h ; x++ ) { c = bmp[p+1] | (bmp[p]<<8) ; if ( ( (x0+x) < SCR_XSIZE_TFT) && ( (y0+y) < SCR_YSIZE_TFT) ) LCD_BUFFER[y0+y][x0+x] = c ; p = p + 2 ; } } } /************************************************************** * * LCD屏初始化 * **************************************************************/ void LcdTFTInit(void) { LCDInit(); LCDPowerEnable(0, 1); LCDEnvidOnOff(1); //turn on vedio LcdClearScr( (0x00<<11) | (0x00<<5) | (0x00) ); //565格式 纯黑色 } //在指定区域内填充指定颜色 //区域大小: // (xend-xsta)*(yend-ysta) void LCDFill(u16 xstart,u16 ystart,u16 xend,u16 yend,u16 color) { u16 i,j; for(i = xstart;i<=xend;i++) { for(j = ystart; j <= yend; j++) { LCDPutPixel(i,j,color); } } } //画线 //x1,y1:起点坐标 //x2,y2:终点坐标 void LCDDrawLine(u16 xstart, u16 ystart, u16 xend, u16 yend,u16 color) { u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance; int incx,incy,uRow,uCol; delta_x=xend-xstart; //计算坐标增量 delta_y=yend-ystart; uRow=xstart; uCol=ystart; if(delta_x>0)incx=1; //设置单步方向 else if(delta_x==0)incx=0;//垂直线 else {incx=-1;delta_x=-delta_x;} if(delta_y>0)incy=1; else if(delta_y==0)incy=0;//水平线 else{incy=-1;delta_y=-delta_y;} if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 else distance=delta_y; for(t=0;t<=distance+1;t++ )//画线输出 { LCDPutPixel(uRow,uCol,color);//画点 xerr+=delta_x ; yerr+=delta_y ; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; } } } //画矩形 void LCDDrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color) { LCDDrawLine(x1,y1,x2,y1,color); LCDDrawLine(x1,y1,x1,y2,color); LCDDrawLine(x1,y2,x2,y2,color); LCDDrawLine(x2,y1,x2,y2,color); } //在指定位置画一个指定大小的圆 //(x,y):中心点 //r :半径 void LCDDrawCircle(u16 x0,u16 y0,u8 r,u16 color) { int a,b; int di; a=0;b=r; di=3-(r<<1); //判断下个点位置的标志 while(a<=b) { LCDPutPixel(x0-b,y0-a,color); //3 LCDPutPixel(x0+b,y0-a,color); //0 LCDPutPixel(x0-a,y0+b,color); //1 LCDPutPixel(x0-b,y0-a,color); //7 LCDPutPixel(x0-a,y0-b,color); //2 LCDPutPixel(x0+b,y0+a,color); //4 LCDPutPixel(x0+a,y0-b,color); //5 LCDPutPixel(x0+a,y0+b,color); //6 LCDPutPixel(x0-b,y0+a,color); a++; //使用Bresenham算法画圆 if(di<0)di +=4*a+6; else { di+=10+4*(a-b); b--; } LCDPutPixel(x0+a,y0+b,color); } }
Lcd.h
#ifndef __TFTLCD_H #define __TFTLCD_H #include "2440addr.h" #include "def.h" //RGB5:6:5颜色定义 #define clWhite 0xFFFF //白色 #define clBlack 0x0000 //黑色 #define clDRed 0x8000 //暗红色,为全色的一半 #define clLRed 0xF800 //亮红色 #define clDMagenta 0x8010 //暗紫色 #define clLMagenta 0xF81F //亮紫色 #define clGreen 0x07E0 //绿色 #define clDBlue 0x0010 //暗蓝色 #define clLBlue 0x001F //亮蓝色 #define clDCyan 0x0410 //暗青色 #define clLCyan 0x07FF //亮青色 #define clDYellow 0x8400 //暗黄色 #define clLYellow 0xFFE0 //亮黄色 #define clDGray 0x8410 //暗灰色 #define clLGray 0xF79E //亮灰色 #define clLArgent 0xCE79 //亮银色 #define MVAL_USED (0) //0=each frame 1=rate by MVAL #define INVVDEN (1) //0=normal 1=inverted #define BSWP (0) //Byte swap control #define HWSWP (1) //Half word swap control #define PNRMODE (3) // 设置为TFT屏 #define BPPMODE (12) // 设置为16bpp模式 //TFT_SIZE #define LCD_XSIZE_TFT (480) #define LCD_YSIZE_TFT (272) #define SCR_XSIZE_TFT (480) #define SCR_YSIZE_TFT (272) #define HOZVAL_TFT (LCD_XSIZE_TFT-1) #define LINEVAL_TFT (LCD_YSIZE_TFT-1) //Timing parameter for 4.3' LCD 实际值-1的结果 #define VBPD (1) //垂直同步信号的后肩 #define VFPD (1) //垂直同步信号的前肩 #define VSPW (7) //垂直同步信号的脉宽 #define HBPD (1) //水平同步信号的后肩 #define HFPD (1) //水平同步信号的前肩 #define HSPW (40) //水平同步信号的脉宽 #define CLKVAL_TFT (2) //时钟分频因子,VCLK = HCLK/((CLKVAL_TFT+1)*2) #define M5D(n) ((n) & 0x1fffff) //用于设置显示缓存区时,取低21位地址 void LcdTFTInit(void); void LCDFill(u16 xstart,u16 ystart,u16 xend,u16 yend,u16 color); void LCDDrawLine(u16 xstart, u16 ystart, u16 xend, u16 yend,u16 color); void LCDDrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color); void LCDDrawCircle(u16 x0,u16 y0,u8 r,u16 color); #endif
注意,触摸屏不打开cache 和打开cache的速度是不同的(一个要每次去sram取指写入数组,一个直接在内部cache中写入数据),但是打开了cache之后用到延时函数的的驱动可能会遭遇到延时时间大大缩短导致设备驱动不起来的情况(比如at24c02,写入读出太快,设备反应不过来,设备没反应,驱动包错)