用Proteus开发电子钟项目

目录

电子钟

一、电子钟项目设计需求

项目概述

功能需求

*时钟走时*:电子时钟应该能够准确计时,显示小时、分钟和秒。

*数码管显示内容如下:*

*按键功能如下:*

*走时要求:*

*闹钟要求:*

*滴答模拟声效:*

*时间调整*:用户可以在此页面下调整时间

*数码管显示内容如下:*

*按键功能如下:*

*闹铃时间调整*:用户通过提供的按键来调整闹钟时间设置。

*数码管显示内容如下:*

*按键功能如下:*

额外要求:

*按键功能如下:*

二、项目实战

应用层变量

变量

标志位

时钟走时 时间调整 闹铃时间调整 倒计时功能

函数定义

变量说明

逻辑流程

时间调整

闹铃时间调整

倒计时功能

按键功能实现

蜂鸣器发出100ms提示音和闹钟铃声

Key_Task 函数

Logic_Task 函数

闹钟走时、倒计时走时和模拟滴答声音

函数概述

静态变量

主要逻辑

1. 时间更新

2. 闹钟逻辑

3. 倒计时逻辑

4. 模拟滴答声效

三、最终代码总和


电子钟

一、电子钟项目设计需求

项目概述

本项目的目的是开发一款基于51单片机的电子时钟。该时钟将使用8位数码管来显示时间,并通过四个独立按键实现用户交互,支持时钟走时、时间调整和闹钟功能。

功能需求

该电子钟具有时钟走时、时间调整、闹铃和功能。

*时钟走时*:电子时钟应该能够准确计时,显示小时、分钟和秒。

*数码管显示内容如下:*
*SMG0**SMG1**SMG2**SMG3**SMG4**SMG5**SMG6**SMG7*
小时十位小时个位小数点分钟十位分钟个位小数点秒十位秒个位
*按键功能如下:*

**S7:**** 切换到时间调整状态

**S6:**** 切换闹钟开关

**S5:**** 切换模拟生效开关

**S4:**** 关闭当次闹铃

*走时要求:*

时钟上电即为此状态下,从23:59:51开始走时,以方便测试数据边界。

在此状态下,LED0亮起,离开走时页面时,LED0熄灭。

*闹钟要求:*

若开启闹钟功能,当走时到达设定时间时(小时和分钟参数和设定闹铃时间相同),蜂鸣器会持续响起。

开启了闹钟功能,LED6会亮起,否则,LED6熄灭;上电默认关闭。

当前响起的闹铃声可通过S4关闭。

*滴答模拟声效:*

利用继电器的开合模拟机械走时的滴答声。开启后当秒参数为偶数时继电器断开,为奇数时打开。

开启了模拟声效功能,LED7会亮起,否则,LED7熄灭;上电默认关闭。

*时间调整*:用户可以在此页面下调整时间

*数码管显示内容如下:*
*SMG0**SMG1**SMG2**SMG3**SMG4**SMG5**SMG6**SMG7*
S光标-(中)小时十位小时个位F光标-(中)分钟十位分钟个位

此模式下,可以通过S6按键选择小时和分钟两个走时参数,选中的参数前光标亮起,再通过S5\S4对其进行调整。

进入此页面时的默认值为进入时的 小时 和 分钟 值,光标在小时前。

调整参数不会立刻生效后,再按S7重新切换回走时页面时参数方才生效。

在此状态下,LED1亮起,离开走时页面时,LED1熄灭。

*按键功能如下:*

*S7:* 切换到闹铃时间调整状态

*S6:* 切换选择调整参数

**S5:**** 对选中参数+1

**S4:**** 对选中参数-1

*闹铃时间调整*:用户通过提供的按键来调整闹钟时间设置。

*数码管显示内容如下:*
*SMG0**SMG1**SMG2**SMG3**SMG4**SMG5**SMG6**SMG7*
nL光标-(中)闹铃小时十位闹铃小时个位光标-(中)闹铃分钟十位闹铃分钟个位

此模式下,可以通过S6按键选择闹铃小时和闹铃分钟两个走时参数,选中的参数前光标亮起,再通过S5\S4对其进行调整。

进入此页面时的默认值为之前设置的 小时 和 分钟 值,光标在小时前。初始为00时00分。

在此状态下,LED2亮起,离开走时页面时,LED2熄灭。

*按键功能如下:*

**S7:**** 切换到走时状态,前述调整的走时将会生效,秒参数从0开始计时。

**S6:**** 切换选择调整参数

**S5:**** 对选中参数+1

**S4:**** 对选中参数-1

额外要求:

  1. 每次按键,蜂鸣器都会发出100ms的提示音。

  2. 闹钟铃声变为“滴~滴~滴~滴 ~ (四拍)休止(四拍)”的循环,周期是2秒。

  3. 在闹铃界面和走时界面间再添加一个倒计时功能页面,

此页面下可实现倒计时功能:

  • 每次进入倒计时页面页面,为倒计时参数设定状态(LED4亮起),默认为5分钟(秒),可按S6键增加,10、15、20。。。60分钟几种参数,顺序循环切换。

  • 确定倒计时时间后,按下S5后开始倒计时,倒计时中,按下S5停止倒计时,按下S6回到倒计时参数设定(LED4熄灭)默认状态。

  • 倒计时结束时,时间停在00:00。蜂鸣器报警,按下S6回到倒计时参数设定默认状态,同时关闭报警。

*按键功能如下:*

*S7:* 切换到走时状态,前述调整的走时将会生效,秒参数从0开始计时。

*S6:* 切换选择预设的倒计时参数,每次在倒计时中触发此界面,自动回到进入倒计时的页面的初始状态

