51单片机秒表(定时器扫描按键数码管)

利用定时器对代码的优化

①对按键代码的优化

之前的按键代码

unsigned char Key()
{
			unsigned char KeyNumber=0;
			
			if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
			if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
			if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
			if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
			
			return KeyNumber;
}

该代码中为了消抖加入了一个死循环,子函数中加入死循环对主函数main的运行有极大的影响,比如当我按下按键不松手的时候,main中的程序就无法执行。因此,我们可以利用定时器,定时扫描按键,是否按下。

优化后的代码

main.c中,利用定时器0,每隔20ms对Key_Loop函数进行一次调用,

void Timer0_Routine() interrupt 1
{
	    static unsigned int T0Count1,T0Count2,T0Count3;
		TL0 = 0x18;		//设置定时初值
	    TH0 = 0xFC;		//设置定时初值
		T0Count1++;
		if(T0Count1>=20)//每隔20ms对Key_Loop这个函数调用
		{
			T0Count1=0;
			Key_Loop();
		}
}

key.c中,定时器每隔20ms对Key_Loop函数进行一次调用,因为定时器是中断函数,因此每次结束时会销毁调用函数的所有变量,所以我们设置两个静态变量NowState,LastState来记录每次按键的状态,每到20ms时对Key_Loop函数调用,此时NowState已经不是该时刻的按键状态了,因此将它赋值给上一个按键状态LastState,然后NowState再通过Key_GetState函数重新获取当前的按键状态,然后在进行if判断当前按键状态和上一个按键状态,如果上一个时间点按下,这个时间点未按下,则是松手的瞬间,按键有效,然后将Key_KeyNumber置数判断是哪个按键按下,这里还需要调用一个函数用来获取当前按键键码并且对Key_KeyNumber清零,否则即使松手也会一直返回按键键码。而在main.c当中我们只需要调用Key函数就可以获取按键键码。并且无论我们长时间的按下按键也不会对主函数中的程序运行有任何影响。

#include <REGX52.H>
#include "Delay.h"

unsigned char Key_KeyNumber;

/**
  * @brief  获取按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Key_KeyNumber;
	Key_KeyNumber=0;
	return Temp;
}
/**
  * @brief  获取当前按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key_GetState()
{
			unsigned char KeyNumber=0;
			
			if(P3_1==0){KeyNumber=1;}
			if(P3_0==0){KeyNumber=2;}
			if(P3_2==0){KeyNumber=3;}
			if(P3_3==0){KeyNumber=4;}
			
			return KeyNumber;
}

/**
  * @brief  按键驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Key_Loop(void)
{
			static unsigned char NowState,LastState;
			LastState=NowState;     //按键状态更新
			NowState=Key_GetState();//获取当前按键状态
	    //如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
			if(LastState==1 && NowState==0)
			{
				Key_KeyNumber=1;
			}
			if(LastState==2 && NowState==0)
			{
				Key_KeyNumber=2;
			}
			if(LastState==3 && NowState==0)
			{
				Key_KeyNumber=3;
			}
			if(LastState==4 && NowState==0)
			{
				Key_KeyNumber=4;
			}
}

②对数码管扫描的代码进行优化

之前的数码管代码,通过在主函数中调用,依靠主函数中while的运行来实现动态的扫描

void Shuma(unsigned char Location,Number)
{
		switch(Location)
		{
			case 1:P2_4=1;P2_3=1;P2_2=1;break;
			case 2:P2_4=1;P2_3=1;P2_2=0;break;
			case 3:P2_4=1;P2_3=0;P2_2=1;break;
			case 4:P2_4=1;P2_3=0;P2_2=0;break;
			case 5:P2_4=0;P2_3=1;P2_2=1;break;
			case 6:P2_4=0;P2_3=1;P2_2=0;break;
			case 7:P2_4=0;P2_3=0;P2_2=1;break;
			case 8:P2_4=0;P2_3=0;P2_2=0;break;
		}
		P0=shumatable[Number];
		Delay(1);
		P0=0x00;//消影
}

优化后的代码

main.c中,利用定时器0,每隔2ms对Shuma_Loop函数进行一次调用,显示一位数码管,每2ms显示一位,以此来实现动态数码管的显示

void Timer0_Routine() interrupt 1
{
	    static unsigned int T0Count1,T0Count2,T0Count3;
	    TL0 = 0x18;		//设置定时初值
	    TH0 = 0xFC;		//设置定时初值
		T0Count2++;
		if(T0Count2>=2)
		{
			T0Count2=0;
			Shuma_Loop();
		}
}

shuma.c中,因为我们定义数码管的Location是1-8位,因此我们的Shuma_Loop函数每2ms被调用一次,显示一位数码管总共显示8位数码管实现一个动态的循环,因为该函数时写在定时器中断里边的,又有数码管扫描显示函数,因此该函数不需要在主函数中调用,数码管就可以显示;至于Shuma_Scan函数中第几位要显示什么数字,需要我们自行设定,因此引入一个数码管显示缓存区,通过设置缓存区所要显示的数字,因为数码管是2ms显示一位,等待下一次循环显示该位时,直接从缓存区里拿到需要显示的数字。因此在主函数中只需要调用Shuma_SetBuf函数就可以。

#include <REGX52.H>
#include "Delay.h"

//数码管显示缓存区
unsigned char Shuma_Buf[9]={0,10,10,10,10,10,10,10,10};

//数码管段码表
unsigned char shumatable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
                           //0,1,2,3,4,5,6,7,8,9,空,横杠
/**
  * @brief  设置显示缓存区
  * @param  Location 要设置的位置,范围:1~8
  * @param  Number 要设置的数字,范围:段码表索引范围
  * @retval 无
  */
