单片机应用篇-- --倒计时数字钟,矩阵按键可调

开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!

项目要求

这篇也和之前那篇文章单片机应用篇-- --数码管60秒计时,独立按键可调 差不多,而且原因也差不多哦,也是平台有人私信让我帮忙瞧瞧,直接上图。
在这里插入图片描述

项目分析

这次的项目和上次差不多,而且上次写的的时候也是按照矩阵按键检测写的,与上次的区别就是上次是只有秒,而这次时、分、秒都有了,上次只有四个按键,而这次有16个按键,需要多个按键处理函数。

  • 首先,就是对定时器、数码管显示的初始化
void Init(){
    
    
	//参数初始化
	hour=0;
	minute=0;
	second=0;
	t0=0;
	flag=0;
	flag1=0;
	flag2=0;
	//定时器0初始化
	TMOD=0x01;
	TH0=(65536-9174)/256;   //10ms
	TL0=(65536-9174)%256;
	EA=1;
	ET0=1;
	TR0=0;
}
  • 接下来是矩阵按键检测,这里面也要设置一个标志位,因为Func功能没有允许的情况下就让0-9这几个按键起作用(不能人家正倒计时走呢,你瞎按改变时分秒的值吧)
void KeyDown(){
    
    
	char a=0;
	GPIO_KEY=0x0f;
	if(GPIO_KEY!=0x0f){
    
    
		Delay(10);
		if(GPIO_KEY!=0x0f){
    
    
			GPIO_KEY=0x0f;
			switch(GPIO_KEY){
    
    
				case 0x07 : KeyVal=0;flag3=1;break;
				case 0x0b : KeyVal=1;flag3=1;break;
				case 0x0d : KeyVal=2;flag3=1;break;
				case 0x0e : KeyVal=3;flag3=1;break;
			}
			GPIO_KEY=0xf0;
			switch(GPIO_KEY){
    
    
				case 0x70 : KeyVal=KeyVal;break;
				case 0xb0 : KeyVal=KeyVal+4;break;
				case 0xd0 : KeyVal=KeyVal+8;break;
				case 0xe0 : KeyVal=KeyVal+12;break;
			}
		}
		while((a<150) && (GPIO_KEY!=0xf0)){
    
    
			Delay(1);
			a++;
		}
	}
}
  • 检测完就要处理了,这个处理的目的就是要设置按键与功能的对应了。
void Keypross(){
    
    
	if(flag3==1){
    
    
		flag3=0;
		switch(KeyVal){
    
    
			case 0:Numpross(7);break;
			case 1:Numpross(8);break;
			case 2:Numpross(9);break;
			case 3:Uppross();break;
			case 4:Numpross(4);break;
			case 5:Numpross(5);break;
			case 6:Numpross(6);break;
			case 7:Downpross();break;
			case 8:Numpross(1);break;
			case 9:Numpross(2);break;
			case 10:Numpross(3);break;
			case 11:Funcpross();break;
			case 12:Numpross(0);break;
			case 13:break;
			case 14:Backpross();break;
			case 15:Enterpross();break;
			
		}
	}
}
  • 按键Func功能,当按下这个按键之后,要进入设置功能,就要让定时器暂停,这里就要用到那个标志位,允许改变时分秒
void Funcpross(){
    
    
	TR0=0;	//停止计时
	flag=1; //允许设置时间
}
  • 按键Up功能,首先判断Func按键是否按下过,按下过的话,让改变数字的那位向后加一位
void Uppross(){
    
    
	if(flag==1){
    
    
		flag1++;
		if(flag1>5){
    
    
			flag1=0;
		}
	}
}
  • 按键Down功能,和Up一样,若按下过,让改变数字的那位向前减一位
void Downpross(){
    
    
	if(flag==1){
    
    
		flag1--;
		if(flag1<0)
			flag1=5;
	}
}
  • 按键数字功能,这里就要判断Func是否按下和Up和Down是否调过位(默认为第一位,也就是小时的十位)