*S5:* 开始倒计时,再按停止倒计时

*S4:* 报警后,按下关闭报警,并回到倒计时参数设置初始状态

二、项目实战

应用层变量

变量
  • uchar Page;
    • 用于控制当前的应用页面或模式。0 表示走时模式(即正常显示时间),1 表示调整模式(用于调整时间),2 表示闹铃模式(用于设置或查看闹铃时间)。

  • uchar Hour, Min, Sec;
    • 分别用于存储当前的小时、分钟和秒数。

  • uchar Alarm_Hour, Alarm_Min, Alarm_Sec;
    • 分别用于存储设置的闹铃小时、分钟和秒数。

  • uchar Adj_Hour, Adj_Min, Adj_Sec;
    • 在调整模式下,用于临时存储用户想要调整到的小时、分钟和秒数。

  • uchar Djs_Min, Djs_Sec;
    • 这里Djs是一个倒计时功能的缩写,Djs_Min, Djs_Sec用于存储分钟和秒数。

  • uchar Butten_Flag;
    • 用于记录按钮的按下状态,每次按键,蜂鸣器都会发出100ms的提示音。

标志位
  • bit Alarm_Flag;
    • 闹铃标志位。当设置为1时,表示闹铃已经到达预设时间,需要触发闹铃动作(发出声音)。

  • bit Dida_Flag;
    • Dida是通过Protues的继电器模拟闹钟的滴答声音,Dida_Flag用于滴滴声是否应该被触发。

  • bit Buzzer_Flag;
    • 蜂鸣器标志位。当设置为1时,表示应该激活蜂鸣器以产生声音。

  • bit Djs_Flag;
    • Djs_Flag用于是否开启倒计时功能。

  • bit Adj_Time;
    • 调整时间选择标志位。当设置为1时,表示当前处于时间调整模式,用户可以调整小时、分钟或秒数。

  • bit Alarm_Time;
    • 调整闹铃时间选择标志位。当设置为1时,表示当前处于闹铃时间调整模式,用户可以调整闹铃的小时、分钟或秒数。

/********************应用层变量****************************/
//两个参数
uchar Page;//0是走时,1是调整,2是闹铃
uchar Hour,Min,Sec,Alarm_Hour,Alarm_Min,Alarm_Sec,Adj_Hour,Adj_Min,Adj_Sec,Djs_Min,Djs_Sec;//相关时间的定义
uchar Butten_Flag;
//开关量
bit Alarm_Flag,Dida_Flag,Buzzer_Flag,Djs_Flag;
//状态量
bit Adj_Time;//调整时间参数选择
bit Alarm_Time;//调整闹铃参数选择

时钟走时 时间调整 闹铃时间调整 倒计时功能

这段代码是一个典型的嵌入式或微控制器应用中的任务函数,用于管理和更新显示内容。它每0.5秒(通过检查TickDisplay_Tick之间的差值来确定)更新一次显示的内容,并根据当前的页面(Page变量)来显示不同的信息。下面是这段代码的详细解释:

函数定义
  • 函数名void Display_Task(void)

  • 功能:显示内容执行任务,每0.5秒刷新一次显示内容。

  • 参数:无。

  • 返回值:无。

变量说明
  • static uint Display_Tick;:一个静态变量,用于记录上一次更新显示的时间点(以某种形式的计时器Tick为单位)。静态变量在函数调用之间保持其值不变。

  • Tick:未在代码中直接定义,但假设它是一个全局变量或外部变量,表示当前的时间或计数值(通常来自系统的定时器)。

  • Hour, Min, Sec:分别表示当前时间的小时、分钟和秒。

  • Adj_Hour, Adj_Min, Adj_Sec:在调整模式下,用于存储用户想要调整到的小时、分钟和秒数。但注意,在给出的代码片段中,Adj_Sec并未被使用。

  • Alarm_Hour, Alarm_Min, Alarm_Sec:分别表示闹铃的小时、分钟和秒数。

  • Djs_Min, Djs_Sec:可能表示倒计时功能的分钟和秒数。

  • Adj_Time, Alarm_Time:布尔标志位,分别用于指示当前是否在调整时间或闹铃时间。

  • LED0, LED1, LED2, LED3, LED4:可能用于控制LED灯的亮灭,以指示当前的状态或页面。

  • Dsp_Bit:一个字符数组或缓冲区,用于存储要显示的字符串。

  • Seg_Tran():一个函数,可能负责将Dsp_Bit中的字符串转换为适合显示设备(如七段显示器)的格式,并发送到该设备。

逻辑流程
  1. 检查时间差:通过比较TickDisplay_Tick的差值,判断是否需要更新显示内容。如果差值大于500(即0.5秒,假设Tick的单位是毫秒),则继续执行更新操作。

  2. 更新Display_Tick:将Display_Tick更新为当前的Tick值,以便下一次比较。

  3. 根据Page更新显示内容

    • Page == 0:显示当前时间(小时、分钟、秒)。

    • Page == 1:如果Adj_Time为假(0),则显示调整模式的小时和分钟;如果为真(非0),则显示分钟调整模式(F表示分钟)。

    • Page == 2:如果Alarm_Time为假(0),则显示闹铃的小时和分钟;如果为真(非0),则显示分钟调整模式 。

    • Page == 3:显示倒计时功能的分钟和秒,并设置LED灯的状态。

    • default:不做任何操作。

  4. 更新LED灯状态:根据当前的页面或调整状态,设置LED灯的状态。

  5. 调用Seg_Tran():将Dsp_Bit中的字符串发送到显示设备,以更新显示内容。

