目录
*时钟走时*:电子时钟应该能够准确计时,显示小时、分钟和秒。
电子钟
一、电子钟项目设计需求
项目概述
本项目的目的是开发一款基于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* |
---|---|---|---|---|---|---|---|
n | L | 光标-(中) | 闹铃小时十位 | 闹铃小时个位 | 光标-(中) | 闹铃分钟十位 | 闹铃分钟个位 |
此模式下,可以通过S6按键选择闹铃小时和闹铃分钟两个走时参数,选中的参数前光标亮起,再通过S5\S4对其进行调整。
进入此页面时的默认值为之前设置的 小时 和 分钟 值,光标在小时前。初始为00时00分。
在此状态下,LED2亮起,离开走时页面时,LED2熄灭。
*按键功能如下:*
**S7:**** 切换到走时状态,前述调整的走时将会生效,秒参数从0开始计时。
**S6:**** 切换选择调整参数
**S5:**** 对选中参数+1
**S4:**** 对选中参数-1
额外要求:
-
每次按键,蜂鸣器都会发出100ms的提示音。
-
闹钟铃声变为“滴~滴~滴~滴 ~ (四拍)休止(四拍)”的循环,周期是2秒。
-
在闹铃界面和走时界面间再添加一个倒计时功能页面,
此页面下可实现倒计时功能:
-
每次进入倒计时页面页面,为倒计时参数设定状态(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秒(通过检查Tick
和Display_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
中的字符串转换为适合显示设备(如七段显示器)的格式,并发送到该设备。
逻辑流程
-
检查时间差:通过比较
Tick
和Display_Tick
的差值,判断是否需要更新显示内容。如果差值大于500(即0.5秒,假设Tick
的单位是毫秒),则继续执行更新操作。 -
更新
Display_Tick
:将Display_Tick
更新为当前的Tick
值,以便下一次比较。 -
根据
Page
更新显示内容:
-
Page == 0
:显示当前时间(小时、分钟、秒)。 -
Page == 1
:如果Adj_Time
为假(0),则显示调整模式的小时和分钟;如果为真(非0),则显示分钟调整模式(F表示分钟)。 -
Page == 2
:如果Alarm_Time
为假(0),则显示闹铃的小时和分钟;如果为真(非0),则显示分钟调整模式 。 -
Page == 3
:显示倒计时功能的分钟和秒,并设置LED灯的状态。 -
default
:不做任何操作。
-
-
更新LED灯状态:根据当前的页面或调整状态,设置LED灯的状态。
-
调用
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();
}
}