单片机万年历实现(报告+源码)

基于DS1302时钟/日历芯片的电子万年历的设计与实现

摘要

        近年来随着计算机在社会领域的渗透和大规模集成电路的发展,单片机的应用正在不断地走向深入,由于它具有功能强,体积小,功耗低,价格便宜,工作可靠,使用方便等特点,因此特别适合于与控制有关的系统,越来越广泛地应用于自动控制,消费电子,智能化仪器,仪表,数据采集,信息处理,军工产品以及家用电器等各个领域,单片机往往是作为一个核心部件来使用,在根据具体硬件结构,以及针对具体应用对象特点的软件结合,以作完善。

        本设计尝试使用DS1302实时时钟模块,在LCD1602上显示日期、时、分、,设计完成一个能够设定及调节日期和时间的电子万年历,使用户能够轻松简便的获取当前时间。本文从理论和实践两个角度出发,对一个功能齐全,时间准确,运行稳定的电子万年历进行设计与实现分析。本文首先较为详尽地介绍了面向对象分析与设计的有关概念与技术,特别深入介绍了在本系统中运用到的知识,如:面向对象的分析设计流程;系统中所用到的开发技术特点、结构框架以及使用方法。接着对电子万年历的可行性进行了分析,然后对系统的设计思想、设计目标与系统的整体结构进行了明确的规划。最后对系统的实现效果,应用程序的设计与实现作了较为详细的讲解。本电子万年历是基于DS1302时钟/日历模块,通过C51单片机编程、烧录、运行来实现的。其主要功能有:使用LCD1602显示日期和时间(时、分、秒),可以通过按键设定及修改日期和时间。本文在撰写过程中,力求将理论与实践应用相结合,对各种理论进行阐述的同时配合系统从实际应用和操作技巧上加以说明,希望能够更充分地体现到这些知识与技术在本系统中的应用与实现。

关键词DS1302,LCD1602,软件开发,单片机,万年历

1.概述

1.1课题题目:基于DS1302时钟/日历芯片的电子万年历的设计与实现

1.2系统的主要目标:

        本系统目标是实现电子万年历所需的各种基本功能,能够很好的实现日期及时间的基本功能,还可以实现用户通过按键自行更改时间的功能。

1.3 系统的开发环境及运行环境:

操作系统:  Windows 10

开发工具:  Keil uVision5stc-isp-15xx-v6.89B烧录软件,51单片机开发板

2.系统需求分析

  1. 单片机作为一种成本低,运行稳定的随着网络在全球各地广泛普及,电子万年历是单片机系统的应用,它主要包含硬件和软件结合使用。传统时钟无法满足日期显示要求,传统日历无法满足时间显示要求,电子万年历的产生,完美的解决了两个问题。电子万年历是一种非常广泛的日常计时工具,在现代社会会越来越流行。传统的日历及时钟等时间获取方式正在遭受电子万年历的巨大冲击。电子万年历依托51单片机开发,成本低,运行稳定,功能齐全,以LCD1602显示时间简洁、清晰、准确。随着电子网络技术普及,电子万年历将打破传统认知,广泛运用在多平台,多场合。本依托51单片机开发的电子万年历凭借电子化高效运作,借助单片机运行稳定的特点,可提供用户放心使用。
  2. 各模块功能

表2-1电子万年历系统功能模块表

模块名称

模块功能

LCD1602显示模块

显示日期、时间

DS1302时钟模块

控制时间计时

4x4矩阵按键模块

提供对时间设置操作

电源电路模块

供电、保证时钟运行

3.可行性分析

3.1本设计采用了DS1302时钟芯片实现时钟,其计时准确,接口简单,使用方便,工作电压范围宽,功耗低,满足本设计的要求。DS1302是DS1202的升级产品,与DS1202兼容。

3.2硬件控制电路除了STC89c52rc芯片处理器,DS1302时钟芯片外,还用到了LCD1602液晶显示屏显示时间,根据各芯片的功能互相连接成电子万年历的控制电路。主要由几个子模块构成,包括按键(时间调整)模块,时钟模块,显示模块等。

