【制作】基于金沙滩51单片机的电子跑表

基于金沙滩51单片机的电子跑表

很久之前学51单片机的时候做的了,现在分享一下。
基于金沙滩51单片机,很推荐这款单片机开发板,教程很好。

零、完成功能

本项目完成以下功能:
时钟模式:

  1. 在数码管上显示分、秒

跑表模式:

  1. 跑表显示范围:0-999.0秒
  2. 按下启动键开始计时
  3. 按下暂停键暂停计时
  4. 按下继续键继续计时
  5. 按下复位键计时归零

壹、硬件电路图

下面是项目用到的硬件电路图,完整原理图请点击:金沙滩51单片机原理图

数码管电路

贰、程序源码

注释挺多的,程序挺简单的,就不多介绍了,有问题可以在下面讨论

#include<reg52.h>
#define MAX_NUMBER 9999	  //最大值(单位:0.1)
#define INIT_NUMBER 0		 //初始值

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit keyout = P2^0;
sbit key16 = P2^7; 
sbit key13 = P2^4;
sbit key14 = P2^5;
sbit key15 = P2^6;

unsigned char code smg[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};/*数码管真值表*/

unsigned char smgbuff[] = {0xff,0xff,0xff,0xff,0xff,0xff};/*数码管显示缓存*/

unsigned int n = 0;

unsigned char add = 1;

bit keysta1 = 1;	//按键1的当前状态
bit keysta2 = 1;	//按键2的当前状态
bit keysta3 = 1;	//按键3的当前状态
bit keysta4 = 1;	//按键4的当前状态

bit p = 0;		//计时状态,是否在计时,0否,1是,2暂停

char mode = 1; //时钟模式

unsigned char time[2] = {0,0};//分,秒
unsigned char timeb[2] = {1,1};//分,秒

bit keybackup1 = 1;	//按键1备份
bit keybackup2 = 1;	//按键2备份	
bit keybackup3 = 1;	//按键3备份
bit keybackup4 = 1;	//按键4备份	

unsigned int nb = 1; //第一次显示

void main()
{

	unsigned char th = 0,tl = 0;
	
	
	
	ENLED=0;
	ADDR3=1;

	TMOD = 0x11;	/*让定时器1,0工作在模式1*/
	TH0 = 0x0DC;	/*10ms,计时用*/
	TL0 = 0x00;
	TH1 = 0x0F5;	/*3ms,刷新用*/
	TL1 = 0x33;

	ET0 = 1;	/*开定时器1,0的中断*/
	ET1 = 1;
	EA = 1;		/*开总中断*/
	TR0 = 0;	/*打开定时器*/
	TR1 = 1;

	TR0 = 1;

	keyout = 0;		//按键初始化

	while(1)
	{

		switch(mode)
		{
			case 0:

			if(nb != n)		//如果备份值不等于n,那么n变了,于是重载缓存
			{
				nb = n;
				if((n/100000) == 0)smgbuff[0] = 0xff;else smgbuff[0] = smg[n/100000];
				if((n/10000) == 0)smgbuff[1] = smg[0];else smgbuff[1] = smg[(n/10000)%10];
				if((n/1000) == 0)smgbuff[2] = smg[0];else smgbuff[2] = smg[(n/1000)%10];
				if((n/100) == 0)smgbuff[3] = smg[0];else smgbuff[3] = smg[(n/100)%10];
				if((n/10) == 0)smgbuff[4] = smg[0];else smgbuff[4] = smg[(n/10)%10];
				if((n) == 0)smgbuff[5] = smg[0];else smgbuff[5] = smg[n%10];
				if(n == 0)smgbuff[4] = smg[0];
				
				smgbuff[4] = smgbuff[4]&0x7f;	//加小数点
				/*以上是 载数码管缓存代码,大家自己研究这里不多解释*/
			}
			
			if(keysta2 != keybackup2)		//按键状态发生了变化
			{
				//if(keybackup1 == 0)			//上次是0,这次变化了,说明是抬起按键
				{
				//这里写抬起按键事件	
					
				}
				
				if(keybackup2 == 1)
				{
				//同理,这里写按下事件
					if(p == 0)	//如果没有计时则开始计时
					{
						p = 1;
					}
					else		//否则在计时了就暂停计时
					{
						//TR0 = 0;
						p = 0;
					}
				}
				keybackup2 = keysta2;//备份按键	
			}
			
			if(keysta1 != keybackup1)		//按键2状态发生了变化
			{
				//if(keybackup2 == 0)			//上次是0,这次变化了,说明是抬起按键
				{
				//这里写抬起按键事件	
					
				}
				
				if(keybackup1 == 1)
				{
				//同理,这里写按下事件
					//TR0 = 0;	//停止计时
					//TH0 = 0x4C;	/*重置50ms,计时初值,这里避免到一半继续计时,导致有50ms内误差*/
					//TL0 = 0x00;
					p = 0;	//改状态为停止计时
					n = INIT_NUMBER;	//n置初值,为什么放到这?考虑没停止定时器置初值后进n-1中断导致n的值为998
				}
				keybackup1 = keysta1;//备份按键	
			}
	
			if(keysta4 != keybackup4)		//按键3状态发生了变化
			{
				static char ok = 1;
				if(keybackup4 == 1)
				{
					if(ok){
	
						add = 27;	//n置初值,为什么放到这?考虑没停止定时器置初值后进n-1中断导致n的值为998
						ok = 0;
					}
					else
					{
						add = 1;
						ok = 1;
					}
				}
				keybackup4 = keysta4;//备份按键	
			}

			break;
			case 1:

			smgbuff[1] = 0xff;

			//分
			if(time[0]!=timeb[0])
			{
				timeb[0]=time[0];
				smgbuff[2] = smg[timeb[0]/10];
				smgbuff[3] = smg[timeb[0]%10];
				smgbuff[3] = smgbuff[3]&0x7f;	//加小数点
			}

			//秒
			if(time[1]!=timeb[1])
			{
				timeb[1]=time[1];
				smgbuff[4] = smg[timeb[1]/10];
				smgbuff[5] = smg[timeb[1]%10];
			}
			break;
		}
	}
}

