基于单片机AT89C51实时时钟设计.docx
《基于单片机AT89C51实时时钟设计.docx》由会员分享,可在线阅读,更多相关《基于单片机AT89C51实时时钟设计.docx(49页珍藏版)》请在冰豆网上搜索。
![基于单片机AT89C51实时时钟设计.docx](https://file1.bdocx.com/fileroot1/2023-8/13/2a2af78a-3510-42b2-b7d5-aabe0901dc51/2a2af78a-3510-42b2-b7d5-aabe0901dc511.gif)
基于单片机AT89C51实时时钟设计
基于单片机AT89C51
----实时时钟设计
学号
姓名
一.系统分析
本电路是由AT89C51单片机为控制核心,具有在线编程功能,低功耗,能在4.5V超低压工作;显示部分用LCD1602实现,能够同时显示16x02即32个字符;闹钟部分由蜂鸣器构成,蜂鸣器是一种一体化结构的电子讯响器;时钟电路由DS1302提供,它是一种高性能、低功耗、带RAM的实时时钟电路,可以对年、月、日、星期、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V.采用三线接口与单片机进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。
DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。
可产生年、月、日、周日、时、分、秒,具有使用寿命长,精度高和低功耗等特点,同时具有掉电自动保存功能,本设计中为DS1302增加了备用电池,当主电源掉电时,备用电池自动为其供电;温度的采集由DS18B20构成;当闹铃时间到时,通过蜂鸣器播放闹铃铃声。
二.系统功能框图的设计
三.系统硬件设计本设计采用的元器件如下:
单片机AT89C51
LM016L
DS1302
晶振CRYSTAL
DS18B20
24C02C
电容CAP
电解电容CAP-ELEC
电阻RES
排阻RESPACK-8
按键BUTTON
四.系统软件设计(流程图和软件设计)
主程序流程图
时间调整程序流程图
定时闹铃程序流程图
C程序:
#include
#include"intrins.h"
#include"24c02.h"
#defineucharunsignedchar
#defineuintunsignedint
//闹钟显示缓冲区
codeucharalarm_one[]="Hello,L.WHou";
codeucharalarm_two[]="GOODMORNINGHLW";
codeucharw1[]={0x01,0x03,0x1D,0x11,0x1D,0x03,0x01,0x00};
codeuintcyc[]={1800,1600,1440,1351,1200,1079,960};//音阶1-7的半周期数
codeuchartone[]={13,15,16,16,15,16,13,12,12,13,15,16,16,15,16,13,13,13,15,16,
16,15,16,13,12,12,15,13,12,13,12,11,12,6,6,12,15,13,12,6,6,15,13,12,13,12,11,
12,6,5,6,0xff};//乐曲《康定情歌》的简谱表
codeuchartime[]={8,8,8,4,4,8,8,12,4,8,8,8,4,4,8,16,8,8,8,8,4,4,8,8,12,4,8,8,4,
4,4,4,8,24,8,24,8,24,8,16,8,8,8,4,4,4,4,8,16,8,32};//节拍表
ucharH0,L0,cnt;
ucharcodetabe1[]="20--";//液晶一直显示的字符
ucharcodetabe2[]=":
:
";
ucharcodetabe3[]="Alarmset:
";
ucharmiao,fen,shi,nian,yue,ri,week,wendu_shi,wendu_ge,wendu_shu,variate,alarm_shi,alarm_fen,alarm_miao;
ucharflag=1,count=0,flag_up=0,flag_down=0,flag_alarm=0,alarm_on,alarm_count=0,done=0;
uintwendu;
//定义IO
sbitDQ=P1^0;//ds18b20温度传送数据IO口
sbitds1302_rst=P1^1;//1302复位
sbitds1302_io=P1^2;//数据输入输出
sbitds1302_sclk=P1^3;//串行时钟
sbitalarm_out=P3^6;
sbitlcd1602_rs=P2^0;//1602命令、数据选择
sbitlcd1602_rw=P2^1;//1602写
sbitlcd1602_e=P2^2;//1602使能
sbitset=P2^4;//选择按键
sbitup=P2^5;//加
sbitdown=P2^6;//减
sbitnao=P2^7;
sbitACC0=ACC^0;
sbitACC7=ACC^7;
sbitMODE=P3^2;
//延时
voiddelay(ucharx)
{
uchary,z;
for(z=x;z>0;z--)
for(y=110;y>0;y--);
}
voiddelay1()
{
_nop_();
_nop_();
}
voiddelay2(uintms)
{
uinti,j;
for(j=0;jfor(i=0;i<120;i++);
}
ds18b20_delay(ucharxus)//延时xus
{
while(xus--);
return0;
}
/**********************ds18b20***********************/
voidds18b20_init()//DS18B20初始化
{
ucharx=0;
DQ=1;
ds18b20_delay(8);
DQ=0;
ds18b20_delay(80);
DQ=1;
ds18b20_delay(14);
x=DQ;
ds18b20_delay(20);
}
voidwrite_onechar(uchardat)//写一字节
{
uchari;
for(i=8;i>0;i--)
{
DQ=0;
if(dat&0x01==0x01)
DQ=1;
else
DQ=0;
ds18b20_delay(5);
DQ=1;
ds18b20_delay
(1);
dat>>=1;
}
}
ucharread_onechar()//读一字节
{
ucharvalue=0,i;
for(i=8;i>0;i--)
{
value>>=1;
DQ=0;
ds18b20_delay
(1);
DQ=1;
if(DQ==1)
value|=0x80;
else
value&=0x7f;
ds18b20_delay(4);
}
returnvalue;
}
uintread_wendu()//DS18B20读温度
{
uchara=0,b=0;
uintwen=0x0000;
ds18b20_init();//初始化
write_onechar(0xcc);//跳过读序号列号的操作
write_onechar(0x44);//启动温度转换
ds18b20_delay(150);
ds18b20_init();//初始化
write_onechar(0xcc);//跳过读序号列号的操作
write_onechar(0xbe);//读取温度寄存器等(共可读9个寄存器)前两个就是温度
ds18b20_delay(150);
b=read_onechar();//读取温度值低位
a=read_onechar();//读取温度值高位
wen=a;
wen=(wen<<8)|b;
returnwen;
}
/**********************ds1302***************************/
voidwrite_byte(uchardat)//写一字节
{
uchark;
ACC=dat;
for(k=8;k>0;k--)
{
ds1302_io=ACC0;
ds1302_sclk=0;//拉低sclk
ds1302_sclk=1;//拉高sclk
ACC>>=1;
}
}
ucharread_byte()
{
uchark;
for(k=8;k>0;k--)
{
ACC7=ds1302_io;
ds1302_sclk=1;//拉高sclk
ds1302_sclk=0;//拉低sclk
ACC>>=1;
}
returnACC;
}
voidds1302_write(ucharadd,uchardate)
{
ds1302_rst=0;
ds1302_sclk=0;//在rst没拉高之前先把sclk置0
ds1302_rst=1;
write_byte(add);
write_byte(date);
ds1302_sclk=1;
ds1302_rst=0;
}
uchards1302_read(ucharadd)
{
uchartemp;
ds1302_rst=0;
ds1302_sclk=0;//在rst没拉高之前先把sclk置0
ds1302_rst=1;
write_byte(add);
temp=read_byte();
ds1302_sclk=1;
ds1302_rst=0;
returntemp;
}
voidds1302_init()//ds1302初始化
{
ds1302_rst=0;
ds1302_sclk=1;
ds1302_write(0x8e,0x00);//写允许
ds1302_write(0x80,0x58);
ds1302_write(0x82,0x56);
ds1302_write(0x84,0x09);
ds1302_write(0x86,0x12);
ds1302_write(0x88,0x05);
ds1302_write(0x8a,0x05);
ds1302_write(0x8c,0x12);
ds1302_write(0x8e,0x80);//写保护
}
/***************************lcd1602***************************/
voidlcd_write_com(ucharcom)//命令字写入lcd单元中
{
lcd1602_rw=0;
delay1();
lcd1602_rs=0;//RW=1,RS=0,写LCD命令字
delay1();
P0=com;//将com中的命令字写入LCD数据口
delay1();
lcd1602_e=1;//E端时序
delay1();
lcd1602_e=0;
delay1();
lcd1602_rw=1;
delay(5);
}
voidlcd_write_date(uchardate)//数据写入lcd单元中
{
lcd1602_rw=0;
delay1();
lcd1602_rs=1;//RW=0,RS=1,写LCD命令字
delay1();
lcd1602_e=1;//E端时序
delay1();
P0=date;//将dat中的显示数据写入LCD数据口
delay1();
lcd1602_e=0;
delay1();
lcd1602_rw=1;
delay(5);
}
voidlcd1602_init()//LCD初始化函数
{
ucharlcdnum;
P3=0xdf;
lcd_write_com(0x38);
lcd_write_com(0x0c);
lcd_write_com(0x06);
lcd_write_com(0x01);
lcd_write_com(0x80);
for(lcdnum=0;lcdnum<8;lcdnum++)//写第一行数据
{
lcd_write_date(tabe1[lcdnum]);
delay
(2);
}
lcd_write_com(0x80+0x40);//写入第二行数据
for(lcdnum=0;lcdnum<13;lcdnum++)
{
lcd_write_date(tabe2[lcdnum]);
delay
(2);
}
}
/****************************显示部分*****************************/
voidwrite_time1(ucharadd,uchardate)//日期送显示
{
ucharshi,ge;
shi=date/10;
ge=date%10;
lcd_write_com(0x80+add);
lcd_write_date(0x30+shi);
lcd_write_date(0x30+ge);
}
voidwrite_time2(ucharadd,uchardate)//时间送显示
{
ucharshi,ge;
shi=date/10;
ge=date%10;
lcd_write_com(0x80+0x40+add);
lcd_write_date(0x30+shi);
lcd_write_date(0x30+ge);
}
ucharbcd_decimal(ucharbcd)//bcd转换十进制
{
uchardecimal;
decimal=bcd>>4;
decimal=decimal*10+(bcd&=0x0f);
returndecimal;
}
voidwendu_decimal(uintdat)//温度转换后送显示
{
floattemp;
temp=dat*0.625;//dat*0.0625*10
wendu_shi=temp/100;//取十位temp除不用定义类型
wendu_ge=((uint)temp)%100/10;//取个位temp求模要定义类型,为
//16位,故定义uint
wendu_shu=((uint)temp)%100%10;//取小数
lcd_write_com(0x80+0x40+10);//送显示
lcd_write_date(0x30+wendu_shi);
lcd_write_date(0x30+wendu_ge);
lcd_write_date(0x2e);
lcd_write_date(0x30+wendu_shu);
lcd_write_date(0xdf);//温度符号
lcd_write_date(0x43);
}
voidwrite_week(ucharwe)//星期送显示
{
lcd_write_com(0x80+0x0d);
switch(we)
{
case1:
lcd_write_date('M');
lcd_write_date('O');
lcd_write_date('N');
break;
case2:
lcd_write_date('T');
lcd_write_date('U');
lcd_write_date('E');
break;
case3:
lcd_write_date('W');
lcd_write_date('E');
lcd_write_date('D');
break;
case4:
lcd_write_date('T');
lcd_write_date('H');
lcd_write_date('U');
break;
case5:
lcd_write_date('F');
lcd_write_date('R');
lcd_write_date('I');
break;
case6:
lcd_write_date('S');
lcd_write_date('A');
lcd_write_date('T');
break;
case7:
lcd_write_date('S');
lcd_write_date('U');
lcd_write_date('N');
break;
}
}
voidlcd_write_com1(ucharcom,bitrs)//命令字写入lcd单元中
{
lcd1602_rs=rs;
lcd1602_rw=0;//RW=1,RS=0,写LCD命令字
P0=com;
delay1();
delay1();//将com中的命令字写入LCD数据口
lcd1602_e=0;
delay1();
delay1();
lcd1602_e=1;//E端时序
delay1();
delay1();
lcd1602_e=0;
delay1();
delay1();
}
/***************LCD1602闹钟显示函数******************/
voiddisplay_1602_alarm()
{
uchari,j,k;
P3=0xdf;
lcd_write_com(0x38);
lcd_write_com(0x0c);
lcd_write_com(0x06);
lcd_write_com(0x01);
lcd_write_com(0x80);//液晶显示位置
delay2(5);
for(i=0;i{
lcd_write_date(alarm_one[i]);
delay2(5);
}
lcd_write_com1(0x40,0);
lcd_write_com1(0x40,0);
for(j=0;j<8;j++)
lcd_write_com1(w1[j],1);
lcd_write_com1(0x80+0x0f,0);
lcd_write_com1(0x00,1);
lcd_write_com(0x80+0x40);//液晶显示位置
delay2(5);
for(k=0;k{
lcd_write_date(alarm_two[k]);
delay2(5);
}
}
/***********************按键处理***************************/
ucharkey_bcd(ucharkey_decimal)//转成ds1302所需的BCD码
{
uchartemp;
temp=(((key_decimal/10)&0x0f)<<4)|(key_decimal%10);
returntemp;
}
voidkey_up_down()//加减键处理
{
if(up==0)
{
delay
(2);
flag_up=1;//加更新标志
while(!
up);
switch(count)
{
case1:
miao++;
if(miao>59)
miao=0;
break;
case2:
fen++;
if(fen>59)
fen=0;
break;
case3:
shi++;
if(shi>23)
shi=0;
break;
case4:
week++;
if(week>7)
week=1;
break;
case5:
ri++;
if(ri>31)
ri=1;
break;
case6:
yue++;
if(yue>12)
yue=1;
break;
case7:
nian++;
if(nian>99)
nian=0;
break;
}
}
if(down==0)//减键处理
{
delay
(2);
flag_down=1;//减更新标志
while(!
down);
switch(count)
{
case1:
miao--;
if(miao==255)
miao=59;
break;
case2:
fen--;
if(fen==255)
fen=59;
break;
case3:
shi--;
if(shi==255)
shi=23;
break;
case4:
week--;
if(week<1)
week=7;
break;
case5:
ri--;
if(ri<1)
ri=31;
break;
case6:
yue--;
if(yue<1)
yue=12;
break;
case7:
nian--;
if(nian==255)
nian=99;
break;
}
}
}
voidc02_init()
{
c_init();
alarm_shi=c02_read_add
(1);//读取闹钟时
delay(200);
alarm_fen=c02_read_add
(2);//分
delay(200);
alarm_miao=c02_read_add(3);//秒
delay(200);
alarm_on=c02_read_add(4);//读取闹钟开关值,为0关,为1开
delay(200);
}
voidalarm_huan()//按下闹钟键后,切换界面
{
ucharnum;
lcd_write_com(0x01);
lcd_write_com(0x80);
for(num=0;num<10;num++)//写第一行数据
{
lcd_write_date(tabe3[num]);
delay
(2);
}
lcd_write_com(0x0f);
write_time2(4,alarm_shi);
lcd_write_date(0x3a);
write_time2(7,alarm_fen);
lcd_write_date(0x3a);
write_time2(10,alarm_miao);
if(alarm_on==0)
{
lcd_write_com(0x80+0x40+13);
lcd_write_date('O');
lcd_write_date('F');
lcd_write_date('F');
}
if(alarm_on==1)
{
lcd_write_com(0x80+0x40+13);
lcd_write_date('');
lc