北交单片机课设电子时钟.docx
《北交单片机课设电子时钟.docx》由会员分享,可在线阅读,更多相关《北交单片机课设电子时钟.docx(33页珍藏版)》请在冰豆网上搜索。
北交单片机课设电子时钟
单片机课程设计
实验课程单片机原理与应用
实验名称电子时钟
姓名
学号
指导教师戴胜华
完成时间2014.6.8
一、设计要求
利用JD51开发板上的4个LED数码管,设计带有闹铃功能的数字时钟,要求:
1.在4位数码管上显示当前时间。
显示格式“时时分分”。
2.由LED闪动做秒显示。
3.利用按键可对时间及闹玲进行设置,并可显示闹玲时间。
当闹玲时间到蜂鸣器发出声响,按停止键使可使闹铃声停止。
4.实现秒表功能(百分之一秒显示)。
二、设计目的
通过电子时钟综合设计,学会利用8051定时器时间计时处理功能,了解按键扫描及控制LED数码管显示原理,掌握单片机和按键以及LED数码管硬件电路设计及控制程序的设计方法。
思考按键消除抖动、LED动态显示与静态显示的特点,从而提高自身解决实际问题的能力。
三、设计原理
利用单片机定时器完成计时功能,定时器0计时中断程序每隔5ms中断一次并当作一个计数,每中断一次计数加1,当计数200次时,则表示1s到了,秒变量加1,同理再判断是否1min钟到了,再判断是否1h到了。
为了将时间在LED数码管上显示,可采用静态显示法和动态显示法,由于静态显示法需要数据锁存器等较多硬件,可采用动态显示法实现LED显示。
四、proteus仿真设计及描述
仿真电路图:
全速执行后,按下sw2进入时钟调整,sw3调整小时,sw4调整分,调好后按sw2确定。
按下sw3进入闹铃时间调整,sw3调整小时,sw4调整分,调好后按sw2确定。
按下sw1进入秒表,按下sw3开始计时,sw1暂停,sw3继续,sw2重新开始计时,sw4退出,返回时钟模式。
五、设计方案及思路
1乐曲的音调与节拍
(1)音调由不同频率的方波产生,音调与频率的关系如下表所示。
要产生音频方波,只要算出某一音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。
利用计时器计时此半周期时间,每当计时到后就将输出方波的I/O反相,然后重复计时此半周期时间再对I/O反相,就可在I/O脚得到此频率的方波。
在ZKS-03实验仪上,产生方波的I/O脚选用P1.7,通过跳线选择器JP1将单片机的P1.7与蜂鸣器的驱动电路相连。
这样P1.7输出不同频率的方波,蜂鸣器便会发出不同的声音。
(2)音乐的节拍是由延时实现的,如果1拍为0.4秒,1/4拍是0.1秒。
只要设定延时时间,就可求得节拍的时间。
延时作为基本延时时间,节拍值只能是它的整数倍。
音调
1
2
3
4
5
6
7
I
频率
262
294
330
349
392
440
494
523
X
F921
F9E1
FA8C
FAD8
FB68
FBE9
FC5B
FC8F
注:
频率以HZ为单位
表1音调和发音频率及定时器初值X的对应
每个音节相应的定时器初值x可按下法计算。
其中,f是音调频率,晶振fosc=11.0592Mhz。
2定时器的使用及时间延迟
首先对定时器进行初始化
图1TMOD控制寄存器
各控制位说明如下:
GATE:
门控位
GATE=0,TR=1启动计数(软件启动);
GATE=1,TR=1且INT=1启动计数(硬件启动)。
C/T:
定时或计数方式选择位。
C/T=0,定时工作方式;C/T=1,计数工作方式。
M1M0:
工作方式选择位。
M1M0=00,方式0:
13位计数(TL0-4、TH0-7)
M1M0=01,方式1:
16位计数(TL0-7、TH0-7)
M1M0=10,方式2:
8位计数,有自动装入功能。
M1M0=11,方式3:
T0分为两个8位计数器,TL0可工作为定时或计数方式,TH0只能工作为定时方式。
图2SCON控制寄存器
SCON各控制位说明如下:
TR0/TR1:
启动计数。
1启动计数;为0停止计数。
TF0/TF1:
计数溢出。
计数结束时为1。
曲调值
DELAY
调4/4
125ms
调3/4
187ms
调2/4
250ms
表2各节拍延时时间对应关系
3蜂鸣器电路
蜂鸣器有交流和直流两种。
直流蜂鸣器驱动简单,一旦在引脚上加入直流电源它就会发出一定频率的声音,此时声音的音调和音量是固定的;而交流蜂鸣器在这方面则显得较灵活,输入声音信号的频率和音长是可控的,因此输出的声响将更逼真、更悦耳。
本实验仪有一个交流蜂鸣器,由于一般I/O口的驱动能力有限,因此不用它直接驱动蜂鸣器,它与P87C52X2的连接方式如下图所示。
Buzzer通过一个跳线与P1.7相连,P1.7输出不同频率的方波信号,蜂鸣器就会发出不同的声音。
如果控制输出信号的频率和音长,蜂鸣器则会发出悦耳的音乐。
图3(交流)蜂鸣器驱动电路
六、软件设计流程及描述
1、计时模块
利用单片机定时器0完成计时功能。
定时器0计时中断程序每隔1ms中断一次并当作一个计数,每中断一次计数加1,当计数1000次时,则表示1s到了,秒变量加1。
当秒变量达到60时,秒变量清零同时分变量加1。
分变量达到60时,分变量清零同时时变量加1。
当时变量达到24时,时变量清零。
由于实验要求由LED闪动做秒显示,因此每隔0.5s即计数500次时,P1.1(驱动LED灯)取反一次,从而实现LED灯闪动一次为1s,秒变量加1。
该模块流程图如下:
2、显示模块
为在各位LED上分别显示不同的字符,需要采用循环扫描显示的方法,即在某一时刻只选通一条位选线,并输出该位的字段码,其余位则处于关闭状态。
可见,各位LED显示的字符并不是同时出现的,但由于人眼的视觉暂留及LED的余辉,可以达到同时显示的效果。
程序流程图如下:
采用动态显示时,需要确定LED各位显示的保持时间。
由于LED从导通到发光有延时,时间太短会造成发光微弱,显示不清晰;如果显示时间太长,则会占用较多的CPU时间。
3、按键判断及处理程序
按键的闭合与否,反映在电压上就是呈现出高电平或低电平。
由于机械触点的弹性作用,在闭合及断开的瞬间,电压信号伴随有一定时间的抖动,抖动时间与按键的机械特性有关,一般是5~10ms。
为了保证CPU确认一次按键动作,既不重复也不遗漏,必须消除抖动的影响。
通过软件消除抖动的方法为:
在程序执行过程中检测到有按键按下时,调用一段延时(约10ms)子程序,然后判断该按键的电平是否仍然保持在闭合状态,如果是,则确认有键按下。
按键判断流程图如下:
按键处理流程图如下:
4、主函数流程图
七、实验源程序
C语言:
#include"reg52.h"
#defineucharunsignedchar
sbitCE573=P2^5;
sbitBUZZER=P2^4;
sbitKEY1=P3^2;
sbitKEY2=P3^3;
sbitKEY3=P3^4;
sbitKEY4=P3^5;
codeunsignedchardigseg[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
//0123456789
codeunsignedcharsegsel[]={0xfe,0xfd,0xfb,0xf7};//数码管段选
intsum=0;
ints=40;//当前时间秒
intmi=0;//当前时间分
inth=0;//当前时间时
inta;
intb;
intk;
intx,y,z,x1,x2;
intswt=1;
intswt2=1;
intmin=1;//闹钟分
inthour=0;//闹钟时
intm=1;
unsignedcharbeat;
codeunsignedsong[]={0X54,0XC4,0X12,0X32,0X34,0X52,0X52,0X52,0X52,0X42,0X32,
0X34,0XC1,0XC2,0X52,0XC2,0X12,0X32,0X34,0X11,0X12,0X31,0X12,0XC2,0X78,0X72,
0X52,0X52,0X42,0X32,0X42,0X52,0X11,0XC1,0XC4,0XC1,0XC1,0XC1,0XC1,0X12,0X32,//音乐宁夏
0X32,0X12,0X42,0X42,0X44,0XC2,0X12,0X32,0X42,0X52,0X72,0X72,0X82,0X41,0X51,
0X42,0X48,0X42,0X12,0X3C,0X72,0X62,0X3C,0X72,0X62,0X3C,0X72,0X62,0x21,0x31,
0x41,0x51,0x61,0x71,0x81,0x91,0x61,0x71,0x81,0x91,0X3F,0X3F,0X54,0XC2,0XC2,
0X42,0XC2,0XC4,0X34,0X32,0X42,0X52,0XC2,0XC4,0XD2,0XC2,0X12,0X22,0X38,0X12,
0X22,0X32,0X42,0X48,0xFF};
codeintnote[]={0x0000,0xFB03,0xFB8E,0xFC0B,0xFC43,0xFCAB,0xFD08,0xFD32,0xFD81,0xFDC7,0xFE05,0xFE21,0xFE55,0xFE83,0xFE99,0xFEC0};
unsignedcharhi_note,low_note;
voiddelay();
voidinitmpu(void)
{
TMOD=0x11;//定时器0和1都以方式一计数
TL0=0xff;
TH0=0xdb;
TL1=0xFF;
TH1=0xDB;
PCON=0x00;
IE=0x8f;//开放所有中断
}
voidDelay(intm)//延时程序
{
while(--m);
}
voidDelay1(intm)//延时程序用来播放音乐
{intgg,mm;
for(;m>0;m--)
{for(gg=180;gg>0;gg--)
for(mm=125;mm>0;mm--);}
}
voidprt(inthour,intmin)//数码管显示子程序
{
inta,b,c,d;
a=hour/10;
b=hour%10;
c=min/10;
d=min%10;
{
P2=segsel[0];
P0=digseg[a];
Delay(100);
P2=segsel[1];
P0=digseg[b];
Delay(100);
P2=segsel[2];
P0=digseg[c];
Delay(100);
P2=segsel[3];
P0=digseg[d];
Delay(100);
}
}
voidtest_beep(charn)//测试蜂鸣器
{
chari;
for(i=0;i{
BUZZER=0;
Delay(10000);
BUZZER=1;
Delay(5000);
}
}
charcheck_K3(void)//按键去抖
{
if(KEY3==0)
{
Delay(2000);
{
if(KEY3==0)
{
return1;
}
}
}
return0;
}
charcheck_K4(void)//按键去抖
{
if(KEY4==0)
{
Delay(2000);
{
if(KEY4==0)
{
return1;
}
}
}
return0;
}
charkey1_flag=0;
charkey2_flag=0;
voidext_int0()interrupt0//外部中断0
{
EA=0;
Delay(4000);
key1_flag=1;
EA=1;
}
voidext_int1()interrupt2//外部中断1
{
EA=0;
Delay(4000);
key2_flag=1;
EA=1;
}
voidtime()interrupt1//计时器0计数器中断程序,用于确定当前时间
{
EA=0;
x1++;
TR0=0;
swt=1;
TL0=0xff;//一秒
TH0=0xdb;
TR0=1;
if(x1==100)
{x1=0;
s++;
if(P1==0xfe)//LED闪烁表示一秒
P1=0xff;
else
P1=0xfe;
}
if(s==60)
{
s=0;
m=1;
mi++;
}
if(mi==60)
{
mi=0;
h++;
}
if(h==24)
h=0;
EA=1;
}
voidtime2()interrupt3//计数器1中断程序,用于播放音乐
{
TR1=0;
TL1=low_note;
TH1=hi_note;
TR1=1;
BUZZER=~BUZZER;
}
voidsinging()
{
unsignedchartemp;
intii=0;
while
(1)
{
prt(h,mi);//显示当前时间
temp=song[ii];//读出乐谱的一个byte
if(temp==0)
{//如果是0就表示音乐结束
TR1=0;//停止计时计数器1
return;//返回
}
beat=temp&0x0f;//取出低阶的4位,这是拍子
temp=(temp>>4)&0x0f;//取出高阶4位当成音符的频率
if(temp==0)
TR1=0;//如果拍子是0就表示休止符
else{
hi_note=note[temp]>>8;//根据音符的频率得到Timer1计数值
low_note=note[temp]&0x00FF;
TR1=1;//启动计时计数器1
}
Delay1(beat);//延迟拍子的时间
TR1=0;//计时器1停止计数
BUZZER=1;
ii++;
if(key2_flag)//按2键退出闹铃
{
key2_flag=0;
P2=0xff;
m=0;
TR1=0;
BUZZER=1;
break;
}
}
}
main()
{
initmpu();
TR0=1;//定时器0开始工作
while
(1)//不断刷新程序
{
prt(h,mi);//显示当前时间
if(key1_flag)//以中断方式响应按键,秒表
{
key1_flag=0;//去除外部中断1标志位
P2=0xff;
test_beep
(1);//驱动蜂鸣器
a=0;
b=0;
while
(1)//不断刷新程序
{
prt(0,0);
if(check_K3())
{
P2=0xff;
break;//key3再次按下,开始计时
}
}
test_beep
(1);
while
(1)
{
if(swt&&swt2)
{
swt=0;
a++;
if(a==60)
{
a=0;
b++;
}
}
prt(b,a);
if(key1_flag==1)//key1再次按下停止计数
{
P2=0xff;
key1_flag=0;
test_beep
(1);
swt2=0;
}
if(check_K3())//按下key3再次计数
{
P2=0xff;
key1_flag=0;
test_beep
(1);
swt2=1;
}
if(check_K4())//按下key4跳出程序
{
P2=0xff;
Delay(8000);
test_beep
(1);
break;
}
if(key2_flag==1)
{
key2_flag=0;
a=0;
b=0;
P2=0xff;
test_beep
(1);
}
}
}
elseif(key2_flag)//调整当前时间
{
P2=0xff;
key2_flag=0;
test_beep(3);
while
(1)
{
prt(h,mi);
if(check_K3())//调时
{
h++;
P2=0xff;
test_beep
(1);
if(h==24)
h=0;
Delay(10000);
}
if(check_K4())//调分
{
mi++;
P2=0xff;
test_beep
(1);
if(mi==60)
{
mi=0;
h++;
}
if(h==24)
h=0;
Delay(10000);
}
if(key2_flag)//再按2键退出调时
{
P2=0xff;
key2_flag=0;
test_beep(3);
Delay(10000);
break;
}
}
}
elseif(check_K3())//设置闹钟时间
{
P2=0xff;
test_beep(3);
while
(1)
{
prt(hour,min);//显示当前闹铃时间
if(check_K3())
{
P2=0xff;
hour++;
test_beep
(1);
if(hour==24)
hour=0;
Delay(10000);
}
elseif(check_K4())
{
P2=0xff;
min++;
test_beep
(1);
if(min==60)
{
min=0;
hour++;
}
if(hour==24)
hour=0;
Delay(10000);
}
if(key2_flag)//按2键退出
{
key2_flag=0;
P2=0xff;
test_beep(5);
Delay(10000);
break;
}
}
}
elseif(check_K4())//观看当前秒单位
{
test_beep
(1);
k=1000;
while(k--)
{
prt(0,s);
}
P1=0xff;
}
if((h==hour)&&(min==mi))//响铃
{
while(m)
{
singing();
}
}
}
}
汇编:
ORG0000H
AJMPMAIN;
ORG000BH
AJMPT0INT;
ORG0013H
CJNER5,#00H,SAL;stopthealarm
AJMPEXINT1;
SAL:
AJMPST;
ST:
MOV43H,#01H;
ORG001BH
AJMPT1INT;
ORG0050H
MAIN:
MOV79H,#00H;
MOV7AH,#00H;
MOVSP,#60H;
MOVDPTR,#DESG;
MOVR2,#20D;
MOVR4,#00H;
MOVR5,#00H;
MOV45H,#00H;
MOV46H,#02H;
SETBEA;
SETBEX1;
SETBET0;
SETBET1;
SETBIT1;
MOVTMOD,#61H;T0astimer,T1ascounter
MOVTL1,#0FFH;
MOVTH1,#0FFH;
SETBTR1;
ACALLADJ;-------------------------------------------------clockinitialize
ACALLBEEP;
MOVR4,#00H;
ACALLALADJ;-----------------------------------------------alarminitialize
ACALLEXC;
ACALLBEEP;
MOVR5,#01H;marktheendofaladj;
MOVTL0,#00H;
MO