基于单片机的多功能电子钟的实现.docx

上传人:b****5 文档编号:27995318 上传时间:2023-07-07 格式:DOCX 页数:36 大小:257.02KB
下载 相关 举报
基于单片机的多功能电子钟的实现.docx_第1页
第1页 / 共36页
基于单片机的多功能电子钟的实现.docx_第2页
第2页 / 共36页
基于单片机的多功能电子钟的实现.docx_第3页
第3页 / 共36页
基于单片机的多功能电子钟的实现.docx_第4页
第4页 / 共36页
基于单片机的多功能电子钟的实现.docx_第5页
第5页 / 共36页
点击查看更多>>
下载资源
资源描述

基于单片机的多功能电子钟的实现.docx

《基于单片机的多功能电子钟的实现.docx》由会员分享,可在线阅读,更多相关《基于单片机的多功能电子钟的实现.docx(36页珍藏版)》请在冰豆网上搜索。

基于单片机的多功能电子钟的实现.docx

基于单片机的多功能电子钟的实现

基于单片机的多功能电子钟的实现

一.实验设计要求:

1、以DS1302实时时钟芯片和液晶显示屏LCD1602为基础设计一款数字钟。

2、用DS18B20采集温度并实时显示。

3、能够进行时间和日期的调整,并具有闹钟和重要日期提醒功能。

4、具有温度报警功能

二.实验仪器、仪表目录

1、DS1302实时时钟芯片1片

2、LCD1602液晶显示屏1个,

3、AT89C52芯片1片

5、DS18B20芯片一片

6、晶振、电容、电阻、开关各若干等

7、proteus仿真软件

8、KeilC51、PC机

三.实验步骤

1、打开Keil软件,新建一个工程digital_clock,选择芯片AT89C52。

2、将编写的程序保存成“.C”的形式。

3、分别编写DS1302.h、DS18B20.h、LCD1602.h和digital.c文件并编译,直到编译通过。

4、打开proteus软件,画出实验电路图。

5、在AT89C52中,载入KEIL程序生成的HEX文档并运行。

6、对功能部分进行调试,观察运行结果,直到达到预期效果。

四.设计原理、运行结果及分析

(一)设计方案原理与设计特点分析

电子钟总的设计模块:

各个模块电路原理分析:

1、DS1302时钟采集模块:

1.1电路原理图:

1.2DS1302分析:

首先DS1302是DALLAS公司推出的涓流充电时钟芯片。

内含有一个实时时钟/日历和31字节静态RAM通过简单的串行接口与单片机进行通信实时时钟/日历电路提供秒分时日日期月年的信息每月的天数和闰年的天数可自动调整时钟操作。

DS1302芯片广脚介绍:

X1、X2为32.768KHz晶振管脚。

GND为地。

RST复位脚。

I/O数据输入/输出引脚。

SCLK串行时钟。

Vcc1,Vcc2电源供电管脚。

与单片机连接的信号线为:

DS1302_IO接P1^0;实时时钟数据线引脚

DS1302_SCLK接P1^1;实时时钟时钟线引脚

DS1302_RST接P1^2;实时时钟复位线引脚

特别注意DS1302芯片在读取或写入数据时,都是一位一位传送的,并且每传送一位,SCLK信号线要有一个负跳变。

即单片机对SCLK咬先送高电平,再送低电平。

数据是通过IO进行传送的。

1.3数据处理子程序流程图

 

 

 

 

因为DS1302芯片在读取或写入数据时,都是一位一位传送的,并且每传送一位,SCLK信号线要有一个负跳变。

所以在对DS1302具体某地址进行一字节数据的写入或读取时,都要调用实时时钟写入一字节(内部函数)Write_DS1302和实时时钟读取一字节(内部函数)Read_DS1302两个函数。

2、按键处理模块

2.1按键连线图

其中按键1为模式键,按键2为加1键,按键3为减1键。

与单片机连线如下:

mode连接P2.4;//设定修改位数

plus连接P2.5;//加键

dec连接P2.6;//减键

setclock连接P2.7//设定闹钟功能