void Numpross(u8 dat){
    
    
	u8 tp;
	if(flag==1){
    
    
		switch(flag1){
    
    
			case 0:tp=hour%10;hour=tp+dat*10;break;
			case 1:tp=hour/10;hour=tp*10+dat;break;
			case 2:tp=minute%10;minute=tp+dat*10;break;
			case 3:tp=minute/10;minute=tp*10+dat;break;
			case 4:tp=second%10;second=tp+dat*10;break;
			case 5:tp=second/10;second=tp*10+dat;break;
		}
	}
}
  • 按键Enter功能,当它按下表示设置完成,Func那个标志位就要清零了,判断一下小时是否在24以内,分和秒是否在60以内(若超过就要强制归为23,59,59),还有定时器开始计时,开始倒计时。
void Enterpross(){
    
    
	flag=0;		//清零,不允许设置时间
	flag1=0;
	if(hour>23)
		hour=23;
	if(minute>59)
		minute=59;
	if(second>59)
		second=59;
	TR0=1;		//开始计时
}
  • 按键Back功能,这个暂时不设置,题目没细说,可自行设计。
void Backpross(){
    
    
//暂无功能
}
  • 按键模块完了,就要进入本体,倒计时了呀,让定时器计时,每一秒让秒自减一,这里就要判断了,减完一之后如果分是正数,分自减一并且秒归为59,若是负数(无符号0减完就是256)它比59大,判断小时的正负,若为正小时自减一并且分归为59,若小时也是负数(比23大),表示时分秒都为0,将时分秒都归零,定时器停止,倒计时结束。最后将时分秒分别求模和求余放到数组中,方便数码管显示。
void Datapross(){
    
    
	if(flag2==1){
    
    
		flag2=0;
		second--;
		if(second>59){
    
    
			if(minute>59){
    
    
				if(hour>23){
    
    
					second=0;
					minute=0;
					hour=0;
					TR0=0;
				}
				else{
    
    
					hour--;
					minute=59;
				}
			}
			else{
    
    
				minute--;
				second=59;
			}	
		}	
	}
	Display[0]=smgduan[hour/10];
	Display[1]=smgduan[hour%10];
	Display[2]=0x40;
	Display[3]=smgduan[minute/10];
	Display[4]=smgduan[minute%10];
	Display[5]=0x40;
	Display[6]=smgduan[second/10];
	Display[7]=smgduan[second%10];
}

完整代码

//main.c
#include "smg.h"

void main(){
    
    
	Init();
	while(1){
    
    
		KeyDown();
		Keypross();
		Datapross();
		Showsmg();
	}
}

