目录

摘  要

1.课程设计任务

1.1课程设计题目

1.2设计的要求 

2.设计总体方案

2.1初步设计方案

2.2芯片的选型

2.2.1时钟芯片的选择

2.2.2温度传感器的选择

2.2.3显示电路的选择

2.2.4输入按键的选择

2.2.5控制电路芯片的选择

2.3总体方案

3.单元模块设计

3.1显示模块

3.2按键模块

3.3蜂鸣器模块

3.4 DS1302时钟芯片模块

3.5 DS18B20温度传感器

4.软件流程图

5.系统电路设计

5.1整体电路

5.2仿真结果

6.系统调试

6.1ds1302读取系统时间出现问题

6.2修改调整的方法

6.3系统设计总结

7.总结

7.1心得体会

7.2系统的优缺点

7.3设计的改进思路

参考文献


摘  要

电子时钟是一种广泛应用于现代社会的计时工具,它可以对年、月、日、时、分和秒等多个时间信息进行计时,并且越来越受到欢迎。采用1602液晶可以使数字电子时钟直观地显示时间信息,包括年、月、日、时、分、秒等,并且具有时间设置等功能。本设计是基于51系列单片机进行的电子时钟设计,具有显示时间、星期和时间设置等功能,同时采用1602液晶显示。在硬件制作和软件编程方面进行同步设计,其中硬件部分包括STC89C52单片机、液晶显示电路和按键调节电路。软件部分包括时间调整程序、液晶显示程序和中断服务程序,所有程序均采用高级C语言编写。最终,经过大量资料查询和指导老师的帮助,小组成功完成了电子时钟的设计和Protues仿真。

关键词 AT89C52  电子钟;  单片机。

1.课程设计任务
1.1课程设计题目

基于51单片机的数字电子钟设计

1.2设计的要求 

用控制器实现一个数字电子钟,具体要求如下。

设计要求:

(1)控制器可选用单片机、ARM 或可编程逻辑器件,显示器件和按键无限

制。

(2) 有基本的时间功能,可切换 24 小时制或 12 小时制显示。

(3)可设定日期(年月日,星期)。

(4)可设定 4 组以上的闹钟,掉电后不丢失。

发挥部分

(5)如何提高时钟的走时精度(一天的误差秒数)。

(6)尽量降低功耗,使用电池供电。

(7)有万年历功能,能通过互联网络自动校准时间。

2.设计总体方案
2.1初步设计方案

按照系统总体要求,用功能模块框图表达整体功能,如图1所示。

图1 功能模块框图

该系统由51单片机、时钟芯片、温度传感器、LCD液晶显示屏、按键、蜂鸣器等组成。其中,时钟芯片作为时钟模块,实现对时间的读取和设置;温度传感器,获取当前温度信息;LCD液晶显示屏用于显示时间和其他相关信息;按键用于对时钟进行设置和调整;蜂鸣器用于发出闹铃提示。

在该系统中,51单片机作为主控芯片,通过和时钟芯片的通信,实现对时间的读取和设置,通过温度传感器获取当前温度信息;并通过LCD液晶显示屏显示当前时间和温度;通过按键进行时钟的设置和调整;当闹铃时间到达时,蜂鸣器会发出提示声音。

2.2芯片的选型
2.2.1时钟芯片的选择

方案一:DS1302这是一款精度高、价格便宜、容易集成和编程的时钟芯片。它具有存储日历信息和时钟时间的能力,并提供可编程的时钟输出。

方案二:DS3231这是一款高精度、低功耗的时钟芯片,它的时钟精度可达到2ppm。它具有年、月、日、星期、时、分和秒等7种计时功能,支持两种闹钟设置。

方案三:PCF8583这是一款具有计时和定时功能的单芯片RTC时钟,适用于需要精确计时的应用。

2.2.2温度传感器的选择

方案一:DS18B20是一款数字温度传感器芯片,具有高精度、数字输出、可编程分辨率等特点。它采用单总线接口,可以通过单一数据线与微控制器通讯,体积小、使用方便。

方案二:LM35是一款精度高、输出线性、温度范围广的模拟温度传感器芯片,可以直接输出温度值,输出电压与温度成线性关系。

2.2.3显示电路的选择

方案一:采用四位共阳极数码管显示,动态扫描显示方式;

方案二:采用液晶显示屏LCD 显示。