2.2按键扫描子程序流程图:

否否否否

是是是是是

是是

2.3加减键处理子程序流程图

否否

是是是

否否

是是是

减1子程序与加1子程序区别只在于修改数值处理不一样,其他都一样。

3、LCD1602显示模块

3.1LCD显示模块电路原理图

3.2LCD1602芯片以及连线分析

液晶显示器是一种功耗极低的被动式显示器件,LCD1602广脚介绍:

D0—D7数据传送引脚,VSS为接地线,VDD为电源线,VEE为LCD驱动电压调节,由此可以调节显示亮度。

RS为寄存器选择信号,高电平选择数据寄存器,低电平选择指令寄存器。

RW为读写控制信号,高电平读,低电平写。

EN使能信号,读状态下高电平有效,写状态下下降沿有效。

RS连接P2^0;寄存器选择信号

RW连接P2^1;读写控制信号线

EN连接P2^2;使能信号线

3.3LCD初始化程序流程图:

在初始化过程中,要反复调用到LCD_writeCommand()函数,此函数实现向LCD1602写入命令的功能。

要特别注意写命令和写数据的RS、RW、EN时序问题

4、温度采集显示模块

4.1温度采集显示模块的原理图:

4.2DS18B20芯片以及连线分析

DSl820数字温度计提供9位(二进制)温度读数,指示器件的温度、信息经过单线接口送入DSl820或从DSl820送出。

因此从主机CPU到DSl820仅需一条线(和地线),DSl820的电源可以由数据线本身提供而不需要外部电源。

DS18B20广脚说明:

VCC为电源线,DQ为数据线,GND为地线。

数据线DQ与单片机P2.3相连接。

4.3温度采集显示模块子程序流程图:

 

 

在此程序中,要特别注意初始化,写和读取数据时的时序处理。

首先,初始化中,主机总线先发送一复位脉冲(最短为480us的低电平信号),接着刻释放总线并进入接收状态。

DSl8b20在检测到总线的上升沿之后,等待15-60us,接着DS18b20发出存在脉冲(低电平持续60-240us)。

写数据时序:

当主机总线先从高拉至低电平时,就产生写时间隙。

读书序:

主机总线先从高拉至低电平时,总线只须保持低电平l7ts之后,再将总线拉高,产生读时间隙。

5、总原理图以及主程序流程图:

5.1总的原理图:

5.2主程序流程图:

二、Proteus仿真结果:

三、实验数据计算处理,性能分析

1、数据计算处理:

1.1计算星期数的算法

days=(today.day+1+2*today.month+3*(today.month+1)/5+today.year+today.year/4-today.year/100+today.year/400)%7,由年月日计算星期,用以显示星期数。

1.2加减键对时间日期改变处理算法

对于年月日,时分秒来说,每个变量的最大值和最小值都不一样,所以当年月份时分秒改变时,进行处理的算法也不一样,年没有最大值,所以不用采取“封顶”措施,每次年加一处理时,直接自加,而对于月份来说,月份是不能超过13的,所以当月份自加到13时要重新置1,具体处理见程序,在每个语句后,我都有分析注释出来。

1.3时分秒进位算法

当秒,分达到60时,向分进位,且秒数置零,从新开始计数。

当时达到24时,也一样处理。

1.4年月日进位算法

因为每个月份的天数都不同,所以事先先设定一数组:

dayofmonth[]={31,28,31,30,31,30,31,31,30,31,30,31}用以判定12个月的最大值,接着判断天数,当超过本月最大天数时,月份加一,且天数要置1。

月份判断比较简单,只要超过12,年份就加1,月份置1。

2、性能分析:

首先,lcd能够正确的显示1302芯片上面的时间和日期。

其次,可以通过三个按键:

K1,K2和K3、K4键对电子钟进行时间和日期的调整和设置闹钟功能。

按K1键进行校时,可以分别对时及分进行单独校时,使其校正到标准时间,校时时需要校正哪一位哪一位就闪烁。

按K2键是对闪烁位进行加一的操作。

