焦耳创意但是经常使用LCM1602,LCM12864,Nokia5110等单色液晶后就觉得,光是用单色液晶自由度太低,显示的东西总是千篇一律,没有创意,拿出去亮不瞎别人的*眼。但对于单片机初学者TFT是个高深莫测的东东,看着长长的代码就头晕·····
在此我向新手(其实我也很新····)讲解一下51单片机驱动TFT液晶屏、还请各位高手多多指点,小生有错误请指出。
PS:作为高中生其实压力和大····马上就高三了···家里逼得紧,连买元件的钱都不给··害的我用无线电的稿费买的····我的冬狮郎COS,克劳德COS~~T.T,好不容易挤出两天来···神马都没做··净码字了·
进入正题··对于广大单片机“玩家”,单片机系统中一环人机界面是很重要,人机界面的主要功能就是向使用者传达有效的信息,常用的人机界面有LED(这也确实算是······)、数码管(C4???)、各种高压屏(辉光管、VFD显示屏等)、LED点阵(外面的广告牌)、段码式液晶(手表神马的)、LCM1602、LCM12864、OLED、TFT屏等等。
首先需要说明各种LCM(LCD模块的简写)的区别,常见的字符型单色LCD如LCM1602,LCM12864,由输入输出缓冲器、指令寄存器、数据储存器、地址计数器、忙碌标志、数据显示储存器、串行/并行数据转换器、光标闪烁控制电路、时序产生电路、偏压产生电路、公共端驱动电路、区段驱动电路、LCD面板等机构构成。主要特点是驱动简单、集成度高。主要缺点是自由度低、不够‘刺眼’。
TFT型的液晶显示器主要的构成包括:萤光管、导光板、偏光板、滤光板、玻璃基板、配向膜、液晶材料、薄模式晶体管等等。含有驱动但是一般不带字库。优点是自由度高,缺点是一般多为并行驱动占用大量的IO(玩51单片机知道IO口的珍贵),不含字库会消耗掉大量的ROM,封装问题(这个对于大多数家庭实验党,和初学者来说是一道‘鸿沟’)。
TFT屏的基本结构旧不过多的介绍了,各种液晶手册里前几页大部分就是讲这个的,大家自己去看看哈O(∩_∩)O
想要驱动TFT首先要对TFT有初步的了解,首先TFT是以像素为单位的,不管是色彩还是地址坐标都是以像素位单位的,一定要区分和LCM1602等中以字符和行为单位的。
再讲TFT驱动前先讲一下什么是16位、24位、32位色彩,首先我们看到的所有颜色都是有光的三原色引起的(红绿蓝··这个还是说一下好),RGB是表示红色绿色蓝色又称为三原色光,英文为R(Red)、G(Green)、B(Blue),在电脑中,RGB的所谓"多少"就是指亮度,并使用整数来表示。所谓的多少位位色彩无非指的就是驱动信号的长度,首先讲24色,为什么先讲24位色呢,因为24位色被称为真彩,为什么称为真彩?通常情况下,RGB各有256级亮度,用数字表示为从0、1、2至255。虽然数字最高是255,但0也是数值之一,因此共256级。按照计算,256 级的RGB色彩总共能组合出约1678万种色彩,即256×256×256=16777216。通常也被简称为1600万色或千万色。也称为24位色(2 的24次方)。24位色对于单片机来说就是每种颜色有8位二进制数够成,三种颜色一共有24位。但51单片机驱动24位色是有和大难度的,因为24位色就需要向每个像素点写入24位的数据,这将对单片机ROM、I/O口和写入速度是个极大的挑战。所以使用51单片机驱动TFT一般选用16位的TFT液晶及驱动。在16位TFT中红色和蓝色的驱动信号被压缩为5位,绿色的驱动信号被压缩为6位,一共16位(见图二),也就是说向每个像素点写入16位的驱动信号。32位色就是在24位的基础上加入8位的饱和度值(好像是······)
图二
再次切入正题···TFT接口一般分为以下4种
XXXXU模式:目前最常用的连接模式,一般是80系统(68系统已经不存在了)。数据位传输有8位,9位,16位和18位。连线分为:CS/,RS(寄存器选择),RD/,WR/,再就是数据线了。优点是:控制简单方便,无需时钟和同步信号。缺点是:要耗费GRAM,所以难以做到大屏(QVGA以上);RGB模式:大屏采用较多的模式,数据位传输也有6位,16位和18位之分。连线一般有:VSYNC,HSYNC,DOTCLK,VLD,ENABLE,剩下就是数据线。它的优缺点正好和MCU模式相反。
SPI模式:采用较少,连线为CS/,SLK,SDI,SDO四根线,连线少但是软件控制比较复杂。
4.VSYNC模式:该模式是在MCU模式下增加了一根VSYNC(帧同步)信号线而已,应用于运动画面更新。
51单片机常见驱动方式为SPI驱动方式、8位并行驱动方式、和16位并行驱动方式,字面意思就是耗费的I/O口数量,但是要注意一点,如果液晶屏尺寸大或者是密度高尽量采用并行驱动模式,因为并行模式会大大提高写入速度,尽 量避免产生闪烁现象。
再再次切入正题···这次以SPI驱动方式(消耗I/O最少)和8位驱动方式(16位基本相同)讲解,首先需要拿到该TFT屏幕的规格书和驱动说明书,拿三星公司的S6B3306驱动说(居然是英文的当时让我看了好久),S6B3306具有3线SPI接口,四线SPI接口,8位并行驱动···PS:好吧三星公司赢了。跳过硬件说明首先看时序。
8位并行式驱动时序
图三
图三为8位并行8080驱动时序图,在规格书中提供了引脚定义,用到的有CS1(片选)、RS(寄存器选择)、RDB(读)WRB(写)DB0~DB7(数据),从时序看出RDB、WRB都是下降沿有效,所以不管是读出还是写入都需要将RDB或WRB拉低然后拉高,RS=0时处理指令,RS=1时处理数据,CS下降沿有效读写都应先将CS拉低,
写入的总时序为:CS拉低→RDB=1(写入时将读出引脚拉高)→RS=0时写入指令,RS=1时写入数据→DB0~DB7写入指令或数据(指令8位,数据16位)→先写高8→将WRB拉低再拉高→再写低8(写入指令时忽略)→将WRB拉低再拉高(写入指令时忽略)→CS拉高→结束
然后看我写的写入函数
void Write_S6B33B_COM(unchar lowbyte)
{
CS1=0;
RDB=1;
RS=0;
P1=lowbyte;
WRD=0;
WRD=1;
CS1=1;
}
void Write_S6B33B_DISPLAY_DATA(unchar hibyte,unchar lowbyte)
{
CS1=0;
RDB=1;
RS=1;
P1=hibyte;
WRD=0;
WRD=1;
P1=lowbyte;
WRD=0;
WRD=1;
CS1=1;
}
明白了吧
然后是4线SPI接口的时序。
图4
图四为4线SPI接口驱动的时序图,在规格书中提供了引脚定义,用到的有CS1(片选)、SCL(时钟)、RS(寄存器选择)、SDI(数据)。由时序可以看出RS=0时处理指令,RS=1时处理数据。在SPI接口下CS1、RS与8位并行(8080)接口时序一样,SCL为时钟线,每写入一个字节SCL都写入一个脉冲。SDI为数据线以SCL脉冲为基准写入数据。写入数据牵扯到一个问题就是如何将16进制数拆分的问题,因为SDI输入数据是一帧一帧的,所以将8位的二进制一一拆分然后以SCL脉冲为基准写入。
写入的总时序为:CS拉低→RS=0时写入指令,RS=1时写入数据→SCL拉高→与0x80(1000 0000)进行及运算→与0x80进行比较等于0x80写入高电平(不等于0x80写入低电平)→SCL拉低→左移一位→是否执行了8次→执行8此后结束。(写入数据时先写高8在写第八执行两次以上顺序)
然后看我写的写入函数
void Write_S6B33B_COM(unchar COMDADA_4SPI)
{
unchar bit8;
RS=0;
CS1=0;
for(bit8=0;bit8<8;bit8++)
{
SCL=0;
if((COMDADA_4SPI&0x80)==0x80)
SDI=1;
else
SDI=0;
// SCL=0;
SCL=1;
COMDADA_4SPI=(COMDADA_4SPI<<1);
}
CS1=1;
}
void Write_S6B33B_DISPLAY_DATA_serial_4SPI(unchar DADA_4SPI)
{
unchar bit8;
RS=1;
CS1=0;
for(bit8=0;bit8<8;bit8++)
{
SCL=0;
if((DADA_4SPI&0x80)==0x80)
SDI=1;
else
SDI=0;
SCL=1;
DADA_4SPI=(DADA_4SPI<<1);
}
CS1=1;
}
void Write_S6B33B_DISPLAY_DATA(unchar hibyte,unchar lowbyte)
{
Write_S6B33B_DISPLAY_DATA_serial_4SPI(hibyte);
Write_S6B33B_DISPLAY_DATA_serial_4SPI(lowbyte);
}
明白了吧
明白写入时序后就是该说明如何显示的问题了,这要查询各液晶屏的用户手册和驱动信息了还是以S6B3306为例
图五
图五为S6B3306的指令列表
驱动液晶屏首先要进行初始化,根据驱动手册中的信息得出需要进行初始化的项目及数值,一般TFT液晶屏必须进行的初始化有震荡模式设置、温度补偿、对比度调节、显示模式设置、RAM设置、起始X轴值设定、起始Y轴值设定、然后打开显示器····
以下是初始化函数void Init_S6B33B()
{
HDReset(); //复位函数
Delay_ms(100);
Write_S6B33B_COM(0x2c);
Delay_ms(50);
Write_S6B33B_COM(0x02); //震荡模式
Write_S6B33B_COM(0x00);//内部时钟
Write_S6B33B_COM(0x28); //温度补偿
Write_S6B33B_COM(0x01);
Write_S6B33B_COM(0x45); //RAM Skip AREA set
Write_S6B33B_COM(0x00);
Write_S6B33B_COM(0x53);
Write_S6B33B_COM(0x00);
Write_S6B33B_COM(0x10);
Write_S6B33B_COM(0x03);
Write_S6B33B_COM(0x30);
Write_S6B33B_COM(0x1d);
Write_S6B33B_COM(0x32);
Write_S6B33B_COM(0x0e);
Write_S6B33B_COM(0x40);
Write_S6B33B_COM(0x80);
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(2);
Write_S6B33B_COM(129);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(4);
Write_S6B33B_COM(131);
Write_S6B33B_COM(0x34);
Write_S6B33B_COM(0x8d);
Write_S6B33B_COM(0x2a);
Write_S6B33B_COM(0x32); //调节对比度值
Write_S6B33B_COM(0x55);
Write_S6B33B_COM(0x00);
Write_S6B33B_COM(0x56);
Write_S6B33B_COM(0x00);
Write_S6B33B_COM(0x57);
Write_S6B33B_COM(0x9f);
Write_S6B33B_COM(0x51); //Display ON
}
在TFT液晶屏中还有一个问题与LCM12864等不同的是,TFT液晶屏中没有清屏函数,每次想要更新信息或刷新屏幕必须重写背景。
之后便是原创模块化的LCD驱动函数···PS都是我一个人写的·····费老劲了····写了好几天,
转帖时请注明出处,谢谢
在附件中有名为lcd.c的文件包含了所有的驱动包含详细的函数介绍
在此拿出几个函数来说明以下以便了解TFT屏驱动
首先定义几个颜色的值(都懂吧不用解释吧·······)
#define C_white 0xffff
#define C_black 0x0000
#define C_green 0x07e0
#define C_blue 0x001f
#define C_red 0xf800
定义的数值为16位二进制数但是写入颜色是需要先写高8再写低8,这样就需要将16位二进制数转换为2个8位的二进制数,牵扯到以下转换
unchar numH;//(numH得到num的高八位)
unchar numL;//(numL得到num的低八位)
numH =colour>>8;
numL =colour;
由函数看出是将6位二进制数右移后强制转换为8位的,由此得到高8位和低8位。
然后是画点函数:
// 名称:画点函数
// 函数原型?FT_point(int x,int y,unshort colour)
// x,x轴位置 y,y轴位置 colour,颜色
//
// 焦耳创意原创
void TFT_point(int x,int y,unshort colour)
{
unchar numH;//(numH得到num的高八位)
unchar numL;//(numL得到num的低八位)
numH =colour>>8;
numL =colour;
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(x+4);
Write_S6B33B_COM(x+4);
Write_S6B33B_DISPLAY_DATA(numH,numL);
}
之后所有的画位图和显示字符都是依赖此画点函数的
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(x+4);
Write_S6B33B_COM(x+4);
为规定显示区域的,+2和+3是驱动手册中提供的好像是非显示区域什么的····
然后是x轴画线和y轴画线一般用来覆盖某个区域用的(当覆盖颜色等于背景色时等于清屏)
// 名称:x轴画线
// 函数原型:TFT_Line_x(int x,int y,int length,int width,unshort colour)
// x,x轴位置 y,y轴位置 length,长度 width,宽度 colour,字符颜色
//
// 焦耳创意原创
void TFT_Line_x(int x,int y,int length,int width,unshort colour)
{
int point_x,point_y;
unchar numH;//(numH得到num的高八位)
unchar numL;//(numL得到num的低八位)
numH =colour>>8;
numL =colour;
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(y+width+1);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(x+4);
Write_S6B33B_COM(x+length+3);
for(point_y=0;point_y<width;point_y++)
{
for(point_x=0;point_x<length;point_x++)
Write_S6B33B_DISPLAY_DATA(numH,numL);
}
}
Y轴雷同请查看lcd.c文件
然后就是指定区域显示指定大小图片,没什么么难理解的,就是指定一个像素写入高8位的颜色再写入低8位的颜色就是这样。在取模的时候注意一下取模方式,从取模的软件得到的图片其实就是高8位颜色和低8位颜色。
// 名称:指定位置放入指定大小图片
// 函数原型?aint(int x,int y,int x1,int y1,unsigned char code *img_dat)
// x,左上角x轴位置 y,左上角y轴位置 x1,图片宽 y1,图片高 *img_dat,图片指针
//
// 焦耳创意原创
void paint(int x,int y,int x1,int y1,unsigned char code *img_dat)//指定位置放入指定大小图片
{
int QQ,p,q;
QQ=0;
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(y+y1+1);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(x+4);
Write_S6B33B_COM(x+x1+3);
for(p=0;p<y1;p++)
{
for(q=0;q<x1;q++)
{
Write_S6B33B_DISPLAY_DATA(img_dat[QQ],img_dat[QQ+1]);
QQ++;
QQ++;
}
}
}
重点要说明一下单色位图的绘制函数PS:当时让我想了好久从早上想到晚上···半夜来了灵感终于写出来了···
为什么单色位图的函数难写因为单色位图的取模方式与彩色图片的取模方式不同,牵扯到转换的问题。我简要说明一下两种取模方式。
彩色图片取模得到的是这样的格式:
高8位颜色,低8位颜色,高8位颜色,低8位颜色,高8位颜色,低8位颜色``````````````````````
明白吗?就是两位问一个像素的颜色
但是单色位图的取模方式就不同了原理如下(图片源自网路)
就是将图片拆成8位8位的一一以一和零代表,所以如何将这些只代表显示与不显示信息变成彩色的显示模式呢?
见我写的函数
}
// 名称:单色位图显示
// 函数原型?bitmap00(int x,int y,int x1,int y1,unshort colour,unshort buckcolour,unsigned char code *img_map)
// x,x轴位置 y,y轴位置 x1,x轴长度 y1,y轴长度 colour,位图颜色 buckcolour,背景颜色 img_map图片指针
//
// 焦耳创意原创
void bitmap00(int x,int y,int x1,int y1,unshort colour,unshort buckcolour,unsigned char code *img_map)
{
int line2,line1,line3,x2=0;
unchar dat;
Write_S6B33B_COM(0x42); // Y ADDRESS
Write_S6B33B_COM(y+2);
Write_S6B33B_COM(y+y1+1);
Write_S6B33B_COM(0x43); // X ADDRESS
Write_S6B33B_COM(x+4);
Write_S6B33B_COM(x+x1+3);
if((x1%8)!=0)
x2=(x1/8+1);
else
x2=x1/8;
for(line2=0;line2<y1;line2++)
{
for(line3=0;line3<x2;line3++)
{
for(line1=7;line1>=0;line1--)
{
dat=img_map[line3*y1+line2];
if((((dat>>line1)&0x01)&&0x01)==1)
TFT_point(x+line3*8+line1,line2+y,colour);
else
TFT_point(x+line3*8+line1,line2+y,buckcolour);
}
}
}
}
其中重要的两句是 dat=img_map[line3*y1+line2];和if((((dat>>line1)&0x01)&&0x01)==1)
dat=img_map[line3*y1+line2]的精髓便是必修了解取模软件的取模方式,即每次使用哪一个数组
if((((dat>>line1)&0x01)&&0x01)==1)这句便是转换的中心语句,先右移line1位在与0x01(00000001)进行及运算在进行及运算比较如果等于1就放入指定位图颜色如果不等于1就放入背景色。这样画上的单色位图就不会影响背景了。
字符放置于位图放置方式雷同请查看lcd.c文件
这样整个TFT屏幕驱动教程就结束了,码字不容易啊····转帖请注明出处
LCD.zip
2.99KB
ZIP
77次下载
TFT驱动.zip
202.22KB
ZIP
82次下载
TFT驱动.doc
293.00KB
DOC
68次下载
TFT驱动.doc
293.00KB
DOC
53次下载
200字以内,仅用于支线交流,主线讨论请采用回复功能。