当前位置:文档之家› 基于51单片机的万年历的设计

基于51单片机的万年历的设计

单片机课程实训SCM PRACTICAL TRAINING目录第一部分课程设计任务书 (1)一、课程设计题目 (1)二、课程设计时间 (1)三、实训提交方式 (1)四、设计要求 (1)第二部分课程设计报告 (2)一、单片机发展概况 (2)二、MCS-51单片机系统简介 (2)三、设计思想 (3)四、硬件电路设计 (3)1. 总体设计 (3)2. 晶振电路 (4)3. 复位电路 (4)4. DS1302时钟电路 (5)5. 温度采集系统电路 (5)6. 按键调整电路 (6)7. 闹钟提示电路 (6)五、软件设计框图 (7)六、程序源代码 (8)1. 主程序 (8)2. 温度控制程序 (11)3. 日历设置程序 (13)4. 时钟控制程序 (18)5. 显示设置程序 (20)七、结束语 (23)八、课程设计小组分工 (23)九、参考文献 (23)第一部分课程设计任务书一、课程设计题目用中小规模集成芯片设计制作万年历。

二、课程设计时间五天三、实训提交方式提交实训设计报告电子版与纸质版四、设计要求(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接口、定时器/计数器、中断系统、内部总线等部件组成。

8051单片机的基本性能有:◆8位CPU;◆布尔代数处理器,具有位寻址能力;◆128B内部RAM,21个专用寄存器;◆4KB内部掩膜ROM;◆2个16位可编程二进制加1定时器/计数器;◆32个(4×8位)双向可独立寻址的I/O口;◆1个全双工UART(异步串行通信口);◆5个中断源,两级中断结构;◆片内振荡器及时钟电路,晶振频率为1.2MHz~12MHz;◆外部程序/数据存储器寻址空间均为64KB;◆111条指令,大部分为单字节指令;◆单一+5V电源供电,双列直插40引脚DIP封装。

三、设计思想整体设计以单片机技术为核心,采用C语言进行软件设计,增加了程序的可读性和可移植性,为了便于扩展和更改,软件的设计采用模块化结构。

程序先向LCD更新时钟芯片的时间与温度传感器的时间,然后进行初始化工作。

程序由一个主函数,两个定时器中断程序,一个时钟设置子程序,一个农历设置子程序,一个温度设置子程序,一个延时子程序,一个调时子程序,一个显示子程序构成。

程序通过按键扫描程序来确定是否调用中断程序来对时间进行调整。

用一子程序完成时分的调整,通过循环扫描四个按键的电平变化来判断对应按键是否按下,并带有去抖动功能,四个按键分别有增加,减小,退出与功能选择的作用。

通过功能选择时钟设置与闹钟设置,使用加或减按键进行预置,完成后可点退出键完成操作。

可分为以下几个功能模块:1)主程序:定时器中断初始化、时钟与温度更新程序与键盘监控。

2)计时:为定时器中断服务子程序,完成刷新计时缓冲区的功能。

3)农历:由阴历换算对照表得出阳历并显示。

4)闹钟:采用定时器中断方式实现闹钟与整点报时。

5) 温度:由温度传感器将温度传送到LCD显示。

6)设置:由按键设置闹钟时间或时钟时间。

7)键盘扫描:判断是否有键按下,并确定键号。

8)LCD显示:完成8位动态显示。

四、硬件电路设计1. 总体设计系统包括单片机主控模块,温度传感器采集模块,日历时钟模块,按键调整模块,蜂鸣器模块,闹钟模块。

如图1所示为系统设计图。

图1 系统设计图如图2所示为系统仿真图。

图2 系统仿真图2. 晶振电路如图3所示,51单片机的内部有一个用于构成振荡器的高增益反相放大器,它的输入端为XTAL1引脚,输出端为XTAL2引脚,两个跨接石英晶体及两个电容就可以构成稳定的自激振荡器。

电容器通常取30pF左右。

图3 晶振电路图4 复位电路3. 复位电路往单片机的复位引脚上输入24个时钟周期以上的高电平,即执行复位操作。

按键复位是指系统在运行时,按下一个开关,就能在RST引脚产生一段时间的高电平,使系统复位,常见的按键复位电路如图4所示。