按K3键是对闪烁位进行减一的操作。

多次按K1键,当全部闪烁位全闪烁完毕时,就可以退出调整模式。

但是,时间和日期显示正确,但温度显示错误。

温度不能正常显示的主要原因是DS18b20的数据传输不正确,不过到现在为止,还没改正过来。

四、实验过程中故障分析与故障排除的描述

故障一:

因为自己的知识有限,所以坦诚的说,源代码都是从网上载的,但是我载了2个程序,一个是运用1602以及18b20和按键处理显示时间以及温度,另一个程序是单运用ds1302和1602显示时间而没有按键处理,所以我做的工作就是这么把这两个程序结合起来,实现显示和调整时间的功能。

这里的故障排除过程:

目的是在有按键调整时间的程序中,要添加DS1302功能。

首先,要在主程序的c文件中,添加#include"DS1302.h"语句,并在main主函数中,加入Initial_DS1302();语句,进行DS1302的初始化,并且添加以下语句:

DS1302_inntial();//初始化DS1302

DateToStr(&CurrentTime);//从DS1302读取日期

TimeToStr(&CurrentTime);//从DS1302读取时间

显示的程序也要有所改变:

DS1302_display_Str(CurrentTime.DateString);//显示日期

此时参数传递过程是先将DS1302内部的时间和日期通过DS1302_getTime函数读取出来存放到Time中,日期通过DateToStr函数将Time中的数据传到DateString数组中,而时间通过TimeToStr函数将Time中的数据传到TimeString数组中,最后通过display_string函数显示出来。

故障二:

通过上面两个函数的合并后,发现编译一直出错,如下图所示:

后来发现,我只是在前面加入头文件"DS1302.h",但是忘记把这个文件放在工程目录下,所以该工程一直找不到,结果一直出错。

故障三:

当修改完程序后,按下按键时,发现什么反应都没有,后来又在排查了程序,感觉程序都没错,但是就是没反应。

偶然之间,发现要长按按键,就可以实现按键调整时间的功能。

因为在按键扫描程序中,设置了软件防抖动功能,所以要长按按键,才能调整时间。

故障四:

温度显示错误。

因为时间有限,前面整合两个程序就花了太多的时间,来不及再调整温度显示模块,自己推测,应该是中间函数参数传递出现错误,在主程序的main函数中,显示温度是display_string("00.0C")语句,所以温度一直显示00.0C,事后有时间会再进行参数调整。

 

五、实验结论以及体会

实验结论:

1、在该电子钟的设计中修改定时或调整时间时采用了闪烁,在编程上,首先进行了初始化定义了程序的入口地址以及中断的入口地址,在主程序的开始定义了一组固定单元用来存储计数的秒,分,时以及定时时间的序号等。

其次,时,分,秒显示用了软件译码(查表)的方式,再用了一段固定的程序段进行进制转化。

最后,用查询方式对按键进行判断,若有键按下,则进行软件延时消抖,避免了抖动引起的干扰,执行相应的定时,选时或调时程序段。

对当前时间或定时时间修改后又返回到最初的显示程序段,如此循环下去。

2、在硬件上,选用DS1302,LCD1602相结合,首先DS1302内含有一个实时时钟/日历和31字节静态RAM通过简单的串行接口与单片机进行通信实时时钟/日历电路提供秒分时日日期月年的信息每月的天数和闰年的天数可自动调整时钟操作,这样读取数据简单。

其次,选用LCD1602进行显示时,数据位串行输入,接口连线少,低功耗,显示清晰。

并且本实验的电子钟即要实现时间的现实,还要实现日期的现实,所以若是运用数码管进行显示的话,就算运用动态显示,所占用的IO口多,并且所需的数码管个数多,硬件复杂。

3、proteus是一个非常好用的仿真软件,其具有强大的电路原理图绘制功能,且可以实现模拟电路仿真、数字电路仿真、单片机及其外围电路组成的系统仿真、键盘、LCD系统仿真等多种功能;和keil联合使用时可以检测所编写的程序的正确与否。

