上篇文章,以按鍵消抖功能,介紹了狀態(tài)機(jī)的基本原理與使用方法。
上篇的狀態(tài)圖如下:
由于只檢測(cè)按下與松開(kāi),并具備按鍵消抖功能,因此用到了如上的4個(gè)狀態(tài),按下抖動(dòng)和松開(kāi)抖動(dòng)是兩個(gè)獨(dú)立的狀態(tài),并且這兩個(gè)抖動(dòng)的狀態(tài),也是可以在多次循環(huán)中連續(xù)運(yùn)行的,這個(gè)狀態(tài)機(jī)的循環(huán)周期設(shè)置的為10ms,當(dāng)在抖動(dòng)狀態(tài)連續(xù)檢測(cè)到某一電平5次后,即認(rèn)為消抖完成,進(jìn)入下一個(gè)穩(wěn)定狀態(tài)。
對(duì)于同一個(gè)功能,狀態(tài)圖不是一成不變的,對(duì)于按鍵消抖,還可以將兩個(gè)抖動(dòng)狀態(tài)共用一個(gè)抖動(dòng)狀態(tài)來(lái)表示。
1 消抖狀態(tài)簡(jiǎn)化
1.1 狀態(tài)圖
將按下抖動(dòng)與松開(kāi)抖動(dòng)共用一個(gè)抖動(dòng)狀態(tài)來(lái)表示,同時(shí)需要將狀態(tài)機(jī)的循環(huán)周期設(shè)置為50ms,這樣,抖動(dòng)狀態(tài)只需經(jīng)過(guò)一次,通過(guò)電平高低即可判定是否真的為按鍵抖動(dòng)。簡(jiǎn)化后的狀態(tài)圖如下:
為了能在抖動(dòng)狀態(tài)時(shí),區(qū)分前一狀態(tài)是松開(kāi)還是按下,進(jìn)而判斷此次是抖動(dòng)還是按鍵真的動(dòng)作,需要增加一個(gè)狀態(tài)來(lái)記錄前一狀態(tài)
KEY_STATUS g_keyStatus = KS_RELEASE; //當(dāng)前循環(huán)結(jié)束的(狀態(tài)機(jī)的)狀態(tài)
KEY_STATUS g_nowKeyStatus = KS_RELEASE; //當(dāng)前狀態(tài)(每次循環(huán)后與g_keyStatus保持一致)
KEY_STATUS g_lastKeyStatus = KS_RELEASE; //上次狀態(tài)(用于記錄前一狀態(tài)以區(qū)分狀態(tài)的來(lái)源)
注意:此處的g_lastKeyStatus用于記錄前一狀態(tài),上篇文章中也有這個(gè)變量,但作用不同,上篇文章中此變量的作用與此處的g_nowKeyStatus作用相同。
1.2 代碼
對(duì)照簡(jiǎn)化后的狀態(tài)圖,編寫(xiě)對(duì)應(yīng)的狀態(tài)機(jī)邏輯代碼:
void key_status_check()
{
switch(g_keyStatus)
{
//按鍵釋放(初始狀態(tài))
case KS_RELEASE:
{
//檢測(cè)到低電平,先進(jìn)行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
}
break;
//抖動(dòng)
case KS_SHAKE:
{
if (KEY0 == 1)
{
g_keyStatus = KS_RELEASE;
if (KS_PRESS == g_lastKeyStatus)
{
printf("=====> key release\r\n");
}
}
else
{
g_keyStatus = KS_PRESS;
if (KS_RELEASE == g_lastKeyStatus)
{
printf("=====> key press\r\n");
}
}
}
break;
//穩(wěn)定短按
case KS_PRESS:
{
//檢測(cè)到高電平,先進(jìn)行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
}
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]);
}
}
注意g_lastKeyStatus變量的作用。
1.3 測(cè)試
2 增加長(zhǎng)按功能
在檢測(cè)按下與松開(kāi)的基礎(chǔ)上,再增加長(zhǎng)按功能,在狀態(tài)圖中需要增加一個(gè)長(zhǎng)按狀態(tài)。然后,對(duì)照著狀態(tài)圖修改代碼即可。
同樣,根據(jù)是否需要區(qū)分兩種抖動(dòng)狀態(tài)以及狀態(tài)機(jī)循環(huán)周期的不同,可以有兩種狀態(tài)圖。
2.1 未簡(jiǎn)化的狀態(tài)圖
先來(lái)看一下循環(huán)周期10ms,區(qū)分按下抖動(dòng)與松開(kāi)抖動(dòng)這種情況增加長(zhǎng)按功能后的狀態(tài)圖:
狀態(tài)圖理清邏輯后,根據(jù)狀態(tài)圖,修改對(duì)應(yīng)的代碼即可,這里不再貼代碼,完整代碼可去我的代碼倉(cāng)庫(kù)查看(文末閱讀原文直達(dá)~)
2.2 簡(jiǎn)化的狀態(tài)圖
下面再來(lái)看簡(jiǎn)化消抖狀態(tài)的具體長(zhǎng)按功能的狀態(tài)機(jī)圖:
對(duì)比可以發(fā)現(xiàn),簡(jiǎn)化的狀態(tài)圖,狀態(tài)可以少一個(gè),不過(guò)抖動(dòng)的狀態(tài),會(huì)有更多的輸入和輸出,因?yàn)槟壳懊扛魻顟B(tài)都有經(jīng)過(guò)這個(gè)狀態(tài)。
如果對(duì)于抖動(dòng)檢測(cè)的要求不高,也可以只保留按下抖動(dòng)的邏輯,松開(kāi)抖動(dòng)的分支去掉,直接跳到松開(kāi)狀態(tài),可以再次簡(jiǎn)化狀態(tài)邏輯。
2.3 代碼
根據(jù)狀態(tài)圖圖,編寫(xiě)對(duì)應(yīng)的狀態(tài)機(jī)邏輯代碼,如下:
void key_status_check()
{
switch(g_keyStatus)
{
//按鍵釋放(初始狀態(tài))
case KS_RELEASE:
{
//檢測(cè)到低電平,先進(jìn)行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_SHAKE;
}
}
break;
//抖動(dòng)
case KS_SHAKE:
{
if (KEY0 == 1)
{
g_keyStatus = KS_RELEASE;
if (KS_SHORT_PRESS == g_lastKeyStatus || KS_LONG_PRESS == g_lastKeyStatus)
{
printf("=====> key release\r\n");
}
}
else
{
if (KS_RELEASE == g_lastKeyStatus)
{
g_PressTimeCnt = 0;
g_keyStatus = KS_SHORT_PRESS;
printf("=====> key short press\r\n");
}
else if (KS_SHORT_PRESS == g_lastKeyStatus)
{
g_keyStatus = KS_SHORT_PRESS;
}
else
{
}
}
}
break;
//穩(wěn)定短按
case KS_SHORT_PRESS:
{
//檢測(cè)到高電平,先進(jìn)行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_PressTimeCnt++;
if (g_PressTimeCnt == 20) //1000ms
{
g_keyStatus = KS_LONG_PRESS;
printf("=====> key long press\r\n");
}
}
break;
//穩(wěn)定長(zhǎng)按
case KS_LONG_PRESS:
{
//檢測(cè)到高電平,先進(jìn)行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_SHAKE;
}
g_PressTimeCnt++;
if (g_PressTimeCnt % 20 == 0) //每隔1000ms打印一次
{
printf("=====> key long press:%d\r\n", g_PressTimeCnt/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]);
}
}
注意,在抖動(dòng)狀態(tài),當(dāng)檢測(cè)為高電平(按鍵松開(kāi)),不管前一狀態(tài)是短按還是長(zhǎng)按,下一狀態(tài)都是松開(kāi)狀態(tài)。
2.4 測(cè)試
3 總結(jié)
本篇繼續(xù)介紹狀態(tài)機(jī)的使用,在上篇的基礎(chǔ)上,通過(guò)簡(jiǎn)化按鍵去抖邏輯,并增加按鍵長(zhǎng)按功能,進(jìn)一步介紹狀態(tài)圖的修改與狀態(tài)機(jī)代碼的實(shí)現(xiàn),并通過(guò)實(shí)際測(cè)試,演示狀態(tài)機(jī)的運(yùn)行效果。
審核編輯 黃昊宇
-
嵌入式
+關(guān)注
關(guān)注
5052文章
18914瀏覽量
300846 -
STM32
+關(guān)注
關(guān)注
2263文章
10847瀏覽量
353797 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
490瀏覽量
27436
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論