單擊,雙擊,長按
/***********************************************************************************************************
程序的功能:一個按鍵的單擊,雙擊,長按。三種按鍵方式,然后做不同的處理。這里就以P1口LED燈變化作為測試。
單擊:點亮P1口上第一個LED燈。(低電平點亮)
雙擊:點亮P1口上第二個LED燈。(低電平點亮)
長按:長按:點亮P1口上的8個LED燈。(低電平點亮)
使用方法:
硬件的連接:P2.0作為按鍵的輸入,P1口作為LED顯示,連接好后,直接復制粘貼生成HEX文件,燒寫進C51就能用。
************************************************************************************************************/
#include<reg51.h>
#define N_key 0 //無鍵
#define S_key 1 //單鍵
#define D_key 2 //雙鍵
#define L_key 3 //長鍵
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
sbit key_input = P2^0; // 按鍵輸入口
unsigned char time_10ms_ok = 0;
unsigned char key = 0;
unsigned char key_driver(void)
{
static unsigned char key_state = key_state_0, key_time = 0;
unsigned char key_return = N_key;
bit key_press;
key_press = key_input; // 讀按鍵I/O電平
switch (key_state)
{
case key_state_0: // 按鍵初始態
if (!key_press) key_state = key_state_1; // 鍵被按下,狀態轉換到按鍵消抖和確認狀態
break;
case key_state_1: // 按鍵消抖與確認態
if (!key_press)
{
key_time = 0; //
key_state = key_state_2; // 按鍵仍然處于按下,消抖完成,狀態轉換到按下鍵時間的計時狀態,但返回的還是無鍵事件
}
else
key_state = key_state_0; // 按鍵已抬起,轉換到按鍵初始態。此處完成和實現軟件消抖,其實按鍵的按下和釋放都在此消抖的。
break;
case key_state_2:
if(key_press)
{
key_return = S_key; // 此時按鍵釋放,說明是產生一次短操作,回送S_key
key_state = key_state_0; // 轉換到按鍵初始態
}
else if (++key_time >= 100) // 繼續按下,計時加10ms(10ms為本函數循環執行間隔)
{
key_return = L_key; // 按下時間>1000ms,此按鍵為長按操作,返回長鍵事件
key_state = key_state_3; // 轉換到等待按鍵釋放狀態
}
break;
case key_state_3: // 等待按鍵釋放狀態,此狀態只返回無按鍵事件
if (key_press) key_state = key_state_0; //按鍵已釋放,轉換到按鍵初始態
break;
}
return key_return;
}
/*=============
中間層按鍵處理函數,調用低層函數一次,處理雙擊事件的判斷,返回上層正確的無鍵、單鍵、雙鍵、長鍵4個按鍵事件。
本函數由上層循環調用,間隔10ms
===============*/
unsigned char key_read(void)
{
static unsigned char key_m = key_state_0, key_time_1 = 0;
unsigned char key_return = N_key,key_temp;
key_temp = key_driver();
switch(key_m)
{
case key_state_0:
if (key_temp == S_key )
{
key_time_1 = 0; // 第1次單擊,不返回,到下個狀態判斷后面是否出現雙擊
key_m = key_state_1;
}
else
key_return = key_temp; // 對于無鍵、長鍵,返回原事件
break;
case key_state_1:
if (key_temp == S_key) // 又一次單擊(間隔肯定<500ms)
{
key_return = D_key; // 返回雙擊鍵事件,回初始狀態
key_m = key_state_0;
}
else
{ // 這里500ms內肯定讀到的都是無鍵事件,因為長鍵>1000ms,在1s前低層返回的都是無鍵
if(++key_time_1 >= 50)
{
key_return = S_key; // 500ms內沒有再次出現單鍵事件,返回上一次的單鍵事件
key_m = key_state_0; // 返回初始狀態
}
}
break;
}
return key_return;
}
void main()
{
P1 = 0xff; //IO口初始化
P2 = 0xff;
//定時器的初始化
TMOD = 0x01; //選擇定時器的工作模式:定時器0,方式1
TH0 = (65535 - 10000)/256; //定時器的初值
TL0 = (65535 - 10000)%256;
EA = 1; //開打總中斷使能
ET0 = 1; //打開定時器0 的使能
TR0 = 1; //打開定時器0 ,開始工作
while(1)
{
if(time_10ms_ok) //time_10ms_ok = 1,表示計時到了10MS。(10MS掃描一次按鍵)
{
time_10ms_ok = 0; //清除計時10MS標志
key = key_read(); //調用掃描按鍵程序,返回一個鍵值
if (key == L_key) //長按:點亮P1口上的8個LED燈。(低電平點亮)
{
P1 = 0x00;
}
else if(key == D_key)//雙擊:點亮P1口上第二個LED燈。(低電平點亮)
{
P1 = 0xfd;
}
else if(key == S_key)//單擊:點亮P1口上第一個LED燈。(低電平點亮)
{
P1 = 0xfe;
}
}
}
}
void timer0(void) interrupt 1 //用的是定時器0, 這個“interrupt 1”中的“1”代表1號中斷即是定時器0中斷。如果是“0”就是外部中斷0;“2“=外部中斷1;”3“定時器1中斷;”4“=串行口中斷
{
TH0 = (65535 - 10000)/256;
TL0 = (65535 - 10000)%256; //定時器0的方式1,得在中斷程序中重復初值。
time_10ms_ok = 1; //定時10MS 的標志
}
|