将keil和proteus联合起来使用是实现电子设计制作的初步阶段,可避免在实际的硬件操作中因为电路原理图或向单片机烧录的程序有误而造成的难以修改的为题。

实验心得:

1、通过本次实验,因为之前接触到的电子系统设计不多,所以一开始,感觉难以入手,就算上网载了很多程序,也看不懂。

后来请求同学的帮助,了解了要对各种芯片编写程序时首先应找到该芯片的数据手册,根据数据手册上的说明、时序要求及流程图编写对应程序。

2、其次,再次巩固了KeilC51工程文件的建立,程序编写以及编译的掌握程度。

最重要的是,因为只是水平有限,要自己编写C程序很难,但在此实验中,最大的收获莫过于看懂别人的程序,分析之后,自己拼凑编写以实现不同的功能。

并且掌握了52C程序的编写过程。

3、掌握了Proteus的使用方法,从实际操作中认识到Proteus在仿真方面的优越性,激发了自己学习Proteus的兴趣;

4、因为自己要修改程序,所以单单花费在程序分析的时间就很多,为了更好的理解程序,我把每句主要程序的后面都注释了该语句的意思,详情可以见程序清单,发现注释语义的工作量也是非常大的。

写实验报告时,每个模块的流程图都是自己画的,用WORD文档画图真的很麻烦,而且不是很美观。

因为时间比较仓促,流程图写的条理性不够,不过相信以后多多练习,就可以做得更好。

5、在这次实验中我遇到了很多故障,不过通过各种渠道(比如网络,请教同学,老师等等)解决了一些故障,虽然没有全部解决,但能在短短一周内通过此次作业,实现电子钟的功能,还是有点成就感的。

在解决这些问题的过程中发现网络确实是一个很好的学习平台,利用前人的经验可以提高自己的解决实际问题的能力。

通过这一个多礼拜的学习实践,使我对所学的知识进行了系统的复习和巩固,在以前学习中不够清晰的概念得到了更好的理解。

相信通过不断的学习,能使自己扬长补短。

 

六、程序清单

1.在KEIL中包含的函数与文件

2.主要程序代码

2.1digital_clock.文件

#include

#include

#include

#include

#include

#defineuncharunsignedchar

#defineunintunsignedint

voidmain()

{

SystemTimecurrent_time;

DS1302_initial();

LCD_initial();

DS18B20_initial();

//LCD_display_str(0,0,"DATE:

");

//LCD_display_str(1,0,"TIME:

");

Ds18B20_readyRead();

while

(1)

{

DS18B20_TO_LCD1602();

DS1302_TO_LCD1602(current_time);

}

}

2.2DS1302.h文件

#ifndef__DS1302__

#define__DS1302__

#include

//宏定义

//#defineuncharunsignedchar//在LCD1602中已定义

//#defineunintunsignedint

#defineWrite_second0x80

#defineWrite_minute0x82

#defineWrite_hour0x84

#defineWrite_day0x86

#defineWrite_month0x88

#defineWrite_week0x8A

#defineWrite_year0x8C

#defineRead_second0x81

#defineRead_minute0x83

#defineRead_hour0x85

#defineRead_day0x87

#defineRead_month0x89

#defineRead_week0x8B

#defineRead_year0x8D

#defineWrite_protect0x8E

//管脚定义

sbitDS1302_DATA=P1^0;//DS1302数据信号

sbitDS1302_CLK=P1^1;//DS1302时钟信号

sbitDS1302_PST=P1^2;//片选与复位信号

typedefstruct_SysTime_

{

uncharSecond;

uncharMinute;

uncharHour;

uncharDay;

uncharWeek;

uncharMonth;

uncharYear;

uncharDateStr[9];

uncharTimeStr[9];

}SystemTime;//定义时间类型

/*************定义子函数************/

//voiddelay_ms(unintt);//延迟函数

voidWrite_DS1302(uncharaddress,unchardat);//写地址与写数据

uncharRead_DS1302(uncharaddress);//读数据

voidDS1302_setProtect(bitflag);//是否写保护

