DS1302与液晶1602简单时钟

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《单片机原理实验》




题目:DS1302和1602时钟
姓名:
学号:
班级:
专业:电子信息工程
指导老师:
提交时间:
DS1302和1602时钟
***
(**学院电子信息工程 ***班 **********)
摘要:
关键词:DS1302 ,液晶1602,89C51。

1.引言
我们在日常生活中常常用到时钟来记录时间,有时候还不能准确的记录下时间,
这就要用到芯片DS1302了。

时间还远远不够,因为人性化的设计自然是要清楚的显示年、月、日、星期、时间。

而在此用到的DS1302非常好的与时间进行同步,并且在掉电时不会对时间有影响,较理
的考虑到了人们的思维模式,从而简单的控制了时钟,结合了常理的理论。

2.原理
DS1302引脚
X1 X2 32.768KHz 晶振管脚
GND 地
CE 复位脚
I/O 数据输入/输出引脚
SCLK 串行时钟
Vcc1,Vcc2 电源供电管脚
各引脚的功能为:
Vcc1:主电源;Vcc2:备份电源。

当Vcc2>Vcc1+0.2V时,由Vcc2
向DS1302供电,当Vcc2< Vcc1时,由Vcc1向DS1302供电。

SCLK:串行时钟,输入,控制数据的输入与输出;
I/O:三线接口时的双向数据线;
CE:输入信号,在读、写数据期间,必须为高。

该引脚有两个功能:
第一,CE开始控制字访问移位寄存器的控制逻辑;其次,
CE提供结束单字节或多字节数据传输的方法。

DS1302控制字:
控制字的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入到DS1302中。

位6:如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;
位5至位1(A4~A0):指示操作单元的地址;
位0(最低有效位):如为0,表示要进行写操作,为1表示进行读操作。

控制字总是从最低位开始输出。

在控制字指令输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从最低位(0位)开始。

同样,在紧跟8位的控制字指令后的下一个SCLK脉冲的下降沿,读出DS1302的数据,读出的数据也是从最低位到最高位。

DS1302时序:
如图,所示
CE 输入驱动高启动所有的数据传输。

CE 输入有两个功能。

首先,CE 打开控制逻辑,允许访问的移位寄存器的地址
/命令序列。

其次,CE 提供了一个终止单字节或多字节数据传输方法。

一个时钟周期是由一个下降沿之后的上升沿序列。

对于数据传输而言,数据必
须在有效的时钟的上升沿输入,在时钟的下降沿输出。

如果CE 为低,所有的I
/ O 引脚变为高阻抗状态,数据传输终止。

对于数据输入:
开始的8 个SCLK 周期,输入写命令字节,数据字节在后8 个SCLK 周期的
上升沿输入。

数据输入位0 开始。

对于数据输出:
开始的8 个SCLK 周期,输入一个读命令字节,数据字节在后8 个SCLK 周
期的下降沿输出。

注意,第一个数据字节的第一个下降沿发生后,命令字的最
后一位被写入(Note that the first data bit to be transmitted occurs on
the first falling edge after the last bit of the command byte is
written. ),命令字节的最后一位被写入。

当CE 仍为高时。

如果还有额外的
SCLK 周期,DS1302 将重新发送数据字节,这使DS1302 具有连续突发读取的
能力。

DS1302驱动程序分析:
开始---设置CE为高---在上升沿传输地址字---读取或写入相应地址的数字---设置CE为低---结束。

10.8.2.1 1602LCD的基本参数及引脚功能
1602LCD分为带背光和不带背光两种,基控制器大部分为HD44780,带背光的比不带背光的厚,是否带背光在应用中并无差别,两者尺寸差别如下图10-54所示:
图10-54 1602LCD尺寸图
1602LCD主要技术参数:
显示容量:16×2个字符
芯片工作电压:4.5—5.5V
工作电流:2.0mA(5.0V)
模块最佳工作电压:5.0V
字符尺寸:2.95×4.35(W×H)mm
引脚功能说明
1602LCD采用标准的14脚(无背光)或16脚(带背光)接口,各引脚接口说明如表10-13所示:
表10-13:引脚接口说明表
第1脚:VSS为地电源。

