先来看一下s3c2440中LCD控制器的结构:
从上面结构图可以看出:整个lcd控制器大致可以由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器几个部分组成。
1。REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的
2。LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,视频数据在不需要 CPU的干预的情况下显示在LCD屏上
3.VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,然后通过数据端口VD[23:0]传送视频数据到LCD驱动器
4.TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,而这些控制信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能产生这些信号的不同形态,从而支持不同的LCD驱动器(即不同的STN/TFT屏)。
LCD提供的外部接口信号:
1.VSYNC/VFRAME/STV:垂直同步信号(TFT)/帧同步信号(STN)/SEC TFT信号
2.HSYNC/VLINE/CPV:水平同步信号(TFT)/行同步脉冲信号(STN)/SEC TFT信号
3.VD[23:0]:LCD像素数据输出端口(TFT/STN/SEC TFT);
4。VDEN/VM/TP:数据使能信号(TFT)/LCD驱动交流偏置信号(STN)/SEC TFT 信号
说明:
A:显示指针从矩形左上角的第一行第一个点开始,一个点一个点的在LCD上显示,在上面的时序图上用时间线表示就为VCLK,我们称之为像素时钟信号;
B:当显示指针一直显示到矩形的右边就结束这一行,那么这一行的动作在上面的时序图中就称之为1 Line;
C:接下来显示指针又回到矩形的左边从第二行开始显示,注意,显示指针在从第一行的右边回到第二行的左边是需要一定的时间的,我们称之为行切换;
D:如此类推,显示指针就这样一行一行的显示至矩形的右下角才把一副图显示完成。因此,这一行一行的显示在时间线上看,就是时序图上的HSYNC
E:然而,LCD的显示并不是对一副图像快速的显示一下,为了持续和稳定的在LCD上显示,就需要切换到另一幅图上(另一幅图可以和上一副图一样或者不一样,目的只是为了将图像持续的显示在LCD上)。那么这一副一副的图像就称之为帧,在时序图上就表示为1 Frame,因此从时序图上可以看出1 Line只是1 Frame中的一行;
F:同样的,在帧与帧切换之间也是需要一定的时间的,我们称之为帧切换,那么LCD整个显示的过程在时间线上看,就可表示为时序图上的VSYNC
下面是时序图:
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;
行频和场频的计算:
行频 = VCLK /[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]
场频 =行频 /[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]
接下来了解下相关寄存器的字段结构:
对于以上这些参数的值将分别保存到REGBANK寄存器组中的LCDCON1/2/3/4/5寄存器中
LCDCON1:LINECNT18--27设置屏的行数,最大1024行,从LINECNT计数到0,真正要显示区域的大小是在LCDCON2中的LINEVAL设置;CLKVAL 8--17用于设置分频因子,STN: VCLK = HCLK / (CLKVAL x 2) ( CLKVAL ≥2 ) TFT: VCLK = HCLK / [(CLKVAL+1) x 2] ( CLKVAL ≥ 0 ); 6 - 5位扫描模式(对于STN屏:4位单/双扫、8位单扫); 4 - 1位色位模式(1BPP、8BPP、16BPP等);
LCDCON2:用于设置VBPD,LINEVAL,VFBD,VSPW,对于STN屏VBPD,VFBD,VSPW值都为0。
LCDCON3:功能类似LCDCON2,只是用于垂直方向的一些参数。
LCDCON5:用于查询和设置一些状态信息。
编程步骤:
1、打开LCD背光
将LCD背光对应的GPIO设置为禁止上拉(GPxUP相应位写入1),选择output类型(GPxCON相应位写入01),输出为高电平(GPxDAT相应位写入1)。
2、打开LCD电源
可以将GPG4选择为LCD_PWREN(GPGCON:9-8写入11),这时候LCD电源的打开/关闭可以通过LCDCON5:3来控制。
也可以自定义其他GPIO用作LCD电源开关,只需将此GPIO设置为禁止上拉(GPxUP相应位写入1),选择output类型(GPxCON相应位写入01),输出为高电平(GPxDAT相应位写入1)打开LCD电源。
3、设置其他信号线
其他信号线包括VD0-VD23和VFRAME、VLINE、VCLK等,分别在GPCCON,GPDCON中选择相应功能。
4、设置LCD的频率(VCLK)
LCD的Datasheet上一般会写有一个推荐的频率,比如我使用的屏幕推荐频率为6.4M,我需要通过一些计算选择一个合适的CLKVAL以产生这个频率:
对于TFT LCD,S3C2440提供的VCLK的计算公式为:
VCLK = HCLK / ((CLKVAL+1)*2)
可以得出:
CLKVAL = HCLK / (VCLK * 2) - 1
我的HCLK是100Mhz(CPU运行在400Mhz, CLKDIV_VAL设置为5,Fclk:Hclk:Pclk = 1:4:8),VCLK使用屏幕推荐的6.4M,得到:
CLKVAL = 100000000 / (6400000 * 2) - 1 = 6.8
选择最接近的整数值7,写入LCDCON1:17-8。
(VCLK其实就是根据 每秒帧数*帧行数*行像素 计算出来的,帧行数和行像素需要包含空白数和同步数)
5、设置其他相关参数
LCD相关的参数主要还有这几个:
LINEVAL: LCD水平像素-1,如320-1 = 319
HOZVAL: LCD垂直像素-1,如240-1 = 239
HFPD: 行开始前的VCLK时钟数(LCD屏幕的Datasheet一般有推荐值)
HBPD: 行结束后的VCLK时钟数(LCD屏幕的Datasheet一般有推荐值)
HSPW: 行之间水平同步的无效VCLK时钟数(LCD屏幕的Datasheet一般有推荐值)
VFPD: 帧数据开始前的空白行数(LCD屏幕的Datasheet一般有推荐值)
VBPD: 帧数据结束后的空白行数(LCD屏幕的Datasheet一般有推荐值)
VSPW: 帧之间垂直同步的无效行数(LCD屏幕的Datasheet一般有推荐值)
(相关寄存器LCDCON2, LCDCON3, LCDCON4)
6、设置视频缓冲区的地址
2440支持虚拟屏幕,可以通过改变LCD寄存器实现屏幕快速移动
PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行字节数,如16位宽320像素,设为320 * 2
OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0
LCDBANK: 视频帧缓冲区内存地址30-22位
LCDBASEU: 视频帧缓冲区的开始地址21-1位
LCDBASEL: 视频帧缓冲区的结束地址21-1位
(相关寄存器LCDSADDR1,LCDSADDR2,LCDSADDR3)
7、确定信号的极性
2440的LCD控制器允许设置VCLK、VLINE、VFRAME等信号的极性(上升沿有效还是下降沿有效),需要对照LCD的Datasheet一一确认。
(相关寄存器LCDCON5)
8、禁止LPC3600/LCC3600模式!
如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)!
9、打开视频输出
ENVID设为1 (LCDCON1:0写入1)
核心代码:(特别注意LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH]中LCD_HEIGHT对应下面rLCDCON2中的行数LINEVAL)
- #include "def.h"
- #include "lcd.h"
- #include "2440addr.h"
- #define LCD_WIDTH 240
- #define LCD_HEIGHT 320
- #define LINEVAL (LCD_HEIGHT - 1)
- #define HOZVAL (LCD_WIDTH - 1)
- //水平同步信号
- #define LCD_HSYNC 5
- #define LCD_LEFT_MARGIN 19
- #define LCD_RIGHT_MARGIN 36
- //垂直同步信号
- #define LCD_VSYNC 1
- #define LCD_UPPER_MARGIN 1
- #define LCD_LOWER_MARGIN 5
- #define LCD_CLKVAL 4 //时钟分频因子,VCLK = HCLK/((LCD_CLKVAL+1)*2)
- #define BPPMODE_TFT 12 //16位TFT
- //for LCDCON5
- #define PNRMODE 3 //显示模式:TFT LCD Panel
- #define BPP24BL 1 //24位输出格式MSB Valid
- #define FRM565 1 //16位输出格式:5:6:5
- #define INVVCLK 0 //下降沿有效
- #define INVVFRAME 1 //翻转VSYNC信号
- #define INVVLINE 1 //翻转HSYNC信号
- #define PWREN 1 //使能PWREN信号
- #define INVVD 0 //正常VD信号极性
- #define INVVDEN 0 //正常VDEN信号极性
- #define INVVDEN 0 //正常VDEN信号极性
- #define BSWP 0 //颜色数据字节不交换
- #define HWSWP 0 //颜色数据半字不交换
- //注意这里的LCD_HEIGHT对应下面rLCDCON2中的行数LINEVAL,否则会出错
- volatile static unsigned short LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];
- extern const unsigned char sunflower_240x320[];
- #define M5D(n) ((n)&0x1fffff) //得到n的低21位
- #define LCD_ADDR ((U32)LCD_BUFFER)
- static void LCD_Port_Init()
- {
- //*** PORT C GROUP
- //Ports : GPC15 GPC14 GPC13 GPC12 GPC11 GPC10 GPC9 GPC8 GPC7 GPC6 GPC5 GPC4 GPC3 GPC2 GPC1 GPC0
- //Signal : VD7 VD6 VD5 VD4 VD3 VD2 VD1 VD0 LCDVF2 LCDVF ** VM VFRAME VLINE VCLK ***
- //Binary : 10 10 , 10 10 , 10 10 , 10 01 , 00 00 , ** 10 , 10 10 , 10 **
- // rGPCCON = 0xaaa956aa;
- rGPCCON = 0xaaaa02a8;
- rGPCUP = 0xffff; // The pull up function is disabled GPC[15:0]
- //*** PORT D GROUP
- //Ports : GPD15 GPD14 GPD13 GPD12 GPD11 GPD10 GPD9 GPD8 GPD7 GPD6 GPD5 GPD4 GPD3 GPD2 GPD1 GPD0
- //Signal : VD23 VD22 VD21 VD20 VD19 VD18 VD17 VD16 VD15 VD14 VD13 VD12 VD11 VD10 VD9 VD8
- //Binary : 10 10 , 10 10 , 10 10 , 10 10 , 10 10 , 10 10 , 10 10 ,10 10
- rGPDCON = 0xaaaaaaaa;
- rGPDUP = 0xffff; // The pull up function is disabled GPD[15:0]
- }
- void LCD_Init(void)
- {
- LCD_Port_Init();
- rLCDCON1 = (LCD_CLKVAL << 8) | (PNRMODE<<5) | (BPPMODE_TFT <<1);
- rLCDCON2 = (LCD_UPPER_MARGIN << 24) |(LINEVAL << 14) |(LCD_LOWER_MARGIN << 6) | (LCD_VSYNC<<0);
- rLCDCON3 = (LCD_LEFT_MARGIN <<19) | (HOZVAL<<8) |(LCD_RIGHT_MARGIN<<0);
- rLCDCON4 = (LCD_HSYNC<<0);
- rLCDCON5 = (FRM565 << 11)|(INVVCLK<<10) | (INVVLINE<<9) | (INVVFRAME<<8) | (0<<7) | (INVVDEN<<6) | (PWREN<<3) |(BSWP<<1) | (HWSWP);
- //将缓存首地址的A[30:22]存在寄存器[29:21]中,数据高地址A[21:1]存在寄存器[20:0]中
- rLCDSADDR1 = ((LCD_ADDR>>22)<<21) | ((M5D(LCD_ADDR >> 1)) << 0);
- //将缓存的低地址A[21:1]写入寄存器,
- rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);
- //LCD_WIDTH×16/16;由于是选择的16位模式,
- //如果是24位模式,每个像素4字节则为LCD_WIDTH×32/16
- rLCDSADDR3 = LCD_WIDTH;
- rLCDINTMSK|=(3); // 屏蔽LCD中断
- rTCONSEL = 0; //无效LPC3480
- rLCDCON1|=1; // ENVID=ON
- }
- //背光开启
- static void LCD_Backlight(void)
- {
- //GPG4 configure as LCD_PWREN
- rGPGCON =rGPGCON &(~(3<<8)) | (3<<8) ;
- rGPGUP =rGPGUP &(~(1<<4)) | (1<<4);
- rGPGDAT = rGPGDAT | (1<<4) ;
- //Enable LCD POWER ENABLE Function
- rLCDCON5 = rLCDCON5&(~(1<<3))|(1<<3); // 有效PWREN信号
- rLCDCON5 = rLCDCON5&(~(1<<5))|(0<<5); // PWREN信号极性不翻转
- }
- /**************************************************************
- 320×240 TFT LCD单个象素的显示数据输出
- **************************************************************/
- static void PutPixel(U32 x,U32 y,U16 c)
- {
- if(x<LCD_WIDTH && y<LCD_HEIGHT)
- LCD_BUFFER[(y)][(x)] = c;
- }
- /**************************************************************
- 320×240 TFT LCD全屏填充特定颜色单元或清屏
- **************************************************************/
- static void Lcd_ClearScr( U16 c)
- {
- unsigned int x,y ;
- for( y = 0 ; y < LCD_HEIGHT ; y++ )
- {
- for( x = 0 ; x < LCD_WIDTH ; x++ )
- {
- LCD_BUFFER[y][x] = c ;
- }
- }
- }
- /**************************************************************
- 在LCD屏幕上用颜色填充一个矩形
- **************************************************************/
- void Glib_FilledRectangle(int x1,int y1,int x2,int y2, U16 color)
- {
- int i;
- int j;
- //for(i=y1;i<=y2;i++)
- //Glib_Line(x1,i,x2,i,color);
- for(i=x1;i<x2;i++)
- for(j=y1;j<y2;j++)
- PutPixel(i,j,color);
- }
- /**************************************************************
- 在LCD屏幕上指定坐标点画一个指定大小的图片
- **************************************************************/
- static void Paint_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) < LCD_WIDTH) && ( (y0+y) < LCD_HEIGHT) )
- LCD_BUFFER[y0+y][x0+x] = c ;
- p = p + 2 ;
- }
- }
- }
- void LCD_test(void)
- {
- LCD_Init();
- LCD_Backlight(); //开启背光
- // Glib_FilledRectangle(10,10,100,100,0xffe0);
- Paint_Bmp(0, 0, 240, 320, sunflower_240x320);
- }