电子设计报告
题目 多功能数字时钟
专业 电子信息科学与技术
班级
姓名
学号
中国矿业大学计算机科学与技术学院
20##年5月
1.需求分析.... 3
1.1背景... 3
1.1.1设计题目... 3
1.1.2设计目的... 3
1.1.3设计意义... 3
1.2任务概述.... 3
1.3功能需求规定.... 3
1.4性能需求规定…………………………………………………………………………………3
1.4.1输入输出………………………………………………………………………………...3
1.4.2时间特性………………………………………………………………………………...3
2概要设计.... 4
2.1总体设计方案... 4
2.2电路原理图... 4
2.3设计流程图... 5
3详细设计.... 6
3.1初始任务设计... 6
3.2主任务设计... 7
3.3报时任务设计... 8
3.4数码管显示程序设计... 10
3.5闹钟设置程序设计... 11
3.6键盘扫描和调时程序设计... 14
4测试分析.... 17
4.1显示功能测试... 17
4.2调时功能测试... 17
4.3闹钟设置功能测试... 18
4.4闹钟报时功能测试... 19
4.5整点报时功能测试... 19
4.6作息时间报时功能测试... 19
4.7整体功能测试... 19
5总结报告.... 20
5.1功能和性能的实现... 20
5.2误差与故障分析... 20
5.3经验与教训... 21
1. 需求分析
1.1背景
1.1.1设计题目:多功能数字时钟
1.1.2设计目的:
(1)巩固、加深和扩大单片机应用的知识面,提高综合及灵活运用所学知识解决问题的能力;
(2)培养针对课题需要,选择和查阅有关手册、图表及文献资料的自学能力,提高组成系统、编程、调试的动手能力;
(3)过对课题设计方案的分析、选择、比较、熟悉单片机用系统开发、研制的过程,软硬件设计的方法、内容及步骤。
1.1.3设计意义:
数字钟已成为人们日常生活中必不可少的必需品,广泛用于个人家庭以及车站、码头、剧场、等公共场合,给人们的生活、学习、工作、娱乐带来了极大的方便。由于数字集成电路的发展和石英晶体振荡器的广泛应用,使得数字钟的精度,远远超过老式钟表,钟表的数字化给人们生产生活带来了极大的方便。诸如定时自动报警、按时自动打铃、时间程序自动控制、定时广播、自动起闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电气的自动启用等,所有这些,都是以钟表数字化为基础的。因此,研究数字钟及扩大其应用,有着非常重要的意义。
1.2任务概述
利用用51单片机、8位数码管和时钟芯片DS1302制作一个时钟日历,该时钟除了能够显示时、分、秒、年、月、日外,还需实现如下功能:
(1) 时分秒年月日都能加减可调,调时能识别平闰年和大小月
(2) 12小时与24小时切换显示功能
(3) 能设置闹钟,闹钟时间到播放音乐
(4) 整点报时功能
(5) 模拟作息时间表,实现上课下课打铃功能
1.3功能需求规定
数字时钟的主要功能为显示时间的功能,只有在用户需要校准时间或者设置闹钟时,用户可通过按键输入对时钟的相应时间进行修改、设置闹钟等操作,此外用户需要进行12与24小时切换显示的时候也只需通过按键操作即可,其他功能不需用户设置。
1.4性能需求规定
1.4.1输入输出
输入:由按键进行输入,需要对按键输入进行延时消抖处理,以便确保用户确实按下了按键。
输出:输出数据必须确保在正确的范围之内,年的范围在00-99之间,月的范围在1-12之间,日的范围在1-31之间,时的范围在0-23之间,分和秒的范围在0-59之间,精度均为整数且必须要求准确无误。
1.4.2时间特性
响应时间:数字时钟要求响应的实时性强,用户操作后数码管显示能立即进行响应,响应时间应该在1秒之内,即毫秒级的。
准确性:闹钟报时、整点报时和作息时间报时的时刻必须准确无误,不能有时间误差。
2. 概要设计
2.1总体设计方案
通过STC89C52芯片P2口控制数码管的位选通,P0口输出控制数码管的段显示。P1.4口输出高低电平驱动蜂鸣器演奏音乐。通过读取DS1302芯片来获得要显示的时间。
用8位数码管动态扫描显示时分秒年月日,其中时分秒和年月日不能同时显示,需要通过按键K1键切换进行显示。时分秒之间用“-”分隔从左到右显示,年月日以“.”分隔从左到右显示。
采用多任务编程实现,首先创建一个主任务,在主任务中主要完成如下功能:
(1) 时间显示,通过调用函数来读取DS1302获取时间,然后将获取的时间送给数码管显示函数进行处理显示相应的时间。
(2) 键盘扫描,主要判断用户是否进行调时、切换显示和闹钟设置等。若用户不进行设置,则正常走时。若用户按下K1键,则进行时分秒与年月日的切换显示。若用户按下K5键,则进行12与24小时的切换显示。若用户按下K2键,则进行调时位的选择,每按下一次选择要调整的位右移一位,即第一次按下选择的是调整时,下次按下时选择的是调整分,再下次按下时选择的是调整秒,再次按下时又选择调整时,年月日的调整位选择类似;选择要调整的位后按下K3键一次对要调整的位进行加1操作,按下K4键对要调整的位进行减1操作;调整完毕后再次按下K2键或K5键退出调时状态。若用户按下K6键,则进入闹钟设置状态,设置闹钟时按K2键进行调整位的选择,按K3键和K4键分别进行加1调整和减1调整,闹钟设置完毕,再次按下K6键退出闹钟设置状态。若果是闹钟正在鸣响的状态,则按下K6键可停止闹钟。
(3) 判断时间是否到了闹钟时间,如果到了闹钟时间,则创建一个闹钟报时任务,在该任务中通过定时器1中断和蜂鸣器来播放一段歌曲音乐。
(4) 判断时间是否到了整点时刻,如果到了整点时刻,则创建一个整点报时任务,在该任务中通过定时器1中断和蜂鸣器来播放一段音乐模拟报时。
(5) 判断时间是否到了作息时间表中的时刻,如果到了相应的时刻,则创建一个按作息时间表报时的任务,在该任务中通过定时器1中断和蜂鸣器来播放一段音乐模拟上下课打铃。
2.2电路原理图
设计的硬件部分主要采用基于51单片机的开发板来实现,其电路原理图如下:
2.3设计流程图
设计的主要总体流程图如下:
3. 详细设计
在本部分设计中主要采用了多任务的设计方法来实现,采用的是RTX51Tiny版的多任务实时操作系统,RTX51专门针对51设计,小巧紧凑,代码开放,使用起来较灵活。
3.1初始任务设计
设计方案:
初始任务的设计比较简单,主要完成以下几项功能:初始化DS1302的时间,设定时器的工作方式,开中断,最后创建主任务,然后再将初始任务删除。
设计源代码:
void sys_init()_task_ 0 //任务0:进行初始化
{
Init_1302(time);
TMOD=0x10; //设置定时器1为工作方式1
EA=1; //开总中断
ET1=1; //开定时器1中断
os_create_task(1); //激活主任务
os_delete_task(0); //撤销任务0
}
3.2主任务设计
设计方案:
在主任务中主要进行时钟的显示,并且不断循环调用检测函数判断是否有按键按下。如果按下调时按键,则暂停从1302读取时间,在检测函数里设置时间,待时间设置完毕后重新写入1302再启动1302进行读取时间;如果按下闹钟设置按键,则调用闹钟设置函数进行闹钟设置;如果在闹钟已响的时候按下闹钟停止按键,则将闹钟停止的标志位置1。如果没有按键按下,则判断是否到达闹钟时刻,如果到达闹钟时刻,并且此时与整点报时任务以及作息时间报时任务不冲突,则将闹钟响的标志位置1并创建闹钟报时任务;接着再判断闹钟停止标志位是否为1,若为1则立即停止定时器并立即撤销闹钟报时任务已到达立刻停止闹钟的功能;接着再判断是否到达整点时刻,若达到整点时刻并且此时不与闹钟报时任务冲突也不是正在设置闹钟的状态,则将整点报时的标志位置1并创建整点报时任务;接着再判断是否到达作息时刻,若到达作息时刻并且与闹钟报时任务以及整点报时任务不冲突同时也必是正在设置闹钟的状态,则创建作息时间报时任务。
设计源代码:
void dis_serv()_task_ 1 //任务1:主任务函数,主要进行时间显示,键盘检测,报时任务的创建
{
while(1)
{
set_1302();
if(setalarm)
setAlarmclock();
if(p)
get_1302(a);
display();
if(a[2]==alarmtime[2]&&a[1]==alarmtime[1]&&a[0]==alarmtime[0]
&&a[6]==alarmtime[6]&&a[4]==alarmtime[4]&&a[3]==alarmtime[3])
{
if(!inter&&!worktime)//非整点报时、非作息时间报时状态
{
ring=1;
os_create_task(2);//闹钟时间到,创建闹钟任务
}
}
if(del_alarm)
{
TR1=0;
del_alarm=0;
os_delete_task(2);//如果按下了停止闹钟键,则删除闹钟任务
}
if(!ring&&!setalarm)//非闹钟响状态
{
for(k=0;k<24;k++)
{
if(a[0]==0x55&&a[1]==0x59&&a[2]==intertime[k])
{
inter=1;
os_create_task(3); //整点报时,创建整点报时任务
break;
}
}
if(!inter)//非整点报时状态
{
classtime();
if(worktime)
{
os_create_task(4);//作息时刻到,创建作息时间任务
}
}
}
os_wait(K_IVL,1,0);
}
}
3.3报时任务设计
设计方案:
报时任务包括三个,分别是闹钟报时任务、整点报时任务和作息时间报时任务。这3个任务功能相似,都是让蜂鸣器演奏音乐报时,在此一并考虑。在这三个任务里都是到预先定义的相应歌曲音乐乐谱数组和频率参数数组中取相应的音高参数,由于在初始任务中将定时器1设定为工作方式1,所以这里通过取得的音高参数计算定时器1的高8位和低8位,然后分别送定时器1的TH1和TL1,最后让TR1=1启动定时器,并延时指定的时间以达到音乐节拍的效果,然后蜂鸣器就可以播放相应的音乐。其中闹钟报时任务是循环播放的,需要用户按键手动停止闹钟,整点报时任务和作息时间报时任务在播放完毕后,分别将整点报时的标志位和作息时间报时的标志位置1,最后删除任务自身。
设计源代码:
void music()_task_ 2 //任务2:闹钟任务
{while(1)
{ i=0;
while(notetab[i].delay) //音符的持续时间不为0
{ if(!notetab[i].tone) TR1=0; //音符为0,关闭定时器0
else
{ th1=tonetab_E[notetab[i].tone-1]/256;
TH1=th1; //音符参数送TH1高8位
tl1=tonetab_E[notetab[i].tone-1]%256;
TL1=tl1; //音符参数送TL1低8位
TR1=1; //启动定时器0
}
for(j=0;j<notetab[i].delay;j++) //音符的持续时间
{ delay10ms(5); }
i++;
}
}
}
void music2()_task_ 3 //任务3:整点报时任务
{ i=0;
while(notetab3[i].delay)
{ if(!notetab3[i].tone) TR1=0;
else
{ th1=tonetab[notetab3[i].tone-1]/256;
TH1=th1; //音符参数送TH1高8位
tl1=tonetab[notetab3[i].tone-1]%256;
TL1=tl1; //音符参数送TL1低8位
TR1=1; //启动定时器1
}
for(j=0;j<notetab3[i].delay;j++) //音符的持续时间
{ delay10ms(2); }
i++; }
i=0;
while(notetab2[i].delay)
{if(!notetab2[i].tone) TR1=0;
else
{th1=tonetab_D[notetab2[i].tone-1]/256;
TH1=th1; //音符参数送TH1高8位
tl1=tonetab_D[notetab2[i].tone-1]%256;
TL1=tl1; //音符参数送TL1低8位
TR1=1; //启动定时器0
}
for(j=0;j<notetab2[i].delay;j++) //音符的持续时间
{ delay10ms(3); } //与1/4拍对应的持续时间
i++;
}
TR1=0;
inter=0;
os_delete_task(3);//删除任务
}
void music3()_task_ 4 //任务4:作息时间报时任务
{ uchar count;
for(k=0;k<26;k++)
{i=0;
while(notetab1[i].delay)
{ if(!notetab1[i].tone) TR1=0;
else
{ th1=tonetab[notetab1[i].tone-1]/256;
TH1=th1; //音符参数送TH1高8位
tl1=tonetab[notetab1[i].tone-1]%256;
TL1=tl1; //音符参数送TL1低8位
TR1=1; //启动定时器0
}
for(j=0;j<notetab1[i].delay;j++) //音符的持续时间
{delayms(4); }
i++;
count++;
if(count==10) break;
}
if(count==10) break;
}
TR1=0;
worktime=0;
os_delete_task(4);//删除任务
}
3.4数码管显示程序设计
设计方案:
数码管显示程序通过对8位数码管动态扫描并利用人眼视觉暂留效应来显示时间,即通过对单片机的P0口和P2口的循环赋值并延时较短的时间来实现。在显示相应的时间之前,首先调用处理函数对从1302读取获得的时间进行处理,在处理之前也要判断是否是正在设置闹钟,如果正在设置闹钟则处理设置的闹钟时间在数码管上实时的显示出来,否则就处理从1302获得的时间进行显示。在处理函数中,首先判断有关年月日与时分秒切换显示的标志位是否为1,如果为1则处理年月日的数据进行年月日的显示,否则如果为0则处理时分秒的数据进行时分秒的显示,此时还要判断12与24小时的切换显示的标志位是否为1,如果为1则进行12小时模式下的数据处理,否则进行正常24小时模式下的数据处理,数据具体的处理方法主要是分离BCD码然后送显示缓冲区数组。
设计源代码:
void display() //数码管显示函数
{ uchar i;
P2=0xff;
P0=0;
if(!setalarm)
{ dealNumber(a);}
if(setalarm)
{ dealNumber(alarmtime); }
for(i=0;i<8;i++)
{P0=display_buff[i];
P2=wei[i];
delayms(1);
P2=0xff;
P0=0; }
}
void dealNumber(uchar *t)
{ switch(flg1) //根据flg1值判断显示时分秒还是年月日
{case 0: //显示时分秒
if(q) //显示12小时模式用
{t[2]<<=3;
t[2]>>=3;
}
display_buff[0]=display_code[t[2]/16];//分离BCD码,以显示BCD码的十位与个位
display_buff[1]=display_code[t[2]%16];
display_buff[2]=0x40;
display_buff[3]=display_code[t[1]/16];
display_buff[4]=display_code[t[1]%16];
display_buff[5]=0x40;
display_buff[6]=display_code[t[0]/16];
display_buff[7]=display_code[t[0]%16];
break;
case 1: // 显示年月日
display_buff[0]=display_code[2];
display_buff[1]=display_code[0];
display_buff[2]=display_code[t[6]/16];
display_buff[3]=display_code[t[6]%16]+0x80;
display_buff[4]=display_code[t[4]/16];
display_buff[5]=display_code[t[4]%16]+0x80;
display_buff[6]=display_code[t[3]/16];
display_buff[7]=display_code[t[3]%16];
break;
}
}
3.5闹钟设置程序设计
设计方案:
在闹钟设置程序里首先判断是否进行加操作,即判断K3键按下的标志位是否为1,如果为1则要进行加1操作,此时接着判断K1键按下的标志位的值,若为1则说明当前显示的是年月日状态,再判断K2键按下的标志位的值,根据该值选择是对年还是月还是日进行加1,如果K1键按下的标志位为0,则说明当前显示的是时分秒状态,再判断K2键按下的标志位的值,根据该值选择是对时还是分还是秒进行加1。接着再判断是否进行减操作,即判断K4键按下的标志位是否为1,如果为1则要进行减1操作,此时接着判断K1键按下的标志位的值,若为1则说明当前显示的是年月日状态,再判断K2键按下的标志位的值,根据该值选择是对年还是月还是日进行减1,如果K1键按下的标志位为0,则说明当前显示的是时分秒状态,再判断K2键按下的标志位的值,根据该值选择是对时还是分还是秒进行减1。其中在对日进行加减操作时要判断当前年份是否是闰年,如果是闰年,则2月的日期范围在1-29之间,否则范围在1-28之间,对其他月份的日期进行加减操作时要保证范围符合大小月的日期范围。
设计源代码:
void setAlarmclock() //设置闹钟时间函数
{ int i;
for(i=0;i<7;i++)
alarmtime[i]=BCD_to_Dec(alarmtime[i]);
if(flg3)
{flg3=0;
if(flg1==0)
{ switch(flg2)
{ case 1:alarmtime[2]++; //时加1
if(alarmtime[2]==24) alarmtime[2]=0;
break;
case 2:alarmtime[1]++; //分加1
if(alarmtime[1]==60) alarmtime[1]=0;
break;
case 3:alarmtime[0]++; //秒加1
if(alarmtime[0]==60) alarmtime[0]=0;
break;
default:break;
}
}
if(flg1==1)
{
switch(flg2)
{
case 1:alarmtime[6]++; //年加1
if(alarmtime[6]==100) alarmtime[6]=0;
break;
case 2:alarmtime[4]++; //月加1
if(alarmtime[4]==13) alarmtime[4]=1;
break;
case 3:alarmtime[3]++; //日加1
if(alarmtime[4]==2)
{ leapyear(alarmtime[6]);
if(leap)//润年
{ if(alarmtime[3]==30) alarmtime[3]=1; }
else
{ if(alarmtime[3]==29) alarmtime[3]=1; }
}
else
{ switch(alarmtime[4])
{case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: if(alarmtime[3]==32) alarmtime[3]=1; break;
case 4:
case 6:
case 9:
case 11: if(alarmtime[3]==31) alarmtime[3]=1; break;
}
} break;
default:break;
} } }
if(flg4)
{flg4=0;
if(flg1==0)
{ switch(flg2)
{ case 1:if(alarmtime[2]==0) alarmtime[2]=24; //时减1
alarmtime[2]--;
break;
case 2:if(alarmtime[1]==0) alarmtime[1]=60; //分减1
alarmtime[1]--;
break;
case 3:if(alarmtime[0]==0) alarmtime[0]=60; //分减1
alarmtime[0]--;
break;
default:break;
}
}
if(flg1==1)
{ switch(flg2)
{case 1:if(alarmtime[6]==0) alarmtime[6]=100; //年减1
alarmtime[6]--;
break;
case 2:if(alarmtime[4]==0||alarmtime[4]==1) alarmtime[4]=13;//月减1
alarmtime[4]--;
break;
case 3:if(alarmtime[4]==2)
{ leapyear(alarmtime[6]);
if(leap)//润年
{if(alarmtime[3]==0||alarmtime[3]==1) alarmtime[3]=30; }
else
{if(alarmtime[3]==0||alarmtime[3]==1) alarmtime[3]=29; }
}
else
{ switch(alarmtime[4])
{ case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: if(alarmtime[3]==0||alarmtime[3]==1) alarmtime[3]=32; break;
case 4:
case 6:
case 9:
case 11: if(alarmtime[3]==0||alarmtime[3]==1) alarmtime[3]=31; break;
}
}
alarmtime[3]--; break;
} } }
for(i=0;i<7;i++)
alarmtime[i]=Dec_to_BCD(alarmtime[i]);
}
3.6键盘扫描和调时程序设计
设计方案:
在键盘扫描程序里进行切换显示和时间设置。如果按下K1键则进行切换显示,如果此时处于非调时状态,则对年月日与时分秒的切换显示标志flg1进行赋值操作,初次按下时赋值为1,再次按下时赋值为0,如此循环;如果处于调时状态,则按下K1键就对1302进行写操作启动1302计时数码管显示的标志位置1。如果按下K2键,则进入调时状态,并为选择调整位的标志位flg2进行赋值操作,每按下一次进行加1,表示要调整的位右移一位,加到4则再置为1,然后对1302进行写操作停止计时,同时对数码管显示的标志位置0,将调时标志位set置0。如果按下K3和K4键,则分别进行加操作和减操作,并根据flg2的值判断调哪一位,具体方法与闹钟设置程序中的方法相似。如果按下K5键则进入12与24小时的切换模式,如果当前处于24小时模式则按下之后显示12小时模式的时间,再次按下时又切换到24小时,状态的切换通过K5键按下的次数标志位flg5进行判断,同时K5键与K1一样具有退出调时状态的功能。如果按下K6键则进入设置闹钟状态,首次按下时为进入闹钟设置状态并将闹钟设置标志位setalarm置为1,再次按下时为退出闹钟设置状态并将闹钟设置标志位setalarm置为0,该判断通过K6键按下次数的标志位flg6进行判断,如果当前处于闹钟响铃的状态则按下K6键将闹钟响标志位ring置0,将删除闹钟的标志位del_alarm置1以便在主任务中删除闹钟任务。
设计源代码:
if(K1==0) //K1键为时分秒和年月日切换显示选择键,同时具有退出调时的功能,
{delayms(5);//延时5ms 消抖
if(K1==0)
{if(set) //非调时状态
{ flg1++;
if(flg1==2) flg1=0;
}
}
while(K1==0){}
do{delayms(5);}//延时5ms 消抖
while(K1==0);
if(!set) //K1键具有退出调时的功能
{ write_1302(0x80,a[0]);
write_1302(0x8e, 0x80); //写保护
set=1;
p=1; //数码管恢复显示从DS1302读出的值
flg2=0 ;
}
}
if(K2==0) //K2键为进入调时模式,同时做调时模式和闹钟设置模式调整位的选择
{ delayms(5);//延时5ms 消抖
if(K2==0)
{flg2++;
if(flg2==4) flg2=1;
}
while(K2==0){}
do{delayms(5);}//延时5ms 消抖
while(K2==0);
if(set&&!setalarm)//按下K2建停止计时进入调时状态
{write_1302(0x8e, 0x00);//允许写
write_1302(0x80, 0x80); //DS1302停止计时
p=0; //数码管显示停止从DS1302读值
set=0;//进入调时标志
}
}
if(K5==0) //K5键进行12小时与24小时模式转换,同时具有退出调时的功能
{ delayms(5);//延时5ms 消抖
if(K5==0)
{if(set&&!setalarm) //非调时、非设置闹钟状态
{ flg5++;
if(flg5==3) flg5=1;
}
}
while(K5==0){}
do{delayms(5);}//延时5ms 消抖
while(K5==0);
if(set&&!setalarm) //非调时、非设置闹钟状态
{ if(flg5==1)//首次按下K5建显示调为12小时模式
{write_1302(0x8e, 0x00);
write_1302(0x80, 0x80);
p=0; //数码管停止显示从DS1302读出的值
temp=BCD_to_Dec(a[2]);
if(temp>=1&&temp<=12)
{ am=1; pm=0; }
else { pm=1; am=0; }
if(temp>=13&&temp<=23) temp-=12;
if(temp==0) temp=12;
a[2]=Dec_to_BCD(temp);
time[2]=a[2];
if(am==1) write_1302(0x84,time[2]+0x80);//设置12小时上午模式
if(pm==1) write_1302(0x84,time[2]+0xa0);//设置12小时下午模式
q=1;//12小时标志
}
if(flg5==2)//再次按下K5建时显示调为24小时模式
{ write_1302(0x8e, 0x00);
write_1302(0x80, 0x80);
p=0; //数码管停止显示从DS1302读出的值
temp=BCD_to_Dec(a[2]);
if(pm)
{if(temp==12) temp=0;
else temp+=12;
}
a[2]=Dec_to_BCD(temp);
time[2]=a[2];
write_1302(0x84,time[2]);//设置24小时模式
q=0;//24小时标志
}
write_1302(0x80,a[0]);
write_1302(0x8e, 0x80);
p=1; //数码管恢复显示从DS1302读出的值
}
if(!set)//K5键具有退出调时的功能
{write_1302(0x80,a[0]);
write_1302(0x8e, 0x80); //写保护
set=1;
p=1; //数码管恢复显示从DS1302读出的值
flg2=0 ;
}
}
if(K6==0) //K6键为进入和退出闹钟设置模式,同时具有停止闹铃的功能
{delayms(5);//延时5ms 消抖
if(K6==0)
{ if(!ring)
{ flg6++;
if(flg6==2) flg6=0;
}
}
while(K6==0){}
do{delayms(5);}//延时5ms 消抖
while(K6==0);
if(!ring)
{ if(flg6==1) //首次按下K6建进入闹钟设置状态
{setalarm=1;//进入闹钟设置标志为1
alarmtime[6]=a[6];//将闹钟时间的年月日设置为当前年月日
alarmtime[4]=a[4];
alarmtime[3]=a[3];
}
if(flg6==0)//再次按下K6建退出闹钟设置状态
{setalarm=0; }//退出闹钟设置标志为0
}
if(ring)
{ring=0;
del_alarm=1;
}
}
4. 测试分析
4.1显示功能测试
测试过程:
将程序烧录到单片机中后,可以看到时分秒以24小时模式显示并正常走时,然后按下K1键,数码管从显示时分秒切换到显示年月日,再按下一次K1键又显示时分秒,再按下一次K1键又能显示年月日,再按回到时分秒的状态后接着再按下K5键,数码管从当前的24小时模式显示变成对应的12小时显示,再按下K5键,时间显示又变成24小时模式,再按下一次又回到12小时模式。
测试结果分析:
从测试结果可以得出程序基本实现了正常的显示功能,并且实现了时分秒与年月日的切换以及12与24小时模式的切换功能。
4.2调时功能测试
测试过程:
(1)时的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K2键1次,发现时分秒停止走时;此时按下一次K3键,发现时显示加1,连续按下K3键,时显示连续加1,一直加到23再按K3键,时显示又从0开始连续加1;再按下K4键一次发现时显示减1,连续按下K4键,发现时显示连续减1,一直减到0后再按K4键,时显示又从23开始连续减1。按K1键退出,发现时分秒正常走时,并接着调整好的时间显示。再按下K5键一次,时间切换到12小时模式,再重复上述对时的加减操作,发现时加到12后从1开始加1,时减到1后从12开始减1,最后按K5键退出,发现时分秒正常走时,并接着调整好的12小时模式的时间显示。
(2)分的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K2键2次,发现时分秒停止走时,接着再按K3键,发现分显示加1,连续按K3键,分显示连续加1,一直加到59后又从0开始加1;再按K4键,发现分显示减1,连续按下K4键,分显示连续减1,一直减到0后又从59开始减1。按K1键退出,发现时分秒正常走时,并接着调整好的时间显示。再按下K5键一次,时间切换到12小时模式,再重复上述对分的加减操作,最后按K5键退出,发现时分秒正常走时,并接着调整好的12小时模式的时间显示。
(3)秒的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K2键3次,发现时分秒停止走时,接着再按K3键,发现秒显示加1,连续按K3键,秒显示连续加1,一直加到59后又从0开始加1;再按K4键,发现秒显示减1,连续按下K4键,秒显示连续减1,一直减到0后又从59开始减1。按K1键退出,发现时分秒正常走时,并接着调整好的时间显示。再按下K5键一次,时间切换到12小时模式,再重复上述对秒的加减操作,最后按K5键退出,发现时分秒正常走时,并接着调整好的12小时模式的时间显示。
(4)年的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K1键1次,时间变成年月日显示,按下K2键1次,接着再按K3键一次,发现年显示加1,连续按K3键发现年显示连续加1,年的后两位加到99后从0开始加1,按下K4键一次,发现年显示减1,连续按下K4键,年显示连续减1,年的后两位减到0后又从99开始减1。最后按K1键退出,再按K1键切换到时分秒的显示,发现时分秒正常走时。
(5)月的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K1键1次,时间变成年月日显示,按下K2键2次,接着再按K3键一次,发现月显示加1,连续按K3键发现月显示连续加1,一直加到12后又从1开始加1,再按下K4键,发现月显示减1,连续按下K4键发现月显示连续减1,一直减到1又从12开始减1。最后按K1键退出,再按K1键切换到时分秒的显示,发现时分秒正常走时。
(6) 日的加减调整
将程序烧录到单片机中后,当前显示为24小时模式的时分秒,按下K1键1次,时间变成年月日显示,其中年显示的是2012,按下K2键2次,接着再按K3键将月调整为2,再按下K2键1次,再连续按K3键,发现日显示连续加1,一直加到29后又从1开始加1,再连续按下K4键,发现日显示连续减1,一直减到1后又从29开始减1;按下K2键1次,接着再按K3键将年调整为2011,重复上述对日的加减操作发现日加到28后从1开始加1,日减到1后从28开始减1;接着再按下K2键将月分别调整为1、3、5、7、8、10、12,重复上述对日的加减操作发现日加到31后从1开始加1,日减到1后从31开始减1;接着再按下K2键将月分别调整为4、6、9、11,重复上述对日的加减操作发现日加到30后从1开始加1,日减到1后从30开始减1。最后按K1键退出,再按K1键切换到时分秒的显示,发现时分秒正常走时。
测试结果分析:
通过上述测试可以得出,程序实现了基本的年月日时分秒的加减调整功能,并且同时能在12与24小时两种模式下调整,时分秒和年月的调整范围在正常范围之内,对日的调整除了能保证在正常的每个月的日期范围之内,还实现了对平年和润年的2月份日期的识别。程序存在的缺陷是在选择调整的位的时候仅实现了功能但没有给出提示,比如让数码管闪烁等,用户只能根据按下K2键的次数判断和尝试。
4.3闹钟设置功能测试
测试过程:
将程序烧录到单片机中后,按下K6键一次,发现此时数码管显示清零,按下K2键1次,再按下K3键,则时显示加1,连续按下K3键,时连续加1,加到23后从0开始加1,按下K4键,则时减1,连续按下K4键,时连续减1,减到0后从23开始减1,再按下K2键1次,再通过按K3键和K4键分别对分和秒进行加减操作,发现分和秒加到59后从0开始加1,减到0时从59开始减1,最后按1次K6键退出闹钟的设置,发现时分秒走动显示当前的时间而不是刚设置的闹钟时间。
测试结果分析:
通过上述测试可以得出,闹钟设置程序能基本完成对闹钟时间的设置并且不会影响正常的时间显示,能正常的退出闹钟设置状态。
4.4闹钟报时功能测试
测试过程:
将程序烧录到单片机中后,按下K6键将闹钟时间设定为指定的时间后后按K6键退出闹钟的设置,然后再按K2键将当前时间调整为闹钟时间的前1秒,按K1键退出调时状态,等待1秒后,到达闹钟时间,可以听到蜂鸣器播放音乐,同时数码管时分秒正常显示,然后按下再按下K6键1次,蜂鸣器停止播放音乐,同时数码管时分秒正常显示。
测试结果分析:
通过上述测试可以得出,闹钟程序基本实现了到达闹钟时刻的报时功能,并且蜂鸣器报时的时候不影响数码管显示,即数码管没有黑屏而是正常显示时分秒,说明了主任务与闹钟报时任务之间相互没有影响;到达闹钟时刻的时候蜂鸣器立刻播放音乐,按下停止键的时候蜂鸣器立刻停止播放音乐,达到了实时性的目的。程序的不足之处是,在蜂鸣器响的时候数码管的显示有轻微的闪烁,说明主任务与闹钟报时任务之间任务切换的时间片以及数码管动态扫描的延时时间设置的不太合理,没有很好的协调好。
4.5整点报时功能测试
测试过程:
将程序烧录到单片机中后,按下K2键进入调时状态,将当前时间调整为任意整点时刻的5秒之前,按K1键退出调时,当时间到达任意小时的59分55秒时,蜂鸣器开始发出滴滴的声音,直到到达00分00秒时,蜂鸣器发出最后滴的一声后开始播放音乐,同时数码管时分秒能正常显示,过一段时间之后蜂鸣器自动停止播放音乐。
测试结果分析:
通过上述测试可以得出,整点报时程序基本实现了到达整点时刻的报时功能,并且蜂鸣器报时的时候不影响数码管显示,即数码管没有黑屏而是正常显示时分秒,说明了主任务与整点报时任务之间相互没有影响;到达整点时刻,即00分00秒的时刻时,蜂鸣器准时提示最后一声的提示音并播放音乐,达到了准确性的目的。程序的不足之处是,在蜂鸣器响的时候数码管的显示有轻微的闪烁,说明主任务与整点报时任务之间任务切换的时间片以及数码管动态扫描的延时时间设置的不太合理,没有很好的协调好。
4.6作息时间报时功能测试
测试过程:
将程序烧录到单片机中后,按下K2键进入调时状态,将当前时间调整为模拟的作息时间表中的任意时刻的前1秒,按K1键退出调时,等待1秒后到达作息时间表中的时刻,蜂鸣器开始播放音乐,同时数码管时分秒能正常显示,过一段时间之后蜂鸣器自动停止播放音乐。
测试结果分析:
通过上述测试可以得出,作息时间报时程序基本实现了到达作息时间时刻的报时功能,并且蜂鸣器报时的时候不影响数码管显示,即数码管没有黑屏而是正常显示时分秒,说明了主任务与作息时间报时任务之间相互没有影响;到达作息时间时刻,蜂鸣器准时提示播放音乐,达到了准确性的目的。程序的不足之处是,在蜂鸣器响的时候数码管的显示有轻微的闪烁,说明主任务与作息时间报时任务之间任务切换的时间片以及数码管动态扫描的延时时间设置的不太合理,没有很好的协调好。
4.7整体功能测试
测试过程:
将程序烧录到单片机中后,按下K6键将闹钟时间设置为8点整,再按K6键退出,再按K2键将当前时间设置为07:59:55,按K1键退出,发现到达8点整时刻蜂鸣器只播放整点报时的音乐,而不播放闹钟报时和作息时间报时的音乐;再K6键将闹钟时间设置为作息时间表中的非整点时刻,再按K6键退出,再按K2键将当前时间设置为该时刻的前1秒,按K1键退出,等待1秒后,发现蜂鸣器只播放闹钟报时的音乐,而不播放作息时间报时的音乐;再K6键将闹钟时间设置为作息时间表中的作息时刻的后1秒,再按K6键退出,再按K2键将当前时间设置为相应作息时刻的前1秒,按K1键退出,等待2秒后,发现蜂鸣器播放整点报时或作息时间报时的音乐而不播放闹钟报时的音乐。
测试结果分析:
通过上述测试可以得出,闹钟报时、整点报时和作息时间报时这三个任务之间相互互不影响,即如果闹钟时间和整点时刻及作息时间这三个时间为同一个时刻时,蜂鸣器报时只响应其中的一个任务,而不会出现混乱;当某一时刻同时被其中的任意两个任务满足时也只蜂鸣器报时只响应其中的一个任务,而不会出现混乱,这样程序基本实现了保证各个任务之间不发生冲突的功能。
5. 总结报告
5.1功能和性能的实现
设计的最终结果实现了以下功能:
⑴能够正常显示时分秒年月日
⑵时分秒年月日都能加减可调,调时能识别平闰年和大小月
⑶12小时与24小时切换显示功能
⑷能设置闹钟,闹钟时间到播放音乐
⑸整点报时功能
⑹模拟作息时间表,实现上课下课打铃功能
⑺允许同时满足闹钟报时、整点报时和作息时间报时,但不会发生冲突
设计所具有的性能:
⑴实时性
到达闹钟时刻或整点时刻或作息时间时刻能蜂鸣器立即响应报时,而不会有延时,设置时间时,时间设置完毕,退出后设置的时间能被立即显示出来
⑵准确性
到达闹钟时刻或整点时刻或作息时间时刻蜂鸣器能准确报时,而不会有误差,调整的年月日时分秒的时候,年月日时分秒的设置所允许的有效范围均在正常范围之内,而不会出现超出范围的情况
设计的最终效果基本到达了开始在需求分析中提出的功能及性能需求的目的,基本完成了预期的设计计划。
5.2误差与故障分析
设计总共包括5个任务,最初一开始在任务0中就将5个任务同时创建出来,然后在各个任务里进行判断是否达到指定时刻,任务之间通过发送信号量的形式通信,但这样程序下载之后数码管根本就不显示,直接黑屏,而且蜂鸣器也不会响,出现这种故障显然是由于时间片的设置与任务之间的切换时间设置不合理造成的,所以我放弃了这种方法,而是采用创建一个任务作为主任务,在主任务中判断满足相应的条件时,才创建相应的任务而不是一开始就创建出来,在任务执行完毕不需要的时候将任务删除(主任务不删除),这样能最大程度减少CPU资源的占用,从而使程序运行起来更加流畅,进而达到实时性的要求。设计过程中出现过同一个时刻被三个报时任务同时满足的情况,这种情况如果不做处理,三个任务就会轮换抢占CPU,而蜂鸣器只有1个,所以这样造成最终的结果就是蜂鸣器报时混乱,不能区分是哪个任务的报时,而且蜂鸣器播放的音乐也变得混乱,起初的想法还是通过在任务中通过发送和等待信号量的方法来解决,但最终发现这样做实际的效果不好,虽然能勉强实现,但实时性不强,不能满足要求,所以我采用了更简单的方法就是在主任务中设置标志位来判断同时满足几个任务的情况下该创建哪个任务,这样实现起来就比较简单,而且实时性也好一些。设计最终还存在的不足就是蜂鸣器报时播放音乐的时候,数码管不稳定,表现为轻微的闪烁,这是由于数码管显示的延时时间、任务切换的时间还有RTX51时间片的设置没有协调好,处理不当造成的。
5.3经验与教训
由于个人硬件能力不是很强,所以选择了难度偏小的数字钟作为设计题目,这样硬件主要采用开发板,设计的主要工作在软件设计方面,但在实际的设计过程中我发现即便选的题目简单也不意味着实际做起来就容易,由于要实现的功能多,所以实现起来要考虑的方面、细节就很多,设计也就变得复杂,本次设计中我没有采用传统的方法,而是采用多任务的设计方法来完成我所需要的功能要求,主要利用RTX51所提供的任务构造方法来设计我所需要的任务。通过电子课程设计,我较为完整地熟悉了电子设计的整体流程,再次锻炼了自己的设计和编程能力,并使我对多任务的设计方法有了更深刻体会和了解,为以后的设计积累了经验。