基于STC89C82的电子万年历Word文档格式.docx
《基于STC89C82的电子万年历Word文档格式.docx》由会员分享,可在线阅读,更多相关《基于STC89C82的电子万年历Word文档格式.docx(19页珍藏版)》请在冰豆网上搜索。
选择,加,减,确定。
显示模块采用LCD1602显示屏,时钟模块采用DS1302时钟芯片实现对时间,日期的操作,温湿度的测量采用简单的DTH11芯片。
系统方框图:
电子万年历系统方框图
四、主要元器件介绍
主控制模块:
这个系统的主要控制我们选用了STC89C52单片机作为中心控制,引脚图如下图所示,STC89C52是一种带8K字节闪烁可编程可檫除只读存储器的低电压,高性能的微处理器。
它不仅拥有MCS51单片机的优点而且拥有低功耗的空闲和省电模式,可以降低我们电路的功耗,内部包含了定时器,程序存储器,数据存储器等硬件,符合我们电路的硬件要求,方便的构成一个最小系统,且价格合理,便宜程序编写。
2.DS1302时钟电路
我们的时钟电路是用时钟芯片DS1302,备用电池,晶振三部分构成,引脚图如下图所示,DS1302是用3线的串行接口,内部集成了可编程日历时钟,通过单片机的控制来设置芯片,Vcc1和Vcc2是双电源,外部电源和备用电源,现如今的DS1302芯片本身带有备用电池,备用电池可以是时钟在主电源中断的情况下继续运行工作。
DS1302引脚图:
DS1302时钟模块:
3.LCD显示
显示模块我们选用LCD1602液晶显示,它是专门用来显示字母,数字,符号的点阵型液晶,它有几个5x7或是5X11点阵字符组成,每一个点阵字符位都袔显示一个字符,LCD显示如图所示:
4.DHT11温湿度
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。
它应用专用的数字模块采集技术和温湿度传感技术,确保极高的可靠性与长期稳定性。
传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。
因此DHT11具有超快响应、抗干扰能力强、性价比极高等优点。
五、系统电路
程序流程图
主程序首先初始化定时器、LCD1602及DS1302,然后就开始查询按键,有键按下则开始调整时间和日期,若没有按下,则执行下面的时间、日期的显示,最后依次循环这些相同的操作,相应流程图如图所示:
图(12)程序流程图
按键的检测是通过中断的办法来实现,利用按键进行间调整。
K1按下则开始设置时间及日期,同时在第一行最右端显示被选择的对象,第一次按下K1时,设置年份,若按下K3,则是减1操作,按下K2是加1操作,设置好年后,第二次按下K1时,则是设置月份,按K3减,按K2则加1,依次循环下去,则可以将时间和日期设置完毕,K4是确定键,设置好按下即可保存设置了。
1.硬件设计:
系统总电路图:
第一个:
显示模块
工作原理:
芯片的1,2脚是电源与地,3脚是用来调节显示屛的背光,4,5,6是LCD1602的控制入口,用于控制芯片的读写指令,7至14脚是LCD1602的数据口。
当程序写入单片机的时候,通过控制口读取单片机中的指令,相当于写指令,然后将数据通过数据口进入LCD芯片中,再通过读指令,在显示屏上显示程序中的指令,如日期,时间,温湿度等。
第二个:
DS1302时钟模块
3脚为CLK串行时钟的输入口,4脚DAT为I/O口,是数据输入输出口,5脚RST是复位脚。
给复位脚RST高电平,当CLK产生一个脉冲时,处于上升沿时,通过串行口向单片机读取程序,相当于写指令,在处于下降沿时,仍然通过串行口输出数据,相当于读指令,之后在显示屏上显示相对应的时间日期。
第三个:
时钟振荡
电路由两个30PF的瓷片电容和一个12MHZ的晶振组成,并接入单片机的XTAL1和XTAL2引脚,让单片机处于内部振荡模式。
在加电后延迟大概10ms振荡器振荡,在XTAL2引脚产生3V左右的正弦波时钟信号。
两个电容的作用是为了提高电路的运行速度。
第四个:
按键电路模块
四个按键通过一个TTL逻辑与门将其与单片机的中断入口相连,使单片机能全面的控制这四个按键的工作,在将四个按键分别与单片机的不同引脚相接,使四个按键分工合作工作,在程序中嵌入按键中断程序,每个按键各自有中断的功能,K1中断功能为选择,K2的中断功能为加,K3的中断功能为减,K4的中断功能为确定。
第五个:
温湿度测量模块
DHT11芯片内部本身含有一个电阻式感湿元件和NTC测温元件,它们与一个高性能的8位单片机相连接。
此芯片温湿度测量元件共用引脚2与单片机P2.3相连。
2.软件设计
软件总设计:
主程序首先对系统环境初始化,设置定时器T0工作模式为16位定时/计数器模式,置位总中断允许位EA,并对键盘端口置位,再对LCD1602初始化,DS1302初始化。
接着扫描键盘,在键盘程序里面是对时间、日期及闹钟的调整,最下面是时间的显示。
软件程序编写:
软件程序编写的好坏直接影响着系统运行情况的良好。
因本程序涉及的模块较多,所以程序编写也采用模块化设计,C语言具有编写灵活、移植方便、便于模块化设计的特点,所以本系统的软件采用C51编写。
6、调试及性能分析
硬件调试:
1.硬件调试问题:
1)第一次焊完PCB板后接上电源,发现显示屛不亮
2)显示屏幕可显示,但是亮度不够
2.问题分析:
1)可能是电路虚焊,或是某些电线未焊
2)可能是电路虚焊,或是某些电阻过大导致电流小
3)没调节滑阻
3.问题解决:
1)用万用表全面检查每一条导线的焊接,发现有一条外接导线漏焊,补焊后,显示屏显示正常。
2)通过调节滑阻,使屏幕显示更加清晰
软件调试:
在经过硬件调试之后,进入了软件调试模块,由于我们系统涉及到了多个模块,故而在程序的编写上花费的时间较长,多个芯片的编程,需要调用多个子程序,因此发现的问题也较多,在此次的软件编程上出现的问题有:
1.调试问题:
1)初次软件调试时,在确保程序没有错误的情况下,将程序写入单片机,但是在做时间与日期的调整是发现按键操作出现功能不稳定,灵敏度不高,不能立马做出相对应的改变
2)LCD显示屏出现闪烁
问题分析:
1)按键存在抖动,是不是在程序中加上延时能改善这个问题
2)LCD屏幕闪烁,有可能是在执行了清屏程序时序太长
问题解决:
1)在程序中添加去抖动延时,基本解决了问题。
2)将不需要的清屏程序去掉,就可解决这个问题。
2.在软件调试过程中,当调节时间和日期后,单片机上电后更新的是PC的时间,后来查找资料发现,是设置ds1302的问题,
性能分析:
通过完成电子万年历这一作品之后发现,STC89C52单片机的程序编写较为简便快捷,之前所使用的AT89C52需要专门的程序编写适配器,故而用STC89C52
代替。
蓝屏背光的LCD1602显示正常,在各方面此显示屏的使用率较高。
温湿度测量模块所采用的芯片DHT11相对于温度与湿度分别测量最为简单方便,且次芯片本身具有超快响应,抗干扰能力强,低功耗等优势。
设计总结:
通过这次的课程设计,让我收益颇多,以往自己疏于加强的发面在这段实验中都有很大的提高,比如知识的巩固,软件的运用,心态的调整,以及团体的集体荣誉感,虽然这次的课程设计结果有点差强人意,但是我学会了如何自主求知,如何遇事时沉稳淡定,学会了关键时候还是要靠自己的,感谢这段时间老师以及同学的帮助,让我能够发现自己的缺点,改善自己。
也感谢自己的队友们,不嫌弃我的无知,给予我最大的帮助,让我体会到这个团体共同努力时所迸发的力量有多绚丽。
7、完整的源程序
#include<
reg51.h>
#include<
intrins.h>
string.h>
#defineuintunsignedint
#defineucharunsignedchar
sbitIO=P1^0;
//DS1302数据线
sbitSCLK=P1^1;
//DS130时钟线
sbitRST=P1^2;
//DS1302复位线
sbitRS=P2^0;
//LCD数据/命令选择端
sbitRW=P2^1;
//LCD读/写控制
sbitEN=P2^2;
//LCD使能端
sbitK1=P3^4;
//选择
sbitK2=P3^5;
//加
sbitK3=P3^6;
//减
sbitK4=P3^7;
//确定
uchartCount=0;
ucharMonthsDays[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
uchar*WEEK[]={"
SUN"
"
MON"
TUS"
WEN"
THU"
FRI"
SAT"
};
ucharLCD_DSY_BUFFER1[]={"
DATE00-00-00"
//显示格式
ucharLCD_DSY_BUFFER2[]={"
TIME00:
00:
00"
ucharDateTime[7];
//所读取的日期时间
charAdjust_Index=-1;
//当前调节的时间对象:
,,分,是,日,月,年(1,2,3,4,6)
ucharChange_Flag[]="
-MHDM-Y"
;
//(分,时,日,月,年)(不调节秒与周)
/*---------延时程序------------------*/
voidDelayMS(uintms)
{
uchari;
while(ms--){for(i=0;
i<
120;
i++);
}
//-----------向DS1302写入一字节------------------//
voidWrite_A_Byte_TO_DS1302(ucharx)
{uchari;
for(i=0;
8;
i++){
IO=x&
0x01;
//每一位与1与存入IO中
SCLK=1;
SCLK=0;
//一个高脉冲将数据送入液晶控制器
x>
>
=1;
//右移
}
//-----------从DS1302读取一字节------------------//
ucharGet_A_Byte_FROM_DS1302()
{uchari,b=0x00;
b|=_crol_((uchar)IO,i);
//每一个高脉冲读取一位数据
returnb/16*10+b%16;
//返回BCD码
//-----------从DS1302指定位置读数据------------------//
ucharRead_Data(ucharaddr)
uchardat;
RST=0;
RST=1;
//RST高电平时读/写
Write_A_Byte_TO_DS1302(addr);
//先写入地址
dat=Get_A_Byte_FROM_DS1302();
RST=0;
returndat;
//---------向DS1302某地址写入数据--------------------//
voidWrite_DS1302(ucharaddr,uchardat)
{SCLK=0;
Write_A_Byte_TO_DS1302(addr);
Write_A_Byte_TO_DS1302(dat);
SCLK=0;
//高脉冲写入数据
//--------------设置时间----------------//
voidSET_DS1302()
//写控制字,取消写保护
Write_DS1302(0x8E,0x00);
//分时日月年依次写入
for(i=1;
7;
i++)
{//分的起始地址10000010(0x82),后面依次是时,日,月,周,年,写入地址每次递增2
Write_DS1302(0x80+2*i,(DateTime[i]/10<
<
4)|(DateTime[i]%10));
}
Write_DS1302(0x8E,0x80);
//加保护
//----------读取当前日期时间------------//
voidGetTime()
{uchari;
i++){DateTime[i]=Read_Data(0X81+2*i);
//-----------读LCD状态------------------//
ucharRead_LCD_State()
{ucharstate;
RS=0;
RW=1;
EN=1;
//输出:
D0~D7=状态字
DelayMS
(1);
state=P0;
//从P0口读LCD状态
EN=0;
DelayMS
(1);
returnstate;
//-----------忙等待------------------//
voidLCD_Busy_Wait()
while((Read_LCD_State()&
0x80)==0x80);
DelayMS(5);
}
//-----------向LCD写数据------------------//
voidWrite_LCD_Data(uchardat)
LCD_Busy_Wait();
RS=1;
EN=0;
RW=0;
//写数据,EN为高脉冲,
P0=dat;
//-------------写LCD指令-------------------//
voidWrite_LCD_Command(ucharcmd)
RW=0;
//写指令,EN高脉冲,输出:
D0~D7=数据
P0=cmd;
//-------------LCD初始化-------------------//
voidInit_LCD()
Write_LCD_Command(0x38);
//设置16*2显示,5*7点阵,8位数据接口
Write_LCD_Command(0x01);
//显示清零,数据指针清零
Write_LCD_Command(0x06);
//写一个字符后地址指针自动加1
Write_LCD_Command(0x0c);
//设置开显示,不显示光标
//------------------------------------------
//设置液晶显示位置
voidSet_LCD_POS(ucharp){
Write_LCD_Command(p|0x80);
//相当于在0x80基础上加入位置量
//----在LCD上显示字符串---------//
voidDisplay_LCD_String(ucharp,uchar*s)
{uchari;
Set_LCD_POS(p);
16;
{
Write_LCD_Data(s[i]);
//在固定位置显示时间日期
DelayMS
(1);
//---------日期与时间值转换为数字字符----------------//
voidFormat_DateTime(uchard,uchar*a)
a[0]=d/10+'
0'
a[1]=d%10+'
//判断是否为闰年
ucharisLeapYear(uinty)
{return(y%4==0&
&
y%100!
=0)||(y%400==0);
//求自2000.1.1开始的任何一天是星期几
//函数没有通过,求出总天数后再求星期几
//因为求总天数可能会超出uint的范围
voidRefreshWeekDay()
{uinti,d,w=5;
//已知1999.12.31是周五
for(i=2000;
2000+DateTime[6];
d=isLeapYear(i)?
366:
365;
w=(w+d)%7;
d=0;
DateTime[4];
{d+=MonthsDays[i];
d+=DateTime[3];
//保存星期,0~6表示星期日,星期一,二,...,六,为了与DS1302的星期格式匹配,返回值需要加1
DateTime[5]=(w+d)%7+1;
//*****年月日时分++/--********//
voidDateTime_Adjust(charx)
{switch(Adjust_Index)
case6:
//年00-99
if(x==1&
DateTime[6]<
99)DateTime[6]++;
if(x==-1&
DateTime[6]>
0)DateTime[6]--;
//获取2月天数
MonthsDays[2]=isLeapYear(2000+DateTime[6])?
29:
28;
//如果年份变化后当前月份的天数大于上限则设为上限
if(DateTime[3]>
MonthsDays[DateTime[4]])
{DateTime[3]=MonthsDays[DateTime[4]];
RefreshWeekDay();
//刷新星期
break;
case4:
//月01-12
DateTime[4]<
12)DateTime[4]++;
DateTime[4]>
1)DateTime[4]--;
case3:
//日00-28、29、30、31,调节之前首先根据年份得出该年中断二月天数
//根据当前月份决定调节日期的上限
DateTime[3]<
MonthsDays[DateTime[4]])DateTime[3]++;
DateTime[3]>
0)DateTime[3]--;
case2:
//时
DateTime[2]<
23)DateTime[2]++;
DateTime[2]>
0)DateTime[2]--;
case1:
//分
DateTime[1]<
59)DateTime[1]++;
DateTime[1]>
0)DateTime[1]--;
//---定时器0每秒刷新LCD显示----//
voidT0_INT()interrupt1
TH0=-50000/256;
TL0=-50000%256;
if(++tCount!
=2)return;
tCount=0;
//按指定格式生成待显示的日期时间串
Format_DateTime(DateTime[6],LCD_DSY_BUFFER1+5);
Format_DateTime(DateTime[4],LCD_DSY_BUFFER1+8);
Format_DateTime(DateTime[3],LCD_DSY_BUFFER1+11);
//星期
strcpy(LCD_DSY_BUFFER1+13,WEEK[DateTime[5]-1]);
//时分秒
Format_DateTime(DateTime[2],LCD_DSY_BUFFER2+5);
Format_DateTime(DateTime[1],LCD_DSY_BUFFER2+8);
Format_DateTime(DateTime[0],LCD_DSY_BUFFER2+11);
//显示年月日,星期,时分秒
Display_LCD_String(0x00,LCD_DSY_BUFFER1);
Display_LCD_String(0x40,LCD_DSY_BUFFER2);
//----------键盘中断(INT0)-------------//
voidEX_INT0()interrupt0