1. 基于proteus的51单片机开发实例20-LCD12864液晶显示汉字
1.1. 实验目的
图1 LCD128*64液晶电路
我们已经学习了数码管显示数字,LCD1602液晶显示字母和数字,本例中,我们来学习使用LCD12864液晶显示汉字。目的是了解LCD12864液晶的电路设计和编程方法。
1.2. 设计思路
本实例使用51单片机设计LCD128*64液晶的显示驱动电路,并编写汉字显示程序。液晶数据端口数据收发采用并行通信方式,单片机P2口的8位接到LCD126*64液晶的8个数据端口。然后使用单片机P3口的5个I/O口作为液晶读写控制信号的控制端口。
1.3. 基础知识
1.3.1. LCD128*64液晶简述
从字面来理解LCD128*64液晶:其横向可以显示128个点,纵向可以显示64个点。每一个点对应可以显示一个二进制位。一个数字或字母的显示需要8*8个点,一个汉字的显示则需要16*16个点。
使用LCD128*64液晶显示信息时,需要把显示信息先保存到液晶的数据存储器RAM中。
现在市面上的LCD128*64液晶有两大类:带字库和不带字库。这两个的最大区别是汉字显示的问题。如果不带字库,为了显示一个汉字,就需要传送一大批点阵数据。如果带字库,显示一个汉字,仅仅需要传送两个字节汉字内码。
具体到我们的实例都是在proteus环境下实现的,而proteus是英国人开发的软件,它里面的128*64液晶不带字库,所以我们要显示汉字就得用字模软件生成字库,然后编程驱动这一堆数据来显示汉字。
本例使用的128*64液晶的内部结构如下图所示。
LCD128*64内部结构
从图中可以看出,该液晶实际上是由左、右两块独立的64*64液晶拼接而成的。这两块液晶分别由CS1和CS2两个片选信号来选择。
LCD128*64的控制指令
1、显示控制指令
RW=0,RS=0时,向数据总线上发送0x3f表示打开液晶显示,发送0x3e表示关闭液晶显示。
2、显示起始行设置指令
RW=0,RS=0时,向数据向上发送11xx xxxx,低6位表示起始行的行号(0~63)。
3、页地址设置指令
RW=0,RS=0时,向数据总线发送1011 1xxx,低3位表示所要设置的页地址(0~7)。
4、列地址设置指令
RW=0,RS=0时,向数据总线发送01xx xxxx。低6位表示所要设置的列地址(0~63).页地址和列地址共同决定了显示的位置,设置了这两个地址,单片机就能用读、写指令对该地址进行数据读写。
5、读状态指令
RW=1,RS=0时,读取数据总线的状态。
尤其是DB7的状态,该状态指示液晶的空闲状态。改为读出为1,表示液晶处于忙状态,此时不宜对液晶进行读写操作。为0时,表示液晶处于空闲状态,可以对液晶进行读写操作。
6、写数据指令
RW=0,RS=1时,此时允许向总线写数据。
7、读数据
RW=1,RS=1时,可以读取显示的数据。
1.3.2. 汉字字模生成和提取
本例中我们使用的字模提取工具是“字模提取 V2.2”软件。下面介绍一下使用方法。
打开该软件,在文字输入区输入汉字,使用ctrl+enter组合键结束文字的输入。
输入汉字
然后在参数设置中,首先选择汉字的字体和大小。
选择字体和大小
在其它参数中,如下图所示选择。
其他参数
然后进入“提取方式”页面,选择C51格式,就会生成字模了。
生成字模
1.4. 电路设计
本实例电路如图1所示。对于LCD128*64的数据通信采用并行方式,P2口接液晶的D0~D7口,另外用单片机的P3.3~P3.7口连接液晶的控制端口E、RW、RS、CS1、CS2。
1.5. 程序设计
程序代码如下。
#include //
#include //
//LCD128*64指令
#define LCD_OFF 0x3E //关显示
#define LCD_ON 0x3F //开显示
#define BEGIN 0xC0 //设置起始行
#define SET_PAGE 0xB8 //设置起始页
#define SET_Y 0x40 //设置起始列
//LCD控制引脚定义
sbit CS1=P3^3;
sbit CS2=P3^4;
sbit RS=P3^5;
sbit RW=P3^6;
sbit E=P3^7;
//字模:老马识途单片机
unsigned char code DisData1[]=
{0x20,0x20,0x24,0x24,0x24,0x24,0xBF,0x64,0x24,0x34,0x28,0x24,0x22,0x20,0x20,0x00,
0x10,0x08,0x04,0x02,0x3F,0x45,0x44,0x44,0x42,0x42,0x42,0x41,0x78,0x00,0x00,0x00};//
unsigned char code DisData2[]=
{0x00,0x02,0x02,0xF2,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x80,0x80,0x80,0x00,0x00,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x48,0x80,0x40,0x3F,0x00,0x00};//
unsigned char code DisData3[]=
{0x40,0x40,0x42,0xCC,0x00,0x00,0x00,0xFC,0x04,0x04,0x04,0x04,0x04,0xFC,0x00,0x00,
0x00,0x00,0x00,0x3F,0x10,0x88,0x40,0x23,0x19,0x01,0x01,0x09,0x11,0x23,0xC0,0x00};//
unsigned char code DisData4[]=
{0x40,0x40,0x42,0xCC,0x00,0x90,0x88,0x94,0x92,0xF1,0x92,0x94,0x88,0x90,0x10,0x00,
0x00,0x40,0x20,0x1F,0x20,0x48,0x46,0x50,0x60,0x5F,0x40,0x40,0x42,0x4C,0x40,0x00};//
unsigned char code DisData5[]=
{0x00,0x00,0xF8,0x49,0x4A,0x4C,0x48,0xF8,0x48,0x4C,0x4A,0x49,0xF8,0x00,0x00,0x00,
0x10,0x10,0x13,0x12,0x12,0x12,0x12,0xFF,0x12,0x12,0x12,0x12,0x13,0x10,0x10,0x00};//
unsigned char code DisData6[]=
{0x00,0x00,0x00,0xFE,0x20,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x20,0x20,0x00,0x00,
0x00,0x80,0x60,0x1F,0x02,0x02,0x02,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x00};//
unsigned char code DisData7[]=
{0x10,0x10,0xD0,0xFF,0x90,0x10,0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,
0x04,0x03,0x00,0xFF,0x00,0x83,0x60,0x1F,0x00,0x00,0x00,0x3F,0x40,0x40,0x78,0x00};//
unsigned char code DisData8[]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEB,0x49,0x49,0x55,0x55,0x22,0x22,0x00,0x00};
unsigned char Y_i; //列
unsigned char Page_i; //页
//函数声明
//空闲状态检测
bit CheckBusy(void);
//向液晶写指令
void WriteInstruction(unsigned command);
//向液晶写数据
void WriteData(unsigned dat);
//清除屏幕显示
void ClearLCD(void);
//显示图形
void Draw(unsigned char page,unsigned char yi,unsigned char *ps,unsigned char n);
//显示汉字
void Display_Character(unsigned char *p);
//指定显示位置
void gotoxy(unsigned page_i,unsigned y_i);
//延时函数
void delay(void);
void main(void)
{
ClearLCD(); //清屏
gotoxy(2,0); //指定显示位置
Display_Character(DisData1);
delay();
Display_Character(DisData2);
delay();
Display_Character(DisData3);
delay();
Display_Character(DisData4);
delay();
Display_Character(DisData5);
delay();
Display_Character(DisData6);
delay();
Display_Character(DisData7);
delay();
while(1)
{
}
}
bit CheckBusy(void)
{
bit flag;
RW=1; //RW=1,RS=0时,E=1时,状态送到数据总线
RS=0;
E=1;
_nop_();
_nop_();
_nop_();
_nop_();
P2=0x00;
flag=(bit)P2&0x80;
E=0;
RS=1;
RW=1;
return flag;
}
void WriteInstruction(unsigned command)
{
while(CheckBusy==1) ;
RW = 0;
RS = 0;
P2=command;
_nop_();
_nop_();
E=1;
_nop_();
_nop_();
E=0;
RS=1;
RW=1;
}
void WriteData(unsigned dat)
{
while(CheckBusy==1) ;
RW=0;
RS=1;
E=1;
_nop_();
_nop_();
P2=dat;
_nop_();
_nop_();
E=0;
_nop_();
_nop_();
RS=0;
RW=1;
}
void ClearLCD(void)
{
unsigned char i,j;
CS1=1;
CS2=1;
WriteInstruction(LCD_ON);
WriteInstruction(BEGIN);
for(i=0;i<8;i++)
{
WriteInstruction(SET_PAGE+i);
WriteInstruction(SET_Y);
for(j=0;j<64;j++)
WriteData(0x80);
}
}
void Draw(unsigned char page,unsigned char yi,unsigned char *ps,unsigned char n)
{
unsigned char i;
WriteInstruction(SET_PAGE+page);
WriteInstruction(SET_Y+yi);
for(i=0;i
LCD128*64液晶的工作流程如下:清屏(初始化)-指定显示位置(页地址和列地址的选择)-向指定区域RAM送待显示数据。
1.6. 实例仿真
将写好的程序编译生成HEX文件,然后将HEX文件转载到proteus电路中的单片机内,开始仿真。液晶会逐个显示“老马识途单片机”这几个字。
视频加载中...1.7. 总结
通过本实例,我们了解LCD128*64液晶的工作原理、编程实现。至此我们已经学习了数码管,字符液晶、点阵液晶的知识,这对我们以后的综合实例的学习打下了坚实的基础。