4.系统详细设计

        电子万年历是一个简单的单片机应用实例,其功能实现包括LCD1602显示模块,SD1302时钟模块,按键(时间调整)模块,其中涉及到的方法和模块的详细设计及流程图如下。

4.1显示模块

        本设计中的显示模块采用LCD1602显示屏,它有体积小,重量轻,功耗低等显著优点,并且特别适用于51系列单片机,在本设计中采用单片机的P0系列端口作为LCD1602的8位数据端口,接至LCD1602的D0-D7引脚,另外LCD1602的RS(指令,数据选择端),RW(指令,数据读写选择端),E(使能端)分别接至单片机的P1.0,P1.1,P2.5引脚。LCD1602电路图如下(图4.1.1),实物图如下(图4.1.2)

 图4.1.1  LCD1602电路图   

 图4.1.2  LCD1602实物图

4.2时钟电路模块

        本设计选用DS1302充电时钟芯片 ,其内部具备VCC1和VCC2两个电源,工作时电路将由两者较大者供电,这是因为如果主电源出现问题时能够始终保持时钟的连续工作,具体描述为VCC1 在单电源和电池操作系统中提供低功率操作,以及低功率电池备份。在使用涓流充电器的系统中,可充电能源连接到这个引脚。VCC2是双电源配置中的主电源引脚。VCC1 连接到备用电源,在没有主电源的情况下维持时间和日期。DS1302将从较大的V运行CC1 或VCC2.当VCC2 大于VCC1 + 0.2 V, VCC2 将为DS1302提供动力。当VCC2 小于VCC1, VCC1 将为DS1302提供动力。x1和x2提供计时脉冲。在工作时需要进行初始化,否则不能正常运行。它是3线的串行接口,占用的引脚相对较少,在内部集成可以编程的时钟日历,操作者可以根据自己对功能的需求,在单片机上进行设置。可以使用外部电源和备份电源双电源供电,当外部电源能源耗尽时,可以使用备份电源使时钟芯片继续运作。本设计中所采用的时钟模块为DS1302时钟模块.它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.0V~5.5V.DS1302有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等.时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容.DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为COH~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读).DS1302采用三线接口与单片机进行同步通信:SCLK用于同步串行接口上的数据移动。I/O(数据输入/输出)- I/O引脚是3线接口的双向数据引脚。RST (Reset)—在读写时,复位信号必须是高的。 DS1302时钟模块电路图如下(图4.2.1):DS1302时钟模块实物图如下(图4.2.2)

图4.2.1 时钟模块电路图

图4.2.2 时钟模块实物图

4.3矩阵按键模块

        本设计采用中断控制扫描的方式来实现CPU对按键的处理。中断控制扫描方式是利用外部中断源,响应输入信号。当无按键按下时,CPU执行正常工作程序。当有按键按下时,CPU立即产生中断。在中断服务子程序中扫描键盈,判断是哪一个键被按下,然后执行该键的功能子程序。它是将各个按键都连接到一个与门上。当有任何一个按键按下时,都会使与门输出为低电平,从而引起单片机的中断,它的好处是不用在主程序中持续不断地循环查询,如果有键按下,单片机再去做相应的处理。4x4矩阵按键电路图如下(图4.3.1):4x4矩阵按键实物图如下(图4.3.2)

 图4.3.1  4x4矩阵按键电路图

   图4.3.2  4x4矩阵按键实物图

4.4电源模块

        电源供电路,是硬件电路工作的基础,本设计中电源的输入电压选择直流10V,由开关电源为系统供电。在图中电源芯片的3脚为电源的输入端。L2为二极管,在电路中的作用是防反接保护,如果操作者将电源接反,系统不会对外供电。D2在电路中起到续流的作用,保护芯片不被产生的感应电压击穿。USB供电电路图如下(图4.4):

 图4.4 电源模块电路图