void Shuma_SetBuf(unsigned char Location,Number)
{
	Shuma_Buf[Location]=Number;
}

/**
  * @brief  数码管扫描显示
  * @param  Location 要显示的位置,范围:1~8
  * @param  Number 要显示的数字,范围:段码表索引范围
  * @retval 无
  */
void Shuma_Scan(unsigned char Location,Number)
{
	  P0=0x00;  //段码清0,消影
		switch(Location) //位码输出
		{
			case 1:P2_4=1;P2_3=1;P2_2=1;break;
			case 2:P2_4=1;P2_3=1;P2_2=0;break;
			case 3:P2_4=1;P2_3=0;P2_2=1;break;
			case 4:P2_4=1;P2_3=0;P2_2=0;break;
			case 5:P2_4=0;P2_3=1;P2_2=1;break;
			case 6:P2_4=0;P2_3=1;P2_2=0;break;
			case 7:P2_4=0;P2_3=0;P2_2=1;break;
			case 8:P2_4=0;P2_3=0;P2_2=0;break;
		}
		P0=shumatable[Number];//段码输出
		
}

/**
  * @brief  数码管驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Shuma_Loop(void)
{
	static unsigned char i=1;
	Shuma_Scan(i,Shuma_Buf[i]);
	i++;
	if(i>=9){i=1;}
}

 ③完整代码

#include <REGX52.H>
#include "Delay.h"
#include "key.h"
#include "Shuma.h"
#include "Timer0.h"
#include "AT24C02.h"

unsigned char KeyNum;

unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;

void main()
{
	Timer0_Init();
	while(1)
	{
		 KeyNum=Key();
		 if(KeyNum==1)
		 {
			 RunFlag=!RunFlag;
		 }
		 if(KeyNum==2)
		 {
			 Min=0;
			 Sec=0;
			 MiniSec=0;
		 }
		 if(KeyNum==3)
		 {
			 AT24C02_WriteByte(0,Min);
			 Delay(5);
			 AT24C02_WriteByte(1,Sec);
			 Delay(5);
			 AT24C02_WriteByte(2,MiniSec);
			 Delay(5);
		 }
		 if(KeyNum==4)
		 {
			 Min=AT24C02_ReadByte(0);
			 Sec=AT24C02_ReadByte(1);
			 MiniSec=AT24C02_ReadByte(2);
		 }
			 Shuma_SetBuf(1,Min/10);
			 Shuma_SetBuf(2,Min%10);
			 Shuma_SetBuf(3,11);
			 Shuma_SetBuf(4,Sec/10);
			 Shuma_SetBuf(5,Sec%10);
			 Shuma_SetBuf(6,11);
			 Shuma_SetBuf(7,MiniSec/10);
			 Shuma_SetBuf(8,MiniSec%10);		 
		  
	}
}

void Sec_Loop(void)
{
	if(RunFlag)
	{
			MiniSec++;
			if(MiniSec>=100)
			{
				MiniSec=0;
				Sec++;
				if(Sec>=60)
				{
					Sec=0;
					Min++;
					if(Min>=60)
					{
						Min=0;
					}
				}
			}		
	}
}

void Timer0_Routine() interrupt 1
{
	  static unsigned int T0Count1,T0Count2,T0Count3;
		TL0 = 0x18;		//设置定时初值
	  TH0 = 0xFC;		//设置定时初值
		T0Count1++;
		if(T0Count1>=20)//每隔20ms对Key_Loop这个函数调用
		{
			T0Count1=0;
			Key_Loop();
		}
		T0Count2++;
		if(T0Count2>=2)
		{
			T0Count2=0;
			Shuma_Loop();
		}
		T0Count3++;
		if(T0Count3>=10)
		{
			T0Count3=0;
			Sec_Loop();
		}		
		
		
}

  • 12
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
引用\[1\]中提到了使用定时器来定时扫描独立按键数码管,以代替延时函数。定时器的好处是可以在主函数中的延时不影响按键检测和数码扫描的情况下进行定时扫描定时器会在溢出时执行中断程序,实现定时的扫描。 在引用\[2\]中,主程序利用定时器0,每隔2ms对Shuma_Loop函数进行一次调用,以实现动态数码管的显示。每2ms显示一位数码管。 而在引用\[3\]中,主程序利用定时器0,每隔20ms对Key_Loop函数进行一次调用,以实现对按键的检测。 综上所述,通过使用定时器,可以实现51单片机秒表定时器功能。可以根据需要设置定时器的时间间隔,并在定时器中断中调用相应的函数来实现所需的功能。 #### 引用[.reference_title] - *1* [51——单片机秒表定时器扫描独立按键数码管)](https://blog.csdn.net/ssssshhbh/article/details/129229740)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [51单片机秒表定时器扫描按键数码管)](https://blog.csdn.net/qq_42786221/article/details/123566767)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值