北京交通大学单片机实验报告电子时钟.docx

上传人:b****7 文档编号:11057707 上传时间:2023-02-24 格式:DOCX 页数:24 大小:778.22KB
下载 相关 举报
北京交通大学单片机实验报告电子时钟.docx_第1页
第1页 / 共24页
北京交通大学单片机实验报告电子时钟.docx_第2页
第2页 / 共24页
北京交通大学单片机实验报告电子时钟.docx_第3页
第3页 / 共24页
北京交通大学单片机实验报告电子时钟.docx_第4页
第4页 / 共24页
北京交通大学单片机实验报告电子时钟.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

北京交通大学单片机实验报告电子时钟.docx

《北京交通大学单片机实验报告电子时钟.docx》由会员分享,可在线阅读,更多相关《北京交通大学单片机实验报告电子时钟.docx(24页珍藏版)》请在冰豆网上搜索。

北京交通大学单片机实验报告电子时钟.docx

北京交通大学单片机实验报告电子时钟

北京交通大学单片机实验报告-电子时钟

单片机课程设计

实验报告

电子时钟

电子时钟

、实验目的

学习8051定时器时间计时处理、按键扫描及LED数码管显示的设计方法。

二、设计任务及要求

利用实验平台上4个LED数码管,设计带有闹铃功能的数字时钟,要求:

1.在4位数码管上显示当前时间。

显示格式“时时分分”

2.由LED闪动做秒显示。

3.利用按键可对时间及闹玲进行设置,并可显示闹玲时间。

当闹玲时间到蜂鸣器发出声响,按停止键使可使闹玲声停止。

4.拓展:

使数字时钟的闹铃为音乐。

(电子音调发生器)

三、硬件设计

1.显示模块为了将时间在LED数码管上显示,可采用静态显示法和动态显示法,由于静态显示法需要数据锁存器等较多硬件,可采用动态显示法实现LED显示。

方法是将

所有位的段选线相应并联,由一个8位I/O口控制,从而形成段选线的多路复用,同时各位的公共端分别由相应的I/O线控

制,实现分时选通

硬件电路图如下,图中10k电阻起到限流作用;三极管起到驱动数码管的作用。

若不使用三极管,数码管发光微弱。

2.闹铃模块

闹铃声由交流蜂鸣器产生,电路图如下。

当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时间。

按键的闭合与否,反映在电压上就是呈现出高电平或低电平。

由于机械触点的弹性作用,在闭合及断开的瞬间,电压信号伴随有一定时间的抖动,抖动时间与按键的机械特性有关,一般是5~10ms。

为了保证CPU确认一次按键动作,既不重复也不遗漏,必须消除抖动的影响。

通过软件消除抖动的方法为:

在程序执行过程中检测到有按键按下时,调用一段延时(约10ms)子程序,然后判断该按键的电平是否仍然保持在闭合状态,如果是,则确认有键按下。

按键判断流程图如下:

 

按键处理流程图如下:

4.音乐响铃模块音乐闹铃程序:

单片机演奏一个音符,是通过引脚,周期性的输出一个特定频率的方波。

这就需要单片机,在半个周期内输出低电平、另外半个周期输出高电平,周而复始。

众所周知,周期为频率的倒数,可以通过音符的频率计算出周期;演奏时,要根据音符的不同,把对应的半个周期的定时时间初始值,送入定时器,再由定时器按时输出高低电平。

另外,音乐的节拍是由延时实现的。

我所使用的单片机音乐演奏程序中,包括了两个数据表,其中存放了事先算好的各种音符频率所对应的半周期的定时时间初始值。

有了这些数据,单片机就可以演奏低音、中音、高音,三个八度共21个音符。

演奏乐曲时,就根据音符的不同数值,从表中找到定时时间初始值,送入定时器即可控制音调。

通过调用延迟来实现节拍数。

乐曲的数据,也要写个数据表:

表中每三个数字,说明了一个音符,它们分别代表:

第一个数字是音符的数值;第二个数字是123之一,代表低音、中音、高音;第三个数字是时间长度,以半拍为单位。

乐曲数据表的结尾是三个0。

音节与频率的关系如下表所示

音调

X

音调

X

音调

X

低音1

F921

中音1

FC8F

高音1

FE47

低音2

F9E1

中音2

FCEE

高音2

FE77

低音3

FA8C

中音3

FD44

高音3

FEA2

低音4

FAD8

中音4

FD6B

高音4

FEB6

低音5

FB68

中音5

FDB4

高音5

FEDA

低音6

FBE9