4.5  51最小系统电路图

51最小系统电路图如下图(图4.5)

 图4.5  51最小系统电路图

4.6电子万年历运行流程图

图4-5 电子万年历运行流程

分析:万年历开始运行时,首先运行初始化函数,将时间初始化,进一步将时间显示在LCD1602液晶显示屏上,应时钟使用后第二次初始化时初始化时间为上一次关闭时钟时的时间,所以需用户选择是否更改时间,若用户选择为否,则直接确定时间并开始运行,若用户选择为是,则进入相应时间调整,使用4x4按键进行调整,具体可调整的时间由光标闪烁指示。最后确定时间并开始运行万年历。

5.系统测试

        在系统测试中,我们首先对各个子模块进行单元测试,即把每一个模块作为一个单独的实体来测试,保证每个模块作为一个单元能正确运行。然后在完成所有模块后,我们将各子模块集成起来,再对它进行系统测试,找出系统设计或编码上的错误,以及验证系统是否实现了指定的功能。最后找几个其他的同学再对系统进行黑盒子测试,验证系统是否满足用户的功能需求。具体实现效果如下:

5.1万年历初始化效果

5.2用户调整时间,

5.2.1按键s4控制光标移动(下图分别为秒、分、时、日、月、年的调整)

 

 

 5.2.2按键s8控制时间上调,s12控制时间下调(下图分别为秒上调、分上调,时下调,日下调、月上调、年下调

 

 

 5.3万年历运行结果

问题分析:在测试的过程,特别是在集成测试之后,发现了许多问题及功能缺陷,最后经过修改调试后都解决了。比如,有一个问题是,月份调整出现了00月和大于12月的月份。在进行了分析之后,发现是因为月份设置没有考虑月份只有1-12月,在重新添加月份设置限制条件之后,问题得到了解决。

6.设计总结或结论

        本设计以STC89c52rc单片机为主控器,使用DS1302时钟芯片、LCD1602显示模块,利用Keil uVision5stc-isp-15xx-v6.89B烧录软件,51单片机开发板进行了基于单片机的电子万年历开发。通过各个硬件设备的特点设计来了相应的软件系统,达到了本设计的目的,是一款实用且非常有可开发性的产品,设计结构简单、调试方便、成本低、功耗低。采用模块化的设计思想,设计开发周期短,各个模块具有独立性,有很大的扩展空间。

        本系统基本实现了关键的功能模块,在功能上基本满足了用户的需求,但是由于时间较紧,有些模块以及整个系统还有许多不完善的地方,如操作性不太友好,时间调整不够方便快捷等。

        通过这次软件工程课程设计开发的实践训练,进一步掌握软件工程的方法和技术,提高软件开发的实际能力,培养工程设计能力和综合分析、解决问题的能力。学习和实践在分析和设计计算机应用系统所需要的知识,包括面向对象的系统分析与设计,编码和测试方面的知识,并将其运用于软件开发的全过程;进一步加强和提高软件工程文档的编写能力;培养协作能力和团队精神。

附源码如下:

//1.实验板上S4键为功能键,按下后可选择调节哪个部分。
//2.S8为数字增大键。
//3.S12为数字减小键。
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar a,miao,shi,fen,ri,yue,nian,flag,temp,key1n;
#define yh 0x80  //第一行的初始位置
#define er 0x80+0x40 //第二行初始位置
sbit dula=P2^6;
sbit wela=P2^7;
sbit wr=P1^1;
sbit rs=P1^0;
sbit en=P2^5;
sbit SCLK=P2^1;  // 6  P1.1
sbit IO=P2^0;    // 7  P1.0
sbit RST=P2^4;   //时钟 5  P1.2
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
sbit key=P3^7;
sbit key1=P3^0;
sbit key2=P3^1;
sbit key3=P3^2;
uchar code tab1[]={"2022-  -      "};
uchar code tab2[]={"  :  :  "};
void delay(uint xms)//延时函数
{
	uint x,y;
	for(x=xms;x>0;x--)
	 for(y=110;y>0;y--);
}
/******************液晶写入************************/
write_1602com(uchar com)//液晶写入指令函数
{
	rs=0;//置为写入命令
	P0=com;//送入数据
	delay(1);
	en=1;//拉高使能端
	delay(1);
	en=0;//完成高脉冲
}
write_1602dat(uchar dat)
{
	rs=1;//置为写入数据
	P0=dat;//送入数据
	delay(1);
	en=1;
	delay(1);
	en=0;
}
/*********************over***********************/
/********************ds1302****************************/
void write_byte(uchar dat)
{
	ACC=dat;
	RST=1;
	for(a=8;a>0;a--)
	{
		IO=ACC0;
		SCLK=0;
		SCLK=1;
		ACC=ACC>>1;
	}
}
uchar read_byte()
{
	RST=1;
	for(a=8;a>0;a--)
	{
		ACC7=IO;
		SCLK=1;
		SCLK=0;
		ACC=ACC>>1;

	}
	return (ACC);
}
void write_1302(uchar add,uchar dat)
{

	RST=0;
	SCLK=0;
	RST=1;
	write_byte(add);
	write_byte(dat);
	SCLK=1;
	RST=0;
}
uchar read_1302(uchar add)
{
	uchar temp;
	RST=0;
	SCLK=0;
	RST=1;
	write_byte(add);
	temp=read_byte();
	SCLK=1;
	RST=0;
	return(temp);
}
uchar BCD_Decimal(uchar bcd)
{
 uchar Decimal;
 Decimal=bcd>>4;
 return(Decimal=Decimal*10+(bcd&=0x0F));
}
void ds1302_init()
{
RST=0;
SCLK=0;
write_1302(0x80,miao|0x00);//允许写
}


void write_sfm(uchar add,uchar dat)//写时分秒
{
	uchar gw,sw;
	gw=dat%10;
	sw=dat/10;
	write_1602com(er+add);
	write_1602dat(0x30+sw);
	write_1602dat(0x30+gw);
				
}
void write_nyr(uchar add,uchar dat)
{
	uchar gw,sw;
	gw=dat%10;
	sw=dat/10;
	write_1602com(yh+add);
	write_1602dat(0x30+sw);
	write_1602dat(0x30+gw);
	
}

lcd_init()//液晶初始化函数
{       wr=0;
	write_1602com(0x38);//设置液晶工作模式
	write_1602com(0x0c);//开显示不显示光标
	write_1602com(0x06);//整屏不移动,指针加一
	write_1602com(0x01);
	write_1602com(yh+2);//字符写入的位置
	for(a=0;a<14;a++)
	{
	write_1602dat(tab1[a]);
	//delay(3);
	}
	write_1602com(er+4);
	for(a=0;a<8;a++)
	{
	write_1602dat(tab2[a]);
	//delay(3);
	}

}

void keyscan()
{
   key=0;
	if(key1==0)//key1为功能键
	{
	delay(5);
	if(key1==0)
	{
	while(!key1);
	key1n++;
	if(key1n==9)
		key1n=1;
	switch(key1n)
	{
	
	case 1: TR0=0;//关闭定时器
			write_1602com(er+0x0b);//写入光标位置
		   	write_1602com(0x0f);//设置光标为闪烁
		   	temp=(miao)/10*16+(miao)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x80,0x80|temp);//miao
		   	write_1302(0x8e,0x80);
		   	break;
	case 2:  write_1602com(er+8);//fen
			break;
	case 3: write_1602com(er+5);//shi
			break;
	case 4: write_1602com(yh+0x0b);//ri
			break;
	case 5: write_1602com(yh+0x08);//yue
			break;
	case 6: write_1602com(yh+0x05);//nian
			break;
	case 7:
			write_1602com(0x0c);//设置光标不闪烁
			TR0=1;//打开定时器
			temp=(miao)/10*16+(miao)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x80,0x00|temp);//miao
		   	write_1302(0x8e,0x80);
            break;								
	}
	}
	}
	if(key1n!=0)//当key1按下以下。再按以下键才有效
	{
	
	if(key2==0)  //上调键
	{
	delay(5);
	if(key2==0)
	{
	while(!key2);
	switch(key1n)
	{
	case 1:miao++;
			if(miao==60)
				miao=0;
			write_sfm(0x0a,miao);//写入新的秒数
			temp=(miao)/10*16+(miao)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x80,0x80|temp);
		   	write_1302(0x8e,0x80);
			write_1602com(er+0x0b);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
			break;
	case 2:fen++;
			if(fen==60)
				fen=0;
			write_sfm(0x07,fen);
			temp=(fen)/10*16+(fen)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x82,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(er+8);
			break;
	case 3:shi++;
			if(shi==24)
				shi=0;
			write_sfm(4,shi);
			temp=(shi)/10*16+(shi)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x84,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(er+5);
			break;
	case 4:ri++;
			if(ri==31)
				ri=0;
			write_nyr(10,ri);
			temp=(ri)/10*16+(ri)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x86,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(yh+11);
			break;
	case 5:yue++;
			if(yue==13)
				yue=1;
			write_nyr(7,yue);
			temp=(yue)/10*16+(yue)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x88,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(yh+8);
			break;
	case 6:nian++;
		 if(nian==100)
			nian=0;
			write_nyr(4,nian);
			write_1602com(yh+5);
			break;
	}

	}

	}
	if(key3==0)
	{
	delay(5);
	if(key3==0)
	{
	while(!key3);
	switch(key1n)
	{
		case 1:miao--;
				if(miao==-1)
					miao=59;
			write_sfm(0x0a,miao);//写入新的秒数
			write_1602com(er+0x0b);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置
			break;
		case 2:fen--;
				if(fen==-1)
					fen=59;
			write_sfm(7,fen);
			temp=(fen)/10*16+(fen)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x82,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(er+8);
			break;

		case 3:shi--;
			   if(shi==-1)
					shi=23;
			write_sfm(4,shi);
			temp=(shi)/10*16+(shi)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x84,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(er+5);
			break;
		case 4:ri--;
				if(ri==-1)
					ri=30;
			write_nyr(10,ri);
			temp=(ri)/10*16+(ri)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x86,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(yh+11);
			break;
		case 5:yue--;
				if(yue==0)
					yue=12;
			write_nyr(7,yue);
			temp=(yue)/10*16+(yue)%10;
		   	write_1302(0x8e,0x00);
		   	write_1302(0x88,temp);
		   	write_1302(0x8e,0x80);
			write_1602com(yh+8);
			break;	
		case 6:nian--;
		 if(nian==-1)
			nian=99;
			write_nyr(4,nian);
			write_1602com(yh+5);
			break;	
	}

	}

	}

	}

}

void init()
{
	TMOD=0x11;
	TH0=0;
	TL0=0;
	EA=1;
	ET0=1;
	TR0=1;

}

void cmg(void)//数码管锁存函数
{
dula=1;
P0=0x00;
dula=0;
wela=1;
P0=0x00;
wela=0;
}
void main()
{

   cmg();//数码管锁存
	lcd_init();
	ds1302_init();
	init();
	while(1)
	{

keyscan();

    }
}

void timer0() interrupt 1
{
	miao = BCD_Decimal(read_1302(0x81));
	fen = BCD_Decimal(read_1302(0x83));
	shi  = BCD_Decimal(read_1302(0x85));
	ri  = BCD_Decimal(read_1302(0x87));
	yue = BCD_Decimal(read_1302(0x89));
	nian=BCD_Decimal(read_1302(0x8d));
	write_sfm(10,miao);
	write_sfm(7,fen);
	write_sfm(4,shi);
	write_nyr(10,ri);
    write_nyr(7,yue);
}

以上内容为本人练习,仅供参考,若读者发现内容有误请私信指正,谢谢~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你代码有bug!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值