从上述函数可知,刚开始初始时间是从23:59:51开始初始化,闹钟开始走时

通过按下S7按键可以依置切换至时间调整、闹铃时间调整和倒计时功能;S6:切换闹钟开关;S5: 切换模拟生效开关;S4:关闭当次闹铃

时间调整

时间初始化为00:00:00,当光标在小时前面时候按下S5可以使小时+1,S4可以使小时-1,按下S6可以使光标移动到分钟前,修改分钟值。

闹铃时间调整

开始闹铃时间设置为00:00:00,当光标在小时前面时候按下S5可以使小时+1,S4可以使小时-1,按下S6可以使光标移动到分钟前,修改分钟值,当时间走时达到闹铃时间时,就会发出所设置的铃声。

倒计时功能
  • 每次进入倒计时页面页面,为倒计时参数设定状态(LED4亮起),默认为5分钟,可按S6键增加,10、15、20.......60分钟几种参数,顺序循环切换。

  • 确定倒计时时间后,按下S5后开始倒计时,倒计时中,按下S5停止倒计时,按下S6回到倒计时参数设定(LED4熄灭)默认状态。

  • 倒计时结束时,时间停在00:00。蜂鸣器报警,按下S6回到倒计时参数设定默认状态,同时关闭报警。

按键功能实现

通过不同的页面实现不同的功能Page代表的0,1,2,3分别代表着时钟走时 时间调整 闹铃时间调整 倒计时功能。

