2440LCD控制器与TFT屏的总结
一、 学习前的困惑
从刚学2440到现在也有一个多月了,对里面的一些基本的结构还算是有所了解。但是在做裸机程序来控制TFT屏时遇到了很多的麻烦,从最开始的一无所知到现在能够将图像和文字还有字符显示到上面,是经历了一个痛苦又漫长的过程,下面我来分享一下我的经验。
二、 2440LCD控制器内部结构和一些基本的概念
2440LCD控制器可以产生LCD所需要的控制信号,来完成对STN及TFT的控制。在对2440的LCD控制器操作之前必须要对一些概念和TFT的时序有所了解,然后才能深入的了解寄存器配置是一个怎么回事。
1、2440LCD控制器内部结构图
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;
b:REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的;
c:LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,视频数据在不需要CPU的干预的情况下显示在LCD屏上;
d:VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,然后通过数据端口VD[23:0]传送视频数据到LCD驱动器;
e:TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,而这些控制 信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能产生这些信号的不同形态,从而支 持不同的LCD驱动器(即不同的STN/TFT屏)。
2、基本概念
a、LCD都是以点来显示的,将不同的点显示不同的颜色出来就是一幅图像;
b、行(line):显示器的指针从LCD的最左侧的点开始到最右侧显示点结;
c、帧(fram):从最顶端的行开始到最底端的行为一帧,也即一幅的图像为一帧;
d、HSYNC/VLINE/CPV:显示一行数据时间频率水平同步信号(TFT)/行同步脉冲信号(STN)/SEC TFT信号;
e、VSYNC/VFRAME/STV:显示一帧数据所需要的时间垂直同步信号(TFT)/帧同步信号(STN)/SEC TFT信号;
三、 常见的TFT屏的时序分析
图1 TFT时序分析
VSYNC与HSYNC前面已经定义过了。
VCLK:LCD指针移动一个点的时间频率,也称像素移动频率
VD[23:0]:LCD像素数据输出端口(TFT/STN/SEC TFT);
VDEN/VM/TP:数据使能信号(TFT)/LCD驱动交流偏置信号(STN)/SEC TFT 信号;
LEND/STH:行结束信号(TFT)/SEC TFT信号;
LCD_LPCOE:SEC TFT OE信号;
LCD_LPCREV:SEC TFT REV信号;
LCD_LPCREVB:SEC TFT REVB信号
VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin;
VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin;
VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len;
HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin;
HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin;
HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len;
四、 2440LCD程序分析
2440LCD有5个控制寄存器:LCDCON1/2/3/4/5,3个Vedio data寄存器:LCDSADDR1/2/3等寄存器。
1、 单个像点的显示函数
void PutPixel(U32 x,U32 y, U32 c )
{
if ( (x < SCR_XSIZE_TFT) && (y < SCR_YSIZE_TFT) )
LCD_BUFFER[(y)][(x)] = c;
}
2、 初始化寄存器的配置函数
void Lcd_Init(void)
{
rGPCUP = 0x00000000;
rGPCCON = 0xaaaa02a9;//GPC567为输入,GPC0为输出
rGPDUP = 0x00000000;
rGPDCON=0xaaaaaaaa; //Initialize VD[15:8] LCD的数据口
rLCDCON1=(CLKVAL_TFT<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0;
// TFT LCD panel,16bpp TFT,ENVID=off,设置了VCLK的值
rLCDCON2=(VBPD<<24)|(LINEVAL_TFT<<14)|(VFPD<<6)|(VSPW);
rLCDCON3=(HBPD<<19)|(HOZVAL_TFT<<8)|(HFPD);
rLCDCON4=(MVAL<<8)|(HSPW);
rLCDCON5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (0<<7) | (0<<6) | (1<<3) |(BSWP<<1) | (HWSWP);//输出为5:6:5格式
//10位表示VD数据在SCLK下降沿时被提取 8、9位表示VSYNC与HSYNC脉冲为负极性 7数据脉冲极性正常
//6位数据全能脉冲极性正常 3位全能LCD_POWER
rLCDSADDR1=(((U32)LCD_BUFFER>>22)<<21)|M5D((U32)LCD_BUFFER>>1);//系统内存地址A[30:22]处的Bank位置为图像缓冲
rLCDSADDR2=M5D( ((U32)LCD_BUFFER+(SCR_XSIZE_TFT*LCD_YSIZE_TFT*2))>>1 );//SCR_XSIZE_TFT*LCD_YSIZE_TFT*2 即先右移一位
//取低21位
rLCDSADDR3=(((SCR_XSIZE_TFT-LCD_XSIZE_TFT)/1)<<11)|(LCD_XSIZE_TFT/1);//offsize的值为0
rLCDINTMSK|=(3); // MASK LCD Sub Interrupt
rTCONSEL &= (~7) ; // Disable LPC3480
rTPAL=0; // Disable Temp Palette 暂存的调色寄存器
}
可以参照上面对一一些参数的定义解释来看些程序,同时也要结合LCD控制寄存器来对程序进行分析。
五、 TFT显示图像和字符
1、 TFT图像显示
/**************************************************************
在LCD屏幕上指定坐标点画一个指定大小的图片
**************************************************************/
void Paint_Bmp(int x0,int y0,int h,int l,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) ;//采用的16位显示 格式 5:5:5:1
if ( ( (x0+x) < SCR_XSIZE_TFT) && ( (y0+y) < SCR_YSIZE_TFT) )
LCD_BUFFER[y0+y][x0+x] = c ;
p = p + 2 ;
}
}
}
2、 字符的显示
每一个字符要占用8*16个点。因为我采用的是3.5TFT屏没有字库,所有要编写一个ASCII码的库函数:font.c
/**************************************************************
在LCD屏幕上指定坐标点写ASCII码
参数1:X的坐标
参数2:Y的坐标
参数3:显示的字符输入ASCII码
参数4:文字颜色
参数5:背景色
参数6:是否显示设定的背景色
**************************************************************/
void Lcd_PutASCII(unsigned int x,unsigned int y,unsigned char ch,unsigned int c,unsigned int bk_c,unsigned int st)
{
unsigned short int i,j;
unsigned char *pZK,mask,buf;
pZK = &__VGA[ch*16];//VGA为ASCII码的库,每一个字符对应16个数,每个数有8位
//字符显示原理是8*16个点,行8个点,列16个点
for( i = 0 ; i < 16 ; i++ )
{
mask = 0x80;
buf = pZK[i];
for( j = 0 ; j < 8 ; j++ )
{
if( buf & mask ) //对于buf中的某一位若为1,则在显示屏上对应的点要打开
{
PutPixel(x+j,y+i,c);
}
else //否则对应的点就显示底色
{
if( !st )
{
PutPixel(x+j,y+i,bk_c);
}
}
mask = mask >> 1; //对应其余位
}
}
}