4用DS1302与12864LCD设计的可调式中文电子日历.docx
《4用DS1302与12864LCD设计的可调式中文电子日历.docx》由会员分享,可在线阅读,更多相关《4用DS1302与12864LCD设计的可调式中文电子日历.docx(29页珍藏版)》请在冰豆网上搜索。
4用DS1302与12864LCD设计的可调式中文电子日历
学习情境5-可调式数字钟
之用DS1302与12864LCD设计的可调式中文电子日历
☆点名,复习
1、DS1302的引脚及其功能,以及DS1302与单片机的硬件连接?
2、如何编写基于1602LCD的显示驱动程序?
☆新课讲授
5.3基于DS1302与LCD12854设计的可调数字钟
上节课我们详细介绍了LCD1602的显示控制指令,掌握了如何编写该种型号的LCD的显示驱动程序。
这节课我们学习另外一种液晶显示模块—12864(KS0108)
5.3.1LCD12864技术资料
5.3.1.112864(KS0108)字符型LCD简介
12864D使用KS0108(或其兼容芯片)作为控制器,适配M6800系列时序,具有8位标准数据总线。
6条控制线及电源线可显示各种字符及图形。
每个KS0108拥有64×64位(512字节)的显示RAM,12864D显示屏上的64×64点,显示RAM中的数据直接作为显示驱动信号。
具有操作指令简单,低功耗的特点。
5.3.1.2、引脚说明
管脚号
管脚
电平
说明
1
CSA
H/L
片选择信号,低电平时选择前64列。
2
CSB
H
片选择信号,低电平时选择后64列。
3
GND
0V
逻辑电源地。
4
VCC
5V
逻辑电源。
5
VEE
-10V
LCD驱动电源。
6
D/I
H/L
数据\指令选择,高电平:
数据D0-D7将送入显示RAM;
低电平:
数据D0-D7将送入指令寄存器执行。
7
R/W
H/L
读\写选择,高电平:
读数据;低电平:
写数据。
8
E
H.H/L
读写使能,高电平有效,下降沿锁定数据。
9-16
DB0
H/L
数据输入输出引脚。
5.3.1.3、12864LCD的指令说明
1、指令列表
2、指令功能详解
(1)读状态字(readstatus)
格式
BUSY
0
ON/OFF
RESET
0
0
0
0
★BUSY=1,表示KS0108正在处理计算机发来的指令或数据。
此时接口电路被封锁,不能接受除读状态字以外的任何操作。
BUSY=0表示KS0108接口控制电路已处于“准备好”状态,等待计算机的访问。
★ON/OFF:
表示当前的显示状态。
ON/OFF=1表示关显示状态;ON/OFF=0表示开显示状态。
★RESET表示当前KS0108的工作状态,即反映RST端的电平状态。
当RST为低电平状态时KS0108处于复位工作状态,RESET=1。
当RST为高电平状态时,KS0108为正常工作状态,RESET=0。
★在占领设置和数据读写时要注意状态字中的BUSY标志。
只有在BUSY=0时,计算机对KS0108的操作才能有效。
因此计算机在每次对KS0108操作之前,都要读出状态字判断BUSY是否为“0”。
若不为“0”,则计算机需要等待,直至BUSY=0为止。
(2)显示开关(displayon/off)
格式
0
0
1
1
1
1
1
D
该指令设置显示开关/触发器的状态,由此控制显示数据锁存器的工作方式,从而控制显示上的显示状态。
D位为显示开/关的控制位。
当D=1为显示设置,显示数据锁存器正常工作,显示屏上呈现所许的效果。
此时在状态字中ON/OFF=0。
当D=0为关显示设置,显示数据锁存器被置零,显示屏呈不显示状态,但显示存储器并没有被破坏,在状态组中ON/OFF=1。
(3)显示起始行设置(Displaystartline)
格式
1
1
L5
L4
L3
L2
L1
L0
该指令设置了显示起始行寄存器的内容。
KS0108有64行显示的管理能力,该指令中L5~L0为显示起始行的地址,取值在00~3FH(1~64)范围内,它规定了显示屏上最顶一行所对应的显示存储器的行地址。
如果定时间隔地,等间距地修改(如加一或减一)显示起始行寄存器的内容,则显示屏将呈现显示内容向上或向下平滑滚动的显示效果。
(4)页面地址设置[Setpage(Xaddress)]
格式
1
0
1
1
1
P2
P1
P0
该指令设置了页面地址-X地址寄存器的内容。
KS0108将显示存储器分成了8页,指令代码中P2~P0就是要确定当前所要选择的页面地址,取值范围为00~07H,代表第1~8页。
该指令规定了以后的读/写操作将在哪一个页面上进行。
执行本指令后,下面的读写操作将在指定页内,直到重新设置。
页地址就是DDRAM的行地址,页地址存储在X地址计数器中,P2-P0可表示8页,读写数据对页地址没有影响,除本指令可改变页地址外,复位信号(RST)可把页地址计数器内容清零。
表2DDRAM地址映像表
Y1
Y2
Y3
Y4
…………
Y62
Y63
Y64
X=0
Line0
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB0
Line1
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB1
Line2
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB2
Line3
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB3
Line4
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB4
Line5
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB5
Line6
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB6
Line7
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB7
…………
…………
…………
X=7
Line60
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB4
Line61
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB5
Line62
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB6
Line63
1/0
1/0
1/0
1/0
…………
1/0
1/0
1/0
DB7
(5)列地址设置(SetYaddress)
格式
0
1
C5
C4
C3
C2
C1
C0
该指令设置了Y地址计数器的内容,C5~C0=0~3FH(1~64)代表某一页面上的某一单元地址,随后的一次读或写数据将在这个单元上进行。
Y地址计数器具有自动加一功能,在每一次读/写数据后它将自动加一,所以在连续进行读/写数据时,Y地址计数器不必每次都设置一次。
页面地址的设置和列地址的设置将显示存储器单元唯一地确定下来,为后来的显示数据的读/写作了地址的选通。
(6)写显示数据(Writedisplaydata)
格式
数据
该操作将8位数据写入先前已确定的显示存储器的单元内,操作完成后列地址计数器自动加1。
(7)读显示数据(Readdisplaydata)
格式
数据
该操作将KS0108接口部的输出寄存器内容读出,然后列地址计数器自动加1。
5.3.2硬件电路设计
硬件主要使用了DS1302和LCD12864,电路结构简单,具体如下图所示:
图1硬件电路
5.3.3软件设计
软件设计中主要是针对DS1302芯片和LCD12864进行程序的设计,而DS1302的驱动程序在上个项目中我们已经作了详细介绍,在此不再赘述。
我们主要编制LCD12864的驱动程序。
根据对LCD12864资料的分析,程序编制主要有:
读取LCD的状态,忙检查,向LCD写入命令,向LCD写入数据,LCD初始化,设置LCD液晶的显示位置,显示字符等。
除此之外,我们把要显示的字符和汉字用一个专门的程序来编写,命名为DotMatrix.c。
1、LCD12864显示驱动程序
(1)检查LCD是否忙
bitLCD_IS_BUSY()
{
LCD_DB_PORT=0xFF;
RW=1;
_nop_();
DI=0;
E=1;
_nop_();
E=0;
return(bit)(P0&0x80);
}
(2)向LCD写入一个字节(一般用于发送命令)
voidWrite_Byte_To_LCD(ucharcomand)
{
while(LCD_IS_BUSY());
LCD_DB_PORT=0xFF;
RW=0;
_nop_();
DI=0;
LCD_DB_PORT=comand;
E=1;
_nop_();
E=0;
}
(3)向LCD写入数据
voidWrite_Data_To_LCD(uchardat)
{
while(LCD_IS_BUSY());
LCD_DB_PORT=0xFF;
RW=0;
_nop_();
DI=1;
if(!
Reverse_Display)//根据Reverse_Display决定是否反相显示
LCD_DB_PORT=dat;
else
LCD_DB_PORT=~dat;
E=1;
_nop_();
E=0;
}
(4)初始化LCD
voidLCD12864_Initialization()
{
CS1=1;
CS2=1;
Write_Byte_To_LCD(0x38);
Write_Byte_To_LCD(0x0F);
Write_Byte_To_LCD(0x01);
Write_Byte_To_LCD(0x06);
Write_Byte_To_LCD(LCD_START_ROW);
}
(5)通用显示函数从第P页第L列显示W个字节数据,具体显示的数据在r所指的数组中
voidLCD_Show(ucharP,ucharL,ucharW,uchar*r)reentrant
{
uchari;
if(L<64)
{
CS1=1;
CS2=0;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL+L);
if(L+W<64)
{
for(i=0;iWrite_Data_To_LCD(r[i]);
}
else
{
for(i=0;i<64-L;i++)
Write_Data_To_LCD(r[i]);
CS1=0;
CS2=1;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL);
for(i=64-L;iWrite_Data_To_LCD(r[i]);
}
}
else
{
CS1=0;
CS2=1;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL+L-64);
for(i=0;iWrite_Data_To_LCD(r[i]);
}
}
(6)显示一个8×16点阵字符
voidDisplay_char(ucharP1,ucharL1,uchar*M)reentrant
{
LCD_Show(P1,L1,8,M);
LCD_Show(P1+1,L1,8,M+8);
}
(7)显示一个16×16点阵字符(汉字上半部分与下半部分分别处在相邻两页中)
voidDisplay_Word(ucharP2,ucharL2,uchar*M)reentrant
{
LCD_Show(P2,L2,16,M);
LCD_Show(P2+1,L2,16,M+16);
}
2、DS1302时钟程序
DS1302时钟程序上一项目有详细介绍,在此忽略。
3、字符汉字显示程序
在此程序中,我们主要定义了3个数组,分别为:
(1)年、月、日、星期、时、分、秒等汉字点阵(16×16)数组ucharcodeDATE_TIME_WORDS[];
(2)一、二、三、四、五、六和天等汉字点阵(16×16)ucharcodeWEEK_WORDS[];
(3)0-9等数字点阵(8×16)ucharcodeDigits[];
各个汉字数字的具体字模可用专门的取字模软件得到,在文后的完整程序代码中有它们的具体字模数组。
4、主程序
主程序较简单,有初始化子程序Initialization(),其中包括LCD的初始化、显示初始化以及中断初始化工作。
有2个中断服务子程序:
一个用来刷新显示,一个用来处理4个按键。
☆课堂小结
本文主要针对不含字库的LCD12864进行了时钟和日期的显示,具体的介绍了LCD12864显示程序的设计。
同时我们在程序设计时也要善于把程序分解,分解成一个一个的小程序,这对于C程序的应用大有好处。
当然通过这个项目的学习,我们也学习到了如何应用字模软件得到我们所要显示的汉字和数字的字模。
希望同学们仔细研究文后的完整程序代码。
☆完整程序代码
1、LCD12864显示驱动程序
//--------------------------LCD_12864.c-------------------------
//名称:
12864LCD显示驱动程序(不带字库)
//-----------------------------------------------------------------
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
#defineLCD_DB_PORTP0//液晶数据线端口DB0-DB7
#defineLCD_START_ROW0xc0//起始行命令
#defineLCD_PAGE0xB8//页指令
#defineLCD_COL0x40//列指令
bitReverse_Display=0;//是否反相显示(白底黑字/黑底白字)
//液晶引脚定义
sbitDI=P2^0;
sbitRW=P2^1;
sbitE=P2^2;
sbitCS1=P2^3;
sbitCS2=P2^4;
sbitRST=P2^5;
//---------------------------------------------------------------
//检查LCD是否忙
//--------------------------------------------------------------
bitLCD_IS_BUSY()
{
LCD_DB_PORT=0xFF;
RW=1;
_nop_();
DI=0;
E=1;
_nop_();
E=0;
return(bit)(P0&0x80);
}
//---------------------------------------------------------------
//向LCD写入一个字节(一般用于发送命令)
//--------------------------------------------------------------
voidWrite_Byte_To_LCD(ucharcomand)
{
while(LCD_IS_BUSY());
LCD_DB_PORT=0xFF;
RW=0;
_nop_();
DI=0;
LCD_DB_PORT=comand;
E=1;
_nop_();
E=0;
}
//---------------------------------------------------------------
//向LCD写入数据
//--------------------------------------------------------------
voidWrite_Data_To_LCD(uchardat)
{
while(LCD_IS_BUSY());
LCD_DB_PORT=0xFF;
RW=0;
_nop_();
DI=1;
if(!
Reverse_Display)//根据Reverse_Display决定是否反相显示
LCD_DB_PORT=dat;
else
LCD_DB_PORT=~dat;
E=1;
_nop_();
E=0;
}
//---------------------------------------------------------------
//初始化LCD
//--------------------------------------------------------------
voidLCD12864_Initialization()
{
CS1=1;
CS2=1;
Write_Byte_To_LCD(0x38);
Write_Byte_To_LCD(0x0F);
Write_Byte_To_LCD(0x01);
Write_Byte_To_LCD(0x06);
Write_Byte_To_LCD(LCD_START_ROW);
}
//---------------------------------------------------------------
//通用显示函数从第P页第L列显示W个字节数据,具体显示的数据在r所指的数组中
//--------------------------------------------------------------
voidLCD_Show(ucharP,ucharL,ucharW,uchar*r)reentrant
{
uchari;
if(L<64)
{
CS1=1;
CS2=0;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL+L);
if(L+W<64)
{
for(i=0;iWrite_Data_To_LCD(r[i]);
}
else
{
for(i=0;i<64-L;i++)
Write_Data_To_LCD(r[i]);
CS1=0;
CS2=1;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL);
for(i=64-L;iWrite_Data_To_LCD(r[i]);
}
}
else
{
CS1=0;
CS2=1;
Write_Byte_To_LCD(LCD_PAGE+P);
Write_Byte_To_LCD(LCD_COL+L-64);
for(i=0;iWrite_Data_To_LCD(r[i]);
}
}
//---------------------------------------------------------------
//显示一个8×16点阵字符
//--------------------------------------------------------------
voidDisplay_char(ucharP1,ucharL1,uchar*M)reentrant
{
LCD_Show(P1,L1,8,M);
LCD_Show(P1+1,L1,8,M+8);
}
//--------------------------------------------------------------------
//显示一个16×16点阵字符(汉字上半部分与下半部分分别处在相邻两页中)
//----------------------------------------------------------------------
voidDisplay_Word(ucharP2,ucharL2,uchar*M)reentrant
{
LCD_Show(P2,L2,16,M);
LCD_Show(P2+1,L2,16,M+16);
}
2、DS1302时钟程序
#include
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
sbitSDA=P1^0;//DS1302数据线
sbitCLK=P1^1;//DS1302时钟线
sbitRST=P1^2;//DS1302复位线
charAdjust_Index=-1;//当前调节的时间对象:
秒,分,时,日,月,年(0,1,2,3,4,6)
ucharMonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};//一年中每个月的天数,二月的天数由年份决定
ucharDateTime[7];//所读取的日期时间
//函数声明
voidWrite_Byte_TO_DS1302(ucharX);//向DS1302写入一个字节
ucharRead_Byte_FROM_DS1302();//从DS1302中读取一个字节
ucharRead_Data_FROM_DS1302(ucharaddr);//从DS1302指定位置读取数据,读数据
voidWrite_Data_TO_DS1302(ucharaddr,uchardat);//向DS1302指定位置写入数据,写数据
voidSET_DS1302();//设置时间
voidGetTime();//读取当前时间
ucharIs_Leapyear(uintyear);//判断是否为闰年
//*--------写字节函数,向DS1302写入一