第2脚:VDD接5V正电源。

第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。

第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。

当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。

第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

第7~14脚:D0~D7为8位双向数据线。

第15脚:背光源正极。

第16脚:背光源负极。

10.8.2.3 1602LCD的指令说明及时序
1602液晶模块内部的控制器共有11条控制指令,如表10-14所示:
表10-14:控制命令表
1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。

(说明:1为高电平、0为低电平)
指令1:清显示,指令码01H,光标复位到地址00H位置。

指令2:光标复位,光标返回到地址00H。

指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 S:屏幕上所有文字是否左移或者右移。

高电平表示有效,低电平则无效。

指令4:显示开关控制。

D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。

指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标。

指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符。

指令7:字符发生器RAM地址设置。

指令8:DDRAM地址设置。

指令9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。

指令10:写数据。

指令11:读数据。

与HD44780相兼容的芯片时序表如下:
表10-15:基本操作时序表
读写操作时序如图10-55和
10-5
6所示:
图10-55 读操作时序
图10-56 写操作时序
10.8.2.4 1602LCD的RAM地址映射及标准字库表
液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。

要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,图10-57是1602的内部显示地址。

图10-57 1602LCD内部显示地址
例如第二行第一个字符的地址是40H,那么是否直接写入40H就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位D7恒定为高电平1所以实际写入的数据应该是01000000B(40H)+10000000B(80H)=11000000B(C0H)。

在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标是自动右移的,无需人工干预。

每次输入指令前都要判断液晶模块是否处于忙的状态。

