在嵌入式軟件開發中,狀態機編程是一個十分重要的編程思想,它也是嵌入式開發中一個常用的編程框架。掌握了狀態機編程思想,可以更加邏輯清晰的實現復雜的業務邏輯功能。
1 狀態機思想
狀態機,或稱有限狀態機FSM(Finite State Machine),是一種重要的編程思想。
狀態機有3要素:狀態、事件與響應
狀態:系統處在什么狀態?
事件:發生了什么事?
響應:此狀態下發生了這樣的事,系統要如何處理?
狀態機編程前,首先要根據需要實現的功能,整理出一個對應的狀態轉換圖(狀態機圖),然后就可以根據這個狀態轉換圖,套用狀態機編程模板,實現對應是狀態機代碼了。
狀態機編程主要有 3 種方法:switch-case 法、表格驅動法、函數指針法,本篇先介紹最簡單也最易理解的switch-case 法。
2 狀態機實例
下面以按鍵消抖功能,來介紹switch-case 法的狀態機編程思路。
2.1 按鈕消抖狀態轉換圖
狀態機機編程前,首先要明確的對應功能的狀態機需要幾個狀態,本例的按鍵功能,只檢測最基礎的按下與松開狀態(暫不實現長按、雙擊等狀態),并增加對應的按鈕去抖功能,因此,需要用到4個狀態:
穩定松開狀態
按下抖動狀態
穩定按下狀態
松開抖動狀態
對應的狀態轉換圖如下:

