对于人机界面中的液晶屏的使用大家都不陌生。从简单的电子表到手机、平板电脑,显示器、液晶电视都能看到他们的身影。早其我们常用的液晶屏,比如段码LCD,1602,12232,12864,等非黑即透明,随着技术的不断进步,现在在手机和平板电脑等电子设备上用的主要是TFT液晶屏,本人一直也是对液晶的显示十分好奇,按捺不住,就淘了几个小尺寸TFT液晶屏(呵呵,囊中羞涩)捣鼓捣鼓。发现这东西其实还是很好玩的,除了涉及的知识面比较广,需要阅读不少手册及资料外,只要你要一定的单片机开发的基础,操作起来并不复杂,主要难点还是数据处理和字库的制作上。下面就说道说道这个TFT液晶。
什么是TFT?
TFT液晶屏也就是thin film transistor 即薄膜晶体管显示屏,它的每一个像素点都是由集成在其后的薄膜晶体管来驱动的。
常用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位
R4 R3 R2 R1 R0 G5 G4G3 G2 G1G0B4B3B2B1B0
高8位 低8位
这就是所谓的5-6-5格式。
字符或图像到底是怎样显示出来的?
首先可将光看作是一种电磁波,以电场和磁场相互垂直而交互震荡的方式向前传播。电场在某个方向上震荡,震荡的幅度越大,光所具有的能量越大。某个方向上震荡的光可以分解成两个垂直方向上的分量。
偏光片:作用是让某个方向上震荡的光通过,而把垂直方向上震荡的光挡住。
偏光片组:
第一偏光片仅让在某个方向上震荡的光通过,而第二偏光片再把所通过的光挡住,即可阻绝光的行进,达到关闭光的效果。
液晶具有双折射系数的特性,并且在不同的电场下,会有不同的排列方式,因此当光通过液晶时,会受其影响而改变或保持其震荡的方向,当液晶不改变光的震荡方向时,光无法通过第二个偏光板而被关闭,而当液晶将光的震荡方向改变时,光可以在分解成两个分量,虽然一个分量无法通过第二个偏光片,但是仍还有一个分量可以通过第二个偏光片,而成为打开状态。英雌,可用施加的电场来改变液晶的排列方式,来实现光的开关的来实现显示功能。具体液晶是个什么东西,有那些种类,大家想了解就百度吧,这里就不再细谈了。
电场是如何改变的?
首先了解一下TFT(thin-film transister )薄膜电晶体
主要结构是一个非晶矽半导体薄膜,TFT 就有一个门极gate;一个源极source;和一个漏极drain.看看这几电极的名称是不是很熟悉,对了场效应管也是这样命名的,两者类似,但又有不同之处。但是都可以理解成一个受控的开关。
这些开关以矩阵的方式进行排列。
彩色的TFT将水平方向的每个像素在次分成3个RGB像素,各个次像素的可以独立的改变,故也分别对应一个TFT。这样3个次像素组成一个像素。
呵呵,看了上面的图,是不是就想到了单片机矩阵按键的动态扫描程序。呵呵不错,逆向思维,矩阵键盘的扫描是读状态,这个是写状态。具体过程如下。
在水平方向上的同一条扫描线上,所有TFT的门极都连在一起,所以施加的电压是一样的,若在某一条扫面线上施加足够大的正电压,则这条扫描线上所有的TFT 都会被打开。此时该扫描线上的像素电极,会与垂直方向的资料线(漏极)连接,经由对应的资料线送入相应的视信号,将像素电极充电到适当的电压。接着施加足够大的负电压,关闭TFT,直到下次再重新写入信号。其间使得电荷保存在液晶电容上;在按照这种方式扫描下一行。再送入下一行的视信号,如此依次将整个画面的视信号写入,在重新自第一行开始写入,(一般重复的频率为60-70Hz)。
对每个像素中的液晶光阀而言,液晶上所施加的电压和光的穿透度具有一定的关系,因此,只要依据所要显示的画面,控制施加在液晶上的电压,即可将各个像素设定在适当的光穿透度,配合均匀的背光源就显示出想要的画面了。这就是主动式矩阵型液晶的显示原理。
就几款液晶屏的参数做一下总结说明
1、1.44寸液晶屏(以下数据来自液晶屏数据手册)
LCD type : 1.45”active matrix TFT-LCD
Rsolution: 128(W)X128(H) Pixels
Display mode : transmissive type
Display color: 262Kcolor
driverIC: ILI9163C
Luminance: 120cd/m2
Contrast ratio: 400:1
Viewing direction: 6o’clock
Interface : 4 wire SPI interface
Back light: 1 white LED ,18ma ,3,15V
2、引脚说明:
VCC: 电源 +3,3V
GND: 电源地
CS: 片选 (低电平有效)
RST: 复位 (低电平有效)
AO: 寄存器选择信号(低电平:选择命令寄存器;高电平:选择数据寄存器)
SDA: data input in SPI mode 在SPI模式下的数据输入
SCL: 在SPI模式下的同步时钟输入
LED: 背光LED电源,
呵呵,从引脚定义上就可以看出是不是在SPI模式下只需要4条IO口线就可以和MCU构成一个显示系统了。(其实还用一种模式只要3条IO口线就可以)。
再看1.8寸TFT的相关数据
显示点阵数: 128W x 160H dots
模块外形尺寸: 34W x 45.83H x 2.65T mm
可视区域: 28.03W x 35.04H mm
像素尺寸: 0.06W * 3 * 0.18H mm
像素中心距: 0.18W * 0.18H mm
占空比: 1/400
视角: 6点钟
LCD模式: 260k color
IC: ST7735B
主要引脚定义:同1.44’
再看2.2英寸的屏
Size 2.2inch
Resolution: 240*320
Interface 4-wire SPI
Color depth 262k/65k
Technology a-Si
Pixel pitch(mm): 0.141*0.141
Viewing direction: 6o’clock
LED numbers 4 LEDs
Driver IC ILI9340C
主要引脚定义同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): 寄存器选择信号(低电平:选择命令寄存器;高电平:选择数据寄存器)。
写命令
void write_command(uchar c)
{
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;//片选无效
}
写数据
void write_data(uchar d)
{
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初始化函数***************/
void lcd_initial()
{
reset=0;
delay(100);
reset=1;
delay(100);
//------------------------------------------------------------------//
//-------------------Software Reset-------------------------------//
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); //Power control 功耗控制1
write_data(0x23); //VRH[5:0] `
write_command(0xC1); //Power control 功耗控制2
write_data(0x10); //SAP[2:0];BT[3:0]
write_command(0xC5); //VCM control
write_data(0x3e); //对比度调节
write_data(0x28);
write_command(0xC7); //VCM control2
write_data(0x86); //--
write_command(0x36); // Memory Access Control 存储器访问控制
write_data(0x68); //C8 //48 68竖屏//28 E8 横屏
//cc同c8
write_command(0x3A); //像素格式设置
write_data(0x55);
write_command(0xB1);//帧速率控制
write_data(0x00);
write_data(0x18);
write_command(0xB6); // Display Function Control
write_data(0x08);
write_data(0x82);
write_data(0x27);
write_command(0xF2); // 3Gamma Function Disable
write_data(0x00);
write_command(0x26); //Gamma curve selected
write_data(0x01); //共4条曲线供选择,分别是1248;这里选择1,
write_command(0xE0); //Set Gamma
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); //Set Gamma
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); //Exit Sleep
delay(120);
write_command(0x29); //Display on
write_command(0x2c);
}
在写入数据时要先确定,图像的起始坐标和终止坐标,可以用如下的代码实现
static void LCD_SetPos(unsigned int x0,unsigned int x1,unsigned int y0,unsigned int y1)//设置位置
{
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);
至于这些写入的数据的意义及数值大小,请查阅液晶屏对应的液晶屏的数据手册。
了解一下颜色代码:
几种常用的颜色的代码
#define White 0xFFFF //白
#define Black 0x0000 //黑
#define Red 0x001F //红
#define Blue 0xF800 //蓝
#define Magenta 0xF81F //紫
#define Green 0x07E0 //绿
#define Cyan 0x07FF //青
#define Yellow 0xFFE0 //黄
好了说了这么多,一个字晕。好吧,来点实际的简单的,咱先啥字符也不显示,只是让液晶屏显示不同的颜色。
刷整个屏幕的颜色,可以用如下代码实现
/*********显示色彩******************/
void dsp_single_colour(DH,DL)//前景颜色,背景颜色
{
unsigned int i,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);
}
}
延时函数
/******延时函数************************/
void delay(uint time)
{
uint i,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液晶屏的初始化工作的主要内容。
常用的颜色代码。
如何让液晶屏显示不同的颜色。
最后仔细观察图片,可以了解
什么是隔行扫面,前景颜色,背景颜色。
以及,花屏是什么原因造成的。
至于如何显示字符,汉字,图片,下回再说。