LCD简介
LCD(Liquid Crystal Display) ,即液晶显示屏,是一种采用了液晶控制透光度技术来实现色彩的显示器,LCD 有很多种类型,常见的有 :
- STN(超扭曲向列),它的特点是功耗低,但亮度不足,响应时间长;(1602那种类型的)
- TFT(薄膜晶体管),它的特点是响应时间短,画面清晰,但功耗稍高,(自己实验用的应该是这个类型的,这个类型当作计算机液晶显示设备)。
- LTPS(低温多晶硅),各方面性能优越,但技术要求高;
- OLED(有机发光二极管),各方面性能优越,但技术要求高。
S5PV210 LCD 控制器
S5PV210 的 LCD 控制器由一个逻辑单元组成,它的作用是:
把 LCD 图像数据从一个位于系统内存的 video buffer 传送到一个外部的 LCD 驱动器接口。
LCD驱动接口支持 3 种接口:
- RGB 接口:(自己用的使这种)
- indirect-i80 接口
- UV 接口
S5PV210 的 LCD 控制器支持多种颜色格式,例如
- RGB (1BPP 到 24BPP)
- YCbCr 4:4:4 (只有本地总线)
LCD 控制器可以通过编程满足不同的需求, 即
满足水平、垂直方向的像素数目,满足数据接口的数据线宽度、接口时序和刷新速率。
S5PV210 LCD 关键特性介绍
总线接口 : AMBA AXI 64 位主模式 /AHB 32 位从模式,本地视频总线
(YCbCr/RGB).
视频输出接口:RGB 接口(并行 24 位,串行 8 位) ,Indirect i80 接口,YUV
接口.
- 支持 i80/RGB 双输出模式
- 支持 8/16/24 多种 BPP 模式
- 4/8/16 位的可编程 DMA
- 支持 256 x 32 位的调色板
- 支持最大为 16MB 的虚拟屏幕
信号类型
- VSYNC:垂直同步信号,每个 VSYNC 信号表示一帧数据的开始.
- HSYNC: 水平同步信号,每个 HSYNC 信号表示一行数据的开始.
- VCLK: 像素时钟信号,每个 VCLK 信号表示一个像素数据.
- VDEN: 数据使能信号.
- VD: Video Data,数据信号.
S5PV210 LCD 控制器子模块概述
LCD 控制器模块由
VSFR,VDMA,VPRCS,VTIME 以及 video clock 组成。为了配置 LCD 显示控制模块,VSFR 有 121 个可编程寄存器集,一个 gamma LUT 寄存器集(64 个寄存器),一个 i80 命令寄存器集(12 个寄存器)和 5 个 256 x 32调色板内存。
VDMA 是一个专用的显示 DMA 通道,用于才能够 frame 内存里传输视频数据到VPRCS。利用特殊的 DMA,用户可以在没有 CPU 干涉的情况下传输视频数据到屏幕上示。
VPRCS 从 VDMA 中接收视频数据并在转换视频数据为合适的数据格式后(例如:8BPP 或 16BPP 模式) 通过 RGB_VD 或 SYS_VD 端口传送到显示设备上(如: LCD)
VTIME 由可编程逻辑模块组成,
在不同的 LCD 驱动下支持各种接口时序和波特率。 VTIME 模块产生 RGB_VSYNC, RGB_HSYNC, RGB_VCLK, RGB_VDEN SYS_CS0,SYS_CS1,SYS_WE 等等信号。
信号时序图分析
210手册(1207页)
- VSYNC 的有效启动脉冲是高电平有效
- VSYNC 脉冲宽度为(VSPW+1)个 HSYNC 信号周期,在这个周期内数据无效
- VSYNC 有效启动脉冲后还要经过(VBPD+1)个 HSYNC 信号周期,有效的数据才会出现
- 跟随着连续发出(LINEVAL+1)行的有效数据
- 最后经过(VFPD+1)个无效行,完整的一帧数据就传输结束,紧接着下一次VSYNC 启动脉冲才能发出.
- HSYNC 的有效启动脉冲是高电平有效.
- HSYNC 脉冲宽度为(HSPW+1)个 VCLK 信号周期,在这个周期内像素数据无效.
- HSYNC 有效启动脉冲后还要经过(HBPD+1)个 VLCK 信号周期,有效的像素数据才会出现.
- 跟随着连续发出(HOZVAL+1)个的有效像素数据.
- 最后经过(HFPD+1)个无效数据,完整的一行数据就传输结束,紧接着下一次HSYNC 启动脉冲才能发出.
基本模式是类似的。
参数的计算
查找数据手册:S70-AT070TN92.pdf,13和14页
根据表格和图计算相应参数
垂直信号参数
tvpw 可取 typ.值为 10(中间值) ,而 tvpw 的值就是 VSYNC 的脉冲宽度,也就是说 VSPW + 1 = 10,所以 VSPW = 9
tvb 可取 typ.值为 23,而 tvb 的值是 VSYNC 前面经过(VSPW + 1 )+(VBPD + 1)
tvfp 可取 typ.值为 22,而 tvfp 的值是后面经过(VFPD + 1)的无效行,也就是说 tvfp = VFPD + 1= 22,所以 VFPD = 21
tvd 可取 typ.值为 480, 而 tvd 的值就是 (LINEVAL+1) ,其实就是 y 轴的 LCD分辨率,也就说 tvd = (LINEVAL+1)= 480,所以 LINEVAL = 479的无效行,也就是说 tvb =
(VSPW + 1 )+(VBPD + 1) = 23,相当于 10 + VBPD + 1 = 23,所以 VBPD = 12
水平信号参数
thpw 可取 typ.值为 20(中间值),而 thpw 的值就是 HSYNC 的脉冲宽度,也就是说 HSPW + 1 = 20,所以 VSPW = 19.
thb 可取 typ.值为 46,而 tvb 的值是 VSYNC 前面经过(HSPW + 1 )+(HBPD + 1)的无效行,也就是说 thb= (HSPW + 1 )+(HBPD + 1) = 46,相当于 20 + VBPD + 1 = 46,所以 HBPD = 25.
thfp 可取 typ.值为 210,而 thfp 的值是后面经过(HFPD + 1)的无效行,也就是说 thfp = HFPD + 1= 210,所以 VFPD = 209。
时钟频率计算
RGB_VCLK (Hz) = HCLK / (CLKVAL+1), CLKVAL >= 1
Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x
{(HSPW+1) + (HBPD +1) + (HFPD+1) + (HOZVAL + 1) } x { ( CLKVAL+1 ) /
( HCLK ) } ]
数据格式
14BPP和24BPP,数据存储的地址方式不同,具体查手册。
硬件端口功能
该液晶屏接口有 45Pin,其中 VD0~VD23 是数据信号线引脚,VDEN 是数据信号使能引脚,VSYNC 是垂直同步信号引脚,HSYNC 是水平同步信号引脚,VCLK 是像素时钟信
号引脚,Xi2SCL2,Xi2SDA2 分别是 I2C 的 SCL、SDA 引脚,在电容屏的触摸中会用到。 XENIT14、 XEINT15 引脚是外部中断引脚。
程序的编写
根据210手册1218页,OVERVIEW OF PROGRAMMER’S MODEL里面提到了23个寄存器的作用,根据这个来编写程序。
一共9步,在程序里体现。
#define GPF0CON ( *(volatile unsigned int *)0xE0200120 )
#define GPF1CON ( *(volatile unsigned int *)0xE0200140 )
#define GPF2CON ( *(volatile unsigned int *)0xE0200160 )
#define GPF3CON ( *(volatile unsigned int *)0xE0200180 )
#define GPD0CON ( *(volatile unsigned int *)0xE02000A0 )
#define GPD0DAT ( *(volatile unsigned int *)0xE02000A4 )
#define CLK_SRC1 ( *(volatile unsigned int *)0xe0100204 )
#define CLK_DIV1 ( *(volatile unsigned int *)0xe0100304 )
#define DISPLAY_CONTROL ( *(volatile unsigned int *)0xe0107008 )
/* LCD Controler Pins */
#define VIDCON0 ( *(volatile unsigned int *)0xF8000000 )
#define VIDCON1 ( *(volatile unsigned int *)0xF8000004 )
#define VIDTCON2 ( *(volatile unsigned int *)0xF8000018 )
#define VIDTCON3 ( *(volatile unsigned int *)0xF800001c )
#define WINCON0 ( *(volatile unsigned int *)0xF8000020 )
#define WINCON2 ( *(volatile unsigned int *)0xF8000028 )
#define SHADOWCON ( *(volatile unsigned int *)0xF8000034 )
#define VIDOSD0A ( *(volatile unsigned int *)0xF8000040 )
#define VIDOSD0B ( *(volatile unsigned int *)0xF8000044 )
#define VIDOSD0C ( *(volatile unsigned int *)0xF8000048 )
#define VIDW00ADD0B0 ( *(volatile unsigned int *)0xF80000A0 )
#define VIDW00ADD1B0 ( *(volatile unsigned int *)0xF80000D0 )
#define VIDW00ADD2 ( *(volatile unsigned int *)0xF8000100 )
#define VIDTCON0 ( *(volatile unsigned int *)0xF8000010 )
#define VIDTCON1 ( *(volatile unsigned int *)0xF8000014 )
/*垂直信号*/
#define VSPW 9 //tvpw=VSPW+1的值是VSYNC的脉冲宽度,现根据表确定tvpw
#define VBPD 12 //tvb = (VSPW + 1 )+(VBPD + 1),tvb 的值前面(VSPW + 1 )+(VBPD + 1)的无效行,先根据表确定tvb
#define LINEVAL 479//tvd = (LINEVAL+1)= 480,根据表确定tvd的值,表示y轴大小
#define VFPD 21 //tvfp 的值是后面经过(VFPD + 1)的无效行,现根据表确定tvfp
/*水平信号*/
#define HSPW 19 //thpw =HSPW + 1 = 20,现根据表确定thpw
#define HBPD 25 //thb= (HSPW + 1 )+(HBPD + 1) = 46, thb 的值是 VSYNC 前面经过(HSPW + 1 )+(HBPD + 1)的无效行,根据表先确定thb
#define HOZVAL 799//thd=HOZVAL +1,表示的x轴大小
#define HFPD 209//thfp = HFPD + 1= 210, thfp 的值是后面经过(HFPD + 1)的无效行,先根据表确定thfp
/****描述上下左右定点的位置*/
#define LeftTopX 0
#define LeftTopY 0
#define RightBotX 799
#define RightBotY 479
/**SCR_YSIZE_TFT = 480,SCR_XSIZE_TFT = 800,描述行和列的大小*/
#define SCR_XSIZE_TFT (800)
#define SCR_YSIZE_TFT (480)
volatile unsigned int LCD_BUFFER[SCR_YSIZE_TFT][SCR_XSIZE_TFT];
void lcd_init(void);
void lcd_draw_pixel(int row, int col, int color);
void lcd_clear_screen(unsigned long color);
void Glib_Rectangle(int x1,int y1,int x2,int y2,int color);
void lcd_init(void)
{
/** 1.设置相关GPIO引脚用于LCD */
GPF0CON = 0x22222222; // GPF0[7:0] LCD_VD[0-23]
GPF1CON = 0x22222222; // GPF1[7:0]
GPF2CON = 0x22222222; // GPF2[7:0]
GPF3CON = 0x22222222; // GPF3[7:0]
/**
**2.显示路径的选择
* 0b10: RGB=FIMD I80=FIMD ITU=FIMD
*/
DISPLAY_CONTROL = 2<<0;
/**3.打开背光,见原理图不再核心板上*/
GPD0CON |= 1<<4;
GPD0DAT |= 1<<1;
/**4.VIDCONx,设置接口类型、时钟、极性和使能LCD控制器等*/
/* 4.1:设置VIDCON0 -->CLKVAL_F[13:6]:该值需要根据LCD手册做相应的修改
* HCLKD=166.75MHz,DCLK(min) = 20ns(50MHz)
* VCLK = 166.75 / (4+1) = 33.35MHz
* (手册P14页可知DCLK频率的经典为33.3MHz)
* CLKDIR [4]:1 = 由CLKVAL_F分频
* ENVID [1]:1 = 使能视频输出
* ENVID_F [0]:1 = 在当前帧结束的情况下使能视频输出
*/
VIDCON0 &= ~((3<<26) | (1<<18) | (0xff<<6) | (1<<2));
VIDCON0 |= ((4<<6) | (1<<4) );
VIDCON0 |= 0x3; /* 开启总控制器 */
/*4.2设置VINC0N1---->. 设置极性(该值需要根据LCD手册做相应的修改)
* IVDEN [4]:0 = 正常
* IVSYNC[5]:1 = 反极性
* IHSYNC[6]:1 = 反极性
* IVCLK [7]:0 = Video data is fetched at VCLK falling edge
*/
/*.设置极性相反的原因:
S70-AT070TN92(p13页):VSYNC 和 HSYNC 都是低脉冲
S5PV210 芯片手册(p1207) 时序图:VSYNC 和 HSYNC 都是高脉冲有效,所以需要反转。*/
VIDCON1 &= ~(1<<7); /* 在vclk的下降沿获取数据 */
VIDCON1 |= ((1<<6) | (1<<5)); /* HSYNC极性反转, VSYNC极性反转 */
/**5.VIDTCONx,设置时序和长宽等 ,所用参数前面已经提到*/
VIDTCON0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);/*时序*/
VIDTCON1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);
/* 设置长宽LINEVAL[21:11]:多少行 = 480,HOZVAL [10:0] :水平大小 = 800*/
VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);
/** 6.配置WINCON0,设置window0的数据格式
Swap使能),很关键的一位,能够解决掉重影问题
BPPMODE_F[5:2]:1011 = 24 BPP
ENWIN_F [0]: 1 = 使能视频输出及视频控制信号
/
WINCON0 &= ~(0xf << 2);
WINCON0 |= (1<<15)|(0xB<<2);
WINCON0 |= 1; /* 开启窗口0 */
/**7. 配置VIDOSD0A/B/C,设置window0的坐标系 */
/* 窗口0,右下角的位置(800,480) */
VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);/*Window Shadow Control Register*/
/**8.配置VIDW00ADD0B0和VIDW00ADD1B0,设置framebuffer的地址*/
VIDW00ADD0B0 = LCD_BUFFER;
/* VBASEL = VBASEU + (LINEWIDTH+OFFSIZE) x (LINEVAL+1)
* = 0 + (800*4 + 0) * 479
* =
*/
VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);
/**配置SHADOWCON,使能dma通道0;*/
SHADOWCON = 0x1; /* 使能通道0 */
}
/* 画一个像素点 */
void PutPixel(unsigned long x,unsigned long y, unsigned long c )
{
if ( (x < SCR_XSIZE_TFT) && (y < SCR_YSIZE_TFT) )
LCD_BUFFER[(y)][(x)] = c;
}
/* 清屏 */
/***先扫描行,行里面描像素。
lcd_clear_screen 函数就是利用这个原理,外循环表示一行一行的扫,内循环表
示一行里面一个一个像素描。总共有 480 行,每行里面有 800 个像素点。这就
是 Webee210 液晶屏的分辨率 800*480.*/
void lcd_clear_screen( unsigned long c)
{
unsigned int x,y ;
for( y = 0 ; y < SCR_YSIZE_TFT ; y++ )
{
for( x = 0 ; x < SCR_XSIZE_TFT ; x++ )
{
LCD_BUFFER[y][x] = c ;
}
}
}
/* 用于画直线:横线和竖线 */
void Glib_Line(int x1,int y1,int x2,int y2,int color)
{
int dx,dy,e;
dx=x2-x1;
dy=y2-y1;
if(dx>=0)
{
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 1/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 2/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 8/8 octant
{
e=dy-dx/2;
while(x1<=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1+=1;
e+=dy;
}
}
else // 7/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1+=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
else //dx<0
{
dx=-dx; //dx=abs(dx)
if(dy >= 0) // dy>=0
{
if(dx>=dy) // 4/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1+=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 3/8 octant
{
e=dx-dy/2;
while(y1<=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1+=1;
e+=dx;
}
}
}
else // dy<0
{
dy=-dy; // dy=abs(dy)
if(dx>=dy) // 5/8 octant
{
e=dy-dx/2;
while(x1>=x2)
{
PutPixel(x1,y1,color);
if(e>0){y1-=1;e-=dx;}
x1-=1;
e+=dy;
}
}
else // 6/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
PutPixel(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
}
/* 用于画方框 */
void Glib_Rectangle(int x1,int y1,int x2,int y2,int color)
{
Glib_Line(x1,y1,x2,y1,color); //矩形的上边长
Glib_Line(x2,y1,x2,y2,color); //矩形的右边长
Glib_Line(x1,y2,x2,y2,color); //矩形的下边长
Glib_Line(x1,y1,x1,y2,color); //矩形的左边长
}