基于51单片机的万年历设计(共69页).doc
精选优质文档-倾情为你奉上山 东 工 商 学 院SHANDONG INSTITUTE OF BUSINESS AND TECHNOLOGY单片机课程实训SCM PRACTICAL TRAINING实训设计题目Title Of Training 万年历设计_ 分院(系别)Department 信息与电子工程学院_ 专业Speciality 电子信息工程 班 级Class2013级1班设计作者Author XXX完成日期Date2015年 12月18日组 别Team 09_ 指导教师Advisor XXX_专心-专注-专业目 录067第一部分 课程设计任务书一、课程设计题目万年历的设计二、课程设计时间一周三、实训提交方式提交实训设计报告四、设计要求设计一台液晶显示屏显示的万年历,要求如下。(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液晶能显示阳历年、月、日、时、分、秒和温度及相应的农历时间。DS18B20温度电路复位电路晶振电路蜂鸣器电路按键电路AT89C52单片机液晶显示电路DS1302时钟电路图为硬件系统设计框架四、 硬件电路设计4.1电路原理图4.2单片机控制模块 AT89C52是一个低电压,高性能8位,片内含8k bytes的可反复擦写的只读和256 bytes的随机存取数据存储器(),器件采用的高密度、非易失性存储技术生产,兼容标准MCS-51,片内置通用8位和Flash,AT89C52单片机在电子行业中有着广泛的应用。(1) 复位电路 按键手动复位是通过使复位端经电阻与VCC接通而实现的。(2) 时钟振荡电路 基于系统运行速度,采用12MHZ的石英晶振,并使用两个小电容作为微调电容。 4.3温度模块 DS18B20温度传感器具有耐磨耐碰,体积小,使用方便,封装形式多样,适用于各种狭小空间设备数字测温和控制领域。测温范围 55125,固有测温分辨率0.5,工作电源为 35V。 DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。4.4时钟模块 DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。4.5液晶显示模块 本系统选择的LCD是AMPIRE128×64的汉字图形型液晶显示模块,可显示汉字及图形。单片机P1口作为数据输出口,RS,RW,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时间数据模块设计开始DS1302初始化从DS1302中读出数据,放入RAM调用显示子程序LCD显示时间扫描按键时间设置数据写回DS1302保存返回主显单5.4闹钟设定模块设计5.5阳历转阴历模块设计5.6按键设置时间模块设计六、程序源代/*文件包含*/#include < reg52.h >#include < character.h >#include < lcd.h >#include < clock.h >#include < sensor.h>#include < calendar.h >#include < key.h >/*预定义*/#define uchar unsigned char#define uint unsigned int/*/sbit bell = P2 0; /定义蜂鸣器端口/* 名称: Timer0_Service() inturrupt 1* 功能: 中断服务程序 * 入口参数:* 出口参数:*/void Timer0_Service() interrupt 1 static uchar count = 0; static uchar flag = 0; /记录鸣叫的次数 count = 0; TR0 = 0; /关闭Timer0 TH0 = 0x3c; TL0 = 0XB0; /延时 50 ms TR0 = 1 ; /启动Timer0 count +; if( count = 20 ) /鸣叫 1 秒 bell = bell; count = 0; flag +; if( flag = 6 ) flag = 0; TR0 = 0; /关闭Timer0 /* 名称: Timer2_Servie() interrupt 5* 功能: 中断服务程序 * 入口参数: * 出口参数:*/uchar HexNum_Convert(uchar HexNum)/*时间存储个位和十位的方式*/uchar Numtemp;Numtemp=(HexNum>>4)*10+(HexNum&0X0F);return Numtemp;/* 函数名称:main()* 功 能:* 入口参数:* 出口参数:*/void main( void ) uchar clock_time6 = 0X00,0X59,0X23,0X09,0X04,0X11; /定义时间变量 秒 分 时 日 月 年uchar alarm_time2 = 10, 06; /闹钟设置 alarm_time0: 分钟 alarm_time1 :小时uchar temperature2; /定义温度变量 temperature0 低8位 temperature1 高8位 Lcd_Initial(); /LCD初始化Clock_Fresh( clock_time ); /时间刷新,proteus会调用当前系统时间Clock_Initial( clock_time ); /时钟初试化/*中断初始化*/ EA = 1; /开总中断 ET0 = 1; /Timer0 开中断ET2 = 1; /Timer2 开中断 TMOD = 0x01 ; /Timer0 工作方式 1RCAP2H = 0x3c; RCAP2L = 0xb0; /Timer2 延时 50 ms while( 1 ) switch( Key_Scan() ) case up_array: Key_Idle(); break; case down_array: Key_Idle(); break; case clear_array: Key_Idle(); break; case function_array: Key_Function( clock_time, alarm_time ); case null: 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/*/#define uchar unsigned char#define uint unsigned int/* 名称: get_moon_day(uchar month_p,uint table_addr)* 功能: 读取数据表中农历的大月或小月 ,如果大月返回1, 小月返回0* 入口参数:* 出口参数:*/bit get_moon_day( uchar month_p,uint calendar_address ) uchar temp,temp1;temp1=(month_p+3)/8;temp=0x80>>(month_p+3)%8);temp=year_codecalendar_address+temp1&temp;if(temp=0)return(0);elsereturn(1);/* 名称: void Calendar_Convert( uchar * clock_time )* 功能: 输入BCD的阳历数据, 输出BCD阴历数据( 1901 - 2099 )* 入口参数: c_flag:阳历的世纪标志 clock_time: 时钟地址* 出口参数: 无* 说明: c_flag = 0 :21世纪 c_flag = 1 :19世纪 */void Calendar_Convert( uchar c_flag, uchar * clock_time ) bit flag_month, flag_year; uchar year, month, day, month_point; /定义 年 月 天 uchar temp1, temp2, temp3; uint calendar_address; /定义农历地址 uint day_number; uchar clock_moon3; /定义阴历 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; /Bit6Bit5:春节所在的阳历月份 temp1 >>= 5 ; /春节(正月初一)所在的阳历日期 temp2 = year_code calendar_address + 2 & 0x1f; /Bit4Bit0:春节所在的阳历日期 /计算春节(正月初一)离当年元旦 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; elsetemp1=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_moon2 = temp1 | ( year % 10 ); temp1 = month / 10; temp1 <<= 4; clock_moon1 = temp1 | ( month % 10 ); temp1 = day / 10; temp1 <<= 4; clock_moon0 = temp1 | ( day % 10 ); Lcd_Lunar_Calendar( clock_moon );/* 名称: void Week_convert( uchar * clock_time )* 功能: 输入BCD阳历数据;输出BCD星期* 入口参数: * 出口参数:* 说明: 算法: ( 日期 + 年份 + 所过闰年 + 月校正 ) / 7 的余数就是星期 如果是闰年又不到 3 月份上述之和 要减一天 再*/void Week_Convert( bit c, uchar * clock_time ) uchar year, month, day; /定义 年 月 天 uchar temp; clock_time += 3; /指向日 day = ( * clock_time >> 4 ) * 10 + ( *clock_time & 0x0f ); /BCD转换十进制 clock_time +; /指向月 month = ( * clock_time >> 4 ) * 10 + ( * clock_time & 0x0f ); clock_time +; /指向年 year = ( * clock_time >> 4 ) * 10 + ( * clock_time & 0x0f ); if( c = 0 ) /如果为21世纪,年份数加100 year += 100; temp = year / 4; /所过闰年数只算1900年之后的 temp = year + temp; temp = temp % 0x07; /为节省资源,先进行一次取余,避免数大于0xff,避免使用整型数据 temp = temp + day + table_week month - 1 ; if( ( year % 4 = 0 ) && ( month <3 ) ) temp -=1; Lcd_Week( temp % 7 );/*/#endif#ifndef _CHARACTER#define _CHARACTER/*预定义*/#define uchar unsigned char #define uint unsigned int/*内容: 0 1 2 3 4 5 6 7 8 9 空白 - T :*大小: 8X16字符 */uchar code letter_logo1616=/*- 文字: 0 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,/*- 文字: 1 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,/*- 文字: 2 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,/*- 文字: 3 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,/*- 文字: 4 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,/*- 文字: 5 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,/*- 文字: 6 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,/*- 文字: 7 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,/*- 文字: 8 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,/*- 文字: 9 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,/*- 文字: : -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,/*- 文字: - -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,/*- 文字: T -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x1C,0x0C,0xFC,0xFC,0x0C,0x1C,0x00,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,/*- 文字: 空白 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=8x16 -*/0x00,0x00,0