显示可调闹钟万年历设计.docx
《显示可调闹钟万年历设计.docx》由会员分享,可在线阅读,更多相关《显示可调闹钟万年历设计.docx(33页珍藏版)》请在冰豆网上搜索。
显示可调闹钟万年历设计
带温度显示可调闹钟万年历
摘要
本设计由数据显示模块、温度采集模块、时间处理模块和调整设置模块四个模块组成。
系统最大的特点是体现了较强的人机交互和独立的模块化程序设计。
温度采集选用DS18B20芯片,数据显示采用1602A液晶显示模块,在第一行显示年月日、星期以及当前的状态,第二行显示温度和时间,合理的利用液晶显示区域。
51主芯片利用定时中断产生时间,控制着液晶的显示更新、温度的实时变化以及按键的读取处理,而对于闹钟,实际上就是时间里的一个嵌套程序。
时间和闹钟的值由按键调整设置,采用通用的二十四小时制。
该电路采用51单片机作为核心,功耗小,能在3V的低压工作,电压可选用3~5V电压供电。
综上所述此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。
关键字:
万年历;温度计;闹钟;液晶显示
一、方案设计与论证
根据要求,系统分为四个模块进行方案设计:
1.数据显示模块
方案一:
数据采集处理后采用六位数码管动态扫描,循环依次显示年月日(如09.01.01)、当前时间(如12.00.00)和温度星期(如+23.5_1),数码管用74LS164芯片驱动,硬件电路复杂且显示略显混乱,在软件方面,扫描部分由于要显示的数据太多而显的不清晰。
方案二:
考虑到要显示的内容颇多,故运用1602A显示实时数据,第一行显示状态以及年月日星期(如S2009—01—01THU),第二行显示温度和实时时间(22.0C12:
00:
00),在处理按键设置时,第二行暂时屏蔽温度的显示而显示设置的内容。
这样虽然在程序方面多了1602A的一些初始化和读写子函数的定义,但程序的模块化却更加的清楚。
而且采用1602ALCM的液晶显示模块后不仅满足了大量数据的显示,,系统的硬件电路变的十分简单清晰明朗。
本设计采用了这种方案。
2温度采集模块
采用常用的温度采集芯片DS18B20单线数字温度传感器进行温度的采集。
DS18B20的微型化、低功耗、抗干扰能力强以及仅需一根数据线的简洁电路设计很适合本设计。
但对其的电路连接却有不同的想法。
方案一:
在寄生电源供电方式下,DS18B20从单线信号线上汲取能量,在信号线DQ处于高电平期间把能量储存在内部电容里,在信号线处于低电平期间消耗电容上的电能工作,直到高电平到来再给寄生电源(电容)充电。
独特的寄生电源方式虽不再需要本地电源,但要想使DS18B20进行精确的温度转换,I/O线必须保证在温度转换期间提供足够的能量,工作电源VCC必须保证在5V,当电源电压下降时,寄生电源能够汲取的能量也降低,会使温度误差变大。
方案二:
DS18B20的外部电源供电方式 在外部电源供电方式下,DS18B20工作电源由VDD引脚接入,此时I/O线不需要强上拉,不存在电源电流不足的问题,可以保证转换精度,外部电源供电方式是DS18B20最佳的工作方式,工作稳定可靠,抗干扰能力强,而且电路也比较简单。
在外接电源方式下,可以充分发挥DS18B20宽电源电压范围的优点,即使电源电压VCC降到3V时,依然能够保证温度量精度。
外接电源解决了电压方面的苛刻要求,虽多接了一根线,但对于本设计的近距离测试来说还是很适合的。
本设计采用此接法。
3时间处理模块
这是本设计再方案采取时争议最大的部分,两个方案在闹钟的处理方面都是一样先比较再调用中断给蜂鸣器高电平,使其发声,蜂鸣器的电路采用常规的电路接法,用一个PNP三极管放大。
但问题的焦点在于是采用时钟芯片DS1302还利用51自身的定时器生成万年历。
方案一:
DS1302慢速充电时钟芯片包括实时时钟/日历和31字节的静态RAM,它经过一个简单的串行接口与微处理器通信,实时时钟/日历提供秒、分、时、日、周、月和年等信息。
采用DS1302生成时间,大大简化了程序,且电路不是很复杂,在断电的情况下可由备份电源供电,消耗小于一微瓦的功率便能保存数据和时钟信息,时间得以延续。
但是硬件多加了一个芯片和一个晶振,多用三个I/O口,增加了成本,而且软件部分完全可以利用51的内部资源解决,没有充分利用已有资源。
方案二:
仅利用51内部的定时器和中生成秒,再利用程序累加清零等基本时间,尔后再调用C中一些经典的算法生成年月日以及星期等信息。
但是该方案必须保证系统一直处于供电的情况,不能断电,否则时间由初始化了,所以该方案中又添加了三个设置按钮,也就是增加了一个设置调整模块,在再次供电后可以调整时间以同步,完美的解决了时间的连续性问题。
这虽使得电路和方案一复杂度相当,但是系统的设计对资源的利用更充分,方案也更经济。
本设计采用了方案二。
4调整设置模块
这个模块完全是第三个模块的一个衍生品,它就是为了调整时间以便断电后正常使用。
该模块包括四个按键(两个中断,两个设置),一个中断就是简单要停止闹钟;另一个中断时是停止时钟,进入设置状态,包括调整时间以同步和设置闹钟的值,而另外两个设置建一个就是为了此时增加或减少时间值,另一个的作用是在调整完了当前量后进入下一个量的调整或退出调整。
二、系统的硬件设施与实现
1路概述及总体框架图
本系统是以AT89C51单片机为控制核心单元,系统突出特点是环境适应能力较强,功耗较低,成本低廉,经济实惠。
温度采集部分由DS18B20构成,他是一种新型数字温度传感器,具有体积小、精度高、适用电压宽而且采用独特的一线总线的传输方式,简化了电路连接。
数据显示部分由LCM1602A模块构成,一体化的LCM部分提供了两行共三十二个字符的显示空间,使得本系统大量的数据能够实时更新。
按键部分是常规的接法。
系统硬件总体框架图如下图所示。
2.主要模块的电路设计
(1)单片机主控制模块的设计
AT89S52单片机为40引脚双列直插芯片,有四个I/O口P0,P1,P2,P3,MCS-51单片机共有4个8位的I/O口(P0、P1、P2、P3),每一条I/O线都能独立地作输出或输入。
单片机的最小系统如下图所示,18引脚和19引脚接时钟电路,XTAL1接外部晶振和微调电容的一端,在片内它是振荡器倒相放大器的输入,XTAL2接外部晶振和微调电容的另一端,在片内它是振荡器倒相放大器的输出.第9引脚为复位输入端,接上电容,电阻及开关后够上电复位电路,20引脚为接地端,40引脚为电源端.如图-1所示
图-1主控制系统
(2)温度采集模块
采用数字式温度传感器DS18B20仅需要一条数据线进行数据传输,使用P3.5与DS18B20的I/O口连接加一个4.7K的上拉电阻,其中Vcc接电源,Vss接地。
(3)显示模块的设计
1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形。
这些字符有:
阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码。
但1602液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。
要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,下表是RT-1602A的内部显示地址
三、系统软件设计
本系统软件主程序采用结构化程序设计方法,各个功能模块相互独立,主程序的流程图如下图所示。
主程序可分为初始化,中断按键处理量大部分。
主程序流程图:
四、指标测试
1.测试仪器
1
直流稳压电源
CA17303D
2
数字万用表
DT9208
2.硬件测试
电子万年历的电路系统比较简单,但对于焊接方面却不可轻视,尤其要严防静电:
RT-1602A模块中的控制、驱动电路是低压、微功耗的CMOS电路,极易被静电击穿,静电击穿是一种不可修复的损坏,而人体有时会产生高达几十伏或上百伏的高压静电,所以,在焊接以及使用中都应极其小心,严防静电等。
在本电子万年历的设计调试中遇到了很多的问题。
回想这些问题只要认真多思考都是可以避免的,以下为主要的问题:
(1)烧入程序后,液晶显示空屏,而且亮度不均匀。
解决:
首先对调用的仔细检查程序,可以解决显示闪动问题。
其次,检查接线是否正确。
由于其亮度由10K的电位器来调节,适当调节防止过暗或过亮的情况发生。
(2)LED数码管的断码错乱,原因出于没有认真看清a、b、c等引脚信息。
解决:
重新排列74LS47的输出端,相应接入LED数码管,即可解决出现在的断码或乱码。
(3)对万年历修改时间或日期时,有时LED数码管被屏蔽掉,造成不亮现象。
解决:
根据仪器的测试,发现电路的驱动能力不足,最后在DS1302时钟芯片的/CS、SCLK、RET端接入5.1K的上拉电阻后,电路的驱动能力才能满足,即可解决不亮现象。
3.软件测试
电子万年历是多功能的数字型,可以看当前日期,时间,还有温度的仪器。
电子万年历功能很多,所以对于它的程序也较为复杂,所以在编写程序和调试时出现了相对较多的问题。
最后经过多次的模块子程序的修改,一步一步的完成,最终解决了软件。
在软件的调试过程中主要遇到的问题如下:
(1)修改时间、日期时没有与按键自动对应上。
解决:
把不相关的程序暂时屏蔽,对按键的子程序独立调试,发现在调用按键自动更新时,时间与按键协调不好,所以会造成错乱。
最后该延时为中断进行修改,使得时间、日期与按键自动对应上,最后解决了此问题.
(2)加入温度的程序后,进行修改时间、日期时相应的数码管位没有按要求闪动。
解决:
由于DS18B20是串行通信数据,只用一个口线传输,在处理采集的模拟信号时需要一定的时间,当把万年历的程序相接入时,会对延时有很大的影响。
所以在调用温度子程序时,先关闭定时器1中断允许,在温度子程序反回时再打开定时器1中断允许。
最终解决了此问题。
4测试结果分析与结论
<1>测试结果分析
(1)在测试中遇到液晶屏不显示时,首先使用试测仪对电路进行测试,观察是否存在漏焊,虚焊,或者元件损坏.
(2)液晶屏显示不正常,还有亮度不够,首先使用试测仪对电路进行测试,观察电路是否存在短路现象。
查看烧写的程序是否正确无误,对程序进行认真修改。
<2>测试结论
经过多次的反复测试与分析,可以对电路的原理及功能更加熟悉,同时提高了设计能力与及对电路的分析能力.同时在软件的编程方面得到更到的提高,对编程能力得到加强.同时对所学的知识得到很大的提高与巩固.
五、作品总结
在整个设计过程中,发挥团队精神,分工合作,充分发挥人的主观能动性,自主学习,学到了许多没学到的知识。
较好的完成了作品。
基本达到了预期的目的,在最初的设计中,发挥“三个臭皮匠,顶个诸葛亮”的作用。
相互学习、相互讨论、研究。
完了最初的设想。
在电路焊接时虽然没什么大问题,但从中也知道了焊接在整个作品中的重要性,虽然电路工程量不大,但也要注意。
当然也不能急于求成,否则反而达到事倍功半的效果。
对电路的设计、布局要先有一个好的构思,才显得电路板美观、大方。
程序编写中,由于思路不清晰,开始时遇到了很多的问题,经过静下心来思考,和同组员的讨论,看了相关的程序,理清了思路。
在此次设计中,知道了做凡事要有一颗平常的心,不要想着走捷径,一步一脚印。
也练就了我们的耐心,做什么事都在有耐心。
此次比赛中学到了很多很多东西,这是最重要的。
总之,参加电子竞赛我们的能力得到了全方位的提高。
参考文献
[1]陈正振编电子电路设计与制作广西交通职业技术学院信息工程系2007
[2]杨子文编单片机原理及应用西安电子科技大学出版社2006
[3]王法能编单片机原理及应用科学出版社2004
系统电路图
附录B源程序
#include
#include
#defineuintunsignedint
#defineucharunsignedchar
sbitDS1302_CLK=P1^2;//实时时钟时钟线引脚
sbitDS1302_IO=P1^1;//实时时钟数据线引脚
sbitDS1302_RST=P1^0;//实时时钟复位线引
sbitAT24C02_WP=P1^4;
sbitAT24C02_SCL=P1^5;
sbitAT24C02_SDA=P1^6;
sbitDS18B20_DQ=P2^3;
sbitACC0=ACC^0;
sbitACC7=ACC^7;
charhide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year;//秒,分,时到日,月,年位闪的计数
sbitSet=P2^4;//模式切换键
sbitUp=P2^5;//加法按钮
sbitDown=P2^6;//减法按钮
sbitout=P2^7;//立刻跳出调整模式按钮
chardone,count,temp,flag,up_flag,down_flag;
ucharTempBuffer[5],week_value[2];
voidshow_time();//液晶显示程序
/***********1602液晶显示部分子程序****************/
sbitLcdRs=P2^0;
sbitLcdRw=P2^1;
sbitLcdEn=P2^0;
sfrDBPort=0x80;
//P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口
//内部等待函数
unsignedcharLCD_Wait(void)
{
LcdRs=0;
LcdRw=1;_nop_();
LcdEn=1;_nop_();
LcdEn=0;
returnDBPort;
}
//向LCD写入命令或数据
#defineLCD_COMMAND0//Command
#defineLCD_DATA1//Data
#defineLCD_CLEAR_SCREEN0x01//清屏
#defineLCD_HOMING0x02//光标返回原点
voidLCD_Write(bitstyle,unsignedcharinput)
{
LcdEn=0;
LcdRs=style;
LcdRw=0;_nop_();
DBPort=input;_nop_();//注意顺序
LcdEn=1;_nop_();//注意顺序
LcdEn=0;_nop_();
LCD_Wait();
}
//设置显示模式************************************************************
#defineLCD_SHOW0x04//显示开
#defineLCD_HIDE0x00//显示关
#defineLCD_CURSOR0x02//显示光标
#defineLCD_NO_CURSOR0x00//无光标
#defineLCD_FLASH0x01//光标闪动
#defineLCD_NO_FLASH0x00//光标不闪动
voidLCD_SetDisplay(unsignedcharDisplayMode)
{
LCD_Write(LCD_COMMAND,0x08|DisplayMode);
}
//设置输入模式************************************************************
#defineLCD_AC_UP0x02
#defineLCD_AC_DOWN0x00//default
#defineLCD_MOVE0x01//画面可平移
#defineLCD_NO_MOVE0x00//default
voidLCD_SetInput(unsignedcharInputMode)
{
LCD_Write(LCD_COMMAND,0x04|InputMode);
}
voidLCD_Initial()
{
LcdEn=0;
LCD_Write(LCD_COMMAND,0x38);//8位数据端口,2行显示,5*7点阵
LCD_Write(LCD_COMMAND,0x38);
LCD_Write(LCD_COMMAND,0x38);
LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR);//开启显示,无光标
LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN);//清屏
LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE);//AC递增,画面不动
}
//液晶字符输入的位置************************
voidGotoXY(unsignedcharx,unsignedchary)
{
if(y==0)
LCD_Write(LCD_COMMAND,0x80|x);
if(y==1)
LCD_Write(LCD_COMMAND,0x80|(x-0x40));
}
//将字符输出到液晶显示
voidPrint(unsignedchar*str)
{
while(*str!
='\0')
{
LCD_Write(LCD_DATA,*str);
str++;
}
}
/***********DS1302时钟部分子程序******************/
typedefstruct__SYSTEMTIME__
{
unsignedcharSecond;
unsignedcharMinute;
unsignedcharHour;
unsignedcharWeek;
unsignedcharDay;
unsignedcharMonth;
unsignedcharYear;
unsignedcharDateString[11];
unsignedcharTimeString[9];
}SYSTEMTIME;///////////////////////////////////////////////////////////////////////////////定义的时间类型
SYSTEMTIMECurrentTime;
#defineAM(X)X
#definePM(X)(X+12)//转成24小时制
#defineDS1302_SECOND0x80//时钟芯片的寄存器位置,存放时间
#defineDS1302_MINUTE0x82
#defineDS1302_HOUR0x84
#defineDS1302_WEEK0x8A
#defineDS1302_DAY0x86
#defineDS1302_MONTH0x88
#defineDS1302_YEAR0x8C
voidDS1302InputByte(unsignedchard)/////////////////////////实时时钟写入一字节(内部函数)
{
unsignedchari;
ACC=d;
for(i=8;i>0;i--)
{
DS1302_IO=ACC0;//相当于汇编中的RRC
DS1302_CLK=1;
DS1302_CLK=0;
ACC=ACC>>1;
}
}
unsignedcharDS1302OutputByte(void)//实时时钟读取一字节(内部函数)
{
unsignedchari;
for(i=8;i>0;i--)
{
ACC=ACC>>1;//相当于汇编中的RRC
ACC7=DS1302_IO;
DS1302_CLK=1;
DS1302_CLK=0;
}
return(ACC);
}
voidWrite1302(unsignedcharucAddr,unsignedcharucDa)//ucAddr:
DS1302地址,ucData:
要写的数据
{
DS1302_RST=0;
DS1302_CLK=0;
DS1302_RST=1;
DS1302InputByte(ucAddr);//地址,命令
DS1302InputByte(ucDa);//写1Byte数据
DS1302_CLK=1;
DS1302_RST=0;
}
unsignedcharRead1302(unsignedcharucAddr)//读取DS1302某地址的数据
{
unsignedcharucData;
DS1302_RST=0;
DS1302_CLK=0;
DS1302_RST=1;
DS1302InputByte(ucAddr|0x01);//地址,命令
ucData=DS1302OutputByte();//读1Byte数据
DS1302_CLK=1;
DS1302_RST=0;
return(ucData);
}
voidDS1302_GetTime(SYSTEMTIME*Time)//获取时钟芯片的时钟数据到自定义的结构型数组
{
unsignedcharReadValue;
ReadValue=Read1302(DS1302_SECOND);
Time->Second=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_MINUTE);
Time->Minute=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_HOUR);
Time->Hour=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_DAY);
Time->Day=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_WEEK);
Time->Week=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_MONTH);
Time->Month=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
ReadValue=Read1302(DS1302_YEAR);
Time->Year=((ReadValue&0x70)>>4)*10+(ReadVal