上篇文章,介紹了將按鍵檢測增加長按功能,并將按下抖動與松開抖動共用一個抖動狀態來表示,其狀態圖如下:
仔細研究這個狀態圖,其它還存在一些問題:
短按狀態,只要按下去,不需要等按鍵再釋放,就會觸發短按事件。對于需要按下再松開作為一次短按的應用來說,此狀態圖也不滿足需求
長按狀態,必須先經過短按狀態,即長按按鍵,會先觸發一個短按,再觸發一個長按。如果實際應用中需要分別使用短按和長按,則此狀態圖不滿足要求
本篇,就來解決上述兩個問題,并再增加一個按鍵雙擊檢測,實現一個功能更全面的按鍵檢測。
1 增加雙擊檢測
增加一個雙擊檢測,需要增加兩個狀態:
等待再次按下
確認第2次按下
同時,之前的“短按狀態”和“長按狀態”分別改為“確認按下”和“確認長按”。
1.1 狀態圖修改
修改后的狀態圖如下,有以下幾點需要注意:
“確認按下”不是短按觸發的條件,需要等松開后,經消抖進入到“等待再次按下”一段時間后(200ms),沒有再次被按下,才觸發短按事件,這樣就解決了本篇開頭提到的第1個問題
“確認按下”不是短按觸發的條件,另一個用途是,當此狀態繼續保持按下狀態一段時間后(1s),則會單獨觸發長按事件,同時進入到“確認長按”狀態,這樣就解決了本篇開頭提到的第2個問題
對于雙擊事件的檢測,首先按下按鍵進入“確認按下”狀態,然后在1s內松開進入“等待再次按下”狀態,接著在200ms內再次按下進入“確認第2次按下”狀態,然后在1s內松開,即可觸發雙擊事件,并同時進入“穩定松開”狀態
注意,在“確認第2次按下”狀態下,如果在1s內沒有松開,也會進入到“確認長按”狀態
1.2 程序編寫
根據狀態圖,修改對應的狀態機邏輯,修改后的代碼如下:
void key_status_check()
{
switch(g_keyStatus)
{
//按鍵釋放(初始狀態)
case KS_RELEASE:
{
//檢測到低電平,先進行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
}
break;
//抖動
case KS_SHAKE:
{
if (KEY0 == 1)
{
//從松開狀態來的抖動
if (KS_RELEASE == g_lastKeyStatus)
{
g_keyStatus = KS_RELEASE;
}
//從等待再次按下狀態來的抖動
else if (KS_WAIT_PRESS_AGAIN == g_lastKeyStatus)
{
g_keyStatus = KS_WAIT_PRESS_AGAIN;
}
//從確認按下狀態來
else if (KS_AFFIRM_SHORT_PRESS == g_lastKeyStatus)
{
g_WaitPressAgainCnt = 0;
g_keyStatus = KS_WAIT_PRESS_AGAIN;
}
//從確認再次按下狀態來
else if (KS_AFFIRM_PRESS_AGAIN == g_lastKeyStatus)
{
printf("=====> key double press\r\n");
g_keyStatus = KS_RELEASE;
}
//從確認長按狀態來
else if (KS_AFFIRM_LONG_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_RELEASE;
}
else
{
printf("err!\r\n");
}
}
else
{
//從確認按下狀態來的抖動
if (KS_AFFIRM_SHORT_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_SHORT_PRESS;
}
//從第2次按下狀態來的抖動
else if (KS_AFFIRM_PRESS_AGAIN == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_PRESS_AGAIN;
}
//從確認長按狀態來的抖動
else if (KS_AFFIRM_LONG_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_AFFIRM_LONG_PRESS;
}
//從松開狀態而來
else if (KS_RELEASE == g_lastKeyStatus)
{
g_PressTimeCnt = 0;
g_keyStatus = KS_AFFIRM_SHORT_PRESS;
//printf("=====> key short press\r\n");
}
//從等待再次看下(的松開)狀態而來
else if (KS_WAIT_PRESS_AGAIN == g_lastKeyStatus)
{
g_Press2TimeCnt = 0;
g_keyStatus = KS_AFFIRM_PRESS_AGAIN;
}
else
{
printf("err!\r\n");
}
}
}
break;
//確認按下
case KS_AFFIRM_SHORT_PRESS:
{
//檢測到高電平,先進行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
else
{
if (g_LongPressTimeCnt % 20 == 0) //每隔1000ms打印一次
{
printf("=====> key long press:%d\r\n", g_LongPressTimeCnt/20);
keyEvent = KE_LONG_PRESS;
}
g_LongPressTimeCnt++;
}
}
break;
//等待再次按下
case KS_WAIT_PRESS_AGAIN:
{
//檢測到低電平,先進行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
g_WaitPressAgainCnt++;
if (g_WaitPressAgainCnt == 4) //200ms沒有再次按下
{
printf("=====> key single press\r\n");
g_keyStatus = KS_RELEASE;
}
}
break;
//確認第2次按下
case KS_AFFIRM_PRESS_AGAIN:
{
//檢測到高電平,先進行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_Press2TimeCnt++;
if (g_Press2TimeCnt == 20) //1000ms
{
g_LongPressTimeCnt = 0;
g_keyStatus = KS_AFFIRM_LONG_PRESS;
}
}
break;
//確認長按
case KS_AFFIRM_LONG_PRESS:
{
//檢測到高電平,先進行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_LongPressTimeCnt++;
if (g_LongPressTimeCnt % 20 == 0) //每隔1000ms打印一次
{
printf("=====> key long press:%d\r\n", g_LongPressTimeCnt/20);
}
}
break;
default:break;
}
if (g_keyStatus != g_nowKeyStatus)
{
g_lastKeyStatus = g_nowKeyStatus;
g_nowKeyStatus = g_keyStatus;
//printf("new key status:%d(%s)\r\n", g_keyStatus, key_status_name[g_keyStatus]);
}
}
最后注釋掉的一句是調試打印,調試時可打開,方便觀察狀態變化
1.3 測試
短按、長按、雙擊的測試結果如下:
還有從確認第2次按下狀態到達的長按狀態:
2 功能優化
上面的代碼實現,是在主函數中,每50ms延時執行一次狀態機循環(主函數代碼如下),僅用做演示按鍵狀態機的運行機制。
int main(void)
{
delay_init();
KEY_Init();
uart_init(115200);
printf("hello\r\n");
while(1)
{
key_status_check();
delay_ms(50);
}
}
實際開發中,按鍵檢測程序,應該作為一個獨立的模塊運行,當檢測到某一按鍵狀態觸發時,通知應用程序來使用。
對于stm32裸機開發來說,可以將按鍵狀態機放到一個定時器中斷服務函數中運行,當檢測到某一按鍵狀態觸發后,通知應用程序:
//主函數
int main(void)
{
delay_init();
KEY_Init();
uart_init(115200);
TIM3_Int_Init(500-1,7200-1); //調用定時器使得50ms產生一個中斷
printf("hello\r\n");
while(1)
{
}
}
//定時器3中斷服務程序
void TIM3_IRQHandler(void) //TIM3中斷
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查TIM3更新中斷發生與否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中斷標志
KEY_EVENT keyEvent = key_status_check();
switch (keyEvent)
{
case KE_SHORT_PRESS: printf("檢測到單擊\r\n"); break;
case KE_DOUBLE_PRESS: printf("檢測到雙擊\r\n"); break;
case KE_LONG_PRESS: printf("檢測到長按\r\n"); break;
default:break;
}
}
}
3 總結
本篇在前兩篇按鍵狀態機的基礎上,繼續介紹增加按鍵的雙擊功能,并解決之前狀態存在的兩個問題,通過實測驗證,演示短按、長按、雙擊的使用效果。最后對代碼結構進行優化,使其更符合實際開發應用。
審核編輯 黃昊宇
-
單片機
+關注
關注
6035文章
44554瀏覽量
634650 -
嵌入式
+關注
關注
5082文章
19104瀏覽量
304810 -
STM32
+關注
關注
2270文章
10895瀏覽量
355740 -
狀態機
+關注
關注
2文章
492瀏覽量
27529
發布評論請先 登錄
相關推薦
評論