/*******************************************************************************
函数名程:	void Key_Task(void)
函数功能:	按键执行任务,每2ms执行一次
参数列表:	
返回值  :
*******************************************************************************/
void Key_Task(void)
{
	static uint Key_Tick;							
	
	if((Tick - Key_Tick) > 2)
	{
		Key_Tick = Tick;
		switch (Page)
        {
        	case 0://时钟走时:电子时钟应该能够准确计时,显示小时、分钟和秒
				switch (KeyScan())
						{
							case S7_PRESS://切换到时间调整状态
								Butten_Flag=1;
									Page=1;
									Adj_Hour=Hour;
									Adj_Min=Min;
									Adj_Time=0;
								break;
							case S6_PRESS: //切换到闹铃开关		
								Butten_Flag=1;
									Alarm_Flag = !Alarm_Flag;
									LED6=Alarm_Flag;
								break;
							case S5_PRESS:	//切换模拟生效开关	
								Butten_Flag=1;
									Dida_Flag=!Dida_Flag;
							//开启了模拟声效功能,LED7会亮起,否则,LED7熄灭;上电默认关闭。
									LED7=Dida_Flag;
								break;
							case S4_PRESS: //关闭当次闹铃
								Butten_Flag=1;
								Buzzer_Flag=0;
								Buzzer=0;
								break;
							default:
								break;
						}		
				
        		break;
        	case 1: //时间调整:用户可以在此页面下调整时间
					switch (KeyScan())
						{
							case S7_PRESS://切换到闹铃时间调整状态
								Butten_Flag=1;
									Page=2;
							//进入此页面时的默认值为之前设置的 小时 和 分钟 值,光标在小时前。初始为00时00分。
							//光标在小时前
							Alarm_Time=0;
								break;
							case S6_PRESS: //切换选择调整参数	
								Butten_Flag=1;
									Adj_Time=!Adj_Time;
								break;
							case S5_PRESS:	//对选中参数+1	
								Butten_Flag=1;
									if (!Adj_Time)//选中小时
                                    {
										Adj_Hour++;
										if(Adj_Hour>23)
										{
											Adj_Hour=0;
										}
                                    }
                                    else//选中分钟
                                    {
										Adj_Min++;
										if(Adj_Min>59)
										{
											Adj_Min=0;
										}
                                    }
								break;
							case S4_PRESS: //对选中参数-1
								Butten_Flag=1;
									if (!Adj_Time)//选中小时
                                    {
										Adj_Hour--;
										if(Adj_Hour==0)
										{
											Adj_Hour=23;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Adj_Min--;
										if(Adj_Min==0)
										{
											Adj_Min=59;
										}
										
                                    }
								break;
							default:
								break;
						}		
        		break;
			
			case 2: //闹铃时间调整:用户通过提供的按键来调整闹钟时间设置。
				switch (KeyScan())
						{
							case S7_PRESS://切换到倒计时状态,前述调整的走时将会生效,秒参数从0开始计时。
								Butten_Flag=1;
								Page=3;
								Djs_Min=5;
								Djs_Sec=0;
								Djs_Flag=0;
							    Sec=0;
								break;
							case S6_PRESS:	//切换选择调整闹铃参数		
								Butten_Flag=1;
								Alarm_Time=!Alarm_Time;
							
								break;
							case S5_PRESS: //对选中参数+1	
								Butten_Flag=1;
								 if (!Alarm_Time)//选中小时
                                    {
										Alarm_Hour++;
										if(Alarm_Hour==24)
										{
											Alarm_Hour=0;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Alarm_Min++;
										if(Alarm_Min==60)
										{
											Alarm_Min=0;
										}
										
                                    }		
								break;
							case S4_PRESS://对选中参数-1
								Butten_Flag=1;
								if (!Alarm_Time)//选中小时
                                    {
										Alarm_Hour--;
										if(Alarm_Hour==0)
										{
											Alarm_Hour=23;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Alarm_Min--;
										if(Alarm_Min==0)
										{
											Alarm_Min=59;
										}
										
                                    }			
								break;
							default:
								break;
						}
						break;
				case 3: //倒计时功能
				switch (KeyScan())
						{
							case S7_PRESS://切换到走时状态,前述调整的走时将会生效,秒参数从0开始计时。
								Butten_Flag=1;
								Page=0;//返回到走时
								Min=Adj_Min;
							    Hour=Adj_Hour;
								break;
							case S6_PRESS:	//切换选择调整参数	
								Butten_Flag=1;								
								if(!Djs_Flag)
								{
									Djs_Min+=5;
									if(Djs_Min>=60)//如果分钟大于59
									{
										Djs_Min=5;
									}
									
								}
								else
								{
									Djs_Min=5;
									Djs_Sec=0;
									Djs_Flag=0;
								}
							
								break;
							case S5_PRESS: //开始倒计时	
								Butten_Flag=1;
								Djs_Flag=!Djs_Flag;
								break;
							case S4_PRESS://倒计时结束时,时间停在00:00。蜂鸣器报警,按下S6回到倒计时参数设定默认状态,同时关闭报警。
								Butten_Flag=1;
										if(Buzzer_Flag)
										{
											Buzzer_Flag=0;
											Djs_Min=5;
											Djs_Sec=0;
											Djs_Flag=0;
										}	
												
								break;
							default:
								break;
						}
						break;
				default:	
        		break;
			
	}
	}
}

蜂鸣器发出100ms提示音和闹钟铃声

Key_Task 函数

这个函数负责每2毫秒检查一次按键状态,并根据当前页面(Page)和按键扫描结果(KeyScan() 返回的值)来执行相应的任务。

  • 静态变量 Key_Tick:用于跟踪上一次处理按键任务的时间点,以确保函数不会过于频繁地执行。

  • Page 变量:似乎是一个全局变量,用于指示当前的系统状态或页面(如时钟走时、时间调整、闹铃时间调整等)。

  • 按键扫描和处理

    • Page 为0时(时钟走时),根据按下的键(S7_PRESS, S6_PRESS, S5_PRESS, S4_PRESS)执行不同的任务,切换到时间调整状态、切换闹铃开关、切换模拟声效开关、关闭当次闹铃等。

    • Page 为1时(时间调整),允许用户调整小时和分钟,通过切换调整参数(小时/分钟)和对选中参数进行加减操作。

    • Page 为2时(闹铃时间调整),与时间调整类似,但调整的是闹铃的小时和分钟。

Logic_Task 函数

这个函数负责处理一些需要定时执行的逻辑任务,如发出提示音和闹钟铃声。

  • 静态变量Logic_Tick, Button_Tick, Ls_Tick, i 用于跟踪时间点和状态。

  • 提示音逻辑:每100毫秒检查一次 Butten_Flag(一个全局变量,可能用于指示按键事件),如果为1,则发出提示音,并通过递增 Butten_Flag 来避免连续发出提示音。

  • 闹钟铃声逻辑:通过 Ls_Tick 跟踪时间,并使用 i 来控制闹钟铃声的“滴滴滴滴 (四拍)休止(四拍)”模式。每次 Ls_Tick 超过125毫秒(即大约每1秒),i 的值就会递增,并根据 i 的值控制蜂鸣器(Buzzer)的开关,从而模拟出闹钟铃声的节奏。当 i 达到15时,重置为0,开始下一个周期。

/*******************************************************************************
函数名程:	void Logic_Task(void)
函数功能:	定时按一定逻辑执行任务
参数列表:	
返回值  :
*******************************************************************************/
void Logic_Task(void)
{
	static uint Logic_Tick;
	static uint Button_Tick;
	static uint Ls_Tick;//闹钟铃声
	static uint i;
	if((Tick - Logic_Tick) > 500)
	{	
		Logic_Tick = Tick;
	}
	
	//蜂鸣器发出100ms提示音
	if((Tick-Button_Tick)>100)
	{
		Button_Tick=Tick;
		if(Butten_Flag==1)
		{
			Buzzer=1;
			Butten_Flag++;
		}
		else
		{
			Buzzer=0;
		}
	}
	
	//闹钟铃声变为“滴~滴~滴~滴 ~ (四拍)休止(四拍)”的循环,周期是2秒。
	if((Tick - Ls_Tick) > 125)
	{	
		Ls_Tick = Tick;
		if(Buzzer_Flag)
		{
			switch (i)
        {
        	case 0:
				Buzzer=1;
				i++;
        		break;
        	case 1:
				Buzzer=0;	
				i++;
        		break;
			case 2:
				Buzzer=1;
				i++;
        		break;
			case 3:
				Buzzer=0;	
				i++;
        		break;
			case 4:
				Buzzer=1;
				i++;
        		break;
			case 5:
				Buzzer=0;
				i++;
        		break;
			case 6:
				Buzzer=1;
				i++;
        		break;
			case 7:
				Buzzer=0;
				i++;
        		break;
			case 8:
				i++;
        		break;
			case 9:
				i++;
        		break;
			case 10:
				i++;
        		break;
			case 11:
				i++;
        		break;
			case 12:
				i++;
        		break;
			case 13:
				i++;
        		break;
			case 14:
				i++;
        		break;
			case 15:
				i=0;
        		break;			
        	default:
        		break;
        }
		}
		
	}

}

闹钟走时、倒计时走时和模拟滴答声音

函数概述
  • 函数名: Data_Task

  • 功能: 定时(每秒钟)更新时钟的秒值,并处理相关的逻辑,如闹钟提醒、倒计时和模拟滴答声效。

  • 参数: 无

  • 返回值: 无

静态变量
  • static uint Data_Tick;:用于跟踪上一次处理数据的时间点,以确保函数不会过于频繁地执行。

主要逻辑
1. 时间更新
  • 通过比较 Tick(当前时间)和 Data_Tick(上一次处理时间)的差值来判断是否到了一秒的时间间隔。

  • 如果到了一秒的时间间隔,就更新 Data_Tick 为当前时间 Tick

  • Page 为 0 的情况下(即时钟走时功能),更新秒 Sec、分 Min、时 Hour 的值,并处理进位。

2. 闹钟逻辑
  • Alarm_Flag 为真,且当前的小时和分钟与设定的闹钟小时 Alarm_Hour 和分钟 Alarm_Min 相匹配时,设置 Buzzer_Flag 为 1,以触发闹钟铃声。

3. 倒计时逻辑
  • 如果 Page 等于 3 且 Djs_Flag 为真(表示正在倒计时),则每秒减少 Djs_Sec 的值。

  • 如果 Djs_Min 大于 等于60,则将 Djs_Sec 重置为 59,并减少 Djs_Min 的值。

  • 如果 Djs_Min 为 0闹铃响起,则设置 Buzzer_Flag 为 1 以触发倒计时结束的铃声,并将 Djs_Sec 重置为 0。

4. 模拟滴答声效
  • 如果Dida_Flag为真(表示开启了模拟滴答声效),则根据秒值Sec的奇偶性来控制继电器的开合,以模拟机械走时的滴答声。

    • 如果 Sec 为奇数,继电器打开(Relay=1)。

    • 如果 Sec 为偶数,继电器关闭(Relay=0)。

/*******************************************************************************
函数名程:	void Data_Task(void)
函数功能:	定时处理数据的采集、处理、传输任务//每一秒钟,更新秒值
参数列表:	
返回值  :
*******************************************************************************/
void Data_Task(void)
{
	static uint Data_Tick;
	
	if((Tick - Data_Tick) > 1000)
	{	
		Data_Tick = Tick;
		if(!Page)//走时功能,写在一秒的定时器软件里,时钟上电即为此状态下,从23:59:51开始走时,以方便测试数据边界。
		{
			if(++Sec==60)
			{
			Sec=0;
			if(++Min==60)
			{
				Min=0;
				if(++Hour==24)
				{
					Hour=0;
				}
				//分钟参数和小时参数都确定了,每分钟判断一次
				if(Alarm_Flag&&(Alarm_Hour==Hour)&&(Alarm_Min==Min))
					{
						Buzzer_Flag=1;
					}
				}
			}
		}
		else if((Page==3)&&(Djs_Flag))
		{
			if(--Djs_Sec>=60)
			{
				if(Djs_Min)
				{
					Djs_Sec=59;
					--Djs_Min;
				}
				else
				{
					Buzzer_Flag=1;
					Djs_Sec=0;
				}
			}
		}
		//滴答模拟声效:利用继电器的开合模拟机械走时的滴答声。开启后当秒参数为偶数时继电器断开,为奇数时打开。
		if(Dida_Flag)
		{
			if(Sec%2)//奇数
			{
				Relay=1;
			}
			else//偶数
			{
				Relay=0;
			}
		}
	}

}

三、最终代码总和

// Header:		
// File Name: 
// Author:
// Date:
/********************预编译、宏和变量********************************************/
#include <reg52.h>
#include <stdio.h>

/*************************************************************
                     宏定义区域
**************************************************************/
#define CONTROL(x,y)	P0=y;P2=x;P2=0  	//为锁存器的引脚操作封装一个带参宏

typedef unsigned int uint;	//对系统默认数据类型进行重定义
typedef unsigned char uchar;

//定时器T2相关寄存器
sfr T2MOD = 0xC9;
//定义按键引脚
sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;

enum PERIPHERAL
{
	LED = 0x80,
	DEVICE = 0xa0,
	BIT = 0xc0,
	SEG = 0xe0
};

typedef enum
{
    UNPRESS = 0, 
    S4_PRESS = 4, 
    S5_PRESS, 
    S6_PRESS, 
    S7_PRESS 
}KeyEvent_TypeDef;

/*******************************************************************************
                     变量定义
********************************************************************************/
/********************模板驱动变量****************************/
//控制LED0~7的开关量,正逻辑
bit LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7;
//控制蜂鸣器的自定义变量,正逻辑
bit Buzzer;
//控制继电器的自定义变量,正逻辑
bit Relay;
//Dsp_Bit是数码管显示的字符串数组,考虑到小数点的同位显示,所以字符串长度最长为16
uchar Dsp_Bit[16] = {0};
//数码管显示的各位段码内容,由 Dsp_Bit 通过函数Seg_Tran转换更新
uchar Dsp_Code[8] = {0};
//定时器计数
uint	Tick;

/********************应用层变量****************************/
//两个参数
uchar Page;//0是走时,1是调整,2是闹铃
uchar Hour,Min,Sec,Alarm_Hour,Alarm_Min,Alarm_Sec,Adj_Hour,Adj_Min,Adj_Sec,Djs_Min,Djs_Sec;//相关时间的定义
uchar Butten_Flag;
//开关量
bit Alarm_Flag,Dida_Flag,Buzzer_Flag,Djs_Flag;
//状态量
bit Adj_Time;//调整时间参数选择
bit Alarm_Time;//调整闹铃参数选择

/********************中断区域***************************************************/
/*******************************************************************************
函数名程:	Timer0_Init
函数功能:	定时器T0 中断 1ms 配置
参数列表:	
返回值  :
*******************************************************************************/
void Timer0_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
	EA = 1;
}

/*******************************************************************************
函数名程:	void Timer0_Isr(void) interrupt 1
函数功能:	定时器T0 中断 1ms 中断处理
参数列表:	
返回值  :
*******************************************************************************/
void Timer0_Isr(void) interrupt 1
{
	static uchar dsp_com;
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值

	//定时任务
	Tick++;		
	/***********控制外设的引脚操作*****/
	//更新LED的引脚操作
	CONTROL(LED,~((uchar)LED0|(uchar)LED1<<1|(uchar)LED2<<2|(uchar)LED3<<3|(uchar)LED4<<4|(uchar)LED5<<5|(uchar)LED6<<6|(uchar)LED7<<7));
	//更新蜂鸣器、继电器的引脚操作
	CONTROL(DEVICE,(uchar)Buzzer<<6|(uchar)Relay<<4);	
	//更新一位数码管的引脚操作
	CONTROL(BIT,0);									//消影
	CONTROL(SEG,Dsp_Code[dsp_com]);		//放入要显示的数字到段选锁存
	CONTROL(BIT,1 << (dsp_com));				//选择对应的位选锁存
	if(++dsp_com > 7)								//显示完一次后自增位选,位选到8之后回到1
			dsp_com = 0;
}

/*******************************************************************************
函数名程:	void Seg_Tran(void)
函数功能:	将Dsp_Bit[]的内容,转换成对应的段码放入Dsp_Code[];
参数列表:	
返回值  :
*******************************************************************************/
void Seg_Tran(void)
{
  unsigned char i, j=0, temp;
  for (i=0; i<8; i++, j++)
  {
    switch (Dsp_Bit[j]) 
    { // 低电平点亮段,段码[MSB...LSB]对应码顺序为[dp g f e d c b a]
      case '0': temp = 0xc0; break;
      case '1': temp = 0xf9; break;
//    case '1': temp = 0xcf; break;
      case '2': temp = 0xa4; break;
      case '3': temp = 0xb0; break;
//    case '3': temp = 0x86; break;
      case '4': temp = 0x99; break;
//    case '4': temp = 0x8b; break;
      case '5': temp = 0x92; break;
      case '6': temp = 0x82; break;
//    case '6': temp = 0x90; break;
      case '7': temp = 0xf8; break;
//    case '7': temp = 0xc7; break;
      case '8': temp = 0x80; break;
      case '9': temp = 0x90; break;
//    case '9': temp = 0x82; break;
      case 'A': temp = 0x88; break;
      case 'B': temp = 0x83; break;
      case 'C': temp = 0xc6; break;
      case 'D': temp = 0xA1; break;
      case 'E': temp = 0x86; break;
      case 'F': temp = 0x8E; break;
      case 'H': temp = 0x89; break;
      case 'L': temp = 0xC7; break;
      case 'N': temp = 0xC8; break;
      case 'P': temp = 0x8c; break;
      case 'U': temp = 0xC1; break;
      case '-': temp = 0xbf; break;
      case ' ': temp = 0xff; break;
      case '^': temp = 0xfe; break; // 1 1 1 1 1 1 1 0
      case '_': temp = 0xf7; break; // 1 1 1 1 0 1 1 1
	    default: temp = 0xff;
    }
    if (Dsp_Bit[j+1] == '.')
    {
      temp = temp&0x7f;             // 点亮小数点
      j++;
    }
    Dsp_Code[i] = temp;
  }
}

/*******************************************************************************
函数名程:    KeyEvent_TypeDef KeyScan(void)
函数功能:    检测独立按键是否按下,按下则返回对应键值
返回值  :    S4_PRESS(4):S4按下
            S5_PRESS(5):S5按下
            S6_PRESS(6):S6按下
            S7_PRESS(7):S7按下
            UNPRESS(0):未有按键按下
*******************************************************************************/
KeyEvent_TypeDef KeyScan(void)
{
 
	static uchar S4_value = 0xFF;
	static uchar S5_value = 0xFF;
	static uchar S6_value = 0xFF;
	static uchar S7_value = 0xFF;
	static uchar lock = 0;
	
	//2ms读取一次,读取前左移,将新读取的状态放在最低位,0为按下,1为抬起
	//连续8次都读取到按键按下,keyx_value值变为0x00时方才触发按下事件
	S4_value <<= 1;
	S4_value |= S4;
	S5_value <<= 1;
	S5_value |= S5;
	S6_value <<= 1;	
	S6_value |= S6;
	S7_value <<= 1;
	S7_value |= S7;
	
	if((!S4_value||!S5_value||!S6_value||!S7_value) && !lock)	//如果任意一个按键触发且按键未上锁
	{
		lock = 1;				//上锁,并返回相应键值
		if(!S4_value)
			return S4_PRESS;
		else if(!S5_value)
			return S5_PRESS;
		else if(!S6_value)
			return S6_PRESS;
		else if(!S7_value)
			return S7_PRESS;
	}
	else if(S4_value && S5_value && S6_value && S7_value && lock)			//如果已上锁且所有按键都不触发(触发按键已抬起)
	{
		lock = 0;													
	}
	else{																	//
	}
//	Delayms(2);
	return UNPRESS;  
}

/*******************************************************************************
函数名程:	void Display_Task(void)
函数功能:	显示内容执行任务,0.5s刷新一次显示内容
参数列表:	
返回值  :
*******************************************************************************/
void Display_Task(void)
{
	static uint Display_Tick;
	
	if((Tick - Display_Tick) > 500)
	{	
		Display_Tick = Tick;		
		//sprintf(Dsp_Bit, "A-%2uB %2u", (uint)Num1, (uint)Num2);
		switch (Page)
        {
        	case 0: //时钟走时:电子时钟应该能够准确计时,显示小时、分钟和秒
				sprintf(Dsp_Bit, "%02u .%02u .%02u", (uint)Hour, (uint)Min,(uint)Sec );
				LED0=1;LED1=0;LED2=0;LED3=0;
        		break;
        	case 1: //时间调整:用户可以在此页面下调整时间
				if(!Adj_Time)//选择小时参数
				{
					sprintf(Dsp_Bit, "5-%02uF %02u", (uint)Adj_Hour,(uint)Adj_Min );
				}
				
				else//选择分钟参数
				{
					sprintf(Dsp_Bit, "5 %02uF-%02u", (uint)Adj_Hour,(uint)Adj_Min );
				}
				LED0=0;LED1=1;LED2=0;LED3=0;
        		break;
			case 2:   //闹铃时间调整:用户通过提供的按键来调整闹钟时间设置。
				if(!Alarm_Time)//选择小时参数
				{
					sprintf(Dsp_Bit, "N-%02uF %02u", (uint)Alarm_Hour,(uint)Alarm_Min );
				}
				
				else//选择分钟参数
				{
					sprintf(Dsp_Bit, "N %02uF-%02u", (uint)Alarm_Hour,(uint)Alarm_Min );
				}
				LED0=0;LED1=0;LED2=1;LED3=0;
        		break;
			case 3://倒计时功能页面
				LED0=0;LED1=0;LED2=0;LED3=4;LED4=Djs_Flag;
				sprintf(Dsp_Bit, "CD %02u .%02u", (uint)Djs_Min,(uint)Djs_Sec );	
				break;
        	default:
        		break;
        }
	}
	Seg_Tran();
}

/*******************************************************************************
函数名程:	void Key_Task(void)
函数功能:	按键执行任务,每2ms执行一次
参数列表:	
返回值  :
*******************************************************************************/
void Key_Task(void)
{
	static uint Key_Tick;							
	
	if((Tick - Key_Tick) > 2)
	{
		Key_Tick = Tick;
		switch (Page)
        {
        	case 0://时钟走时:电子时钟应该能够准确计时,显示小时、分钟和秒
				switch (KeyScan())
						{
							case S7_PRESS://切换到时间调整状态
								Butten_Flag=1;
									Page=1;
									Adj_Hour=Hour;
									Adj_Min=Min;
									Adj_Time=0;
								break;
							case S6_PRESS: //切换到闹铃开关		
								Butten_Flag=1;
									Alarm_Flag = !Alarm_Flag;
									LED6=Alarm_Flag;
								break;
							case S5_PRESS:	//切换模拟生效开关	
								Butten_Flag=1;
									Dida_Flag=!Dida_Flag;
							//开启了模拟声效功能,LED7会亮起,否则,LED7熄灭;上电默认关闭。
									LED7=Dida_Flag;
								break;
							case S4_PRESS: //关闭当次闹铃
								Butten_Flag=1;
								Buzzer_Flag=0;
								Buzzer=0;
								break;
							default:
								break;
						}		
				
        		break;
        	case 1: //时间调整:用户可以在此页面下调整时间
					switch (KeyScan())
						{
							case S7_PRESS://切换到闹铃时间调整状态
								Butten_Flag=1;
									Page=2;
							//进入此页面时的默认值为之前设置的 小时 和 分钟 值,光标在小时前。初始为00时00分。
							//光标在小时前
							Alarm_Time=0;
								break;
							case S6_PRESS: //切换选择调整参数	
								Butten_Flag=1;
									Adj_Time=!Adj_Time;
								break;
							case S5_PRESS:	//对选中参数+1	
								Butten_Flag=1;
									if (!Adj_Time)//选中小时
                                    {
										Adj_Hour++;
										if(Adj_Hour>23)
										{
											Adj_Hour=0;
										}
                                    }
                                    else//选中分钟
                                    {
										Adj_Min++;
										if(Adj_Min>59)
										{
											Adj_Min=0;
										}
                                    }
								break;
							case S4_PRESS: //对选中参数-1
								Butten_Flag=1;
									if (!Adj_Time)//选中小时
                                    {
										Adj_Hour--;
										if(Adj_Hour==0)
										{
											Adj_Hour=23;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Adj_Min--;
										if(Adj_Min==0)
										{
											Adj_Min=59;
										}
										
                                    }
								break;
							default:
								break;
						}		
        		break;
			
			case 2: //闹铃时间调整:用户通过提供的按键来调整闹钟时间设置。
				switch (KeyScan())
						{
							case S7_PRESS://切换到倒计时状态,前述调整的走时将会生效,秒参数从0开始计时。
								Butten_Flag=1;
								Page=3;
								Djs_Min=5;
								Djs_Sec=0;
								Djs_Flag=0;
							    Sec=0;
								break;
							case S6_PRESS:	//切换选择调整闹铃参数		
								Butten_Flag=1;
								Alarm_Time=!Alarm_Time;
							
								break;
							case S5_PRESS: //对选中参数+1	
								Butten_Flag=1;
								 if (!Alarm_Time)//选中小时
                                    {
										Alarm_Hour++;
										if(Alarm_Hour==24)
										{
											Alarm_Hour=0;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Alarm_Min++;
										if(Alarm_Min==60)
										{
											Alarm_Min=0;
										}
										
                                    }		
								break;
							case S4_PRESS://对选中参数-1
								Butten_Flag=1;
								if (!Alarm_Time)//选中小时
                                    {
										Alarm_Hour--;
										if(Alarm_Hour==0)
										{
											Alarm_Hour=23;
										}
										
                                    }
                                    else//选中分钟
                                    {
										Alarm_Min--;
										if(Alarm_Min==0)
										{
											Alarm_Min=59;
										}
										
                                    }			
								break;
							default:
								break;
						}
						break;
				case 3: //倒计时功能
				switch (KeyScan())
						{
							case S7_PRESS://切换到走时状态,前述调整的走时将会生效,秒参数从0开始计时。
								Butten_Flag=1;
								Page=0;//返回到走时
								Min=Adj_Min;
							    Hour=Adj_Hour;
								break;
							case S6_PRESS:	//切换选择调整参数	
								Butten_Flag=1;								
								if(!Djs_Flag)
								{
									Djs_Min+=5;
									if(Djs_Min>=60)
									{
										Djs_Min=5;
									}
									
								}
								else
								{
									Djs_Min=5;
									Djs_Sec=0;
									Djs_Flag=0;
								}
							
								break;
							case S5_PRESS: //开始倒计时	
								Butten_Flag=1;
								Djs_Flag=!Djs_Flag;
								break;
							case S4_PRESS://倒计时结束时,时间停在00:00。蜂鸣器报警,按下S6回到倒计时参数设定默认状态,同时关闭报警。
								Butten_Flag=1;
										if(Buzzer_Flag)
										{
											Buzzer_Flag=0;
											Djs_Min=5;
											Djs_Sec=0;
											Djs_Flag=0;
										}	
												
								break;
							default:
								break;
						}
						break;
				default:	
        		break;
			
	}
	}
}	

/*******************************************************************************
函数名程:	void Data_Task(void)
函数功能:	定时处理数据的采集、处理、传输任务//每一秒钟,更新秒值
参数列表:	
返回值  :
*******************************************************************************/
void Data_Task(void)
{
	static uint Data_Tick;
	
	if((Tick - Data_Tick) > 1000)
	{	
		Data_Tick = Tick;
		if(!Page)//走时功能,写在一秒的定时器软件里,时钟上电即为此状态下,从23:59:51开始走时,以方便测试数据边界。
		{
			if(++Sec==60)
			{
			Sec=0;
			if(++Min==60)
			{
				Min=0;
				if(++Hour==24)
				{
					Hour=0;
				}
				//分钟参数和小时参数都确定了,每分钟判断一次
				if(Alarm_Flag&&(Alarm_Hour==Hour)&&(Alarm_Min==Min))
					{
						Buzzer_Flag=1;
					}
				}
			}
		}
		else if((Page==3)&&(Djs_Flag))
		{
			if(--Djs_Sec>=60)
			{
				if(Djs_Min)
				{
					Djs_Sec=59;
					--Djs_Min;
				}
				else
				{
					Buzzer_Flag=1;
					Djs_Sec=0;
				}
			}
		}
		//滴答模拟声效:利用继电器的开合模拟机械走时的滴答声。开启后当秒参数为偶数时继电器断开,为奇数时打开。
		if(Dida_Flag)
		{
			if(Sec%2)//奇数
			{
				Relay=1;
			}
			else
			{
				Relay=0;
			}
		}
	}

}

/*******************************************************************************
函数名程:	void Logic_Task(void)
函数功能:	定时按一定逻辑执行任务
参数列表:	
返回值  :
*******************************************************************************/
void Logic_Task(void)
{
	static uint Logic_Tick;
	static uint Button_Tick;
	static uint Ls_Tick;//闹钟铃声
	static uint i;
	if((Tick - Logic_Tick) > 500)
	{	
		Logic_Tick = Tick;
	}
	
	//蜂鸣器发出100ms提示音
	if((Tick-Button_Tick)>100)
	{
		Button_Tick=Tick;
		if(Butten_Flag==1)
		{
			Buzzer=1;
			Butten_Flag++;
		}
		else
		{
			Buzzer=0;
		}
	}
	
	//闹钟铃声变为“滴~滴~滴~滴 ~ (四拍)休止(四拍)”的循环,周期是2秒。
	if((Tick - Ls_Tick) > 125)
	{	
		Ls_Tick = Tick;
		if(Buzzer_Flag)
		{
			switch (i)
        {
        	case 0:
				Buzzer=1;
				i++;
        		break;
        	case 1:
				Buzzer=0;	
				i++;
        		break;
			case 2:
				Buzzer=1;
				i++;
        		break;
			case 3:
				Buzzer=0;	
				i++;
        		break;
			case 4:
				Buzzer=1;
				i++;
        		break;
			case 5:
				Buzzer=0;
				i++;
        		break;
			case 6:
				Buzzer=1;
				i++;
        		break;
			case 7:
				Buzzer=0;
				i++;
        		break;
			case 8:
				i++;
        		break;
			case 9:
				i++;
        		break;
			case 10:
				i++;
        		break;
			case 11:
				i++;
        		break;
			case 12:
				i++;
        		break;
			case 13:
				i++;
        		break;
			case 14:
				i++;
        		break;
			case 15:
				i=0;
        		break;			
        	default:
        		break;
        }
		}
		
	}

}

/*******************************************************************************
函数名程:	void P_Init(void)
函数功能:	外设初始化
参数列表:	
返回值  :
*******************************************************************************/
void P_Init(void)				//外设初始化
{
	CONTROL(LED,0xff);		//初始化LED熄灭
	CONTROL(DEVICE,0x00);	//初始化所有外设不工作
	CONTROL(BIT,0x00);		//初始化数码管位选无效
	CONTROL(SEG,0xff);		//初始化数码管段选无效
	//定时器T0时基1ms初始化
	Timer0_Init();//
}

/*************************************************************
                      主函数
**************************************************************/
void main(void)
{
/********************初始化********************************************/
	P_Init();
	Hour=23;
	Min=59;
	Sec=51;
	
/********************功能函数*******************************************/
	while(1)
	{
		Display_Task();
		Key_Task();
		Data_Task();
		Logic_Task();
	}        
}

  • 22
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值