一、 实验目的
1、熟悉单片机的结构和各引脚的的功能以及如何用程序控制。
2、学习用单片机对数字时钟控制、按键扫描及LED数码管显示的设计方法。 3、了解键盘的结构以及工作原理,通过单片机的定义实现对数码管时钟的调整。
二、实验要求
1、可以正常准确的显示时间.
2、可以通过键盘输入来对时间进行调整. 3、能够以两种时钟表示方式显示时间. 4、自由发挥其他功能.
三、实验基本原理
利用单片机定时器完成计时功能,定时器0计时中断程序每隔0.05s中断一次并当作一个计数,设定定时1秒的中断计数初值为0,每中断一次中断计数初值加1,当加到20时,则表示1s到了,秒变量加1,同理再判断是否1min钟到了,再判断是否1h到了。采用动态显示法实现LED显示,通过对每位数码管的依次扫描,使对应数码管亮,同时向该数码管送对应的字码,使其显示数字。由于数码管扫描周期很短,由于人眼的视觉暂留效应,使数码管看起来总是亮的,从而实现了各种显示。
利用键盘实现对时钟的调整,定义四个按键,按下第一个按键位置跳变到“分”,
在按定义的第二个键每按一次数字加一,当数字到59时再按一次,直接跳变到00;
用第三个键控制“时”的12小时制还是24小时制,对键盘扫描,如果发现该键被
按下,则表示为12进制,每按一次第四个按键数字加一,当到达12时,再按一次直接跳到1,如果没有发现该按键,则默认为24小时制,当数字是23时,再按一次跳变到00,再按一下第一个键退出对事件的调整。
四、实验设计分析
针对要实现的功能,采用AT89S51单片机进行设计,AT89S51 单片机是一款低功耗,高性能CMOS8位单片机,片内含4KB在线可编程(ISP)的可反复擦写1000次的Flash只读程序存储器,器件采用高密度、非易失性存储技术制造,兼容标准MCS- 51指令系统及80C51引脚结构。这样,既能做到经济合理又能实现预期的功能。 在程序方面,采用分块设计的方法,这样既减小了编程难度、使程序易于理解,又能便于添加各项功能。程序可分为闹钟的声音程序、时间显示程序、日期显示程序,秒表显示程序,时间调整程序、闹钟调整程序、定时调整程序,延时程序等。运用这种方法,关键在于各模块的兼容和配合,若各模块不匹配会出现意想不到的错误。 首先,在编程之前必须了解硬件结构尤其是各引脚的用法,以及内部寄存器、存储单元的用法,否则,编程无从下手,电路也无法设计。这是前期准备工作。第二部分是硬件部分:依据想要的功能分块设计设计,比如输入需要开关电路,输出需要显示驱动电路和数码管电路等。第三部分是软件部分:先学习理解汇编语言的编程方法再根据设计的硬件电路进行分块的编程调试,最终完成程序设计。第四部分是软件画图部分:设计好电路后进行画图,包括电路图和仿真图的绘制。第五部分是软件仿真部分:软硬件设计好后将软件载入芯片中进行仿真,仿真无法完成时检查软件程序和硬件电路并进行修改直到仿真成功。第六部分是硬件实现部分:连接电路并导入程序检查电路,若与设计的完全一样一般能实现想要的功能。最后进行功能扩展,在已经正确的设计基础上,添加额外的功能!
主程序开始
显示时间
按1 是否有调 转? 按27 按3 24
调整分调整时制转换12 按4 制 以下为具体的数字时钟实现程序段:
#include #define uchar unsigned char #define uint unsigned int uchar sec,sec1,min,min1,hou,hou1,t,num,temp; uint num1,num2,num3,n,k=0; uchar code table[]={ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x40}; void init(); uchar keyscan(); void control(); void display(uchar ,uchar ,uchar ,uchar ,uchar ,uchar ,uchar ,uchar ); void delay(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void main(){ init(); while(1){ TR0=0; keyscan(); control(); TR0=1; if(t==20){ t=0; num1++; if(num1==60){ num1=0; num2++; if(num2==60){ num2=0; num3++; if(num3>23) num3=0; } } } sec1=num1/10; sec=num1%10; min1=num2/10; min=num2%10; hou1=num3/10; hou=num3%10; display(hou1,hou,10,min1,min,10,sec1,sec); } } void init(){ t=0; num1=0,num2=0,num3=0; TMOD=0x01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; } void time0() interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; t++; } void display(uchar a,uchar b,uchar c,uchar d,uchar e,uchar f,uchar k,uchar h) { P0=table[a]; P2=0xfe; delay(1); P0=table[b]; P2=0xfd; delay(1); P0=table[c]; P2=0xfb; delay(1); P0=table[d]; P2=0xf7; delay(1); P1=table[e]; P2=0xef; delay(1); P0=table[f]; P2=0xdf; delay(1); P0=table[k]; P2=0xbf; delay(1); P0=table[h]; P2=0x7f; delay(1); } uchar keyscan(){ P3=0xfe; temp=P3; temp=temp&0xf0; while(temp!=0xf0){ delay(5); temp=P3; temp=temp&0xf0; while(temp!=0xf0){ temp=P3; switch(temp){ case 0xee: num=1; break; case 0xde: num=2; break; case 0xbe: num=3; break; case 0x7e: num=4;k++; break; } while(temp!=0xf0){ temp=P3; temp=temp&0xf0; } } } return num; } void control(){ if(num==1){ num1++; P2=0xfe; n=num1%10; if(num1>59) num1=0; P0=table[n];//调整秒 num=0; } if(num==2){ num2++; P2=0xf7; n=num2%10; if(num2>59) num2=0; P0=table[n];//调整分 num=0; } if(num==3){ num3++; P2=0xbf; n=num3%10; if(num3>23) num3=0; P0=table[n];//调整时 num=0; } if(num==4) { if(k%2==0) { if(num3>12)num3=num3-12; } else { if(num3<12) { num3=num3+12; } } num=0; } } 五、实验总结及感想 刚接触单片机这个词语,感觉是那么的深奥,它的功能是那么的强大,能让看 上去似乎毫不相关的东西联系起来,能让静止的事物“活动起来”。刚开始和想象中的一样难,不知如何下手,前面几个课时都是一直在看视频,但是感觉光看不练,有些东西总是记不住,关掉写好的代码连最关键的语句都写不出来。当时看到大家学得是那么的得心应手,我的确很着急。但是单片机用途很多,在以后会变得很重要,虽然难,但是还得硬着头皮一点一点的摸下去。 刚开始几个课时的确没什么收获,之后我想了一下,决定还是重头开始,边看 边写,虽然进度比较慢,但是那些该记的的东西还是记住了,这样那些要记的源程序关键字也不那么陌生了,慢慢的能写一句,慢慢的开始能编好一个小程序了,我开始有点成就感,开始有点喜欢上这程序。 后来我写数字时钟,虽然看似那么平常的东西,但是具体把程序完整的写出来 还是得花些时间,编程要注意的细节很多,一个很小的标点符号都可能导致致命的错误,有的一个很小的错误,也需要你找上几个小时,很浪费时间。在编写过程中也遇到了不少问题。 1、P0口开关问题。P0口比较特殊,它存在高阻态,要使其输入不是高电平就是低 电平,就要接上拉电阻,给其高电平输入。 2、按键编译检测比较繁琐,在编程中,我按下键一次数码管上的数字会跳动好多下, 和预期的有很大差别,使后面调时间带来了极大的问题,但是后面经过小组讨论查找,最终还是把这个问题解决了,这让我深刻的意识到,一个团队是多么的重要,一个人几天都无法完成的任务,一个团队或许只需要几分钟。 我感觉最高兴的时候,不是出现最终的结果那一刻,而是在调试出错误后,大家找 各自错误,相互讨教,寻找结果的过程,因为结果是早就预知的,不过迟早而已。 我一直相信只要付出努力,就一定会有收获。坚持不懈才是成功的秘诀。 因篇幅问题不能全部显示,请点此查看更多更全内容