利用C51单片机实现LED数码管显示秒表。主要使用到的结构是51单片机的三个P口以及定时器。
要求:
制作一个LED数码管显示的秒表,用2位数码管显示计时时间,最小计时单位为"十毫秒",计时范围为0.01-9.99s.当第一次按下并松开计时功能键时,秒表开始计时并显示时间;第二次按下并松开计时器功能键,停止计时,计算两次按下计时功能键的时间,并在数码管上显示;第三次按下计时功能键,秒表清零,再按一次计时功能键,重新开始计时。如果计时到9.99s时将停止计时,按下计时功能键,秒表清零,再按下重新开始计时。
分析:
既然要的是三位数字那么一定要的就是三个7段数码管了,刚开始可能看着这么多的功能可能会感到很复杂,实际上我们从本质上来看,就是说如果不按键就是卡主不动,按一下就会一直走,一直走到9.99然后停止(这个时候再按一下就会清零) 按第二下就会停下来,停在当前计数的时间,按第三下就会归零回到从来没有按过的状态。从这里也可以分析处理9.99秒停止的时候的状态实际上也是按第二下的状态,再按一下就归零。这样看起来就没有那么的复杂,但是还是有些绕,我们的全局变量中当中实际上要做的就是把0-999的数字记录下来的一个值,一个记录下按键次数的值,再一个记录下中断次数的值。
代码如下:
//P0口作为I/O端口输出时,输出级属于开漏电路,必须外接10KΩ上拉电阻
#include <reg51.h>
unsigned char code discode1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};//一共十个数,code关键字是意味着存储到ROM当中去。
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//上面(有小数点)和下面(无小数点)都是从0到9的字节码
unsigned char code discode3[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//第三个数
unsigned char times=0;//中断了多少次
int second;//一共经历的时间,这里要注意的是如果你要记录的数据较大就不能够用char和unsigned char来定义,否则就无法表示
unsigned char key=0;
void main(void)
{
TMOD=0x01;
ET0=1;//允许TO进行中断
EA=1;
second=0;//初值赋值为0
P0=discode1[second/100];//显示对应的秒位
P2=discode2[second/10-(second/100)*10];//显示对应的后面
P1=discode3[second%10];
while(1)
{
if((P3&0x80)==0x00)//正常情况下为1,但开关按下去为0,此处仅仅对P3.7口做了一个判断P3^7?
{
while((P3&0x80)==0x00);//如果你不松开就一直保持在这个循环里面,不改变状态继续之前的中断操作
key++;
switch(key)
{
case 1://第一次按键,启动计时
TH0=0xee;//一次为5ms的中断
TL0=0x00;
TR0=1;//启动定时器的必备条件
break;
case 2:
TR0=0;//暂停计时
break;
case 3://第三下清零,并将次数设置为1
key=0;
second=0;//设置秒数为零
P0=discode1[second/100];//显示对应的秒位
P2=discode2[second/10-(second/100)*10];//显示对应的后面
P1=discode3[second%10];
break;
}
//while((P3&0x80)==0x00){};//按键时间过长在此循环,不添加此步一会又要返回到判断过程中,
}
}
}
void timer() interrupt 1 using 0
{
TR0=0;//停止计时,在设置时间的时候就不计时了,会存在一定误差,在这里暂停说明刚才的中断T0已经溢出,所以后续操作首先要对其进行赋初值
TH0=0xee;
TL0=0X00;
times++;
if(times==2)//2x5ms=10ms是0.01s
{
times=0;
second++;//0.01为单位的数量+1
P0=discode1[second/100];//显示对应的秒位
P2=discode2[second/10-(second/100)*10];//显示对应的后面
P1=discode3[second%10];//0.01位显示
}
if(second==999)//当满了的时候,这个时候停止就好了
{
TR0=0;
//TH0=0xee;
//TL0=0x00;
second=0;
key=2;//用程序设置按键次数为第二次因为下次再按下去就是第三次清零,符合题目要求
}
else
{
TR0=1;//否则开中断继续计时
}
}
代码注释已经分析的很详细了,再提醒一下就是对应的记录次数的变量要找一个>999范围的例如int型,虽然很简单的问题但是很容易忽视。
接下来就是proteus电路了如下所示,()要注意的是P0口作为I/O端口输出时,输出级属于开漏电路,必须外接上拉电阻(图中用的是一个阻排(RESPACK))否则高电平就无法输出。还有就是数码管是选择共阴极(cathnode)的而非共阳极(anode)的
元器件:
存在的问题(已解决):
我不太明白为什么AT89C51单片机为什么可以在不接外部晶振的情况下工作,因为我查到的网上资料都是说不能不接外部晶振的,然而此处的电路没有接却照样可以正常运行。
问题解决:
51单片机实际上是必须要有一个外部的晶振的,但是在proteus当中是可以省略的,因为这个仿真程序已经自动帮我添加了对应的晶振,要改变的话可以右击51单片机然后点击edit property 有一个clock frequency就是对应的晶振频率。