基于51单片机的万年历设计.docx
《基于51单片机的万年历设计.docx》由会员分享,可在线阅读,更多相关《基于51单片机的万年历设计.docx(113页珍藏版)》请在冰豆网上搜索。
基于51单片机的万年历设计
山东工商学院
SHANDONGINSTITUTEOFBUSINESSANDTECHNOLOGY
单片机课程实训
SCMPRACTICALTRAINING
实训设计题目
TitleOfTraining
万年历设计_________________
分院(系别)
Department
信息与电子工程学院_____________
专 业
Speciality
电子信息工程
班级
Class
2013级1班
设计作者
Author
XXX
完成日期
Date
2015年12月18日
组别
Team
09______
指导教师
Advisor
XXX____
第一部分课程设计任务书
一、课程设计题目
万年历的设计
二、课程设计时间
一周
三、实训提交方式
提交实训设计报告
四、设计要求
设计一台液晶显示屏显示的万年历,要求如下。
(1)显示年、月、日、时、分、秒和星期,并有相应的农历显示。
(2)可通过键盘自动调整时间。
(3)具有闹钟功能。
(4)能够显示环境温度,误差小于±1℃。
(5)计时精度:
月误差小于20秒。
第二部分课程设计报告
一、单片机发展概况
单片机诞生于20世纪70年代末,它的发展史大致可分为三个阶段:
第一阶段(1976-1978):
初级单片机微处理阶段。
该时期的单片机具有8位CPU,并行I/O端口、8位时序同步计数器,寻址范围4KB,但是没有串行口。
第二阶段(1978-1982):
高性能单片机微机处理阶段,该时期的单片机具有I/O串行端口,有多级中断处理系统,15位时序同步技术器,RAM、ROM容量加大,寻址范围可达64KB。
第三阶段(1982-至今)位单片机微处理改良型及16位单片机微处理阶段民用电子产品、计算机系统中的部件控制器、智能仪器仪表、工业测控、网络与通信的职能接口、军工领域、办公自动化、集散控制系统、并行多机处理系统和局域网络系统。
二、MCS-51单片机系统简介
MCS-51系列单片机产品都是以Intel公司最早的典型产品8051为核心构成的。
MCS-51单片机由CPU、RAM、ROM、I/O接口、定时器/计数器、中断系统、内部总线等部件组成。
MCS-51以其典型的结构和完善的总线专用寄存器的集中管理,众多的逻辑位操作功能及面向控制的丰富的指令系统,为以后的单片机的发展奠定了基础。
三、设计思想
以AT89C52单片机为核心,外接单片机复位电路,单片机晶振电路,蜂鸣器电路,温度传感器DS18B20,时钟芯片DS1302,按键电路,12864液晶显示电路,通过控制AT89C52单片机的接口来控制蜂鸣器电路,温度传感器DS18B20模块,时钟芯片DS1302模块,按键电路,12864液晶显示模块,使12864液晶能显示阳历年、月、日、时、分、秒和温度及相应的农历时间。
图为硬件系统设计框架
四、硬件电路设计
4.1电路原理图
4.2单片机控制模块
AT89C52是一个低电压,高性能CMOS8位单片机,片内含8kbytes的可反复擦写的Flash只读程序存储器和256bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器和Flash存储单元,AT89C52单片机在电子行业中有着广泛的应用。
(1)复位电路
按键手动复位是通过使复位端经电阻与VCC接通而实现的。
(2)时钟振荡电路
基于系统运行速度,采用12MHZ的石英晶振,并使用两个小电容作为微调电容。
4.3温度模块
DS18B20温度传感器具有耐磨耐碰,体积小,使用方便,封装形式多样,适用于各种狭小空间设备数字测温和控制领域。
测温范围-55℃~+125℃,固有测温分辨率0.5℃,工作电源为3~5V。
DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。
采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。
DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。
4.4时钟模块
DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。
采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。
DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。
4.5液晶显示模块
本系统选择的LCD是AMPIRE128×64的汉字图形型液晶显示模块,可显示汉字及图形。
单片机P1口作为数据输出口,RS,R\W,E分别通过10K的上拉电阻连接到单片机的P0.0,P0.1,P0.2。
VDD接5V电源,VSS接地。
VEE为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
R/W为读写信号线,高电平
(1)时进行读操作,低电平(0)时进行写操作。
E(或EN)端为使能(enable)端,下降沿使能。
DB0-DB7为双向数据总线,同时最高位DB7也是忙信号检测位。
BLA、BLK分别为显示器背光灯的正、负极。
4.6按键模块
本系统使用5个button元件分别设置为增加键、减小键、退出键、设置/OK键。
4个按键与一个4个驱动电阻相连,作为整个按键控制功能系统,以触发AT89C52中断为方式使用设置功能。
4.7蜂鸣器模块
本系统采用BUZZER元件和PNP型三极管及电阻组成了蜂鸣器电路。
五、软件设计框图
5.1主程序流程图
万年历的程序主要包括:
按键识别程序、时间设定程序、温度数据采集程序、时间数据采集程序、液晶屏数据显示程序、定时中断程序、阳历转阴历程序、闹钟设定程序、延时子程序等。
5.2温度数据模块设计
5.3时间数据模块设计
5.4闹钟设定模块设计
5.5阳历转阴历模块设计
5.6按键设置时间模块设计
六、程序源代
/****************************文件包含*************************************/
#include
#include
#include
#include
#include
#include
#include
/*****************************预定义**************************************/
#defineucharunsignedchar
#defineuintunsignedint
/****************************************************************************/
sbitbell=P2^0;//定义蜂鸣器端口
/*****************************************************************************
*名称:
Timer0_Service()inturrupt1
*功能:
中断服务程序
*入口参数:
*出口参数:
*****************************************************************************/
voidTimer0_Service()interrupt1
{
staticucharcount=0;
staticucharflag=0;//记录鸣叫的次数
count=0;
TR0=0;//关闭Timer0
TH0=0x3c;
TL0=0XB0;//延时50ms
TR0=1;//启动Timer0
count++;
if(count==20)//鸣叫1秒
{
bell=~bell;
count=0;
flag++;
}
if(flag==6)
{
flag=0;
TR0=0;//关闭Timer0
}
}
/*****************************************************************************
*名称:
Timer2_Servie()interrupt5
*功能:
中断服务程序
*入口参数:
*出口参数:
*****************************************************************************/
ucharHexNum_Convert(ucharHexNum)/*时间存储个位和十位的方式*/
{
ucharNumtemp;
Numtemp=(HexNum>>4)*10+(HexNum&0X0F);
returnNumtemp;
}
/******************************************************************************
*函数名称:
main()
*功能:
*入口参数:
*出口参数:
********************************************************************************/
voidmain(void)
{
ucharclock_time[6]={0X00,0X59,0X23,0X09,0X04,0X11};//定义时间变量秒分时日月年
ucharalarm_time[2]={10,06};//闹钟设置alarm_time[0]:
分钟alarm_time[1]:
小时
uchartemperature[2];//定义温度变量temperature[0]低8位temperature[1]高8位
Lcd_Initial();//LCD初始化
Clock_Fresh(clock_time);//时间刷新,proteus会调用当前系统时间
Clock_Initial(clock_time);//时钟初试化
/***********************中断初始化***************************/
EA=1;//开总中断
ET0=1;//Timer0开中断
ET2=1;//Timer2开中断
TMOD=0x01;//Timer0工作方式1
RCAP2H=0x3c;
RCAP2L=0xb0;//Timer2延时50ms
while
(1)
{
switch(Key_Scan())
{
caseup_array:
{
Key_Idle();
}
break;
casedown_array:
{
Key_Idle();
}
break;
caseclear_array:
{
Key_Idle();
}
break;
casefunction_array:
{
Key_Function(clock_time,alarm_time);
}
casenull:
{
Clock_Fresh(clock_time);//时间刷新
Lcd_Clock(clock_time);//时间显示
Sensor_Fresh(temperature);//温度更新
Lcd_Temperture(temperature);//温度显示
Calendar_Convert(0,clock_time);
Week_Convert(0,clock_time);
//闹钟报警
if(*alarm_time==HexNum_Convert(*(clock_time+1)))//分钟相吻合
if(*(alarm_time+1)==HexNum_Convert(*(clock_time+2)))//小时相吻合
{
bell=0;
TR2=1;//启动Timer2
}
}
break;
}
}
}
#ifndef_SUN_MOON
#define_SUN_MOON
/*************************************************************************/
#defineucharunsignedchar
#defineuintunsignedint
/********************************************************************************
*名称:
get_moon_day(ucharmonth_p,uinttable_addr)
*功能:
读取数据表中农历的大月或小月,如果大月返回1,小月返回0
*入口参数:
*出口参数:
*********************************************************************************/
bitget_moon_day(ucharmonth_p,uintcalendar_address)
{
uchartemp,temp1;
temp1=(month_p+3)/8;
temp=0x80>>((month_p+3)%8);
temp=year_code[calendar_address+temp1]&temp;
if(temp==0){return(0);}else{return
(1);}
}
/**************************************************************************
*名称:
voidCalendar_Convert(uchar*clock_time)
*功能:
输入BCD的阳历数据,输出BCD阴历数据(1901-2099)
*入口参数:
c_flag:
阳历的世纪标志clock_time:
时钟地址
*出口参数:
无
*说明:
c_flag=0:
21世纪c_flag=1:
19世纪
*****************************************************************************/
voidCalendar_Convert(ucharc_flag,uchar*clock_time)
{
bitflag_month,flag_year;
ucharyear,month,day,month_point;//定义年月天
uchartemp1,temp2,temp3;
uintcalendar_address;//定义农历地址
uintday_number;
ucharclock_moon[3];//定义阴历
clock_time+=3;//指向日
day=(*clock_time>>4)*10+(*clock_time&0x0f);//BCD转换十进制
clock_time++;//指向月
month=(*clock_time>>4)*10+(*clock_time&0x0f);//BCD转换十进制
clock_time++;//指向年
year=(*clock_time>>4)*10+(*clock_time&0x0f);//BCD转换十进制
//定位日历地址
if(c_flag==0)
calendar_address=(year+99)*3;
else
calendar_address=(year-1)*3;
//春节(正月初一)所在的阳历月份
temp1=year_code[calendar_address+2]&0x60;//Bit6~~Bit5:
春节所在的阳历月份
temp1>>=5;
//春节(正月初一)所在的阳历日期
temp2=year_code[calendar_address+2]&0x1f;//Bit4~~Bit0:
春节所在的阳历日期
//计算春节(正月初一)离当年元旦{1月1日(阳历)}的天数;春节只会在阳历的1月或2月
temp3=temp2-1;
if(temp1!
=1)temp3+=0x1f;
//计算阳历月离当年元旦{1月1日(阳历)}的天数
if(month<10)
{day_number=day_code1[month-1]+day;}
else
{day_number=day_code2[month-10]+day;}
//如果阳历的月大于2且该年的2月为闰月,天数加1
//闰年指的就是阳历有闰日或阴历有闰月的一年;
//阳历四年一闰,在二月加一天,这一天叫做闰日:
//农历三年一闰,五年两闰,十九年七闰,每逢闰年所加的一个月叫做闰月。
if((month<=2)||(year%0x04!
=0))day_number-=1;
//day_number++;
//if((month<2)||(year%0x04!
=0))
//day_number-=1;
//判断阳历日在春节(正月初一)之前还是之后
if(day_number>=temp3)//阳历在春节之后或者春节当日
{
day_number-=temp3;
month=1;
month_point=1;//month_point为月份指向,阳历日在春季前就是春季
flag_month=get_moon_day(month_point,calendar_address);//检查该阴历月的大小大月返回1小月返回0
flag_year=0;
if(flag_month==0){temp1=29;}else{temp1=30;}
//闰月所在的月分
temp2=year_code[calendar_address]&0xf0;
temp2>>=4;//提取高四位假如是0表示没有闰月
while(day_number>=temp1)
{
day_number-=temp1;
month_point++;
if(month==temp2)
{
flag_year=~flag_year;
if(flag_year==0)
month+=1;
}
else
month++;
flag_month=get_moon_day(month_point,calendar_address);
if(flag_month)
temp1=30;
else
temp1=29;
}
day=day_number+1;
}
else//阳历在春节之前使用以下代码进行运算
{
temp3-=day_number;
if(year==0)
{year=0xe3;c_flag=1;}
else
year-=1;
calendar_address-=3;
month=0xc;
temp2=year_code[calendar_address]&0xf0;
temp2>>=4;//提取高4位
flag_year=0;
if(temp2==0)
month_point=12;
else
month_point=13;
//flag_year=0;
flag_month=get_moon_day(month_point,calendar_address);
if(flag_month)
temp1=30;
else
temp1=29;
while(temp3>temp1)
{
temp3-=temp1;
month_point--;
if(flag_year==0)
month-=1;
if(month==temp2)
flag_year=~flag_year;
flag_month=get_moon_day(month_point,calendar_address);
if(flag_month)
temp1=0x1e;
else
temp1=0x1d;
}
day=temp1-temp3+1;
}
//HEX->BCD,运算结束后,把数据转换为BCD数据
temp1=year/10;
temp1<<=4;
clock_moon[2]=temp1|(year%10);
temp1=month/10;
temp1<<=4;
clock_moon[1]=temp1|(month%10);
temp1=day/10;
temp1<<=4;
clock_moon[0]=temp1|