1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,如图10-58所示,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”
图10-58 字符代码与图形对应图
10.8.2.5 1602LCD的一般初始化(复位)过程延时15mS
写指令38H(不检测忙信号)
延时5mS
写指令38H(不检测忙信号)
延时5mS
写指令38H(不检测忙信号)
以后每次写指令、读/写数据操作均需要检测忙信号写指令38H:显示模式设置
写指令08H:显示关闭
写指令01H:显示清屏
写指令06H:显示光标移动设置
写指令0CH:显示开及光标设置
液晶1602驱动时序:
开始---LCD 初始化---延时---设置第一行显示位置---显示第一行内容---设置第二行显示位置---显示第二行内容
3. 电路
D 7
D 6D 5D 4D 3D 2D 1D 0
E R W R S V S S V D D V E E
实际电路:
4.程序
#include< reg51.h >
sbit key0=P3^0;//功能键
sbit key1=P3^1;//增大键
sbit key2=P3^2;//减小键
sbit RST=P3^3;
sbit SCLK=P3^4;
sbit SDA=P3^5;
sbit rs=P2^0;
sbit rw=P2^1;
sbit en=P1^7;
#define DS1302_SECOND_WRITE 0x80 #define DS1302_MINUTE_WRITE 0x82 #define DS1302_HOUR_WRITE 0x84 #define DS1302_DAY_WRITE 0x86 #define DS1302_MON_WRITE 0x88
#define DS1302_YEAR_WRITE 0x8c #define DS1302_WEEK_WRITE 0x8a #define DS1302_CURRENT_WRITE 0x90
#define DS1302_SECOND_READ 0x81 #define DS1302_MINUTE_READ 0x83 #define DS1302_HOUR_READ 0x85 #define DS1302_DAY_READ 0x87
#define DS1302_MON_READ 0x89
#define DS1302_YEAR_READ 0x8d
#define DS1302_WEEK_READ 0x8B
#define WRITE_PROTECT 0x8e
#define uchar unsigned char
#define uint unsigned int
uchar sec,min,hour,day,mon,year,week,num,flag;
unsigned char table[]="20 - - ";
unsigned char table1[]=" : : ";
void delayus(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=48;y>0;y--); //这个延时主要是针对1602控制的时间的,如果超过,则1602与1302不同步,如果少了;则1602会少了显示的东西
}
/*****************************/
void write_cmd(uchar com) //写指令
{
rs=0;
rw=0;
P0=com;
delayus(1);
en=1;
delayus(1);
en=0;
}
/********************/
void write_data(uchar dat) //写数据
{
rs=1;
rw=0;
P0=dat;
delayus(1);
en=1;
delayus(1);
en=0;
}
/****************/
void write_sf(uchar add,uchar date) //时分秒函数,前一个是定义位置,后一个则是分离数据
{
uchar ge,shi;
shi=date/10;
ge=date%10;
write_cmd(0x80+add); //设置显示的位置
// delayus(10);
write_data(0x30+shi); //给十位送数据
// delayus(10);
write_data(0x30+ge); //给个位送数据
}
/**********************************/
void lcd_write_week(uchar week)//写液晶星期显示函数{
write_cmd(0x80+12);//设置显示位置
switch(week)
{
case 1: write_data('M');delayus(5);
write_data('O');delayus(5);
write_data('N');break;
case 2: write_data('T');delayus(5);
write_data('U');delayus(5);
write_data('E');break;
case 3: write_data('W');delayus(5);
write_data('E');delayus(5);
write_data('D');break;
case 4: write_data('T');delayus(5);
write_data('H');delayus(5);
write_data('U');break;
case 5: write_data('F');delayus(5);
write_data('R');delayus(5);
write_data('I');break;
case 6: write_data('S');delayus(5);
write_data('A');delayus(5);
write_data('T');break;
case 7: write_data('S');delayus(5);
write_data('U');delayus(5);
write_data('N');break;
}
}
/********************/
void init() //1602初始化{
uchar i;
en=0;
write_cmd(0x38); //显示模式设置write_cmd(0x0c); //开显示,光标不闪烁write_cmd(0x06);
write_cmd(0x01);
write_cmd(0x80);
for(i=0;i<16;i++)
{
write_data(table[i]);
delayus(10);
}
write_cmd(0xc0);
for(i=0;i<10;i++)
{
write_data(table1[i]);
delayus(10); //1602位置的初始化
}
} //以上便是液晶1602的初始化
/***********************************/
uchar DS1302ReadByte() //读一个字节
{
uchar i,dat;
delayus(2);
for(i=0;i<8;i++)
{
dat>>=1;//要返回的数据左移一位
if(SDA==1)//当数据线为高时,证明该位数据为1
dat|=0x80;//要传输数据的当前值置为1,若不是,则为0
SCLK=1;//拉高时钟线
delayus(2);
SCLK=0;//制造下降沿
delayus(2);
}
return dat;//返回读取出的数据
}
/************************************************/
void DS1302WriteByte(uchar dat) //写一个字节
{
uchar i;
SCLK=0;//初始时钟线置为0
delayus(2);
for(i=0;i<8;i++)//开始传输8个字节的数据
{
SDA=dat&0x01;//取最低位,注意DS1302的数据和地址都是从最低位开始传输的delayus(2);
SCLK=1;//时钟线拉高,制造上升沿,SDA的数据被传输
delayus(2);
SCLK=0;//时钟线拉低,为下一个上升沿做准备
dat>>=1;//数据右移一位,准备传输下一位数据
}
}
/********************************/
uchar DS1302Read(uchar cmd) //读命令
{
uchar dat;
RST=0;//初始CE线置为0
SCLK=0;//初始时钟线置为0
RST=1;//初始CE置为1,传输开始
DS1302WriteByte(cmd);//传输命令字,要读取的时间/日历地址
dat=DS1302ReadByte();//读取要得到的时间/日期
SCLK=1;//时钟线拉高
RST=0;//读取结束,CE置为0,结束数据的传输
dat = dat/16*10+dat%16;
return dat;//返回得到的时间/日期
}
/*********** */
void DS1302Write(uchar cmd, uchar dat) //往1302中写数据
{
RST=0; //初始CE线置为0
SCLK=0; //初始时钟线置为0
RST=1; //初始CE置为1,传输开始
DS1302WriteByte(cmd); //传输命令字,要写入的时间/日历地址
DS1302WriteByte(dat); //写入要修改的时间/日期
SCLK=1; //时钟线拉高
RST=0; //读取结束,CE置为0,结束数据的传输
}
/***********************************/
void Init_DS1302() //1302初始化
{
DS1302Write(0x8e,0x00);//写保护关
// DS1302Write(DS1302_SECOND_WRITE,0x56); //初始秒值为0
// DS1302Write(DS1302_MINUTE_WRITE,0x34);//初始分钟值为0
// DS1302Write(DS1302_HOUR_WRITE,0x12); //初始为24小时模式初始时间为0点
// DS1302Write(DS1302_DAY_WRITE,0x10); //2011年1月1日星期6
// DS1302Write(DS1302_MON_WRITE,0x06);
// DS1302Write(DS1302_YEAR_WRITE,0x12);
// DS1302Write(DS1302_WEEK_WRITE,0x06); //这里面主要是初次定时
DS1302Write(0x8e,0x80);
}
/***************************/
void display() //主要是显示程序,从DS1302中读取数值
{
sec=DS1302Read(DS1302_SECOND_READ);
min=DS1302Read(DS1302_MINUTE_READ);
hour=DS1302Read(DS1302_HOUR_READ);
day=DS1302Read(DS1302_DAY_READ);
mon=DS1302Read(DS1302_MON_READ);
year=DS1302Read(DS1302_YEAR_READ);
week=DS1302Read(DS1302_WEEK_READ);
write_sf(8,day);
write_sf(5,mon);
write_sf(2,year);
lcd_write_week(week);
write_sf(0x40+4,hour);
write_sf(0x40+7,min);
write_sf(0x40+10,sec);
}
/***********************************/
void keyscan() //键盘扫描
{
if(key0==0)//确认功能键按下
{
delayus(20);
if(key0==0)//功能键确实按下
{
while(!key0);//释放
num++;//按下次数记录
switch(num)
{
case 1: flag=1;//修改时间,液晶禁止从DS1302读数据
write_cmd(0x0f);//光标开始闪烁
write_cmd(0x80+2);//第一次按下,光标定位到年位置 break;
case 2: write_cmd(0x80+5);//第二次按下,光标定位到月位置
break;
case 3: write_cmd(0x80+8);//第三次按下,光标定位到日位置
break;
case 4: write_cmd(0x80+11);//第四次按下,光标定位到星期位置
break;
case 5: write_cmd(0x80+0x40+4);//第五次按下,光标定位到时位置 break;
case 6: write_cmd(0x80+0x40+7);//第六次按下,光标定位到分位置
break;
case 7: write_cmd(0x80+0x40+10);//第七次按下,光标定位到秒位置
break;
case 8: num=0;//第八次按下,记录清零
write_cmd(0x0c);//不显示光标
DS1302Write(WRITE_PROTECT,0x00);//禁止写保护
DS1302Write(0x80,(sec/10*16)+sec%10);//将调节后的秒写入DS1302
DS1302Write(0x82,(min/10*16)+min%10);//将调节后的分写入DS1302
DS1302Write(0x84,(hour/10*16)+hour%10);//将调节后的时写入DS1302
DS1302Write(0x8a,(week/10*16)+week%10);//将调节后的星期写入DS1302
DS1302Write(0x86,(day/10*16)+day%10);//将调节后的日写入DS1302
DS1302Write(0x88,(mon/10*16)+mon%10);//将调节后的月写入DS1302
DS1302Write(0x8c,(year/10*16)+year%10);//将调节后的年写入DS1302
DS1302Write(WRITE_PROTECT,0x80);//开写保护
flag=0;//时间修改完毕,允许液晶从DS1302读数据
break;
}
}
}
if(num!=0)
{
if(key1==0)//确认增大键按下
{
delayus(20);
if(key1==0)//增大键确实按下
{
while(!key1);
switch(num)
{
case 1: year++;//调节年
if(year==100)year=0;
write_sf(3,year);//将调节后的年送入液晶显示
write_cmd(0x80+3);//光标回到指定处
break;
case 2: mon++;//调节月
if(mon==13)mon=1;
write_sf(6,mon);//将调节后的月送入液晶显示
write_cmd(0x80+6);//光标回到指定处
break;
case 3: day++;//调节日
if(day==32)day=1;
write_sf(9,day);//将调节后的日送入液晶显示
write_cmd(0x80+9);//光标回到指定处
break;
case 4: week++;
if(week==8)week=1;
lcd_write_week(week);//将调节后的星期送入液晶显示
write_cmd(0x80+12);//光标回到指定处
break;
case 5: hour++;
if(hour==24)hour=0;
write_sf(4,hour);//将调节后的小时送入液晶显示
write_cmd(0x80+0x40+4);//光标回到指定处
break;
case 6: min++;
if(min==60)min=0;
write_sf(7,min);//将调节后的分送入液晶显示 write_cmd(0x80+0x40+7);//光标回到指定处
break;
case 7: sec++;
if(sec==60)sec=0;
write_sf(10,sec);//将调节后的秒送入液晶显示
write_cmd(0x80+0x40+10);//光标回到指定处
break;
default:break;
}
}
}
if(key2==0)//确认减小键按下
{
delayus(20);
if(key2==0)//减小键确实按下
{
while(!key2);
switch(num)
{
case 1: year--;//调节年
if(year==-1)year=99;
write_sf(3,year);
write_cmd(0x80+3);
break;
case 2: mon--;//调节月
if(mon==-1)mon=12;
write_sf(6,mon);
write_cmd(0x80+6);
break;
case 3: day--;//调节日
if(day==-1)day=31;
write_sf(9,day);
write_cmd(0x80+9);
break;
case 4: week--;//调节星期
if(week==0)week=7;
lcd_write_week(week);
write_cmd(0x80+12);
break;
case 5: hour--;//调节时
if(hour==-1)hour=23;
write_sf(4,hour);
write_cmd(0x80+0x40+4);
break;
case 6: min--;//调节分
if(min==-1)min=59;
write_sf(7,min);
write_cmd(0x80+0x40+7);
break;
case 7: sec--;//调节秒
if(sec==-1)sec=59;
write_sf(10,sec);
write_cmd(0x80+0x40+10);
break;
default:break;
}
}
}
}
}
/**************************/
void main()
{
init();
Init_DS1302();
while(1)
{
keyscan();
if(flag==0)
{
display();
}
}
}
5.结语
此次学习单片机的两个新的器件,液晶1602和DS1302时钟芯片。

之前看了一些芯片资料并按照上面写的出来很多问题,起初使用数码管写的时钟,因为芯片驱动程序或者是其他的没设置好,导致出现了错误的结果。

因为数码管扫描时时间是很难控制和把握的,所以扫描出现的问题不好解决。

后来学习了DS1302,并在之前学习了1602,所以想让这两者结合起来,之前遇到了很大的阻力。

后来在同学的帮助和老师的提醒下猜将这个程序写了出来。

这个程序还存在一些瑕疵,即使在设置月份的时候没有处理好天数的问题,然后就是在年份的处理上也没弄的很好。

所以要在这个程序上改进并还要认真学习,使之变得完美!
参考文献
[1] 书名:《单片机100例》.
[2] 书名:《单片机的C语言应用程序设计》作者:马忠梅
.出版地:北京航空航天大学出版社
[3] 作者:郭天祥《十天学会单片机》。

相关文档
最新文档