中音6

FDF4

高音6

FEFA

低音7

FC5B

中音7

FE2D

高音7

FF16

程序流程图如下:

 

五、程序清单

#include

#defineucharunsignedchar

#defineuintunsignedint

ucharcodesegcode[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳数码管0~9

ucharcodeselect[]={0x0e,0x0d,0x0b,0x07};/正/常显示时

数码管位选

code

uchar

select1[]={0x0f,0x0d,0x0b,0x07};/高/两位屏蔽

时的位选(第2位只显示dp)

ucharcodeselect2[]={0x0e,0x0d,0x0f,0x0f};/低/两位屏蔽时的位选

ucharbuffer[]={0,0,0,0};//用来存放时间

uinthour,min,sec;

uintalarmhour,alarmmin;

uintstatus=0;//模式值

sbitmusic=P1^0;//闹铃

sbitled=P1^1;//秒驱动LED闪烁

bitringoff=1;//闹铃停止

uintcount=0;//定时器计数

ucharkeyinput;

ucharbuf=0xff;//用来存放按键值

uchartimer1h,timer1l,time;//time为节拍(延迟时间),timer1l、timer1h为计数器1初值

ucharcodefreqh[]={0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC,//低音1~7第一个八度

0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,//中音1~7第二个八度

0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF};//高音1~7第三个八度

ucharcodefreql[]={0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B,//低音1234567

0x8F,0xEE,0x44,0x6B,0xB4,0xF4,0x2D,//中音1234567

0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16};//高音1234567

/*ucharcodesong[]={3,2,2,3,2,2,4,2,2,5,2,2,5,2,2,4,2,2,3,2,2,2,2,2,1,2,2,1,2,2,2,2,2,3,2,2,3,2,3,2,2,1,2,2,4,

3,2,2,3,2,2,4,2,2,5,2,2,5,2,2,4,2,2,3,2,2,2,2,2

1,2,2,1,2,2,2,2,2,3,2,2,2,2,3,1,2,1,1,2,4,

2,2,2,2,2,2,3,2,2,1,2,2,2,2,2,3,1,2,2,2,2,2,3,2,1,4,2,1,3,2,2,2,2,2,

1,2,2,2,2,2,5,1,2,3,2,2,3,2,2,3,5,2,2,4,2,2,3,2,2,4,2,1,2,2,1,1,2,2,1,2,2,

2,2,2,3,2,2,2,2,3,1,2,1,1,2,4,2,1,2,2,2,2,2,3,2,1,4,2,1,3,2,2,1,2,2,2,2,2,

3,2,1,4,2,1,3,2,2,2,2,2,1,2,2,2,3,2,2,3,2,2,4,2,2,5,2,2,5,2,2,4,2,2,3,2,2,

4,2,1,2,2,1,1,2,2,1,2,2,2,2,2,3,1,2,4,0,0,0};//欢乐颂*/

ucharcodesong[]={5,3,2,3,3,1,4,3,1,5,3,2,3,3,1,4,3,1,5,3,1,5,2,1,6,2,1,7,2,1,1,3,1,2,3,1,3,3,1,4,3,1,3,3,2,1,3,1,2,3,1,3,3,2,

3,2,1,4,2,1,5,2,1,6,2,1,5,2,1,4,4,2,1,5,2,1,4,2,2,6,2,1,5,2,1,4,2,2,3,2,1,2,2,1,3,2,

1,2,2,1,1,2,1,2,2,1,

3,2,1,4,2,1,5,2,1,6,2,1,4,2,2,6,7,2,1,1,3,1,5,2,1,6,2,1,7,2,1,1,3,1,2,3,1,3,3,1,4,3,

1,5,3,1,3,3,2,1,3,1,2,3,1,

3,3,2,2,3,1,1,3,1,2,3,1,7,2,1,1,3,1,2,3,1,3,3,1

2,3,1,1,3,1,7,2,1,1,3,2,6,2,1,7,2,1,1,3,2,1,2,1,2,2,

1,

3,2,1,4,2,1,3,2,1,2,2,1,3,2,1,1,3,1,7,2,1,1,3,1

6,2,2,1,3,2,7,2,1,6,2,2,5,2,1,4,2,1,5,2,1,4,2,1,3,2,

1,4,2,1,5,2,1,6,2,1,7,2,1,1,2,1,

6,2,2,1,3,1,7,2,1,1,3,2,7,2,1,6,2,1,7,2,1,1,3,1

2,3,1,1,3,1,7,2,1,1,3,1,6,2,1,7,2,1,0,0,0};//卡农

//一个音符有三个数字。

前为音节、中为第几个八度、后为时长(以半拍为单位)。

延迟tms程序

voiddelay1ms(uintt)

{

uinti;

}

while(t--)for(i=0;i<123;i++);

按键处理程序

************************/

voidkeyprocess(ucharkey){

switch(key)

{

case0xe0:

status++i;f(status>=5)status=0;break;//第一个键被按下,模式值+1

case0xd0:

//第

二个键被按下

switch(status)

{

case0x01:

if(hour<23)hour++;elsehour=0;break;//模式1小时+1,到24变成0

case0x02:

if(min<59)min++;elsemin=0;break;//模式2分钟+1

case0x03:

if(alarmhour<23)alarmhour++;elsealarmhour=0;break;//模式3闹铃小时+1

case0x04:

if(alarmmin<59)alarmmin++;elsealarmmin=0;break;//模式4闹铃分钟+1

}

break;

case0xb0:

//第三个键被按下

switch(status)

{

case0x01:

if(hour>0)hour--;elsehour=23;break;

case0x02:

if(min>0)min--;elsemin=59;break;

case0x03:

if(alarmhour>0)

alarmhour--;elsealarmhour=23;break;

case0x04:

if(alarmmin>0)

alarmmin--;elsealarmmin=59;break;

}

break;

case0x70:

ringoff=~ringoff;break;

//第四个键按下,闹铃开关。

default:

break;

}

}

正常走时时数码管

/******************显示程序*************/

voiddisplay()

uinti;

if(status==3||status==4)//闹铃时间

{

buffer[0]=alarmhour/10;buffer[1]=alarmhour%10;buffer[2]=alarmmin/10;buffer[3]=alarmmin%10;

}

else//正常时间

{

buffer[0]=hour/10;buffer[1]=hour%10;buffer[2]=min/10;buffer[3]=min%10;

}

for(i=0;i<4;i++)

{

if(i==1)

{

P0=segcode[buffer[i]]-0x80;//第二个数码管显示dp

P2=select[i];

delay1ms

(1);//否则数码管上无

显示

P2=0xff;//否则乱码

}

else

{

P0=segcode[buffer[i]];//查找段码值

P2=select[i];//查找位选

delay1ms

(1);//否则数码管上无显示

P2=0xff;//否则乱码

}

}

}