2.2.4输入按键的选择

方案一:采用 4*4键盘;

方案二:采用 4*1按键;

2.2.5控制电路芯片的选择

方案一: 51 单片机;

方案二: ARM

2.3总体方案

设计要求的综合考虑是指在设计系统时,需要综合考虑各种因素,如功能要求、硬件成本、软件实现难度、系统性能等,以选定一个最优的总体方案。在该系统中,根据设计要求的综合考虑,选定的总体方案包括51单片机、DS1302时钟芯片、DS18B20温度传感器、LCD液晶显示屏、8*1按键和蜂鸣器等组件。

其中,DS1302时钟芯片作为时钟模块,通过单片机的SPI总线进行通信,实现对时间的读取和设置;DS18B20温度传感器通过单片机的单总线进行通信,获取当前温度信息。LCD液晶显示屏用于显示时间和其他相关信息。8*1按键用于对时钟进行设置和调整,提供了更加便捷的操作方式。而蜂鸣器则用于发出闹铃提示,增加了系统的实用性和用户体验。

作为主控芯片,51单片机通过和DS1302时钟芯片的通信,实现对时间的读取和设置,并通过DS18B20温度传感器获取当前温度信息。LCD液晶显示屏可以显示当前时间和温度,提高了系统的可视性和易用性。同时,按键可以进行时钟的设置和调整,让用户能够更加方便地对系统进行操作。当闹铃时间到达时,蜂鸣器会发出提示声音,提醒用户。

因此,该系统通过综合考虑各种因素,选定了最优的总体方案,实现了时钟、温度显示和闹铃提示等多种功能,提高了系统的可用性和用户体验。

3.单元模块设计

硬件电路的模块设计包括显示模块、按键模块、闹钟的蜂鸣器模块、DS1302时钟芯片模块、DS18B20温度传感器。

3.1显示模块

LCD1602是一种16字符×2行的液晶显示屏,它使用液晶技术来显示文本和图形。其工作原理是通过液晶分子的旋转来控制光的透过和反射,从而实现图像和文字的显示。LCD液晶显示屏通过并行接口或串行接口与主控芯片进行通信,以实现图像和文字的显示。

 图2 显示模块图

3.2按键模块

按键是一种常见的机械开关,其工作原理是当按键按下时,开关内部的触点短接,导通电路,从而传递按键事件到主控芯片中,实现对系统的设置和调整等功能。

图3 按键模块图

3.3蜂鸣器模块

蜂鸣器是一种能够发出声音的电子元件,其工作原理是通过交替的电压信号来驱动蜂鸣器内部的振荡器,从而产生声音。在该系统中,蜂鸣器用于发出闹铃提示,增加了系统的实用性和用户体验。

图4 蜂鸣器模块图

3.4 DS1302时钟芯片模块

DS1302时钟芯片是一种实时时钟模块,其工作原理是通过一个晶体振荡器产生一个高频信号,然后通过分频电路将其分频为1Hz的信号,作为实时时钟的时钟源。该芯片还内置了时钟和日历计数器,并通过SPI总线与主控芯片通信,实现对时间的读取和设置。

图5 时钟芯片图

3.5 DS18B20温度传感器

DS18B20温度传感器是一种数字温度传感器,其工作原理是基于热敏电阻效应。在DS18B20中,一个独特的ROM存储器存储了器件的唯一序列号,并与传感器的温度传感电路相连。当传感器测量温度时,温度值将被转换为数字信号,并通过单总线接口传输到主控芯片中。

图6 温度传感器图

4.软件流程图

软件是该系统的重要组成部分,在系统的软件设计中我们也才用了模块化设计,将系统的各部分功能编写成子模块的形式,这样增强了系统软件的可读性和可移植性。软件系统流程图见图8。

图8 软件流程图 

时间设置、闹钟设置和温度显示是闹钟应用程序中三个重要的功能,它们可以被用户选择并独立运行。这些功能之间没有必要的先后顺序,用户可以按照自己的需要和喜好选择运行它们。

时间设置是闹钟应用程序中的一个核心功能,它允许用户设置所需的时间,例如小时和分钟。在程序内存中存储时间数据后,用户可以随时查看所设置的时间,并在必要时进行修改。

闹钟设置是另一个重要的功能,它允许用户设置多个闹钟。一旦设置完毕,程序将在预定时间响铃提醒用户。