void timer() interrupt 1
{
	static unsigned char cnt = 0,tim = 0;
	TH0 = 0x4C;	/*50ms,计时*/
    TL0 = 0x00;
	cnt++;
	if(cnt>=2)		//到100ms时,
	{
		cnt = 0;	//清计数
		tim++;

		if(tim == 10)
		{
			time[1]++;
			tim = 0;
			if(time[1]==60)
			{
				time[0]++;
				time[1]=0;
				if(time[0]>99)
				{
					time[0] = 99;
				}
			}
		}

		if(p){
			n+=add;		//n+1
			if(n <= 0)	//考虑到倒计时结束
			{
				n = 0;	//可不加,严谨考虑
				//ET0 = 0;//这个可有可无
				//TR0 = 0;	//关闭定时器
				p = 0;		//把状态设置为停止计时
			}
	
			if(n>MAX_NUMBER) //最大限度
			{
				n = MAX_NUMBER;
				//TR0 = 0;	//关闭定时器
				p = 0;		//把状态设置为停止计时
			}
		}
	}
}

void display() interrupt 3
{
	static unsigned char i = 1;
	static keybuff[] = {0xff,0xff,0xff};		//默认按键抬起
    TH1 = 0x0F5;	/*3ms,刷新数码管,按键扫描*/
    TL1 = 0x33;
	
	keybuff[0] = (keybuff[0]<<1)|key14;		//按键缓冲右移一位,再把新状态加进来
	/*这里实现的是:
	按键时序状态:
	11111111111111111111111111100101100000000000000000001100101111111111111111111
	第一次按键buff:(11111111)抬起
	1111111111111111111(11111111)00101100000000000000000001100101111111111111111111
	第二次按键buff:(11111110)抬起
	11111111111111111111(11111110)0101100000000000000000001100101111111111111111111
	第三次按键buff:(11111100)抬起
	111111111111111111111(11111100)101100000000000000000001100101111111111111111111
	第七次按键buff:(11001011)抬起  这里就是已经消抖了
	1111111111111111111111111(11001011)00000000000000000001100101111111111111111111
	第十五次按键buff:(00000000)按下  
	111111111111111111111111111001011(00000000)000000000001100101111111111111111111
	具体过程大家可以在草稿纸上演示,还有不懂的可以问
	*/
	keybuff[1] = (keybuff[1]<<1)|key15;		//同上理
	keybuff[2] = (keybuff[2]<<1)|key16;	  
	keybuff[3] = (keybuff[3]<<1)|key13;
	
	if((keybuff[2]&0x0F) == 0x00)	//连续4个扫描都为0,也就是4*3=12ms的时间内扫描到的都是0,可认为按下了
	{
		keysta3 = 0;//0按下
	}

	if((keybuff[2]|0xF0) == 0xFF)	//同上理,可认为抬起了
	{
		keysta3 = 1;//1抬起
	}

	if((keybuff[3]&0x0F) == 0x00)	//连续4个扫描都为0,也就是4*3=12ms的时间内扫描到的都是0,可认为按下了
	{
		keysta4 = 0;//0按下
	}

	if((keybuff[3]|0xF0) == 0xFF)	//同上理,可认为抬起了
	{
		keysta4 = 1;//1抬起
	}

	if((keybuff[0]&0x0F) == 0x00)
	{
		keysta1 = 0;//0按下
	}
	/*这里为什么要keybuff[0]&0x0F?
	比如按键缓冲是11110000
	按位与0x0f(00001111)后是0000000,而11110000是可以认为按下的
	再比如缓冲11111000,按位与后是00001000,不是0x00,所以可行
	*/
	
	if((keybuff[0]|0xF0) == 0xFF)	//同上理,可认为抬起了
	{
		keysta1 = 1;//1抬起
	}
	/*-----------------------下面同理对第二个按键处理----------------------*/
	if((keybuff[1]&0x0F) == 0x00)	
	{
		keysta2 = 0;//0按下
	}
	
	if((keybuff[1]|0xF0) == 0xFF)	
	{
		keysta2 = 1;//1抬起
	}
	
	P0=0XFF;
	switch(i)
	{
	case 1:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=smgbuff[5];break;
	case 2:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=smgbuff[4];break;
	case 3:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=smgbuff[3];break;
	case 4:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=smgbuff[2];break;
	case 5:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=smgbuff[1];break;
	case 6:ADDR2=1;ADDR1=0;ADDR0=1;i=1;P0=smgbuff[0];break;
	default:i = 1;
	}

		if(keysta3 != keybackup3)		//按键状态发生了变化
		{

			if(keybackup3 == 1)
			{
				if(mode == 1)
				{
					mode = 0;
				}			 else
				{
					mode = 1;
				}
				//mode = ~mode; //改变模式
				timeb[0] = 255;
				timeb[1] = 255;
				nb = 65535;
			}
			keybackup3 = keysta3;//备份按键	
		}
	
}

叁、项目效果

视频演示:https://www.bilibili.com/video/BV1sv41117ZX

成功完成以上功能。
喜欢的小伙伴支持一下呗~

原文地址:https://www.cnblogs.com/minuy/p/14494078.html