对12MHz晶振频率而言,电路中C取10pF,R取1KΩ。

4. DS1302时钟电路DS1302是一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。

采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。

DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。

DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后背电源双电源引脚,同时提供了对后背电源进行涓细电流充电的能力。

图5示出DS1302的引脚排列,其中Vcc1为后备电源,Vcc2为主电源。

在主电源关闭的情况下,也能保持时钟的连续运行。

DS1302由Vcc1或Vcc2两者中的较大者供电。

当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。

当Vcc2小于Vcc1时,DS1302由Vcc1供电。

X1和X2是振荡源,外接32.768KHz晶振。

RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。

RST 输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。

当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。

如果在传送过程中RSTS 置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。

上电动行时,在Vcc 大于等于2.5V之前,RST必须保持低电平。

中有在SCLK为低电平时,才能将RST置为高电平,I/O为串行数据输入端(双向)。

SCLK始终是输入端。

图5 DS1302时钟芯片图6 温度采集系统电路5. 温度采集系统电路在本万年历当中温度的采集采用数字温度传感器DS18B20。

它属于单总线器件,具有线路简单,体积小的特点。

因此用它来组成一个测温系统,具有线路简单,在一根通信线,可以挂很多这样的数字温度计,十分方便。

具有如下的经济特点:(1)只要求一个端口即可实现通信。

(2)在DS18B20中的每个器件上都有独一无二的序列号。

(3)实际应用中不需要外部任何元器件即可实现测温。

(4)测量温度范围在-55。

C到+125。

C之间。

(5)数字温度计的分辨率用户可以从9位到12位选择。

(6)内部有温度上、下限告警设置。

如图6所示。

6. 按键调整电路按键采用4个独立的按键,一个功能键、一个退出键、一个加按键、一个减按键通过这四个按键可以来合理的设置时钟的调整和闹铃的设置等。

如图7所示与51单片机的P0.0~P0.3的连接示意图。

图7 按键调整电路7. 闹钟提示电路当到达整点时或者当前的时间等于51单片机中设置闹钟时间时蜂鸣器便会发出声音进行提示。

与单片机P0.5引脚的连接电路如图8所示。

图8 闹钟提示电路图9 LCD显示电路8. LCD显示电路在本万年历当中12864液晶显示当前的实时时间重要的阴阳历节日等功能。

12864液晶具有如下的特性:1)提供8位,4位并行接口及串行接口可选2)并行接口适配M6800时序3)自动电源启动复位功能4)内部自建振荡源64×16位字符显示RAM(DDRAM最多16字符×4行,LCD显示范围16×2行)(改为半角输入) 2M位中文字型ROM(CGROM),总共提供8192个中文字型(16×16点阵)16K位半宽字型ROM(HCGROM),总共提供126个西文字型(16×8点阵)64×16位字符产生RAM(CGRAM)15×16位总共240点的ICONRAM(ICONRAM)其与单片机的连接电路如图9所示。