温度显示是一个额外的功能,它可以帮助用户监测周围环境的温度。在这个模块中,程序从传感器获取环境温度,并将其显示在用户界面上。这个功能可以帮助用户了解周围环境的温度,以便做出更好的决策。

总之,时间设置、闹钟设置和温度显示是闹钟应用程序中的三个核心功能,用户可以根据需要和喜好选择运行它们。

5.系统电路设计
5.1整体电路

图7 整体电路图 

5.2仿真结果

5.2.1有基本的时间功能,可切换 24 小时制或 12 小时制显示

图8 仿真结果一 

5.2.2可设定日期(年月日,星期)

在该系统中,设定日期是一个重要的功能,它通过定时器0中断工作方式1来实现。定时器0是单片机中常用的计时器之一,它可以在定时器计数器计数到设定值时触发一个中断。这种中断方式可以使单片机能够精确计时,并且在一定程度上减少了单片机对CPU的占用率。

在设定日期时,系统使用定时器0中断工作方式1来实现闪烁功能。闪烁是一种使指定位以一定频次快速闪烁的效果,可以提醒用户该位正在进行设置。在该系统中,设定位以500ms频次闪烁来进行设置,这意味着该位会以每隔500ms的频率闪烁一次,提醒用户该位正在进行设置。

通过定时器0中断工作方式1,系统可以精确地控制闪烁的频率,并且可以在单片机计算其他操作时同时完成设定日期的功能,提高了系统的效率。因此,该系统的设计合理,实现了较为稳定和可靠的日期设定功能。

图9 仿真结果二

5.2.3可设定 4 组以上的闹钟,掉电后不丢失

在使用闹钟时,我们需要按下闹钟按键来设置闹钟时间。这是一个必要的步骤,因为只有设置好时间,闹钟才能在预定时间响起。一旦我们完成了时间的设置,我们就可以按下闹钟标志按键来开启闹钟。这个按键的作用是告诉闹钟设备,在预定时间响起闹铃。通过这样的设置,我们可以在需要的时间受到提醒,从而更好地规划时间并完成任务。因此,按下闹钟按键并设置好时间,然后按下闹钟标志按键,是使用闹钟的两个关键步骤。

 

图10 仿真结果三

6.系统调试
6.1ds1302读取系统时间出现问题

DS1302是一款实时时钟芯片,通常用于单片机等嵌入式系统中。在读取系统时间时遇到问题存在以下几种可能:

(1)DS1302芯片未正确连接到系统中,需要检查硬件电路连接是否正确,包括引脚、电源等连接是否正常。

(2)DS1302芯片未正确配置,需要检查芯片寄存器的配置是否正确,例如时钟频率、读写模式等。

(3)软件程序中读取DS1302芯片的时间方式不正确,需要检查程序中的代码是否正确。例如,读取时间时需要先发送读取命令,并等待芯片响应后再读取时间数据。

(4)系统中存在其他影响DS1302读取时间的问题,例如电源电压不稳定等问题。

6.2修改调整的方法

为了解决DS1302读取系统时间的问题,可以采取以下方法:

(1)检查硬件连接是否正确,包括引脚、电源等连接是否正常。

(2)检查芯片寄存器的配置是否正确,包括时钟频率、读写模式等设置是否正确。

(3)检查软件程序中读取DS1302芯片的时间方式是否正确,需要仔细检查程序代码,特别是读取时间的代码部分。

(4)如有必要,可以通过示波器等工具检测DS1302芯片的输出信号,以确定是否存在硬件连接或配置问题。

6.3系统设计总结

硬件设计:51单片机电子钟的硬件设计包括主频选取、时钟电路、LCD液晶显示屏、按键、温度传感器、蜂鸣器等。其中,时钟电路是实现电子钟功能的核心,通常采用DS1302时钟芯片,可精准地获取当前时间并进行显示。温度传感器可以实时监测当前环境温度,以便于显示室内温度。同时,蜂鸣器也是电子钟的必备组件之一,可用于闹钟提示等功能。