//数码管显示函数
void Showsmg(){
    
          
	u8 i;
	for(i=0;i<8;i++)
	{
    
    
	   switch(7-i)	  //位选
	   {
    
    
		  case 0: LSA=0;LSB=0;LSC=0;break;
		  case 1: LSA=1;LSB=0;LSC=0;break;
		  case 2: LSA=0;LSB=1;LSC=0;break;
		  case 3: LSA=1;LSB=1;LSC=0;break;
		  case 4: LSA=0;LSB=0;LSC=1;break;
		  case 5: LSA=1;LSB=0;LSC=1;break;
		  case 6: LSA=0;LSB=1;LSC=1;break;
		  case 7: LSA=1;LSB=1;LSC=1;break;
	   }
	  P0=Display[i];
	  Delay(1);
	  P0=0x00;
	}
}
//延时函数
void Delay(u16 i)   //11.0592 1ms
{
    
    
	while(i--){
    
    
		unsigned char i, j;

		_nop_();
		i = 2;
		j = 199;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
} 
//初始化函数
void Init(){
    
    
	//参数初始化
	hour=0;
	minute=0;
	second=0;
	t0=0;
	flag=0;
	flag1=0;
	flag2=0;
	//定时器0初始化
	TMOD=0x01;
	TH0=(65536-9174)/256;   //10ms
	TL0=(65536-9174)%256;
	EA=1;
	ET0=1;
	TR0=0;
}
//按键检测
void KeyDown(){
    
    
	char a=0;
	GPIO_KEY=0x0f;
	if(GPIO_KEY!=0x0f){
    
    
		Delay(10);
		if(GPIO_KEY!=0x0f){
    
    
			GPIO_KEY=0x0f;
			switch(GPIO_KEY){
    
    
				case 0x07 : KeyVal=0;flag3=1;break;
				case 0x0b : KeyVal=1;flag3=1;break;
				case 0x0d : KeyVal=2;flag3=1;break;
				case 0x0e : KeyVal=3;flag3=1;break;
			}
			GPIO_KEY=0xf0;
			switch(GPIO_KEY){
    
    
				case 0x70 : KeyVal=KeyVal;break;
				case 0xb0 : KeyVal=KeyVal+4;break;
				case 0xd0 : KeyVal=KeyVal+8;break;
				case 0xe0 : KeyVal=KeyVal+12;break;
			}
		}
		while((a<150) && (GPIO_KEY!=0xf0)){
    
    
			Delay(1);
			a++;
		}
	}
}
//按键处理
void Keypross(){
    
    
	if(flag3==1){
    
    
		flag3=0;
		switch(KeyVal){
    
    
			case 0:Numpross(7);break;
			case 1:Numpross(8);break;
			case 2:Numpross(9);break;
			case 3:Uppross();break;
			case 4:Numpross(4);break;
			case 5:Numpross(5);break;
			case 6:Numpross(6);break;
			case 7:Downpross();break;
			case 8:Numpross(1);break;
			case 9:Numpross(2);break;
			case 10:Numpross(3);break;
			case 11:Funcpross();break;
			case 12:Numpross(0);break;
			case 13:break;
			case 14:Backpross();break;
			case 15:Enterpross();break;
			
		}
	}
}
//数据处理
void Datapross(){
    
    
	if(flag2==1){
    
    
		flag2=0;
		second--;
		if(second>59){
    
    
			if(minute>59){
    
    
				if(hour>23){
    
    
					second=0;
					minute=0;
					hour=0;
					TR0=0;
				}
				else{
    
    
					hour--;
					minute=59;
				}
			}
			else{
    
    
				minute--;
				second=59;
			}	
		}	
	}
	Display[0]=smgduan[hour/10];
	Display[1]=smgduan[hour%10];
	Display[2]=0x40;
	Display[3]=smgduan[minute/10];
	Display[4]=smgduan[minute%10];
	Display[5]=0x40;
	Display[6]=smgduan[second/10];
	Display[7]=smgduan[second%10];
}
//按键数字处理
void Numpross(u8 dat){
    
    
	u8 tp;
	if(flag==1){
    
    
		switch(flag1){
    
    
			case 0:tp=hour%10;hour=tp+dat*10;break;
			case 1:tp=hour/10;hour=tp*10+dat;break;
			case 2:tp=minute%10;minute=tp+dat*10;break;
			case 3:tp=minute/10;minute=tp*10+dat;break;
			case 4:tp=second%10;second=tp+dat*10;break;
			case 5:tp=second/10;second=tp*10+dat;break;
		}
	}
}
//Up功能处理
void Uppross(){
    
    
	if(flag==1){
    
    
		flag1++;
		if(flag1>5){
    
    
			flag1=0;
		}
	}
}
//Down功能处理
void Downpross(){
    
    
	if(flag==1){
    
    
		flag1--;
		if(flag1<0)
			flag1=5;
	}
}
//Func功能处理
void Funcpross(){
    
    
	TR0=0;	//停止计时
	flag=1; //允许设置时间
}
//Enter功能处理
void Enterpross(){
    
    
	flag=0;		//清零,不允许设置时间
	flag1=0;
	if(hour>23)
		hour=23;
	if(minute>59)
		minute=59;
	if(second>59)
		second=59;
	TR0=1;		//开始计时
}
//Back功能处理
void Backpross(){
    
    
//暂无功能
}

void Timer0() interrupt 1{
    
    
	TH0=(65536-9174)/256;   //重置
	TL0=(65536-9174)%256;
	t0++;
	if(t0==100){
    
    
		t0=0;
		flag2=1;
	}
}

//smg.h
#include "reg52.h"
#include "intrins.h"

typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_KEY P1
u8 smgduan[]={
    
    0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

u8 t0,KeyVal,Display[8],flag,flag1,flag2,flag3;
u16 hour,minute,second;				   
//38译码器					 
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

//数码管显示函数
void Showsmg();
//延时函数
void Delay(u16 i);
//初始化函数
void Init();
//按键检测
void KeyDown();
//按键处理
void Keypross();
//数据处理
void Datapross();
//按键数字处理
void Numpross(u8 dat);
//Up功能处理
void Uppross();
//Down功能处理
void Downpross();
//Func功能处理
void Funcpross();
//Enter功能处理
void Enterpross();
//Back功能处理
void Backpross();

总结

  • 有点遗憾就是,倒计时设置时间的时候让数码管闪烁的功能暂时没有完成,有一个想法就是,让数码管一阵显示正常,一阵显示0x00(不显示)。写出来之后再补充。
  • 从这次可以了看出我之前坚持写博客文章已经逐渐起了作用,阅读量也在增加,而且让我帮忙看看的网友也成功加好友了,让我们一起进步,嘿嘿!

补充

  • 数码管闪烁问题
    设置一个全局变量来控制闪烁频率,这个闪烁其实就是数码管动态扫描的时候循环到标志位的时候让它一阵显示数字一阵不显示,或者是跳过此次循环
void Flash(unsigned char t)				
{
    
    	
	unsigned char i;
	num%=50;
	for(i=0;i<8;i++)
	{
    
    
		switch(7-i)	  //位选
	   {
    
    
		  case 0: LSA=0;LSB=0;LSC=0;break;
		  case 1: LSA=1;LSB=0;LSC=0;break;
		  case 2: LSA=0;LSB=1;LSC=0;break;
		  case 3: LSA=1;LSB=1;LSC=0;break;
		  case 4: LSA=0;LSB=0;LSC=1;break;
		  case 5: LSA=1;LSB=0;LSC=1;break;
		  case 6: LSA=0;LSB=1;LSC=1;break;
		  case 7: LSA=1;LSB=1;LSC=1;break;
	   }
		if((i==t)&&(num<25)){
    
    
			P0=0x00;
			Delay(1);
			P0=0x00;
//			continue;
		}
		else
		{
    
    
			P0=Display[i];
			Delay(1);
			P0=0x00;
			
		}
	}
	num++;
}

而对应其他地方需要需要修改的是:Up和Down功能需要跳过区分时分秒的那两个杠,扩大标志位的范围原来只有6位,现在就要把两个杠包括进去就是8位。

//Up功能处理
void Uppross(){
    
    
	if(flag==1){
    
    
		flag1++;
		if(flag1>7){
    
    		//扩大范围
			flag1=0;
		}
		if((flag1==5)||(flag1==2)){
    
    		//跳过两个 -
			flag1++;
		}
	}
}
//Down功能处理
void Downpross(){
    
    
	if(flag==1){
    
    
		flag1--;
		if(flag1<0)
			flag1=7;
		if((flag1==5)||(flag1==2)){
    
    
			flag1--;
		}
	}
}

最后就是要在主函数中调用

void main(){
    
    
	Init();
	while(1){
    
    
		KeyDown();
		Keypross();
		Datapross();
		if(flag==1)
			Flash(flag1);
		else
			Showsmg();
		
	}
}

注意smg.h文件函数声明的变化

  • Bug问题
    按照上面写的当分(minute)为0且小时(hour)不为0的时候,倒计时的时候会出错,(原因就是没有设置秒的值)所以要在数据处理函数中,在符合该条件时添加一句 second=59; 因为是先判断在处理,所以判断条件改为(==0) 还有一个问题就是,当计时结束后不能重新设置计时,需要在计时结束后退出去(return)
//数据处理
void Datapross(){
    
    
	if(flag2==1){
    
    
		flag2=0;
		second--;
		if(second>59){
    
    
			if(minute==0){
    
    		//修改条件
				if(hour==0){
    
    	//修改条件
					second=0;
					minute=0;
					hour=0;
					TR0=0;
					return;		//退出到主函数
				}
				else{
    
    
					hour--;
					minute=59;
					second=59;	//设置秒的值
				}
			}
			else{
    
    
				minute--;
				second=59;
			}	
		}	
	}
	Display[0]=smgduan[hour/10];
	Display[1]=smgduan[hour%10];
	Display[2]=0x40;
	Display[3]=smgduan[minute/10];
	Display[4]=smgduan[minute%10];
	Display[5]=0x40;
	Display[6]=smgduan[second/10];
	Display[7]=smgduan[second%10];
}

猜你喜欢

转载自blog.csdn.net/weixin_45420737/article/details/109930390