由于按鍵通常處于松開狀態,這里讓狀態機的初始化狀態為松開狀態,然后在這4個狀態中來回切換。
圖中的VT代表按鍵檢測到電平,VT=0即檢測到低電平,可能是按鍵按下,由初始的“穩定松開”狀態轉為“按下抖動”狀態
當持續檢測到低電平(VT=0)一段時間后,認為消抖完成,由“按下抖動”狀態轉為“穩定按下”狀態
在“按下抖動”狀態時,在指定的一段時間內,再次檢測到高電平(VT=1),說明確實是按鈕抖動(比如按鍵被快速撥動了一下又彈起,或強烈震動導致的按鍵抖動),則由“按下抖動”狀態轉為“穩定松開”狀態
2.2 編程實現
2.2.1 狀態定義
對應上面的按鈕狀態圖,可以知道需要用到4個狀態:
穩定松開狀態(KS_RELEASE)
按下抖動狀態(KS_PRESS_SHAKE)
穩定按下狀態(KS_PRESS)
松開抖動狀態(KS_RELEASE_SHAKE)
這里使用枚舉來定義這4個狀態。為了在調試時,能夠把對應狀態名稱以字符串的形式打印出來,這里使用宏定義的一個小技巧:
#符號+自定義的枚舉名稱
即可自動轉變為字符串形式,再將這些字符串放到const char* key_status_name[]數組中,便可通過數組的形式訪問這些狀態的字符串名稱形式。
此外,為了不重復書寫枚舉名稱與對應的枚舉字符串(#+枚舉名稱),進一步使用宏定義的方式,只定義一次狀態,然后通過下面兩條宏定義,實現對枚舉項和枚舉項對應的字符串的分別獲取:
#define ENUM_ITEM(ITEM) ITEM,
#define ENUM_STRING(ITEM) #ITEM,
具體是宏定義、枚舉定義與枚舉名稱數組聲明如下:
#define ENUM_ITEM(ITEM) ITEM,
#define ENUM_STRING(ITEM) #ITEM,
#define KEY_STATUS_ENUM(STATUS) \
STATUS(KS_RELEASE) /*穩定松開狀態*/ \
STATUS(KS_PRESS_SHAKE) /*按下抖動狀態*/ \
STATUS(KS_PRESS) /*穩定按下狀態*/ \
STATUS(KS_RELEASE_SHAKE) /*松開抖動狀態*/ \
STATUS(KS_NUM) /*狀態總數(無效狀態)*/ \
typedef enum
{
KEY_STATUS_ENUM(ENUM_ITEM)
}KEY_STATUS;
const char* key_status_name[] = {
KEY_STATUS_ENUM(ENUM_STRING)
};
宏定義不便理解的,可以將宏定義分別帶入,轉為最終的結果,理解替代后的具體形式,比如下面的宏定義帶入替換示意:
/*
KEY_STATUS_ENUM(STATUS) --> STATUS(KS_RELEASE) ... STATUS(KS_NUM)
KEY_STATUS_ENUM(ENUM_ITEM)
--> ENUM_ITEM(KS_RELEASE) ... ENUM_ITEM(KS_NUM)
--> KS_RELEASE, ... KS_NUM,
KEY_STATUS_ENUM(ENUM_STRING)
--> ENUM_STRING(KS_RELEASE) ... ENUM_STRING(KS_NUM)
--> #KS_RELEASE, ... #KS_NUM,
*/
2.2.2 狀態機實現
下面是狀態機的具體實現:
狀態機函數key_status_check在一個循環中,被每隔10ms調用一次
定義一個g_keyStatus表示狀態機所處的狀態
在每個循環中,switch根據當前的狀態,執行對應狀態所需要執行的邏輯
定義一個g_DebounceCnt用于消抖時間計算,當持續進入消抖狀態,每次循環(10ms)中將此值加1,持續一定次數(5次,即50ms),認為是穩定的按下或松開,消抖完成,跳轉到穩定方向或穩定松開狀態
在每個狀態的執行邏輯中,當檢測到某些條件滿足時,跳轉到其它的狀態
通過狀態的不斷跳轉,實現狀態機的運行
此外,為方便觀察狀態機中狀態的變化,定義了一個g_lastKeyStatus表示前一狀態,當狀態發生變化時,可以將狀態名稱打印出來
KEY_STATUS g_keyStatus = KS_RELEASE; //當前按鍵的狀態
KEY_STATUS g_lastKeyStatus = KS_NUM; //上一狀態
int g_DebounceCnt = 0; //消抖時間計數
void key_status_check()
{
switch(g_keyStatus)
{
//按鍵釋放(初始狀態)
case KS_RELEASE:
{
//檢測到低電平,先進行消抖
if (KEY0 == 0)
{
g_keyStatus = KS_PRESS_SHAKE;
g_DebounceCnt = 0;
}
}
break;
//按下抖動
case KS_PRESS_SHAKE:
{
g_DebounceCnt++;
//確實是抖動
if (KEY0 == 1)
{
g_keyStatus = KS_RELEASE;
}
//消抖完成
else if (g_DebounceCnt == 5)
{
g_keyStatus = KS_PRESS;
printf("=====> key press\r\n");
}
}
break;
//穩定按下
case KS_PRESS:
{
//檢測到高電平,先進行消抖
if (KEY0 == 1)
{
g_keyStatus = KS_RELEASE_SHAKE;
g_DebounceCnt = 0;
}
}
break;
//松開抖動
case KS_RELEASE_SHAKE:
{
g_DebounceCnt++;
//確實是抖動
if (KEY0 == 0)
{
g_keyStatus = KS_PRESS;
}
//消抖完成
else if (g_DebounceCnt == 5)
{
g_keyStatus = KS_RELEASE;
printf("=====> key release\r\n");
}
}
break;
default:break;
}
if (g_keyStatus != g_lastKeyStatus)
{
g_lastKeyStatus = g_keyStatus;
printf("new key status:%d(%s)\r\n", g_keyStatus, key_status_name[g_keyStatus]);
}
}
int main(void)
{
delay_init(); //延時函數初始化
KEY_Init();
uart_init(115200);
printf("hello\r\n");
while(1)
{
key_status_check();
delay_ms(10);
}
}
注:本例程需要使用一個按鍵,需要初始化對應的GPIO,這里不再貼代碼。
2.3 使用測試
將完整的代碼編譯后燒錄到板子中,連接串口,按下與松開按鍵,觀察串口輸出信息。
我的測試輸出信息如下:

前兩次撥動按鍵模擬按鈕抖動的情況,可以看到串口打印出兩次從松開到按下抖動的狀態切換。
然后是按下按鍵,再松開按鍵,可以看到狀態的變化:松開 -> 按下抖動 -> 按下 -> 松開抖動 -> 松開
3 總結
本篇介紹了嵌入式軟件開發中常用的狀態機編程實現,并通過按鍵消抖實例,以常用的switch-case形式,實現了對應的狀態機編程代碼實現,并通過測試,串口打印對應狀態,分析狀態機的狀態跳轉過程。
-
單片機
+關注
關注
6057文章
44806瀏覽量
644103 -
嵌入式
+關注
關注
5121文章
19421瀏覽量
312640 -
STM32
+關注
關注
2282文章
10983瀏覽量
360982 -
狀態機
+關注
關注
2文章
493瀏覽量
27951
發布評論請先 登錄
相關推薦
【連載視頻教程(八)】小梅哥FPGA設計思想與驗證方法視頻教程之基于狀態機的獨立按鍵消抖
怎樣去設計一個基于stm32標準庫獨立按鍵的多按鍵狀態機
如何用狀態機消抖法實現單片機矩陣鍵盤的控制
基于FPGA的按鍵消抖電路設計
VHDL—按鍵消抖
使用狀態機的按鍵消抖處理方法電子鐘程序免費下載

基于stm32標準庫獨立按鍵的多按鍵狀態機的實現

基于STM32按鍵的防抖和松開處理:狀態機

用狀態機消抖法實現藍橋杯單片機矩陣鍵盤的控制

評論