我在學習狀態機按鍵程序時遇到一個問題:我用狀態機按鍵方法,只能實現一個長按事件的響應。如果要能夠響應兩個不同時長的長按,程序該怎么修改呢?我試著修改了一下,可是不成功,百思不得其解,只好請各位高人指點迷津!
單片機程序如下(紅色部分是我自己修改的):
#include<reg52.h>
sbit LED0 = P0^7;
sbit LED1 = P0^6;
sbit LED2 = P0^5;
sbit LED3 = P0^4;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit KEY_INPUT = P2^4; // 按鍵IO
#define KEY_STATE_0 0 // 按鍵狀態
#define KEY_STATE_1 1
#define KEY_STATE_2 2
#define KEY_STATE_3 3
#define KEY_STATE_4 4
#define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定單擊的時間長度,軟件消抖
#define KEY_INTERVAL 30 // KEY_INTERVAL*10MS = 300MS 判定雙擊的時間間隔
#define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定長按的時間長度
#define N_KEY 0 // no click
#define S_KEY 1 // single click
#define D_KEY 2 // double click
#define T_KEY 3 // Triple click
#define L_KEY 10 // long press
unsigned char g_u8_KeyValue; // 按鍵值
// ----------------------------------- key_driver --------------------------
unsigned char key_driver(void)
{
static unsigned char key_state = 0;
static unsigned int key_time = 0;
unsigned char key_press, key_return;
key_return = N_KEY; // 清除 返回按鍵值
key_press = KEY_INPUT; // 讀取當前鍵值
switch (key_state)
{
case KEY_STATE_0: // 按鍵狀態0:判斷有無按鍵按下
if (!key_press) // 有按鍵按下
{
key_time = 0; // 清零時間間隔計數
key_state = KEY_STATE_1; // 然后進入 按鍵狀態1
}
break;
case KEY_STATE_1: // 按鍵狀態1:軟件消抖(確定按鍵是否有效,而不是誤觸)。按鍵有效的定義:按鍵持續按下超過設定的消抖時間。
if (!key_press)
{
key_time++; // 一次10ms
if(key_time>=SINGLE_KEY_TIME) // 消抖時間為:SINGLE_KEY_TIME*10ms = 30ms;
{
key_state = KEY_STATE_2; // 如果按鍵時間超過 消抖時間,即判定為按下的按鍵有效。按鍵有效包括兩種:單擊或者長按,進入 按鍵狀態2, 繼續判定到底是那種有效按鍵
}
}
else key_state = KEY_STATE_0; // 如果按鍵時間沒有超過,判定為誤觸,按鍵無效,返回 按鍵狀態0,繼續等待按鍵
break;
case KEY_STATE_2: // 按鍵狀態2:判定按鍵有效的種類:是單擊,還是長按
if(key_press) // 如果按鍵在 設定的長按時間 內釋放,則判定為單擊
{
key_return = S_KEY; // 返回 有效按鍵值:單擊
key_state = KEY_STATE_0; // 返回 按鍵狀態0,繼續等待按鍵
}
else
{
key_time++;
if(key_time >= LONG_KEY_TIME) // 如果按鍵時間超過 設定的長按時間(LONG_KEY_TIME*10ms=300*10ms=3000ms), 則判定為 長按
{
key_return = L_KEY; // 返回 有效鍵值值:長按
key_state = KEY_STATE_3; // 去狀態3,等待按鍵釋放
}
}
break;
case KEY_STATE_3: //我自己添加的代碼開始
if(key_press)
{
key_return = L_KEY;
key_state = KEY_STATE_0;
}
else
{
key_time++;
if(key_time >= 600)
{
key_return = 5;
key_state = KEY_STATE_4;
}
}
break; //我自己添加的代碼結束
case KEY_STATE_4: // 等待按鍵釋放
if (key_press)
{
key_state = KEY_STATE_0; // 按鍵釋放后,進入 按鍵狀態0 ,進行下一次按鍵的判定
}
break;
default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
key_state = KEY_STATE_0;
break;
}
return key_return; // 返回 按鍵值
}
// ----------------------------------- key_read --------------------------------
unsigned char key_read(void)
{
static unsigned char key_state1=0, key_time1=0;
unsigned char key_return,key_temp;
key_return = N_KEY; // 清零 返回按鍵值
key_temp = key_driver(); // 讀取鍵值
switch(key_state1)
{
case KEY_STATE_0: // 按鍵狀態0:等待有效按鍵(通過 key_driver 返回的有效按鍵值)
if (key_temp == S_KEY) // 如果是[單擊],不馬上返回單擊按鍵值,先進入 按鍵狀態1,判斷是否有[雙擊]的可能
{
key_time1 = 0; // 清零計時
key_state1 = KEY_STATE_1;
}
else // 如果不是[單擊],直接返回按鍵值。這里的按鍵值可能是:[長按],[無效按鍵]
{
key_return = key_temp; // 返回 按鍵值
}
break;
case KEY_STATE_1: // 按鍵狀態1:判定是否有[雙擊]
if (key_temp == S_KEY) // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms) 內,再次有[單擊],則為[雙擊],但是不馬上返回 有效按鍵值為[雙擊],先進入 按鍵狀態2,判斷是否有[三擊]
{
key_time1 = 0; // 清零 時間間隔
key_state1 = KEY_STATE_2; // 改變 按鍵狀態值
}
else // 有[單擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms)內,沒有[單擊]出現,則判定為 [單擊]
{
key_time1++; // 計數 時間間隔
if(key_time1 >= KEY_INTERVAL) // 超過 時間間隔
{
key_return = S_KEY; // 返回 有效按鍵:[單擊]
key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
}
}
break;
case KEY_STATE_2: // 按鍵狀態2:判定是否有[三擊]
if (key_temp == S_KEY) // 有[雙擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms) 內,再次有[單擊],則為[三擊],由于這里只擴展到[三擊],所以馬上返回 有效按鍵值為[三擊]
{
key_return = T_KEY; // 返回 有效按鍵:[三擊]
key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
}
else // 有[雙擊]后,如果在 設定的時間間隔(KEY_INTERVAL*10ms=30*10ms=300ms)內,沒有[單擊],則判定為 [雙擊]
{
key_time1++; // 計數 時間間隔
if(key_time1 >= KEY_INTERVAL) // 超過 時間間隔
{
key_return = D_KEY; // 返回 有效按鍵:[雙擊]
key_state1 = KEY_STATE_0; // 返回 按鍵狀態0,等待新的有效按鍵
}
}
break;
default: // 特殊情況:key_state是其他值得情況,清零key_state。這種情況一般出現在 沒有初始化key_state,第一次執行這個函數的時候
key_state1 = KEY_STATE_0;
break;
}
return key_return; // 返回 按鍵值
}
void main()
{
ENLED = 0;
ADDR3 = 1;
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;
P2 = 0xF7;
TMOD = 0x01;
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
while(1)
{
if(TF0 == 1) // 等待10ms,定時完成
{
TF0 = 0; // 清零10ms定時標志
TH0 = 0xDC;
TL0 = 0x00;
g_u8_KeyValue = key_read(); // 讀取按鍵值
switch(g_u8_KeyValue)
{
case 5: LED1 = !LED1; break; // 單擊 取反LED1
case D_KEY: LED2 = !LED2; break; // 雙擊 取反LED2
case L_KEY: LED3 = !LED3; break; // 三擊 取反LED3
// case L_KEY: LED_ALL_ON(); break; // 長按 點亮所有的LED
}
}
}
}
|