voidDS1302_setTime(uncharaddress,uncharvalue);//设置时间

voidDS1302_getTime(SystemTime*time);//获取时间

voidDS1302_ToStr(SystemTime*time);//转换成可供LCD显示的子程序

voidDS1302_initial(void);//初始化

voidDS1302_TO_LCD1602(SystemTimetime);//DS1302时间在LCD1602中显示

/************延迟函数************/

/*voidDS1302_delayms(unintt)

{uninti,j;

for(i=t;i>0;i--)

for(j=112;j>0;j--);

}

*/

/**********DS1302写地址与写数据函数***********/

voidWrite_DS1302(uncharaddress,unchardat)

{

unchartemp,i;

DS1302_PST=0;//引脚拉低,数据传送中止

DS1302_CLK=0;//引脚拉低,为CLK上升沿写入数据作准备

DS1302_PST=1;//引脚拉高,逻辑控制有效

//写地址

for(i=8;i>0;i--)//连续发送8个二进制位数据,在个CLK的上升沿写入地址与数据

{

DS1302_CLK=0;

temp=address;

DS1302_DATA=(bit)(temp&0x01);//每次传送最低位到P1^0端口

address>>=1;//右移一位

DS1302_CLK=1;

}

//写数据

for(i=8;i>0;i--)

{

DS1302_CLK=0;

temp=dat;

DS1302_DATA=(bit)(temp&0x01);

dat>>=1;

DS1302_CLK=1;

}

DS1302_PST=0;

}

/**********读DS1302某个寄存器中的数据函数*********/

uncharRead_DS1302(uncharaddress)

{

unchartemp,i,data1,data2;

unchardata0=0x00;

DS1302_PST=0;

DS1302_CLK=0;

DS1302_PST=1;

//选中要读取的寄存器,与写地址相同

for(i=8;i>0;i--)

{

DS1302_CLK=0;

temp=address;

DS1302_DATA=(bit)(temp&0x01);

address>>=1;

DS1302_CLK=1;

}

//读数据在每个CLK的下降沿

for(i=8;i>0;i--)//先读最低位

{

if(DS1302_DATA)

data0|=0x80;

DS1302_CLK=1;

data0>>=1;

DS1302_CLK=0;

}

DS1302_PST=1;

data1=data0;

data2=data1/16;//十六进制转十进制

data1=data1%16;

data1=data1+data2*10;

return(data1);

}

/**********是否写保护函数************/

voidDS1302_setProtect(bitflag)

{

if(flag)

{

Write_DS1302(Write_protect,0x80);//0x80控制字节地址,若MSB为1则禁止数据写入DS1302

}

else

Write_DS1302(Write_protect,0x00);

}

/****************设置时间函数******************/

/*voidDS1302_setTime(uncharaddress,uncharvalue)

{

DS1302_setProtect(0);

Write_DS1302(address,((value/10)<<4|(value%10)));//将value的十位取出放到高四位,个位放到低四位

}

*/

/***************获取时间函数********************/

voidDS1302_getTime(SystemTime*time)

{

uncharreadValue;

readValue=Read_DS1302(Read_second);

time->Second=((readValue&0x70)>>4)*10+(readValue&0x0F);//将readValue中的十位取出放到高四位,然后与低四位相加

readValue=Read_DS1302(Read_minute);

time->Minute=((readValue&0x70)>>4)*10+(readValue&0x0F);

readValue=Read_DS1302(Read_hour);

time->Hour=((readValue&0x70)>>4)*10+(readValue&0x0F);

readValue=Read_DS1302(Read_day);

time->Day=((readValue&0x70)>>4)*10+(readValue&0x0F);

readValue=Read_DS1302(Read_week);

time->Week=((readValue&0x70)>>4)*10+(readValue&0x0F);

readValue=Read_DS1302(Read_month);

time->Month=((readValue&0x70)>>4)*10+(readValue&0x0F);

readValue=Read_DS1302(Read_year);

time->Year=((readValue&0x70)>>4)*10+(readValue&0x0F);

}

/******

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 英语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1