软件设计:51单片机电子钟的软件设计包括时钟芯片驱动程序、LCD液晶显示程序、按键扫描程序、温度采集程序等。其中,时钟芯片驱动程序是实现时钟功能的核心,通过读取DS1302时钟芯片的寄存器来获取时间信息并进行显示。LCD液晶显示程序主要用于实现对当前时间和室内温度的显示。按键扫描程序用于检测按键是否被按下,并对按键进行相应的处理。温度采集程序则用于实时采集温度传感器的温度数据并进行显示。

整体设计:51单片机电子钟的整体设计需要充分考虑硬件和软件的配合,以确保系统的稳定性和可靠性。在实现功能的同时,还需要注意到系统的实用性,例如合理布局显示屏幕的显示内容,便于用户阅读。同时还需要注意到电子钟的精度和稳定性,应定期进行时间校准和温度校准。

总之,51单片机电子钟设计需要充分考虑硬件和软件的设计,并进行整体优化。通过合理的设计和优化,可以实现功能完备、稳定可靠的电子钟系统。

7.总结
7.1心得体会

在此次的课程设计中,我深刻认识到自身在编程和调试方面的不足,发现自己所学的知识还有很多需要补充和提高。经过资料的收集和文件的检索,我找到了解决问题的方法,顺利完成了此次课程设计的任务。通过实践,我不仅学到了许多新的知识,还深化了课堂上学到的理论,并增强了自己将理论与实际结合的操作能力。在这个过程中,我获得了宝贵的经验,例如理论必须与实践相结合才能得以应用,知识必须通过实践才能实现其价值。此外,我还提高了自己的动手能力,为今后的学习和工作奠定了良好的基础。

7.2系统的优缺点

该系统的组成使用了多种元件,包括51单片机、DS1302时钟芯片、DS18B20温度传感器、LCD液晶显示屏、8*1按键和蜂鸣器。其优缺点如下:

优点:

(1)高精度:DS1302时钟芯片具有高精度的计时功能,可以确保电子钟的时间准确性。

(2)多功能:除了显示时间之外,还可以显示室内温度,并且可以通过按键设置闹钟功能等,功能较为全面。

(3)显示效果好:使用LCD液晶显示屏,可以显示清晰、鲜艳的数字,同时可以调节亮度,适应不同环境。

(4)操作简便:使用8*1按键,可以方便地进行时间、闹钟等设置操作。

缺点:

(1)复杂度高:该数字电子钟的设计较为复杂,需要使用多种元件,需要对这些元件的使用方法和接线有一定的了解。

(2)成本较高:由于使用了多种元件,所以成本相对较高。

(3)维护困难:如果其中某个元件出现故障,维修和更换难度较大。

总的来说,该数字电子钟的优点在于功能多、显示效果好、操作简便等方面,但同时也存在复杂度高、成本高等缺点。如果在设计时考虑到这些缺点,可以在使用过程中更好地避免和解决相关问题。

7.3设计的改进思路

在该系统的按键检测中,采用了while循环等待按键状态改变,这种方式容易导致死循环问题。当按键一直处于相同状态时,程序就会一直在while循环中等待,无法进行其他操作,导致程序停止响应。

为了解决这个问题,可以采用以下改进思路:

(1)使用定时器中断检测按键状态:使用定时器中断来周期性地检测按键状态,这种方式可以有效避免死循环等待问题,提高程序的响应速度。

(2)使用状态机设计按键检测:采用状态机的方式来设计按键检测程序,通过检测按键状态的变化来切换状态机的状态,从而避免死循环等待问题。

(3)使用中断检测按键状态:使用外部中断或者内部中断来检测按键状态的变化,这种方式可以在按键状态改变时立即进行响应,避免死循环等待问题。

(4)使用其他方法检测按键状态:例如采用电容触摸按键、红外线接收等方式来检测按键状态,这种方式可以避免使用传统按键开关带来的死循环等待问题。

总之,在单片机的按键检测中,需要注意避免死循环等待问题,采用合适的检测方式来确保程序的响应速度和稳定性。

参考文献

[1]张晔,等.单片机应用技术[M].北京:高等教育出版社,2006,6.

[2]谭家玉,郑大宇,等.单片机原理及接口技术[M].哈尔滨:哈尔滨工业大学出版社,2003,5.

[3]周凤娥, 闫祖霞. 基于51单片机的数字电子钟的设计与实现[J]. 计算机与数字工程, 2019, 47(10): 205-206.

[4]李海峰, 任杰. 基于51单片机的数字电子钟的设计与实现[J]. 计算机与数字工程, 2018, 46(11): 184-186.