/********************高两位屏蔽时的显示程序*************/

voiddisplay1()

{

uinti;

if(status==1)

{

buffer[0]=hour/10;

buffer[1]=hour%10;

buffer[2]=min/10;

buffer[3]=min%10;

}

else//status==3时

{

buffer[0]=alarmhour/10;

buffer[1]=alarmhour%10;

buffer[2]=alarmmin/10;

buffer[3]=alarmmin%10;

}

for(i=0;i<4;i++)

{

if(i==1)

{

P0=0x7f;//只显示小数点

P2=select1[i];

显示

delay1ms

(1);//否则数码管上无

P2=0xff;//否则乱码

}

else

P0=segcode[buffer[i]];

P2=select1[i];

delay1ms

(1);//否则数码管上无显示

P2=0xff;//否则乱码

}

}

}

/*****************低两位屏蔽时的显

voiddisplay2()

{

uinti;

if(status==2)

{

buffer[0]=hour/10;buffer[1]=hour%10;buffer[2]=min/10;buffer[3]=min%10;

}

else//status==4时

buffer[0]=alarmhour/10;buffer[1]=alarmhour%10;buffer[2]=alarmmin/10;buffer[3]=alarmmin%10;

}

for(i=0;i<4;i++)

{

if(i==1)

{

P0=segcode[buffer[i]]-0x80;//

第2个数码管显示dp

P2=select2[i];

delay1ms

(1);//否则数码管上无显示

P2=0xff;//否则乱码

}

else

{

P0=segcode[buffer[i]];

P2=select2[i];

delay1ms

(1);//否则数码管上无

显示

P2=0xff;//否则乱码

数码管抖动延迟

voiddelaydd(uintp)

{

uintk;

}

for(k=p;k!

=0;k--)display();//延迟时也显示,避免数码管闪烁

判断按键程序

voidpress()

{

keyinput=P1&0xf0;//取按键状态if(keyinput!

=0xf0)//有按键按下?

{

delaydd(20);//利用数码管显示来

达到延迟的效果。

防止有键按下是数码管闪

if(keyinput!

=0xf0)//开关仍在处于闭合状态

{

buf=keyinput;//将按键值赋给buf

}

else

{

keyprocess(buf);

buf=0xff;

}

}

else

{

keyprocess(buf);/当/松开按键时(无键按下)才进行按键处理

buf=0xff;

}

}

定时器T0中断

voidt0()interrupt1

TH0=(65536-1000)/256;

TL0=(65536-1000)%256;//重新装入计数初值

count++;//正常计时

if(count==550)//定时0.5S到,以下为时钟的正常走钟逻辑。

理论上应为500

{

led=~led;//LED灯亮0.5s,灭0.5s。

达到秒显示的效果

count=0;

if(led==0)//达到1s

sec++;

}

if(sec>=60)

{

sec=0;min++;

}

if(min>=60)

{

min=0;hour++;

if(hour>=24)

hour=0;

}

/**********定时器T1中断。

用来产生不同频率方波********/

