目錄
有消除按鍵的機械抖動的原因:
消除抖動的方法有硬體和軟體兩種方法:
硬體方法
軟體方法
鍵盤的分類:
按鍵消抖的相關優化:
用while來判斷:
用if來判斷:
代碼示例
有消除按鍵的機械抖動的原因:
通常的按鍵所用開關為機械彈性開關。由于機械觸電的彈性作用,按鍵在閉合及斷開的瞬間均伴随有一連串的抖動。鍵抖動會引起一次按鍵被誤讀多次。為了確定CPU對鍵的一次閉合僅作一次處理,必須去除抖動。

消除抖動的方法有硬體和軟體兩種方法:
硬體方法:
常用RS觸發器電路。
軟體方法:
是當檢測出鍵閉合後執行一個10ms~20ms的延時程式,再一次檢測鍵的狀态,如仍保持閉合狀态,則确認真正有鍵按下。
鍵盤的分類:
獨立式鍵盤與矩陣鍵盤(行列鍵盤)
他們的差別也是顯而易見,就是說,我們對于矩陣鍵盤(行列鍵盤)要進行掃描,即檢視哪一行那一列的按鍵發生了電平變化,進行防抖處理後,如果依舊電平變化,那麼我們認為是按下了這個按鍵
按鍵消抖的相關優化:
但是我發現消抖這一過程會将主程式的程序限制在判斷這一環節,我設計的有下面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;
}