菜鸟学习LCD编程的起步Word格式文档下载.docx
《菜鸟学习LCD编程的起步Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《菜鸟学习LCD编程的起步Word格式文档下载.docx(23页珍藏版)》请在冰点文库上搜索。
6800又叫摩托罗拉总线,有R/W,E,然后是地址数据线;
I2C总线,呵呵,当然就是I2C总线咯,还用说么?
由于这个是摩托罗拉的LCD屏,出厂的时候已经配置为6800总线了,所以我们不能用正常的51总线来驱动,但实际上6800是可以兼容8080总线的,先等我介绍下这个总线吧:
R/W脚:
读写脚,为1的时候是读,0的时候是写。
E脚:
使能脚,功能如同51总线的CS,也是低有效的,但数据D0-D7在高的时候锁存。
D/C脚,这个是数据,命令选择脚,1的时候代表总线传输的是数据(不论是读还是写),0的时候代表总线传输命令(不论是读还是写)。
D0-D7:
理所当然的数据脚咯。
驱动原理很简单,在E为低的时候对R/W脚输入读写信号,读就是1,写就是0;
然后选择你读写的是数据还是命令,D/C脚1为数据,0为命令;
最后就是数据脚的数据了。
以上原理,用单片机IO做是很简单的事情,不过使用IO的缺点就是不能复用,浪费了很多的资源,所以在学习板上是使用总线驱动LCD的。
之前说了,某些6800总线是可以用8080总线模拟的,而这个LCD就是可以使用8080总线的了。
6800总线读写控制只需要1只脚R/W,1为读0为写,而8080总线是两只脚RD,WR,其中的某个脚为低就对应响应功能。
根据这个逻辑关系,我们很容易就猜想到为什么6800的R/W脚可以直接接上8080的WR了。
这些都是垫场的开场白拉。
接下来我就以丁丁编写的1815的使用程序,来介绍使用1815驱动lcd的详细步骤。
为了让大家能够理解清楚,我绝对按照分段的形式,一段一段解释。
当然由于本菜鸟水平有限,如果解释错误还请大家不吝赐教。
开始的程序我会讲的很细,后面的我就讲个大概了,只要理解了这个过程就不难了。
注意哦,这个程序是来自"
51单片机世界"
,作者版主丁丁(聂小猛),未经允许,不得抄袭作为商业用途。
/*****************************************
#define
uchar
unsigned
char
uint
int
ulong
long
#include
"
study.h"
LCMD
XBYTE[0xAf00]
//
液晶数据口
LCMC
XBYTE[0xAb00]
液晶命令口
LCD_DOT_X
98
lcd
的宽点数
LCD_DOT_Y
54
//lcd
的高点数
LCD_MAX_X
11
//每行字符,
(
+1
)
*
8
点
LCD_MAX_Y
5
//共多少行,(
********************************************/
如果你写过单片机程序,就会知道任何程序的初始化阶段都是这么些步骤,定义再定义,最后是某些功能的初始化函数。
这些就是刚开始的声明定义过程。
前面的几个define,include我就不多说了,如果你连这个都看不懂,只能先去补习补习c语言了。
XBYTE[0xAf00]和#define
XBYTE[0xAb00]两句是用来定义外部总线接口地址的。
0xAf00用来传递数据,0xAb00用来传递命令,之前说过,是传输命令还是数据主要是取决于A10的,上面两个地址对应的A10其实就是0xaf00->
A10=1,0xab00->
A10=0。
照电子白菜大虾的话说,地址的取值只要确保在A000~AFFF
之间就可以了,所以以下定义一样能正确驱动LCD:
XBYTE[0xA400]
XBYTE[0xA000]
虽然地址不同了,但对A10的作用依然不变。
接下来的两句则是用LCD_DOT_X来表示lcd
的宽点数,LCD_DOT_Y表示高点数。
想象坐标轴,呵呵,是不是好理解了?
这就说明我们现在用的lcd是LCD_DOT_X宽LCD_DOT_Y高的。
最后两句则是告诉我们,这个lcd可以显示5行,每行11个字符。
这些参数都是可以根据lcd不同而变化的。
struct
cursortype
{
x;
y;
};
cursortype
cursor;
void
wridata(uchar
ch)
LCMD=ch;
}
wricmd(uchar
LCMC=ch;
*****************************************/
这几句也都属于初始化步骤。
首先struct
cursortype这个结构体是用来定义光标的,光标在屏幕上当然可以用x和y两个值表示,这样我们到时候用cursor.x或者
cursor.y确定光标位置就方便多了。
Wridata和
wricmd两个函数则是写数据和写命令功能。
仔细看,LCMD就是液晶数据口,LCMC就是液晶命令口,我们对他们进行的操作实际上就是对0xAf00或0xA000地址进行操作。
例如我们用wridata(0x33),也就是说我们把0x33这个数据通过0xAf00这个外部总线接口地址送到SSD181X中。
由于是0xAf00的地址,A10必然为1,控制器就知道此时你要传递的是数据拉~怎么样?
很方便吧。
light(uchar
n)
LCMC=0x81;
LCMC=n;
把这个程序单独列出来是因为想讲讲程序如何传递能够作用的有效参数。
首先这是一个调节lcd亮度(其实更加严格得说是对比度)的函数。
我们看到他用的LCMC,说明此时我们是发送程序给lcd,让他知道我们要调整亮度了。
那么究竟传递什么东西,控制器才能知道我们的意图呢?
不用担心,pdf上会告诉你按什么格式发送指令给控制器才有效。
这是SSD181X
pdf上面的格式之一:
为了方便我只截取其中一个功能设置来进行说明。
最左边的数字表示你发送的命令必须是这样的格式,中间的英文表示该格式命令所起的作用,右边的文字表示这些XXXX,也就是不确定数字的值该如何选取才能获得我们想要的效果。
就以这个方框为例吧,首先我们看中间这个小方框,它告诉我们这条指令的作用是设置对比度寄存器,也就是说我们可以通过对该寄存器的修改来实现调节对比度得调整。
那么具体如何调整呢?
我们再来看看最左边的方框,这个内容告诉我们,我们需要用LCMC发送两个字节数据,其中一个字节是固定的10000001就是0x81,第二个字节则是自己决定。
怎么决定呢?
再看看第三个小方框吧。
如果你e文好,马上就能看懂它的意思。
如果你e文不好……嘿嘿,建议金山词霸!
(倒,谁扔的香蕉皮?
?
!
)
这里的意思就是说,对比度分为64个等级,这样XXXXXX6位二进制数就可以表示完,那么我们需要用哪个等级就设置哪个数,比如现在我们需要等级n的对比度,因此程序中出现了LCMC=n。
这个n哪里来的?
n),嘿嘿,主函数传递进来的。
呵呵,现在明白命令是怎么传送的了吧?
一会儿还有例子,如果不明白不要慌张。
cls(uchar
i,j;
for(j=0x0;
j
wricmd(0xb0+j);
//set
page
//1011xxxx
wricmd(0x10);
column
msb
wricmd(0x0);
lsb
wricmd(0xe0);
modify-read
mode
for(i=0;
i<
98;
i++)
wridata(0x00);
//填满0即清屏
wricmd(0xee);
//reset
这里就开始进入实质性的编程部分了。
这是一个清屏函数,传递的参数ch=9就表示连带ICON一起清除,如果ch=8则表示不清除ICON。
这里的ch实质上表示GDDRAM中的page页面值。
这里有点难以理解。
先看看GDDRAM是怎么构成的。
这是pdf中关于GDDRAM地址分布图的说明,我们可以从这里清晰看到page的概念:
一个page实际上是一个拥有8个高点的行,64点高的屏幕就有8个page,每个page的宽度都是和整个RAM宽度是一样的。
从pdf的最开始我们就可以看到1815的特性是132×
64+1
icon
line,也就是说高为64,换算过来就是8个page。
而GDDRAM的数据分布图与lcd实际的数据显示图是完全一致的,实际上lcd的显示的就是GDDRAM的映射。
因此我们清屏的目的就是为了清除GDDRAM中的值。
因此从for(j=0x0;
j语句中我们得知,循环次数是由ch决定的。
如果ch=8,那么循环8次,就只能清除这8个page(页面)。
只有ch=9,才能清除最后的那一个1
line。
好,我们接着往下看。
进入循环之后,程序要做的事就是一个page一个page得清除信息。
首先wricmd(0xb0+j),翻翻pdf,找到相关信息:
这下知道了,这个语句的意思就是选择要进行读写操作的页面。
因为总共只有8个页面,所以4个不确定位就够拉。
这也就是wricmd()
的参数是“0xb0+j”的原因:
page数只由j,也就是ch控制。
接下来两句wricmd(0x10)和wricmd(0x0),我们同样可以通过pdf的信息得知,作用时用来设置列的低地址和列的高地址。
参数高四位为0001的表示设置高地址,0000的表示设置低地址。
默认情况下高低地址均为0X0000。
接下来就是设置工作模式wricmd(0xe0)。
这个语句的作用是set
read-modify-read
mode,就是设置成读-改-写模式。
?
最后把本次循环我们选择的page填0,也就达到了请零的目的。
需要注意的是I的范围是0-97,为什么?
因为我们现在举例用的lcd宽点数只有98。
wridata(0x00)的作用是把某一列写入0。
没想到吧?
呵呵,因为液晶上一个字节的显示是竖的一排排的,存储在GDDRAM中当然也是竖着拉~也就是一列代表一个字节8位^_^。
刚才我们把工作模式设置为读-改-写,现在当然要结束这个模式啦,这就是wricmd(0xee)这个语句所起的作用。
最后继续循环。
Cls的工作就完成了~
show_asc(uchar
ch);
//***********************
//初始化液晶
lcdlight=32;
initlcd(void)
{
P2=0x00;
//P2作为总线时,其寄存器的值对总线没有影响。
//初始化为0,是为了更好的配合液晶的6800总线。
motorola的液晶内部固定了6800总线方式。
//如果液晶是8080总线,则无需这样做。
wricmd(0x2f);
//SET
POWER
CONTROL,开启一系列与电源有关的功能
wricmd(0x20);
//REGULATOR
RESISTOR
SELECT,内部反馈增益最小
wricmd(0x81);
wricmd(lcdlight);
设置对比度值
wricmd(0x40);
//设置初始显示线,从哪里开始是玻璃上的布线决定的
wricmd(0xa0);
//ADC=0(SEG1~SEG132)
wricmd(0xc8);
//SHL=0(COM1~COM64)
wricmd(0xa2);
//设置LCD
BIAS为1/9
cls(9);
//全部清除,包括icon
wricmd(0xaf);
//开启显示,也就是把GDRAM上的数值显示到屏上
setcursor(0,0);
//设置光标到左上角
到这里,就正是进入我们的初始化了。
什么?
弄了半天你还没有讲初始化啊?
各位看官不要着急,此初始化非彼初始化也。
刚才我们讲的都是整个程序的初始化,而现在我们进入的是液晶屏的初始化。
让我们看看这个过程吧。
首先是uchar
lcdlight=32,顾名思义,这个参数的作用就是调节lcd对比度的拉。
motorola的液晶默认对比度为32。
先让lcdlight=32,到时候利用wricmd函数,直接一个wricmd(lcdlight)语句,多方便~^_^。
闲话少说,接着往下看。
下面是一个initlcd函数,这是可是如假包换的液晶初始化函数。
液晶在每次上电使用都需要初始化,而大多数初始化程序我们都可以不去理会,因为那些都是按照说明书所说的,用于设置COM数和SEG数还有BIAS值的。
这里再解释一下P2=0x00;
的作用。
开始的时候我们就说过,这个液晶使用的是6800总线,这段,其实是为了兼容6800总线加上的,LCD的D/C脚在一开始的时候应该设为0,也就是写成:
P22=0;
就可以了,P22就是接到LCD的D/C脚上的。
另外再提醒一点,一些必须的值,如COM,SEG,BIAS,显示模式等,在使用中这些设置用户是不应该改变的。
到这里,LCD初始化正式完成,已经可以供用户正常使用了。
addr;
hzdata[16];
xdot,i;
addr=16*ch;
readeprom(addr,hzdata,16);
//读出16个字节的点阵数据
xdot=cursor.x*8;
wricmd(0xb0+cursor.y);
//将y位置送入液晶
wricmd(xdot
&
0x0f);
//将x位置送入液晶
wricmd(
0x10
|
(xdot
>
4
));
8;
wridata(hzdata[i]);
//写上半个字符
wricmd(0xb0+cursor.y+1);
wricmd(0x10
for(;
16;
i++)
//写下半个字符
要理解这段程序,必须先知道点阵表示的含义。
我们知道,字母和汉字是按字模位信息显示的,那如何得到汉字的字模信息呢?
难道要我们自己去做?
NO。
DOS前辈们经过艰辛的努力,将制作好的字模放到了一个个标准的库中以免去后辈的麻烦,这就是点阵字库文件。
一般我们使用16*16的点阵宋体字库,所谓16*16,是每一个汉字在纵、横各16点的区域内显示的,前一个16表示列,后一个十六表示行。
不过后来又有了HZK12、HZK24,HZK32和HZK48字库及黑体、楷体和隶书字库。
这段程序是用来在当前光标位置显示一个6×
12
点阵的ASC码字符的。
其实标准的ASC码字符应该是8×
16点阵区域表示,这也就是为什么我们常说“一个汉字占据两个字符位”的原因。
Ch表示的是asc字符的值。
首先说明一点,一个ASC字符分两部分显示,也就是说,把分为上下两个“半ASC码字符”。
汉字同样应该如此显示。
当然这只是我们目前介绍的这个lcd的特性。
如果你使用的是别的种类,千万不要生搬硬套,一定要仔细阅读说明书。
再回头看程序。
首先就是一堆定义,无符号整形数addr表示的是点阵在flash中的物理位置,表示ASC码字符‘1’的字符点阵占据的物理位置是0-15。
为什么?
你看,16*8点阵区域,一个点用1bit表示,‘0’就是灭,‘1’就是亮,那么总共128bit,是不是就是16byte?
同理,字符‘2’是16-30,以此类推。
所以addr=16*ch。
比如我传递进来的ch是4,则addr为64。
而hzdata这个数组是用来存储读出的数据的。
Xdot表示的是横向点位置,在下面我们可以知道它的计算公式是cursor.x*8,就是光标横坐标值乘以8。
由于之前我们将光标设置在左上角,所以cursor.x为0,因此此时横向点位置也为0。
如果我们已经显示了一个ASC字符,此时的cursor.x就应该为1,那么xdot就应该为8:
这应该很好理解,从点阵区域的大小我们可以知道一个ASC码字符从横坐标上看占用的是8个点(0-7),下一个ASC码字符当然应该从8开始拉。
接下来是readeprom(addr,hzdata,16)这个函数。
什么意思呢?
由于它是属于另外一个.c文件,这里只是给出原型:
Readeprom函数原型如下:
readeprom(ulong
ad,uchar
*pst,uint
union
{ulong
addr_l;
{uint
a32;
a10;
}addr_i;
{uchar
a3;
a21;
a0;
}addr;
}