[5]王传博, 王威. 基于单片机的风力发电机控制系统的研究与设计[J]. 机电工程, 2019, 36(2):69-72.

[6]陈建强, 王志豪, 魏锋. 基于单片机的消防报警系统的设计与实现[J]. 消防科学与技术, 2019, 38(6):98-101.

[7]王荣华, 李想. 基于单片机的自动化化学分析仪的研究与设计[J]. 化学教育, 2019, 40(6):49-51.

[8]朱德杰, 陈鹏. 基于单片机的航空监控系统的研究与设计[J]. 航空制导技术, 2019, 38(3):78-82.

[9]李光辉, 赵淑贤. 基于单片机的自动售货机控制系统的研究与设计[J]. 电脑知识与技术, 2019, 15(14):48-51.

[10]张维, 杨强. 基于单片机的工业自动化系统设计与应用[J]. 机械科学与技术, 2019, 38(6):108-112.

附件

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"
#include "Delay.h"
#include "DS18B20.h"

unsigned char i,KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

unsigned char bell_data1[6]={0,0,0,20,30,0};
unsigned char bell_data2[6]={0,0,0,20,30,0};
unsigned char bell_data3[6]={0,0,0,20,30,0};
unsigned char bell_data4[6]={0,0,0,20,30,0};
unsigned char bell_flag=0;   //闹钟功能开关
unsigned char bell_onoff=0;  //闹钟开关

float T;

sbit BEEP=P2^2;//蜂鸣器

void TimeShow(unsigned char *time)//时间显示功能
{
	DS1302_ReadTime();//读取时间
	LCD_ShowNum(1,1,time[0],2);//显示年
	LCD_ShowNum(1,4,time[1],2);//显示月
	LCD_ShowNum(1,7,time[2],2);//显示日
	LCD_ShowNum(2,1,time[3],2);//显示时
	LCD_ShowNum(2,4,time[4],2);//显示分
	LCD_ShowNum(2,7,time[5],2);//显示秒
	LCD_ShowNum(1,10,time[6],1);//显示星期
}

void TimeSet(void)//时间设置功能
{
//		Timer0Init();
	if(KeyNum==2)//按键2按下
	{
		TimeSetSelect++;//设置选择位加1
		TimeSetSelect%=7;//越界清零
	}
	if(KeyNum==3)//按键3按下
	{
		DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
		if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
		if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
		if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
		{
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
			}
			else
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
			}
		}
		if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
		if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
		if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
		if(DS1302_Time[6]>7){DS1302_Time[6]=1;}//星期越界判断
	}
	if(KeyNum==4)//按键3按下
	{
		DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
		if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
		if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
		if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || 
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
			}
			else
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
			}
		}
		if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
		if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
		if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
		if(DS1302_Time[6]<1){DS1302_Time[6]=7;}//星期越界判断
	}
	//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
	if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
	if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
	else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
	if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
	else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
	if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
	else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
	if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
	else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
	if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
	else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
  if(TimeSetSelect==6 && TimeSetFlashFlag==1){LCD_ShowString(1,10," ");}
	else {LCD_ShowNum(1,10,DS1302_Time[6],1);}
}

