所用的LCD为TopPoly-TD035STED4(TFT)型号,240*320的
其VCLK为6.39MHz。 根据s3c2440手册s3c2440处理LCD的时钟源是HCLK,通过寄存器LCDCON1中的CLKVAL可以调整VCLK频率大小,它的公式为:VCLK=HCLK÷[(CLKVAL+1)×2],程序的内部分频为FCLK=400MHz、HCLK=100MHz、PCLK=50MHz(MPLLCON=(92<<12)|(1<<4)|1;),因此得到CLKVAL取整为6。
注:(LCD一般需要三个时序信号:VSYNC、HSYNC和VCLK。VSYNC是垂直同步信号,在每进行一个帧(即一个屏)的扫描之前,该信号就有效一次,由该信号可以确定LCD的场频,即每秒屏幕刷新的次数(单位Hz)。HSYNC是水平同步信号,在每进行一行的扫描之前,该信号就有效一次,由该信号可以确定LCD的行频,即每秒屏幕从左到右扫描一行的次数(单位Hz)。VCLK是像素时钟信号。
其中VSYNC是帧同步信号,VSYNC每发出1个脉冲,都意味着新的1屏视频资料开始发送。而HSYNC为行同步 信号,每个HSYNC脉冲都表明新的1行视频资料开始发送。而VDEN则用来标明视频资料的有效,VCLK是用来锁存视频资料的像数时钟。网上搜到的资料来解释这几个时序信号。
可以查看s3c2440手册‘Figure 15-6. TFT LCD Timing Example’图)
通过上图可以得到:
代码
#define
LCD_WIDTH 240
//
屏幕的宽
#define
LCD_HEIGHT 320
//
屏幕的高
//
垂直同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define
VSPW 1
//
(3-1)
#define
VBPD 1
//
(15-1)
#define
VFPD 1
//
(12-1)
//
水平同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define
HSPW (10-1)
#define
HBPD (20-1)
#define
HFPD (10-1)
//
显示尺寸
#define
LINEVAL (LCD_HEIGHT-1)
#define
HOZVAL (LCD_WIDTH-1)
注:(对于一个已知尺寸的LCD屏,只要确定了VCLK值,行频和场频就应该知道了。但这样还不行的,因为在每一帧时钟信号中,还会有一些与屏显示无关的时钟出现,这就给确定行频和场频带来了一定的复杂性。如在HSYNC信号先后会有水平同步信号前肩(HFPD)和水平同步信号后肩(HBPD)出现,在VSYNC信号先后会有垂直同步信号前肩(VFPD)和垂直同步信号后肩(VBPD)出现,在这些信号时序内,不会有有效像素信号出现,另外HSYNC和VSYNC信号有效时,其电平要保持一定的时间,它们分别叫做水平同步信号脉宽HSPW和垂直同步信号脉宽VSPW,这段时间也不能有像素信号。因此计算行频和场频时,一定要包括这些信号。HBPD、HFPD和HSPW的单位是一个VCLK的时间,而VSPW、VFPD和VBPD的单位是扫描一行所用的时间。也是网上搜的。)
这些信号(VSPW、VFPD、VBPD、LINEVAL、HBPD、HFPD、HSPW和HOZVAL)是通过寄存器LCDCON2、LCDCON3和LCDCON4来配置的,具体查看s3c2440的手册。
LCDCON1寄存器:
LINECNT
:当前行扫描计数器值,标明当前扫描到了多少行。
CLKVAL :决定VCLK的分频 比(上面已经提到过)。LCD控制器输出的VCLK是直接由系统总线(AHB)
的工作频率HCLK(一般为100MHZ)直接分频得到的。做为240*320的TFT屏,应保证得
出的VCLK在5~10MHz之间。
MMODE :VM信号的触发模式(仅对STN屏有效,对TFT屏无意义。)PNRMODE :选择当前的显示模式,
对于TFT屏而言,应选择[11],即TFT LCD panel。
BPPMODE :选择色彩模式,对于真彩显示而言,选择16bpp(64K色)即可满足要求。
ENVID
:
使能LCD信号输出
LCDCON5寄存器:
VSTATUS :当前VSYNC信号扫描状态,指明当前VSYNC同步信号处于何种扫描阶段。
HSTATUS
:
当前HSYNC信号扫描状态,指明当前HSYNC同步信号处于何种扫描阶段。
BPP24BL
:
设定24bpp显示模式时,视频资料在显示缓冲区中的排列顺序(即低位有效还是高位有效)。
对于16bpp的 64K色显示模式,该设置位无意义。
FRM565
:
对于16bpp显示模式,有2中形式,一种是RGB=5:5:5:1,另一种是5:6:5。后一 种模式最为
常用,它的含义是表示64K种色彩的16bit RGB资料中,红色(R)占了5bit,绿色(G)占了
6bit,兰色(B)占了5bit。
INVVCLK 、INVLINE 、INVFRAME 、INVVD :通过前面提到的‘Figure 15-6’的时序图,我们知
道,CPU的LCD控制器输
出的时序默认是正脉冲,而LCD需要VSYNC(VFRAME)、
VLINE(HSYNC)均为负脉冲,因此 INVLINE 和 INVFRAME 必须设为“1 ”,即选择反相输
出。 INVVDEN , INVPWREN , INVLEND 的功能同前面的类似。
PWREN
:
LCD电源使能控制。在CPU LCD控制器的输出信号中,有一个电源使能管脚LCD_PWREN,用
来做为LCD屏电源的开关信号。
ENLEND
:
对普通的TFT屏无效,可以不考虑。
INVVDEN、INVPWREN、INVLEND:是否翻转这些信号,一般为正常,不需要翻转。
BSWP 、HWSWP : 为字节(Byte)或半字(Half-Word)交换使能。由于不同的GUI对
FrameBuffer(显示缓冲区)的管理不同,必要时需要通过调整 BSWP 和 HWSWP
来适应GUI。
因此:
代码
//
for LCDCON1
#define
CLKVAL_TFT 4
//
设置时钟信号
#define
MVAL_USED 0
//
#define
PNRMODE_TFT 3
//
TFT型LCD
#define
BPPMODE_TFT 13
//
24位TFT型LCD
//
for LCDCON5
#define
BPP24BL 0
//
32位数据表示24位颜色值时,低位数据有效,高8位无效
#define
INVVCLK 0
//
像素值在VCLK下降沿有效
#define
INVVLINE 1
//
翻转HSYNC信号
#define
INVVFRAME 1
//
翻转VSYNC信号
#define
INVVD 0
//
正常VD信号极性
#define
INVVDEN 0
//
正常VDEN信号极性
#define
PWREN 1
//
使能PWREN信号
#define
BSWP 0
//
颜色数据字节不交换
#define
HWSWP 0
//
颜色数据半字不交换
rLCDCON1
=
(CLKVAL_TFT
<<
8
)
|
(MVAL_USED
<<
7
)
|
(PNRMODE_TFT
<<
5
)
|
(BPPMODE_TFT
<<
1
)
|
0
;
rLCDCON2
=
(VBPD
<<
24
)
|
(LINEVAL
<<
14
)
|
(VFPD
<<
6
)
|
(VSPW);
rLCDCON3
=
(HBPD
<<
19
)
|
(HOZVAL
<<
8
)
|
(HFPD);
rLCDCON4
=
(HSPW);
//
rLCDCON5
=
(BPP24BL
<<
12
)
|
(INVVCLK
<<
10
)
|
(INVVLINE
<<
9
)
|
(INVVFRAME
<<
8
)
|
(
0
<<
7
)
|
(INVVDEN
<<
6
)
|
(PWREN
<<
3
)
|
(BSWP
<<
1
)
|
(HWSWP);
LCDSADDR1和LCDSADDR2:
LCDBASEL=LCDBASEU+(PAGEWITH+OFFSIZE)×(LINEVAL+1)
LCDSADDR3寄存器:
OFFSIZE:用于虚拟屏幕的偏移长度,如果我们不使用虚拟屏幕,就把它置为0 。
PAGEWIDTH:定义了视口的宽,单位是半字,如在上面的例子中,PAGEWIDTH应该为320×32÷16。
代码
#define
M5D(n) ((n) & 0x1fffff)
//
用于设置显示缓存区时,取低21位地址
//
定义显示缓存区
volatile
U32 LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];
rLCDSADDR1
=
(((U32)LCD_BUFFER
>>
22
)
<<
21
)
|
M5D((U32)LCD_BUFFER
>>
1
);
rLCDSADDR2
=
M5D((M5D((U32)LCD_BUFFER
>>
1
)
+
((LCD_WIDTH
*
32
/
16
+
0
)
*
320
)));
rLCDSADDR3
=
(LCD_WIDTH
*
32
/
16
)
&
0x7ff
;
//
参考s3c2440的手册
简单程序实现:
代码
//
***************************************************
#include
"
def.h
"
#include
"
option.h
"
#include
"
2440addr.h
"
#include
"
2440lib.h
"
#include
"
2440slib.h
"
//
================================
#define
M5D(n) ((n) & 0x1fffff)
//
用于设置显示缓存区时,取低21位地址
#define
LCD_WIDTH 240
//
屏幕的宽
#define
LCD_HEIGHT 320
//
屏幕的高
//
垂直同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define
VSPW 1
//
(3-1)
#define
VBPD 1
//
(15-1)
#define
VFPD 1
//
(12-1)
//
水平同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define
HSPW (10-1)
#define
HBPD (20-1)
#define
HFPD (10-1)
//
显示尺寸
#define
LINEVAL (LCD_HEIGHT-1)
#define
HOZVAL (LCD_WIDTH-1)
//
for LCDCON1
#define
CLKVAL_TFT 4
//
设置时钟信号
#define
MVAL_USED 0
//
#define
PNRMODE_TFT 3
//
TFT型LCD
#define
BPPMODE_TFT 13
//
24位TFT型LCD
//
for LCDCON5
#define
BPP24BL 0
//
32位数据表示24位颜色值时,低位数据有效,高8位无效
#define
INVVCLK 0
//
像素值在VCLK下降沿有效
#define
INVVLINE 1
//
翻转HSYNC信号
#define
INVVFRAME 1
//
翻转VSYNC信号
#define
INVVD 0
//
正常VD信号极性
#define
INVVDEN 0
//
正常VDEN信号极性
#define
PWREN 1
//
使能PWREN信号
#define
BSWP 0
//
颜色数据字节不交换
#define
HWSWP 0
//
颜色数据半字不交换
//
定义显示缓存区
volatile
U32 LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];
//
**********************************************************
//
延时程序
void
delay(
int
a)
{
int
k;
for
(k
=
0
;k
<
a;k
++
) ;
}
//
绘制屏幕背景颜色,颜色为c
void
Brush_Background( U32 c)
{
int
x,y ;
for
( y
=
0
; y
<
LCD_HEIGHT ; y
++
)
{
for
( x
=
0
; x
<
LCD_WIDTH ; x
++
)
{
LCD_BUFFER[y][x]
=
c ;
}
}
}
void
init_port_lcd()
{
rGPCCON
=
0xaaaaaaaa
;
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
;
rLCDCON1
=
(CLKVAL_TFT
<<
8
)
|
(MVAL_USED
<<
7
)
|
(PNRMODE_TFT
<<
5
)
|
(BPPMODE_TFT
<<
1
)
|
0
;
rLCDCON2
=
(VBPD
<<
24
)
|
(LINEVAL
<<
14
)
|
(VFPD
<<
6
)
|
(VSPW);
rLCDCON3
=
(HBPD
<<
19
)
|
(HOZVAL
<<
8
)
|
(HFPD);
rLCDCON4
=
(HSPW);
//
rLCDCON4 = (5<< 0);
rLCDCON5
=
(BPP24BL
<<
12
)
|
(INVVCLK
<<
10
)
|
(INVVLINE
<<
9
)
|
(INVVFRAME
<<
8
)
|
(
0
<<
7
)
|
(INVVDEN
<<
6
)
|
(PWREN
<<
3
)
|
(BSWP
<<
1
)
|
(HWSWP);
rLCDSADDR1
=
(((U32)LCD_BUFFER
>>
22
)
<<
21
)
|
M5D((U32)LCD_BUFFER
>>
1
);
rLCDSADDR2
=
M5D( (M5D((U32)LCD_BUFFER
>>
1
)
+
((LCD_WIDTH
*
32
/
16
+
0
)
*
320
)) );
//
LCD_WIDTH*LCD_HEIGHT*4
rLCDSADDR3
=
(LCD_WIDTH
*
32
/
16
)
&
0x7ff
;
//
参考s3c2440的手册
rLCDINTMSK
|=
(
3
);
//
屏蔽LCD中断
//
rTCONSEL = 0;
//
无效LPC3480
rTCONSEL
&=
(
~
7
);
rTPAL
=
0x0
;
rTCONSEL
&=
~
((
1
<<
4
)
|
1
);
rGPGUP
=
rGPGUP
&
(
~
(
1
<<
4
))
|
(
1
<<
4
);
//
GPG4上拉电阻无效
rGPGCON
=
rGPGCON
&
(
~
(
3
<<
8
))
|
(
3
<<
8
);
//
设置GPG4为LCD_PWREN
rGPGDAT
=
rGPGDAT
|
(
1
<<
4
) ;
//
GPG4置1
rLCDCON5
=
rLCDCON5
&
(
~
(
1
<<
3
))
|
(
1
<<
3
);
//
有效PWREN信号
rLCDCON5
=
rLCDCON5
&
(
~
(
1
<<
5
))
|
(
0
<<
5
);
//
PWREN信号极性不翻转
rLCDCON1
|=
1
;
//
LCD开启
}
int
Main(
int
argc,
char
**
argv)
{
int
i;
U8 key;
U32 mpll_val
=
0
;
int
data;
mpll_val
=
(
92
<<
12
)
|
(
1
<<
4
)
|
(
1
);
//
init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val
>>
12
)
&
0xff
, (mpll_val
>>
4
)
&
0x3f
, mpll_val
&
3
);
ChangeClockDivider(key,
12
);
//
ChangeClockDivider(1,1);
//
1:2:4 FCLK:HCLK:PCLK
//
rCLKDIVN=0x4;
//
1:4:4
//
ChangeMPllValue(82,2,1);
//
FCLK=135.0Mhz
//
ChangeMPllValue(82,1,1);
//
FCLK=180.0Mhz
//
ChangeMPllValue(161,3,1);
//
FCLK=202.8Mhz
//
ChangeMPllValue(117,1,1);
//
FCLK=250.0Mhz
//
ChangeMPllValue(122,1,1);
//
FCLK=260.0Mhz
//
ChangeMPllValue(125,1,1);
//
FCLK=266.0Mhz
//
ChangeMPllValue(127,1,1);
//
FCLK=270.0Mhz
//
MMU_EnableICache();
//
MMU_EnableDCache();
MMU_DisableICache();
MMU_DisableDCache();
init_port_lcd();
while
(
1
)
{
//
黑色背景
Brush_Background(
0x0000
);
delay(
5000000
);
//
白色背景
Brush_Background(
0xffffff
);
delay(
5000000
);
//
蓝色背景
Brush_Background(
0x00ff
);
delay(
5000000
);
//
绿色背景
Brush_Background(
0xff00
);
delay(
5000000
);
//
黄色背景
Brush_Background(
0xffff00
);
delay(
5000000
);
}
return
0
;
}