天天看點

STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:

目錄

有消除按鍵的機械抖動的原因:

消除抖動的方法有硬體和軟體兩種方法:

        硬體方法

        軟體方法

鍵盤的分類:

按鍵消抖的相關優化:

        用while來判斷:

        用if來判斷:

        代碼示例

有消除按鍵的機械抖動的原因:

通常的按鍵所用開關為機械彈性開關。由于機械觸電的彈性作用,按鍵在閉合及斷開的瞬間均伴随有一連串的抖動。鍵抖動會引起一次按鍵被誤讀多次。為了確定CPU對鍵的一次閉合僅作一次處理,必須去除抖動。 

STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:

消除抖動的方法有硬體和軟體兩種方法:

硬體方法:

常用RS觸發器電路。

軟體方法:

是當檢測出鍵閉合後執行一個10ms~20ms的延時程式,再一次檢測鍵的狀态,如仍保持閉合狀态,則确認真正有鍵按下。

STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:
STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:

 鍵盤的分類:

獨立式鍵盤與矩陣鍵盤(行列鍵盤)

STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:
STC89C52單片機獨立按鍵與矩陣鍵盤的消抖與掃描有消除按鍵的機械抖動的原因:消除抖動的方法有硬體和軟體兩種方法: 鍵盤的分類:

他們的差別也是顯而易見,就是說,我們對于矩陣鍵盤(行列鍵盤)要進行掃描,即檢視哪一行那一列的按鍵發生了電平變化,進行防抖處理後,如果依舊電平變化,那麼我們認為是按下了這個按鍵

按鍵消抖的相關優化:

但是我發現消抖這一過程會将主程式的程序限制在判斷這一環節,我設計的有下面2種都是這樣(就簡單的以獨立按鍵消抖為例)

用while來判斷:

#include <REGX52.H>

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
  while (xms){
			i = 2;
			j = 239;
			do
			{
				while (--j);
					} while (--i);
			xms--;		
	}
}

void main(){

	if(P3_1==0){
		Delay(20);
		while(P3_1==0);
		Delay(20);	
		P2_0=~P2_0;
	
	}

}
           

用if來判斷:

#include <REGX52.H>

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
  while (xms){
			i = 2;
			j = 239;
			do
			{
				while (--j);
					} while (--i);
			xms--;		
	}
}

void main(){

	if(P3_1==0){
		Delay(20);
		if(P3_1==0){
		Delay(20);	
		P2_0=0;
		  }
	}

}
           

隻有松手,住程式的程序才會離開while或者if複合語句,主程式的程序限制在判斷這一環節有是沒問題嗎?就是比如我們在按鍵改變數位管這樣的對時間要求比較高的裝置,若按下按鍵不松手數位管的顯示就會有問題了

那麼我們怎麼解決這個問題呢,其實我們需要在這句話上下功夫:

檢測鍵的狀态,如仍保持閉合狀态,則确認真正有鍵按下。

我們之前用while或者if複合語句來進行按鍵狀态的判斷的時候,會出現while或者if死循環的現象,是以我們隻要用定時器,在較短的時間間隔裡來檢測按鍵狀态,這樣按鍵檢測(由中斷程式執行)就不會幹擾主程式

代碼示例

依舊是以獨立按鍵為基礎進行掃描:

主要的思路是,檢測按動變化的節點:此時間狀态按鍵(NowState)是什麼,上一時間狀态按鍵(LastState)是什麼。依此還可以設定是按下生效還是松手生效;這一點在秒表計時的時候很重要,可以避免按下按鍵到松手這一時間而造成的誤差

#include <REGX52.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;//按鍵清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;
	}
}
           

主程式:

#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"

unsigned char KeyNum;

void main()
{
	Timer0_Init();
	while(1)
	{
        KeyNum=Key();
		Nixie_SetBuf(8,KeyNum);
	}
}


void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count1,T0Count2,T0Count3;
	TL0 = 0x18;		//設定定時初值
	TH0 = 0xFC;		//設定定時初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();	//20ms調用一次按鍵驅動函數
	}
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();//2ms調用一次數位管驅動函數,注意在定時器中不能有耗時的操作
	} 						
}
           

數位管:

#include <REGX52.H>

//數位管顯示緩存區
unsigned char Nixie_Buf[9]={};//初始時數位管什麼都不顯示

/**
  * @brief  設定顯示緩存區
  * @param  Location 要設定的位置,範圍:1~8
  * @param  Number 要設定的數字,範圍:段碼表索引範圍
  * @retval 無
  */
void Nixie_SetBuf(unsigned char Location,Number)//設定Nixie_SetBuf(将子函數的資料儲存在數列裡面)
{
	Nixie_Buf[Location]=Number;
}

/**
  * @brief  數位管掃描顯示
  * @param  Location 要顯示的位置,範圍:1~8
  * @param  Number 要顯示的數字,範圍:段碼表索引範圍
  * @retval 無
  */
void Nixie_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=NixieTable[Number];	//段碼輸出
//定時器中不能有耗時的操作,是以這裡沒有延時 
}

//數位管段碼表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};

/**
  * @brief  數位管驅動函數,在中斷中調用
  * @param  無
  * @retval 無
  */
void Nixie_Loop(void)
{
	static unsigned char i=1;
	Nixie_Scan(i,Nixie_Buf[i]);//循環掃描數位管
	i++;
	if(i>=9){i=1;}
}
           

定時器0:

#include <REGX52.H>

void Timer0_Init(void)
{
	TMOD &= 0xF0;		//設定定時器模式
	TMOD |= 0x01;		//設定定時器模式
	TL0 = 0x18;		//設定定時初值
	TH0 = 0xFC;		//設定定時初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定時器0開始計時
	ET0=1;
	EA=1;
	PT0=0;
}