void main()
{
	unsigned char set_bell=0;
	unsigned char set_shift_bell=0;
	
	LCD_Init();
	DS1302_Init();
	Timer0Init();

	LCD_ShowString(1,1,"  -  -  ");//静态字符初始化显示
	LCD_ShowString(2,1,"  :  :  ");
	
//	DS1302_SetTime(DS1302_Time);//设置时间
	DS1302_ReadTime();
	DS1302_SetTime(DS1302_Time);//设置时间
	
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(100);			    //等待转换完成
	

	while(1)
	{
		DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度

		KeyNum=Key();//读取键码
		if(KeyNum==1)//按键1按下
		{
			if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
			else if(MODE==1){MODE=0;DS1302_SetTime(DS1302_Time);}
		}
		switch(MODE)//根据不同的功能执行不同的函数
		{
			case 0:TimeShow(DS1302_Time);break;
			case 1:TimeSet();break;
		}
		switch(KeyNum)
	{
	case 2:	bell_onoff=0;BEEP=1; break;//¹Ø±ÕÄÖÁ	
	case 3:
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(1,12,'-');	//显示负号
			T=-T;			//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(1,12,'+');	//显示正号
		}
		LCD_ShowNum(1,13,T,2);		//显示温度整数部分
		LCD_ShowChar(1,15,'.');		//显示小数点
		LCD_ShowNum(1,16,(unsigned int)(T*10)%10,1);//显示温度小数部分
    break;		
	case 5: 
		set_bell=1;
	  TimeShow(bell_data1);
			while(set_bell)
			{
				TimeShow(bell_data1);
				Delay(50);Delay(50);Delay(50);
				KeyNum=Key();
				switch(KeyNum)
				{
					case 1: bell_flag=!bell_flag;
										if(bell_flag)LCD_ShowNum(2,10,1,1);
                    else LCD_ShowNum(2,10,0,1);					
									break;
					case 2: if(set_shift_bell++>0)set_shift_bell=0;break;
					case 5: TimeShow(bell_data1);set_bell=0;break;
					case 3: bell_data1[4-set_shift_bell]++;break;
					case 4: bell_data1[4-set_shift_bell]--;break;
					default : break;
				}
			}		break;
	case 6: 
		set_bell=1;
	  TimeShow(bell_data2);
			while(set_bell)
			{
				TimeShow(bell_data2);
				Delay(50);Delay(50);Delay(50);
				KeyNum=Key();
				switch(KeyNum)
				{
					case 1: bell_flag=!bell_flag;
										if(bell_flag)LCD_ShowNum(2,11,2,1);
                    else LCD_ShowNum(2,11,0,1);					
									break;
					case 2: if(set_shift_bell++>0)set_shift_bell=0;break;
					case 6: TimeShow(bell_data2);set_bell=0;break;
					case 3: bell_data2[4-set_shift_bell]++;break;
					case 4: bell_data2[4-set_shift_bell]--;break;
					default : break;
				}
			}		break;
	case 7: 
		set_bell=1;
	  TimeShow(bell_data3);
			while(set_bell)
			{
				TimeShow(bell_data3);
				Delay(50);Delay(50);Delay(50);
				KeyNum=Key();
				switch(KeyNum)
				{
					case 1: bell_flag=!bell_flag;
										if(bell_flag)LCD_ShowNum(2,12,3,1);
                    else LCD_ShowNum(2,12,0,1);					
									break;
					case 2: if(set_shift_bell++>0)set_shift_bell=0;break;
					case 7: TimeShow(bell_data3);set_bell=0;break;
					case 3: bell_data3[4-set_shift_bell]++;break;
					case 4: bell_data3[4-set_shift_bell]--;break;
					default : break;
				}
			}		break;
	case 8: 
		set_bell=1;
	  TimeShow(bell_data4);
			while(set_bell)
			{
				TimeShow(bell_data4);
				Delay(50);Delay(50);Delay(50);
				KeyNum=Key();
				switch(KeyNum)
				{
					case 1: bell_flag=!bell_flag;
										if(bell_flag)LCD_ShowNum(2,13,4,1);
                    else LCD_ShowNum(2,13,0,1);					
									break;
					case 2: if(set_shift_bell++>0)set_shift_bell=0;break;
					case 8: TimeShow(bell_data4);set_bell=0;break;
					case 3: bell_data4[4-set_shift_bell]++;break;
					case 4: bell_data4[4-set_shift_bell]--;break;
					default : break;
				}
			}		break;
	default : break;
	};		
			
	    if((bell_data1[3]==DS1302_Time[3] && bell_data1[4]==DS1302_Time[4] && bell_data1[5]==DS1302_Time[5])||
				   (bell_data2[3]==DS1302_Time[3] && bell_data2[4]==DS1302_Time[4] && bell_data2[5]==DS1302_Time[5])||
			       (bell_data3[3]==DS1302_Time[3] && bell_data3[4]==DS1302_Time[4] && bell_data3[5]==DS1302_Time[5])||
			         (bell_data4[3]==DS1302_Time[3] && bell_data4[4]==DS1302_Time[4] && bell_data4[5]==DS1302_Time[5]))
		{
			bell_onoff=1;
		}
			if(bell_flag && bell_onoff)
			{
			BEEP=!BEEP;
			}
			
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=500)//每500ms进入一次
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
	}
}

转自:https://blog.csdn.net/m0_52474147/article/details/131326052