基于51单片机的ds1302与LM016L液晶时钟设计
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于51单片机的时钟设计
一.基本功能
利用AT89c51作为主控器与时钟芯片DS1302组成一个时钟系统,并由LM016L液晶显示。
二.硬件设计
图1.总设计图
1.单片机最小系统
1.1选用AT89C51的引脚功能
图2. AT89C51
XTAL1:单芯片系统时钟的反向放大器输入端。
XTAL2:系统时钟的反向放大器输出端,一般在设计上只要在XTAL1和XTAL2上接上一只石英震荡晶体系统就可以工作了,此外可以在两引脚与地之间加入20PF的小电容,可以使系统更稳定,避免噪音干扰而死机。
RESET:重置引脚,高电平动作,当要对晶体重置时,只要对此引脚电平提升至高电平并保持两个及其周期以上的时间便能完成系统重置的各项动作,使得内部特殊功能寄存器内容均被设成已知状态。
1.2复位电路
如图所示,当按下按键时,就能完成整个系统的复位,使得程序从新运行。
图3.复位电路
1.3时钟电路
时钟电路用于产生单片机工作所需要的时钟信号,单片机本身就是一个复杂的同步时序电路,为了保证同步工作方式的实现,电路应在唯一的时钟信号控制下严格地按时序进行工作。
在AT89C51芯片内部有一个高增益反相放大器,其输入端为芯片引脚X1,输出端为引脚X2,在芯片的外部跨接晶体振荡器和微调电容,形成反馈电路,就构成了一个稳定的自激振荡器。
此电路采用12MHz的石英晶体。
图4.时钟电路
2.时钟芯片部分
2.1 DS1302引脚说明
图.5
1)Vcc1:后备电源,VCC2:主电源。
在主电源关闭的情况下,也能保持时钟的连续运行。
DS1302由Vcc1或Vcc2两者中的较大者供电。
当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。
当Vcc2小于Vcc1时,DS1302由Vcc1供电。
2)X1、X2:振荡源,外接32.768kHz晶振。
3)RST:复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。
RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST 提供终止单字节或多字节数据的传送手段。
当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。
如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。
上电运行时,在Vcc>2.0V之前,RST必须保持低电平。
只有在SCLK
为低电平时,才能将RST置为高电平。
4)I/O为串行数据输入输出端(双向)。
5)SCLK为时钟输入端。
2.2 时钟电路
图.6
3.LM016L液晶
图7.LM016L引脚图LM016L与1602使用方法相同。
引脚说明:
引脚符号功能说明
1 VSS 一般接地
2 VDD 接电源(+5V)
3 V0 液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
4 RS RS为寄存器选择,高电平1时选择数据寄存器、低电平0时
选择指令寄存器。
5 R/W R/W为读写信号线,高电平(1)时进行读操作,低电平(0)时进
行写操作。
6 E E(或EN)端为使能(enable)端,下降沿使能。
7 DB0 底4位三态、双向数据总线0位(最低位)
8 DB1 底4位三态、双向数据总线1位
9 DB2 底4位三态、双向数据总线2位
10 DB3 底4位三态、双向数据总线3位
11 DB4 高4位三态、双向数据总线4位
12 DB5 高4位三态、双向数据总线5位
13 DB6 高4位三态、双向数据总线6位
14 DB7 高4位三态、双向数据总线7位(最高位)(也是busy flang)电路接口说明:
AT889C51 的P0口作为数据的输出端,P3.5和P3.6, P3.7 作为控制
信号的输出端。
三.软件设计
3.1编程语言及编程软件的选择
本设计选择C语言作为编程语言。
C语言虽然执行效率没有汇编语言高,但语言简洁,使用方便,灵活,运算丰富,表达化类型多样化,
数据结构类型丰富,具有结构化的控制语句,程序设计自由度大,有很
好的可重用性,可移植性等特点。
而汇编语言使用起来并没有这么方便。
本设计选用了Keil作为编程软件,.Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。
在开发大型软件时更能体现高级
语言的优势。
3.2时间的显示
ML016L和1602一样,都是两行十六列的液晶显示屏。
通过程序对ML016L 进行初始化后,通过写指令和写数据的操作就可以再液晶屏上显示出对应的数据,而时钟数据则是从DS1302获取。
这样可以显示出时间。
四.程序
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit rs=P2^0; //1602命令\数据选择端
sbit e=P2^1; //1602使能端
sbit sck=P3^6; //DS1302
sbit io=P3^7; //DS1302数据端
sbit rst=P3^5; //DS1302 CE端RST的功能
uint temp; //定义整型的温度数据
float f_temp;
uchar code num[]="0123456789"; // 定义浮点型的温度数据
uchar code nian[]="20 - - ";
uchar code shi[]=" : : ";
uchar read_add[]={0x8d,0x8b,0x89,0x87,0x85,0x83,0x81}; //DS1302读数据地址端
uchar write_add[]={0x8c,0x8a,0x88,0x86,0x84,0x82,0x80}; //DS1302写数据地址端
uchar time_data[]={11,1,12,5,18,00,00}; //设置初始时间uchar disp[14]; //定义数组
void delayms(uint z) //毫秒级延时函数
{。
uint i,j;
for(i=z;i>0;i--)
for(j=120;j>0;j--);
}
void delayus(uint time) //微秒级延时函数{
while(time--);
}
//1602液晶
void write_com(uchar com) //写指令函数{
rs=0;
P0=com;
delayms(5);
e=1;
delayms(5);
e=0;
}
void write_data(uchar date) //写数据函数{
rs=1;
P0=date;。
delayms(5);
e=1;
delayms(5);
e=0;
}
void init() //液晶初始化函数{
uchar num;
write_com(0x38);
write_com(0x06);
write_com(0x0c);
write_com(0x01);
write_com(0x80+0x40);
for(num=0;num<8;num++)
{
write_data(shi[num]);
delayms(1);
}
write_com(0x80);。
for(num=0;num<10;num++)
{
write_data(nian[num]);
delayms(1);
}
}
//DS1302
void write_ds1302_byte(uchar dat) //写一个字节函数{
uchar i;
for(i=0;i<8;i++)
{
sck=0;
io=dat&0x01;
dat=dat>>1;
sck=1;
}
}
void write_ds1302(uchar add,uchar dat) //写一个地址和数据函数{
rst=0;
_nop_();。
sck=0;
_nop_();
rst=1;
_nop_();
write_ds1302_byte(add);
write_ds1302_byte(dat);
rst=0;
_nop_();
io=1;
sck=1;
}
uchar read_ds1302(uchar add) //读地址函数{
uchar i,value;
rst=0;
_nop_();
sck=0;
_nop_();
rst=1;
_nop_();
write_ds1302_byte(add);
for(i=0;i<8;i++)
{
value=value>>1;
sck=0;
if(io)
value=value|0x80;
sck=1;
}
rst=0;
_nop_();
sck=0;
_nop_();
sck=1;
io=0;
return value;
}
void read_rtc(void) //读时钟函数{
uchar i;
for(i=0;i<7;i++)
{
time_data[i]=read_ds1302(read_add[i]);
}
}
void z(void) //周{
write_com(0x80+0x40+9);
switch(disp[2])
{
case 1: write_data('M');
delayms(5);
write_data('o');
delayms(5);
write_data('n');
break;
case 2: write_data('T');
delayms(5);
write_data('u');
delayms(5);
write_data('e');
break;
case 3: write_data('W');
delayms(5);
write_data('e');
delayms(5);
break;
case 4: write_data('T');
delayms(5);
write_data('h');
delayms(5);
write_data('u');
break;
case 5: write_data('F');
delayms(5);
write_data('r');
delayms(5);
write_data('i');
break;
case 6: write_data('S');
delayms(5);
write_data('a');
delayms(5);
write_data('t');
break;
case 7: write_data('S');
delayms(5);
delayms(5);
write_data('n');
break;
}
}
void time_change(void) //时钟处理函数{
disp[0]=time_data[0]%16; //年
disp[1]=time_data[0]/16;
disp[2]=time_data[1]%16; //周
disp[3]=time_data[1]/16;
disp[4]=time_data[2]%16; //月
disp[5]=time_data[2]/16;
disp[6]=time_data[3]%16; //日
disp[7]=time_data[3]/16;
disp[8]=time_data[4]%16; //时
disp[9]=time_data[4]/16;
disp[10]=time_data[5]%16; //分
disp[11]=time_data[5]/16;
disp[12]=time_data[6]%16; //秒
disp[13]=time_data[6]/16;
}
void display() //时钟显示函数{
write_com(0x80+2); //年
write_data(0x30+disp[1]);
write_data(0x30+disp[0]);
z(); //周
write_com(0x80+5); //月
write_data(0x30+disp[5]);
write_data(0x30+disp[4]);
write_com(0x80+8); //日
write_data(0x30+disp[7]);
write_data(0x30+disp[6]);
write_com(0x80+0x40); //时
write_data(0x30+disp[9]);
write_data(0x30+disp[8]);
write_com(0x80+0x40+3); //分
write_data(0x30+disp[11]);
write_data(0x30+disp[10]);
write_com(0x80+0x40+6); //秒
write_data(0x30+disp[13]);
write_data(0x30+disp[12]);
/*write_com(0x80+0x40+9);
write_data(0x30+disp[2]);*/ }
void main()
{
uchar i;
init();
write_com(0x80+14);
write_data('.');
//set_rtc();
while(1)
{
read_rtc();
display();
}
}。