本人一直也是对液晶的显示十分好奇,按捺不住,就淘了几个小尺寸TFT液晶屏(呵呵,囊中羞涩)捣鼓捣鼓。发现这东西其实还是很好玩的,除了涉及的知识面比较广,需要阅读不少手册及资料外,只要你要一定的单片机开发的基础,操作起来并不复杂,主要难点还是数据处理和字库的制作上。下面就说道说道这个TFT液晶。
什么是TFT?
TFT液晶屏也就是thinfilmtransistor即薄膜晶体管显示屏,它的每一个像素点都是由集成在其后的薄膜晶体管来驱动的。
常用TFT模块尺寸:
对角线的尺寸:1.44、1.6、1.8、2.0、2.2、2.4、2.6、2.8、3.0、3.2、3.4、3.6、4.0、4.3、5.7、8.4、10.4、15、17、19、21英寸等。
本人使用过的,1.44、1.8、2.2
屏幕高宽比:4:3或16:9
分辨率:指水平像素和垂直像素的数量。
点距:相邻两个像素之间的距离。
刷新率:每秒更新的画面数。
接口形式:并行接口和串行接口
颜色的表示:
对于黑白或单色像素的信息可以用1个位来表示和存储,
对于一个彩色像素的信息可以用1个多位二进制数来表示和存储。
用来表示彩色像素的二进制数的尾数,称之为颜色的颜色深度或颜色质量
什么是真彩和伪彩
颜色深度在16为以上的称为真彩色,颜色深度在16位以下的称为伪彩色。
比如采用1个16位二进制数来表示一个彩色点:
红色绿色蓝色
5位6位5位
R4R3R2R1R0G5G4G3G2G1G0B4B3B2B1B0
高8位低8位
这就是所谓的5-6-5格式。
字符或图像到底是怎样显示出来的?
首先可将光看作是一种电磁波,以电场和磁场相互垂直而交互震荡的方式向前传播。电场在某个方向上震荡,震荡的幅度越大,光所具有的能量越大。某个方向上震荡的光可以分解成两个垂直方向上的分量。
偏光片:作用是让某个方向上震荡的光通过,而把垂直方向上震荡的光挡住。
偏光片组:
第一偏光片仅让在某个方向上震荡的光通过,而第二偏光片再把所通过的光挡住,即可阻绝光的行进,达到关闭光的效果。
液晶具有双折射系数的特性,并且在不同的电场下,会有不同的排列方式,因此当光通过液晶时,会受其影响而改变或保持其震荡的方向,当液晶不改变光的震荡方向时,光无法通过第二个偏光板而被关闭,而当液晶将光的震荡方向改变时,光可以在分解成两个分量,虽然一个分量无法通过第二个偏光片,但是仍还有一个分量可以通过第二个偏光片,而成为打开状态。英雌,可用施加的电场来改变液晶的排列方式,来实现光的开关的来实现显示功能。具体液晶是个什么东西,有那些种类,大家想了解就百度吧,这里就不再细谈了。
电场是如何改变的?
首先了解一下TFT(thin-filmtransister)薄膜电晶体
主要结构是一个非晶矽半导体薄膜,TFT就有一个门极gate;一个源极source;和一个漏极drain.看看这几电极的名称是不是很熟悉,对了场效应管也是这样命名的,两者类似,但又有不同之处。但是都可以理解成一个受控的开关。
这些开关以矩阵的方式进行排列。
彩色的TFT将水平方向的每个像素在次分成3个RGB像素,各个次像素的可以独立的改变,故也分别对应一个TFT。这样3个次像素组成一个像素。
呵呵,看了上面的图,是不是就想到了单片机矩阵按键的动态扫描程序。呵呵不错,逆向思维,矩阵键盘的扫描是读状态,这个是写状态。具体过程如下。
在水平方向上的同一条扫描线上,所有TFT的门极都连在一起,所以施加的电压是一样的,若在某一条扫面线上施加足够大的正电压,则这条扫描线上所有的TFT 都会被打开。此时该扫描线上的像素电极,会与垂直方向的资料线(漏极)连接,经由对应的资料线送入相应的视信号,将像素电极充电到适当的电压。接着施加足够大的负电压,关闭TFT,直到下次再重新写入信号。其间使得电荷保存在液晶电容上;在按照这种方式扫描下一行。再送入下一行的视信号,如此依次将整个画面的视信号写入,在重新自第一行开始写入,(一般重复的频率为60-70Hz)。
对每个像素中的液晶光阀而言,液晶上所施加的电压和光的穿透度具有一定的关系,因此,只要依据所要显示的画面,控制施加在液晶上的电压,即可将各个像素设定在适当的光穿透度,配合均匀的背光源就显示出想要的画面了。这就是主动式矩阵型液晶的显示原理。
就几款液晶屏的参数做一下总结说明
1、1.44寸液晶屏(以下数据来自液晶屏数据手册)
LCDtype:1.45”activematrixTFT-LCD
Rsolution:128(W)X128(H)Pixels
Displaymode:transmissivetype
Displaycolor:262Kcolor
driverIC:ILI9163C
Luminance:120cd/m2
Contrastratio:400:1
Viewingdirection:6o’clock
Interface:4wireSPIinterface
Backlight:1whiteLED,18ma,3,15V
2、引脚说明:
VCC:电源+3,3V
GND:电源地
CS:片选(低电平有效)
RST:复位(低电平有效)
AO:寄存器选择信号(低电平:选择命令寄存器;高电平:选择数据寄存器)
SDA:datainputinSPImode在SPI模式下的数据输入
SCL:在SPI模式下的同步时钟输入
LED:背光LED电源,
呵呵,从引脚定义上就可以看出是不是在SPI模式下只需要4条IO口线就可以和MCU构成一个显示系统了。(其实还用一种模式只要3条IO口线就可以)。
再看1.8寸TFT的相关数据
显示点阵数:128Wx160Hdots
模块外形尺寸:34Wx45.83Hx2.65Tmm
可视区域:28.03Wx35.04Hmm
像素尺寸:0.06W*3*0.18Hmm
像素中心距:0.18W*0.18Hmm
占空比:1/400
视角:6点钟
LCD模式:260kcolor
IC:ST7735B
主要引脚定义:同1.44’
再看2.2英寸的屏
Size2.2inch
Resolution:240*320
Interface4-wireSPI
Colordepth262k/65k
Technologya-Si
Pixelpitch(mm):0.141*0.141
Viewingdirection:6o’clock
LEDnumbers4LEDs
DriverICILI9340C
主要引脚定义同1.44’英寸。
从上面不同尺寸液晶屏的引脚的定义看出,1.44英寸和1.8英寸及2.2英寸的TFT液晶屏,在和MCU构成显示系统时操作方式是一样的(因为都是4线 SPI),尽管他们使用的驱动IC型号不同。但是只要你翻看IC的数据手册就会发现,他们的寄存器的定义基本是一样的,操作原理相同。
如何构成一个显示系统:
电源+MCU+TFT液晶屏
呵呵,是不是觉得少了点什么?驱动IC那里去了?
其实驱动IC我们是看不到的,它被集成在了液晶屏中,我们只要知道它的寄存器的定义,利用液晶屏的端口会进行读写即可。
电源:3.3V的直流电源,呵呵,这个不用细说,小功率的可以利用ASM1117-3;大功率的可以利用LM2596S-3.3(最高3A的输出)来构成一个电源。
MCU:主要是3个要求。
工作电压,3.3V
够大,
够快。
够大才能存的下程序和数据,够快才会图像流畅。
本人手头只有15L2K08S2这个单片机,8K程序存储区,2K的SRAM,最高时钟33.1776MHz,最高输出8MHz的外部时钟,比起60S2的60KB的FLASH,小了不少,但是对于普通的不太复杂的应用已经足够了,主要是搞通应用的原理。
液晶屏:
以上列举的液晶屏都是串口屏,所以屏的引出脚较少,除此之外还有引出管脚较多的并口屏,
当然串口屏的数据是一位一位的送出的,速度相对是比较慢的。
在操作上,串口和并口原理差不多。这里就先介绍串口屏的使用。
好了,你有了上面的3大件下面在准备点辅料。
1、数据线,USB转串口数据线,用于烧写MCU
2、字符LCD点阵提取软件:百度一下吧,zimo221.exe
3、图片点阵数据提取软件:同样百度,Image2Lcd.exe
4、编程软件:KEIL
5、单片机烧写软件:这个不多说了,看你用的芯片而定。
好的,齐活了,准备开工。
用导线将单片机的电源接口和LCD的电源及LED端口同3.3V电源的输出连接起来。供电的问题解决了。
用导线将自己选择的单片机的端口和LCD的REST、CS、SCL、AO、SDA一一对应的连接起来。数据输出的问题解决。
硬件问题解决,开始代码的编写
TFT初始化函数:
也就是对液晶屏进行基本的配置。
这个基本直接套用就行。(除了个别地方需要修改,后面会说)
TFT驱动芯片的手册还是有必要看看的,最好是看英文原版内容比较详细。
也没有必要全部看,但是下面初始化代码中涉及到的指令及4wireSPI的时序图,还是很有必要了解的,不然显示的界面出了问题,就会感觉无从下手解决。
写数据和写命令
很明显要想让液晶屏显示字符或图像,必须要把要显示的内容转换成数据写到液晶屏的控制器,想写内容数据还要对液晶屏的控制寄存器进行设置,也就是写命令数据。只有一条SDA串口数据线,怎么区分是内容数据还是命令数据呢,那就要靠AO(RS):寄存器选择信号(低电平:选择命令寄存器;高电平:选择数据寄存器)。
写命令
voidwrite_command(ucharc)
{
cs=0;//片选有效
rs=0;//选择命令寄存器
bitdata=c;//送数据
sda=bit7;scl=0;scl=1;
sda=bit6;scl=0;scl=1;
sda=bit5;scl=0;scl=1;
sda=bit4;scl=0;scl=1;
sda=bit3;scl=0;scl=1;
sda=bit2;scl=0;scl=1;
sda=bit1;scl=0;scl=1;
sda=bit0;scl=0;scl=1;
cs=1;//片选无效
}
写数据
voidwrite_data(uchard)
{
cs=0;
rs=1;
bitdata=d;
sda=bit7;scl=0;scl=1;
sda=bit6;scl=0;scl=1;
sda=bit5;scl=0;scl=1;
sda=bit4;scl=0;scl=1;
sda=bit3;scl=0;scl=1;
sda=bit2;scl=0;scl=1;
sda=bit1;scl=0;scl=1;
sda=bit0;scl=0;scl=1;
cs=1;
}
初始化
/*****************TFT初始化函数***************/
voidlcd_initial()
{
reset=0;
delay(100);
reset=1;
delay(100);
//------------------------------------------------------------------//
//-------------------SoftwareReset-------------------------------//
write_command(0x2A);//列地址设置
write_data(0x00);//列起始地址低8位
write_data(0x00);//列起始地址高8位
write_data(0x00);//列终止地址高8位
write_data(0x9F);//列终止地址低8位
//上面的列终止地址为什么设置成0x9f,0x9f=十进制的159,1.8寸的屏的分辨率是128*160.
//也就是说有0-159共160列。下面的同样的道理,只不过是说明行的起始和终止地址。
write_command(0x2B);//行地址设置
write_data(0x00);
write_data(0x00);
write_data(0x00);
write_data(0x7F);
write_command(0xCB);//功耗控制A
write_data(0x39);
write_data(0x2C);
write_data(0x00);
write_data(0x34);
write_data(0x02);
write_command(0xCF);//功耗控制B
write_data(0x00);
write_data(0XC1);
write_data(0X30);
write_command(0xE8);//驱动时序控制A
write_data(0x85);
write_data(0x00);
write_data(0x78);
write_command(0xEA);//驱动时序控制B
write_data(0x00);
write_data(0x00);
write_command(0xED);//电源序列控制
write_data(0x64);
write_data(0x03);
write_data(0X12);
write_data(0X81);
write_command(0xF7);//泵比控制
write_data(0x20);
write_command(0xC0);//Powercontrol功耗控制1
write_data(0x23);//VRH[5:0]`
write_command(0xC1);//Powercontrol功耗控制2
write_data(0x10);//SAP[2:0];BT[3:0]
write_command(0xC5);//VCMcontrol
write_data(0x3e);//对比度调节
write_data(0x28);
write_command(0xC7);//VCMcontrol2
write_data(0x86);//--
write_command(0x36);//MemoryAccessControl存储器访问控制
write_data(0x68);//C8//4868竖屏//28E8横屏
//cc同c8
write_command(0x3A);//像素格式设置
write_data(0x55);
write_command(0xB1);//帧速率控制
write_data(0x00);
write_data(0x18);
write_command(0xB6);//DisplayFunctionControl
write_data(0x08);
write_data(0x82);
write_data(0x27);
write_command(0xF2);//3GammaFunctionDisable
write_data(0x00);
write_command(0x26);//Gammacurveselected
write_data(0x01);//共4条曲线供选择,分别是1248;这里选择1,
write_command(0xE0);//SetGamma
write_data(0x0F);
write_data(0x31);
write_data(0x2B);
write_data(0x0C);
write_data(0x0E);
write_data(0x08);
write_data(0x4E);
write_data(0xF1);
write_data(0x37);
write_data(0x07);
write_data(0x10);
write_data(0x03);
write_data(0x0E);
write_data(0x09);
write_data(0x00);
write_command(0XE1);//SetGamma
write_data(0x00);
write_data(0x0E);
write_data(0x14);
write_data(0x03);
write_data(0x11);
write_data(0x07);
write_data(0x31);
write_data(0xC1);
write_data(0x48);
write_data(0x08);
write_data(0x0F);
write_data(0x0C);
write_data(0x31);
write_data(0x36);
write_data(0x0F);
write_command(0x11);//ExitSleep
delay(120);
write_command(0x29);//Displayon
write_command(0x2c);
}
在写入数据时要先确定,图像的起始坐标和终止坐标,可以用如下的代码实现
staticvoidLCD_SetPos(unsignedintx0,unsignedintx1,unsignedinty0,unsignedinty1)//设置位置
{
write_command(0x2A);//列地址设置
write_data(x0>>8);//列起始地址高8位
write_data(x0);//列起始地址低8位
write_data(x1>>8);//列终止地址高8位
write_data(x1);//列终止地址低8位
write_command(0x2B);//页地址设置
write_data(y0>>8);
write_data(y0);
write_data(y1>>8);
write_data(y1);
write_command(0x2c);//写存储器
}
写命令时,先将命令的地址写入,然后在将设定的数据写入寄存器。如
write_command(0x2A);//列地址设置
write_data(0x00);
write_data(0x00);
write_data(0x00);
write_data(0x9F);
至于这些写入的数据的意义及数值大小,请查阅液晶屏对应的液晶屏的数据手册。
了解一下颜色代码:
几种常用的颜色的代码
#defineWhite0xFFFF//白
#defineBlack0x0000//黑
#defineRed0x001F//红
#defineBlue0xF800//蓝
#defineMagenta0xF81F//紫
#defineGreen0x07E0//绿
#defineCyan0x07FF//青
#defineYellow0xFFE0//黄
好了说了这么多,一个字晕。好吧,来点实际的简单的,咱先啥字符也不显示,
只是让液晶屏显示不同的颜色。
刷整个屏幕的颜色
可以用如下代码实现
/*********显示色彩******************/
voiddsp_single_colour(DH,DL)//前景颜色,背景颜色
{
unsignedinti,j;
for(i=0;i<128;i++)
for(j=0;j<160;j++)
{
write_data(DH>>8);
write_data(DH);
write_data(DL>>8);
write_data(DL);
}
}
延时函数
/******延时函数************************/
voiddelay(uinttime)
{
uinti,j;
for(i=0;i<time;i++)
for(j=0;j<500;j++);
}
好了,将上面的函数组合起来,准备刷屏。
main()
{
lcd_initial();//TFT初始化
while(1)
{
LCD_SetPos(0,159,0,10);//设置位置
dsp_single_colour(Blue,Black);//
delay(2000);
LCD_SetPos(0,159,10,20);//设置位置
dsp_single_colour(Blue,Blue);//
delay(2000);
LCD_SetPos(0,159,20,30);//设置位置
dsp_single_colour(Green,White);//
delay(2000);
LCD_SetPos(0,159,30,40);//设置位置
dsp_single_colour(Green,Green);//
delay(2000);
LCD_SetPos(0,159,40,50);//设置位置
dsp_single_colour(Yellow,White);//
delay(2000);
LCD_SetPos(0,159,50,60);//设置位置
dsp_single_colour(Yellow,Yellow);//
delay(2000);
LCD_SetPos(0,159,60,70);//设置位置
dsp_single_colour(Black,White);//
delay(2000);
LCD_SetPos(0,159,70,80);//设置位置
dsp_single_colour(Black,Black);//
delay(2000);
LCD_SetPos(0,159,80,90);//设置位置
dsp_single_colour(Red,White);//
delay(2000);
LCD_SetPos(0,159,90,100);//设置位置
dsp_single_colour(Red,Red);//
delay(2000);
LCD_SetPos(0,159,100,110);//设置位置
dsp_single_colour(Magenta,Black);//
delay(2000);
LCD_SetPos(0,159,110,120);//设置位置
dsp_single_colour(Magenta,Magenta);//
delay(2000);
LCD_SetPos(0,159,0,120);//设置位置
dsp_single_colour(White,White);//只刷到第121行留下了7行
delay(2000);
}
}
最终结果,滚动刷屏
感觉有点不对劲,不错,最下面故意留了7行没写颜色数据。可以看到如果不写任何数据,屏的状态就是花屏。
通过上面的说明了解了:
TFT液晶屏的操作原理,就是写数据和写命令。
通过对写数据或写命令的代码的分析,可以看到数据是怎样输送的。
TFT液晶屏的初始化工作的主要内容。
常用的颜色代码。
如何让液晶屏显示不同的颜色。
最后仔细观察图片,可以了解什么是隔行扫面,前景颜色,背景颜色。
以及,花屏是什么原因造成的。