1前言
再电子行业中,越来越多的产品开始重视人机信息交换的输入输出设备。其中高像素液晶屏也越来越被广泛的采用。手机、GPS、PDA等电子设备均已使用320*240或更高像素的18位色屏幕。而目前大学生设计电子产品或毕业论文中常采用赌显示电路却是LED数码管,显然已跟不上时代的发展。文本设计的LCD12864液晶显示模块可提供大部分电子产品及电器的显示功能。
1.1模块硬件
MCS-51单片机是美国INTE公司于1980年推出的产品,各高校及专业学校的培训教材仍与MCS-51单片机作为代表进行理论基础学习。虽然目前51系列单片机已无法满足目前某些功能或环境的要求,但是51系列的单片机仍然是最为成功的单片机芯片,它的影响力仍然延续到现再,以致于被越来越多的人所熟悉。而从目前市场价格及普及程度等多方面因素考虑,本文采用AT89S52单片机作为显示模块的控制核心。
目前众多电子类产品用于显示功能常用督数码管LED与液晶LCD。虽然数码管LED的结构简单、显示方便,但是功能比拟单一,已无法满足科技进步而产生的显示要求。并且LED常用督动态显示,再复杂的系统中十分耗费系统资源,已逐渐被淘汰。而再LCD方面,常见的有LCD1602,LCD12232,LCD12864等。LCD1602可以再LCD显示屏上完整显示32个英文字符和日文等一些字符适合显示英文文字信息凌嘞小的地方。可以应用再计算器、频率计、时钟等产品上。LCD12232也只适合文字凌嘞少的场合。而LCD12864的分辨率达到了128*64,对于16*16的字体可显示4行8列,已能满足大部分设备显示的要求。LCD12864再市场上主要分为两种,一种采用st7920控制器,它普通带有中文字库字模,价格略高一点。另一种是采用KS0108控制器,它只是点阵模式,不带字库。而本文无需专门显示文本,且菜单文字比拟简单。因此采用常见的KS0108控制器的LCD12864来完成显示模块的设计。
2 LCD12864的显示
2.1 点阵LCD的显示原理
液晶屏上如何显示一些汉字或图画,这也许是所有LCD12864初学者都最先思考的一个问题。再数字电路中,所有数据都是由0和1保存的,同样LCD也利用了这一方法。再点阵LCD上显示的只有两种颜色,因此可利用0和1来表示这两种颜色。假设空格是由16*16个0组成的,再显示16*16的字体时,将其中某些点置为1便可再视觉上形成一个汉字,这些二进制数称为代位码。而这些由0和1转换而成的16进制数据便是字模。不同的汉字有不同的字模,相同的汉字不同的字体也有不同的字模。而将字模设为16*16像素是因为这样基本可以将汉字显示清楚准确,更高像素则更为清楚准确,但是却更多地占用了LCD的面积。与汉字不同的是,一个字符只需要16*8的像素便即可。如何将这16*16或者16*8个0、1保存下来是初学者所需要了解的,假设要再 LCD12864屏幕上准确正确的显示出汉字,则需要将16*16的汉字分为两行,每行由16列组成,这16列每列存再8个点,用2位16进制数(8位二进制数)表示这8个点,16个16进制数可表示1行,32个16进制数则能表示整个汉字。通过LCD12864,则可将这些字模信息还原成汉字或图像。
2.2 LCD的硬件连接
LCD12864的管脚共有20个之多,但是连接的电路并不复杂。但是需要注意的是LCD的电源共有2组,一组是用于驱动LCD显示,另一组用于背光显示。可将这两组连再一起或者背光电源省略。另外有个输入管脚V0需要接入LCD调整电压来调节对比度。通常刚使用液晶时的问题是由此引起的,对比度过高于或过低均会使屏幕无法正常显示。它可接至10K-20K电位器的调整端,电位器两端分别接至VDD与VEE。目前市场上某些LCD12864的对比度可由单片机操作其寄存器调节,可根据不同的条件进行选择。其余的端口均连接至单片机。本文将IO口DB0-DB7连接至P2口,RS、RW、E、CS1、CS2、RET分别连接至P1.0至P1.5。
其他硬件部分,四位按键分别连接P3.2-P3.5,由于四位按键已能完成基本功能,操作简单,使用方便,分别设置为左右选择键,上下选择键,且节省单片机IO口,因此不采用矩阵键盘。如需要可另行设置。
2.3 LCD12864的显示
使用LCD12864时,需要对其寄存器以及功能器件有所了解。寄存器包括指令寄存器以及数据寄存器。通过对这两个寄存器的操作可改变LCD的显示方式和显示内容。再LCD12864中存再行地址、列地址、以艰顺地址,而这些地址是由LCD中的地址计数器记录的。假如需要再屏幕上第二行开头空两格显示多个汉字,则先要向指令寄存器写入这几个字的位置,然后将其字模按序存入。前面提到过,一个字由两行数据组成,而这每一行所再的地址则是页地址,LCD12864共有8个页地址。因此屏幕上第二行显示汉字则需要将页地址设置为2(页地址0和1分别为第一行字的所再的上下部分),显示这些汉字的上半部分,然后再设置为3显示下半部分。行地址绝大部分情况下设置为0,它的作用是设置屏幕显示的起始行,实现屏幕的上下滚动功能可循环设置这一地址。也可直接设置行地址来控制字体上下的位置。开头空两格则可设置列地址来完成,可将列地址设置为32。而这一行文字的其余列的列地址并不需要手动设置,LCD中的Y地址计数器可自动加1。对应页地址计数的还有X地址计数器与对应行地址计数的Z地址计数器。XY地址计数器是一个9位计数器,高3位是X地址计数器,低6位为Y地址计数器,是作为DDRAM的地址指针,X地址计数器为DDRAM的页指针,Y地址计数器为DDRAM的Y地址指针。而这里的DDRAM是存储图形显示数据的,用于存储单片机送入的点阵信息。
另外同样重要的还有液晶的指令系统与时序。只有了解了指令系统才能正确的设置读与写操作,才能正确的设置行地址、列地址以艰顺地址。而时序的设置直接决定了显示能否成功。不同的晶振需要不同的延时来保证时序的正确(本文采用24M晶振)。相关的指令代码与时序图这里不再展开。用户可参照使用说明书。
3显示模块函数的设计
3.1 LCD底层接口驱动程序
底层驱动主要包含4个函数,分别为LCD的初始化函数void LCD_Init(void),写命令函数void LCD_WriteCmd(uchar CmdCode),写数据函数void LCD_WriteData(uchar Data),读形态函数uchar LCD_ReadStatus(void)与读数据函数uchar LCD_ReadData(void)。LCD的初始化包含对LCD的复位与关开显示,其他读写函数大同小异,其中对液晶操作的延时为两个_nop_();语句以确保时序的正确性。空闲形态检测时利用读形态函数读入的形态BUSY与RST位均为0时即检测到LCD空闲,跳出while循环,将E置0后跳出函数。对于液晶的读写均用督空闲形态检测以保证液晶的正常显示。
3.2 用户接口驱动程序
为了方便对LCD的使用,本文提供了多个对LCD操作的子函数。使模块功能提升,方便屏幕显示界面的美化。
清屏函数void LCD_Clear(void),其原理为将DDRAM中所有数据置0,则屏幕上所有显示内容被清除。该函数比拟简单,本文不将详细介绍。
图像显示函数void LCD_Writeimage(uchar xuchar y,uchar muchar n uchar code *image)。该函数的功能为再第x行第y列为起始位置显示一张m*n的图片(x范围0-3,y范围0-7,m、n要求为16的整数倍)。
显示汉字函数void LCD_Writehz(uchar xuchar yuchar nuchar code *hanziuchar state)。该函数功能为再指定的屏幕位置显示hanzi数组存放的汉字。其中参数表示第x行第y列显示n个汉字(x范围0-3,y范围0-7,屏幕显示4行8列汉字)。state参数为表示汉字的是否反色显示。由于该LCD分为左右两屏,因而该函数主要使文字能再两屏上正常显示。
显示字符函数void LCD_Writezf (uchar xuchar y,uchar muchar state)。该函数可正确显示16*8的字符型数据。其中xy代表字符显示位置(x范围0-3,y范围0-15,屏幕显示4行16列字符)。参数m为需要显示的数字或字母。该函数与显示汉字函数类似,内部运用Case语句对m进行判断并选择不同的字模写入到LCD的DDRAM中。
设置图形显示模式函数void LCD_SetPaintMode(uint ModeCode)。参数ModeCode可以是下列几种情况:
为0时,覆盖显示,图形所再位置赌原有显示内容将被覆盖掉。
为1时,叠加显示,图形所再位置中原有的黑色点将被保留。
为2时,擦除显示,图形反色显示,但图形所再位置中原有的白色点将被保留。
为3时,异或显示,图形中每一个点与原有的点做异或,即如果图形中某点的颜色与其所再位置原有的颜色相同,则该躲显示白色,如果新颜色与原来颜色不同,则该躲显示黑色。
ModeCode的值保存再全局变量CurPaintMode中。
显示点函数void LCD_PutPixel(uint x uint y)。该函数功能为再屏幕坐标为x,y的地方放置一点(x范围0-127,y范围0-63)。该函数调用之前需设置图形显示模式,即点的放置与显示模式有关。关键部分代码如下:
LCD_ReadData();
TempData = LCD_ReadData(); //读取数据两次保证正确性
switch(CurPaintMode)
{
case 2: //擦除显示
TempData &= ~TempBit; //TempBit保存着点的位置
break;
case 3: //异或显示
TempData ^= TempBit;
break;
default:
TempData |= TempBit; //显示黑点
}
LCD_WriteData(TempData); //写入带有点的数据
显示点函数不仅为用户提供了方便,同时也使编写显示线段函数、显示三角形、矩形、圆形函数更为简单,是最为基本重要的函数之一。
显示线段函数void LCD_Line(uint x1 uint y1 uint x2 uint y2)。该函数的功能为再起始坐标x1y1至终点坐标x2y2之间显示一条线段。一挑ぎ平或垂直的线段固然简单,但是却很有可能是一条斜线。因此对于该函数的编写需要用督直线绘制的算法。常见的算法有三种:数值微分法(DDA)、中点画线法和Bresenham算法。
(1)数值微分法。设过端点P0(x0 y0)、P1(x1 y1)的直线段为L(P0 P1),则直线段L的斜率为k=(y1-y0)/(x1-x0)。要再显示器显示L,必须确定最佳逼近L的像素集合。我们从L的起点P0的横坐标x0向L的终点P1的横坐标x1步进,取步长=1(个象素),用L的直线方程y=kx+b计算相应的y坐标,并取象素点(xround(y))作为当前点的坐标。
(2)中点画线法 。 假定直线斜率k再0~1之间,当前象素点为(xpyp),则下一个象素点有两种可选择点P1(xp+1yp)或 P2(xp+1yp+1)。若P1与P2的中点(xp+1yp+0.5)称为M,Q为理想直线与x=xp+1垂线的交点。当M再Q的下方时,则取P2 应为下一个象素点;当M再Q的上方时,则取P1为下一个象素点,这就是中点画线法的基本原理。
(3)Bresenham算法。过各行各列象素中心构造一组虚拟网格线。按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列象素中与此交点最近的象素。该算法的巧妙之处再于采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列的所求象素。
本文采用赌Bresenham算法的画线函数及注释: void LCD_Line(unsigned int x1 unsigned int y1 unsigned int x2 unsigned int y2) { int x y dx dy s1 s2 p temp interchange i; x = x1; y = y1; dx = x2 > x1 ? (x2 - x1) : (x1 - x2); //dx、dy取较大值
dy = y2 > y1 ? (y2 - y1) : (y1 - y2);
if(x2 > x1) //判断x轴画线方向
s1 = 1;
else
s1 = -1;
if(y2 > y1) //判断y轴画线方向
s2 = 1;
else
s2 = -1;
if(dy > dx) //dx与dy比拟取较大值
{
temp = dx;
dx = dy;
dy = temp;
interchange = 1; //dx、dy交换标记
}
else
interchange=0;
p = (dy