voidt1()interrupt3

{

TR1=0;//先关中断

music=~music;//产生方波

TH1=timer1h;

TL1=timer1l;//重新装入初值

}

TR1=1;//在开始计数

延迟半节拍数

*******************/

voiddelay(uchart)

{

uchart1;

unsignedlongt2;for(t1=0;t1<10*t;t1++){

display();//防止响音乐时数码管无

显示

press();

for(t2=0;t2<200;t2++);

}

}

TR1=0;//一个音符发送完后关计数

产生音乐程序

voidsend()

{

TH1=timer1h;

TL1=timer1l;

TR1=1;

}

delay(time);//根据节拍数调用延迟

函数

*******************voidmain(){

uinth=0;uinti,k;music=1;

TMOD=0x11;

TH0=(65536-1000)/256;//1ms计数初值

TL0=(65536-1000)%256;

EA=1;ET0=1;//开中断

ET1=1;

P3=0xff;while

(1){

if(status==1||status==2)TR0=0;//当调整时间时,秒不走。

elseTR0=1;//开始计数

if(status==0)P3=0xff;if(status==1||status==2)P3=0xfe;//调整时间指示灯

if(status==3||status==4)P3=0xfd;//调整闹铃指示灯

press();//判断是否有键按下并

处理

if(hour==alarmhour&&min==alarmmin)//闹铃时间到

{

i=0;

time=1;

while(!

ringoff)

{

k=song[i]+7*song[i+1]-8;//找到相应音符的计数初值

timer1h=freqh[k];

timer1l=freql[k];//装入计数初值

time=song[i+2];

i=i+3;

if(time!

=0)send();//播放音符elsei=0;//循环播放

}

}

music=1;

if(status==0)//给显示加

上一些效果

display();

elseif(status==1||status==3)//高两

位闪烁

{

if(h<75){display();h++;}else

{

display1();

h++;if(h==150)h=0;

}

}

else//低两位闪烁

{

if(h<75){display();h++;}else

{

display2();

h++;

if(h==150)h=0;

六、

1.

2.

3.

实验遇到的问题及解决方法

问题:

数码管上无显示解决方法:

用三极管来驱动数码管的位选端,否则数码管发光微弱。

问题:

数码管显示出现乱码解决方法:

动态显示时,需要确定LED各位显示的保持时间。

在某一位显示结束后,应将P2口置为0xff。

问题:

按下按键时,数码管上的数字跳跃式变化。

解决方法:

按键处理程序调用的位置错误。

当有键按下时,将按键值赋给buf,松开按键时,才对buf中的数据进行按键处理,并重新将buf置为0xff。

若在键按下时进行处理,会导致数字跳跃式变化。

if(keyinput!

=0xf0)

{

buf=keyinput;//有键按下时赋给buf}else{

keyprocess(buf);/当/松开按键时(无键按下)才进行按键处理

buf=0xff;

}

4.问题:

按下按键时,数码管的显示闪烁一次解决方法:

在程序执行过程中检测到有按键按下时,会调用一段延时(约10ms)子程序来消抖。

若通过执行空操作来实现延时10ms,则会使动态扫描的时间间隔变长,数码管亮度变暗。

因此我通过调用数码管显示程序来实现延时10ms,即如下:

voiddelaydd(uintp)

{

uintk;

for(k=p;k!

=0;k--)display();//延迟时也显示,避免数码管闪烁

}

5.问题:

在放音乐过程中,数码管上无显示解决方法:

音乐的节拍是由延时实现的。

当放音乐时,一直在调用延时程序,因此在延时程序中调用数码管显示程序,问题即可得到解决。

如下:

voiddelay(uchart)

{

uchart1;

unsignedlongt2;for(t1=0;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 初中教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1