五、软件设计框图1. 主程序流程图:2. 阴阳历转换流程图:六、程序源代码1. 主程序#include < reg52.h >#include < nongli.h >#include < lcd.h >#include < shezhi.h >#include < time.h>#include < wendu.h >#include < key.h >#define uchar unsigned char#define uint unsigned int/******************************************************************** sbit bell = P2 ^ 0; //定义蜂鸣器端口/******************************************************************** 定时器设置整点报时********************************************************************/ void Timer0_Service() interrupt 1{static uchar count = 0;static uchar flag = 0; //记录鸣叫的次数count = 0;TR0 = 0; //关闭Timer0TH0 = 0x3c;TL0 = 0XB0; //延时 50 msTR0 = 1 ; //启动Timer0count ++;if( count == 20 ) //鸣叫 1 秒{bell = ~ bell;count = 0;flag ++;}if( flag == 6 ){flag = 0;TR0 = 0; //关闭Timer0}}/******************************************************************** 中断服务程序整点报时一分钟********************************************************************/ uchar HexNum_Convert(uchar HexNum)//将16进制数转换成十进制数{uchar Numtemp;Numtemp=(HexNum>>4)*10+(HexNum&0X0F);return Numtemp;}/******************************************************************** * 函数名称:main()* 功能:* 入口参数:* 出口参数:********************************************************************/ void main( void ){uchar clock_time[6] = {0X00,0X59,0X23,0X09,0X04,0X11}; //定义时间变量秒分时日月年uchar alarm_time[2] = { 10, 06}; //闹钟设置alarm_time[0]: 分钟 alarm_time[1] :小时uchar temperature[2]; //定义温度变量 temperature[0] 低8位temperature[1] 高8位Lcd_Initial(); //LCD初始化Clock_Fresh( clock_time ); //时间刷新Clock_Initial( clock_time ); //时钟初试化/***********************中断初始化***************************/EA = 1; //开总中断ET0 = 1; //Timer0 开中断ET2 = 1; //Timer2 开中断TMOD = 0x01 ; //Timer0 工作方式 1RCAP2H = 0x3c;RCAP2L = 0xb0; //Timer2 延时 50 mswhile( 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( ( * clock_time == 0x59 ) && ( * ( clock_time + 1 ) == 0x59 ) ){bell = 0;TR2 = 1; //启动Timer2}//闹钟报警if( * alarm_time == HexNum_Convert(* ( clock_time + 1 ) ))//分钟相吻if( * ( alarm_time + 1 ) == HexNum_Convert(*( clock_time + 2 )) ) //小时相吻合{bell = 0;TR2 = 1; //启动Timer2}}break;}}}2. 温度控制程序#ifndef _SENSOR#define _SENSOR#define uchar unsigned char#define uint unsigned int/***************DS18B20管脚配置*******/sbit dq = P2 ^ 1;/******************************************************************** DS18B20软件延时专用********************************************************************/ void Sensor_Delay(uchar count)//延时函数{while(count--);}/********************************************************************从DS18B20读一个字节********************************************************************/ uchar Sensor_Read_Byte(void){uchar i = 0;uchar temp = 0;for(i=8;i>0;i--){dq = 0; // 给脉冲信号temp >>= 1;dq = 1; // 给脉冲信号if(dq)temp |= 0x80;Sensor_Delay(20);}return (temp);}/********************************************************************向DS18B20写一个字节********************************************************************/ void Sensor_Write_Byte(uchar temp){uchar i = 0;for(i=8;i>0;i--){dq = 0;dq = temp&0x01;Sensor_Delay(20);dq = 1;temp>>=1;}}/******************************************************************** DS18B20初始化********************************************************************/ uchar Sensor_Initial(void){uchar i = 0;dq = 1; // DQ复位Sensor_Delay(1); // 稍做延时dq = 0; // 单片机将DQ拉低Sensor_Delay(100); // 精确延时,大于480usdq = 1; // 拉高总线Sensor_Delay(6); // 稍做延时后i = dq; // 若x=0则初始化成功,若x=1则初始化失败Sensor_Delay(130);return (~i);}/******************************************************************** 读取并显示温度********************************************************************/ void Sensor_Fresh(uchar * temperature ){Sensor_Initial();Sensor_Write_Byte( 0xCC ); // 跳过读序号列号的操作 Sensor_Write_Byte( 0x44 ); // 启动温度转换Sensor_Initial();Sensor_Write_Byte( 0xCC ); // 跳过读序号列号的操作 Sensor_Write_Byte( 0xBE ); // 读取温度寄存器temperature [0] = Sensor_Read_Byte(); //低位temperature [1] = Sensor_Read_Byte(); //高位}#endif3. 日历设置程序#ifndef _SUN_MOON#define _SUN_MOON/******************************************************************** *****/#define uchar unsigned char#define uint unsigned int#include < shezhi.h >#include < lcd.h >/******************************************************************** * 功能: 读取数据表中农历的大月或小月,如果大月返回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_code[calendar_address+temp1]&temp;if(temp==0){return(0);}else{return(1);}}/******************************************************************** * 功能: 输入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_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;elsecalendar_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月/*if( temp1 == 1 )temp3 = temp2 - 1;elsetemp3 = temp2 + 31 - 1;*/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//闰年指的就是阳历有闰日或阴历有闰月的一年;//阳历四年一闰,在二月加一天,这一天叫做闰日://农历三年一闰,五年两闰,十九年七闰,每逢闰年所加的一个月叫做闰月。

相关主题