0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

STM32按鍵消抖——入門狀態(tài)機思維

碼農(nóng)愛學(xué)習(xí) ? 來源:碼農(nóng)愛學(xué)習(xí) ? 作者:碼農(nóng)愛學(xué)習(xí) ? 2022-09-02 21:54 ? 次閱讀

嵌入式軟件開發(fā)中,狀態(tài)機編程是一個十分重要的編程思想,它也是嵌入式開發(fā)中一個常用的編程框架。掌握了狀態(tài)機編程思想,可以更加邏輯清晰的實現(xiàn)復(fù)雜的業(yè)務(wù)邏輯功能。

1 狀態(tài)機思想

狀態(tài)機,或稱有限狀態(tài)機FSM(Finite State Machine),是一種重要的編程思想。

狀態(tài)機有3要素:狀態(tài)、事件響應(yīng)

狀態(tài):系統(tǒng)處在什么狀態(tài)?

事件:發(fā)生了什么事?

響應(yīng):此狀態(tài)下發(fā)生了這樣的事,系統(tǒng)要如何處理?

狀態(tài)機編程前,首先要根據(jù)需要實現(xiàn)的功能,整理出一個對應(yīng)的狀態(tài)轉(zhuǎn)換圖(狀態(tài)機圖),然后就可以根據(jù)這個狀態(tài)轉(zhuǎn)換圖,套用狀態(tài)機編程模板,實現(xiàn)對應(yīng)是狀態(tài)機代碼了。

狀態(tài)機編程主要有 3 種方法:switch-case 法、表格驅(qū)動法函數(shù)指針法,本篇先介紹最簡單也最易理解的switch-case 法。

2 狀態(tài)機實例

下面以按鍵消抖功能,來介紹switch-case 法的狀態(tài)機編程思路。

2.1 按鈕消抖狀態(tài)轉(zhuǎn)換圖

狀態(tài)機機編程前,首先要明確的對應(yīng)功能的狀態(tài)機需要幾個狀態(tài),本例的按鍵功能,只檢測最基礎(chǔ)的按下與松開狀態(tài)(暫不實現(xiàn)長按、雙擊等狀態(tài)),并增加對應(yīng)的按鈕去抖功能,因此,需要用到4個狀態(tài):

穩(wěn)定松開狀態(tài)

按下抖動狀態(tài)

穩(wěn)定按下狀態(tài)

松開抖動狀態(tài)

對應(yīng)的狀態(tài)轉(zhuǎn)換圖如下:

poYBAGMSCi2AVy31AAA8AKcyDe0236.png

由于按鍵通常處于松開狀態(tài),這里讓狀態(tài)機的初始化狀態(tài)為松開狀態(tài),然后在這4個狀態(tài)中來回切換。

圖中的VT代表按鍵檢測到電平,VT=0即檢測到低電平,可能是按鍵按下,由初始的“穩(wěn)定松開”狀態(tài)轉(zhuǎn)為“按下抖動”狀態(tài)

當(dāng)持續(xù)檢測到低電平(VT=0)一段時間后,認為消抖完成,由“按下抖動”狀態(tài)轉(zhuǎn)為“穩(wěn)定按下”狀態(tài)

在“按下抖動”狀態(tài)時,在指定的一段時間內(nèi),再次檢測到高電平(VT=1),說明確實是按鈕抖動(比如按鍵被快速撥動了一下又彈起,或強烈震動導(dǎo)致的按鍵抖動),則由“按下抖動”狀態(tài)轉(zhuǎn)為“穩(wěn)定松開”狀態(tài)

2.2 編程實現(xiàn)

2.2.1 狀態(tài)定義

對應(yīng)上面的按鈕狀態(tài)圖,可以知道需要用到4個狀態(tài):

穩(wěn)定松開狀態(tài)(KS_RELEASE)

按下抖動狀態(tài)(KS_PRESS_SHAKE)

穩(wěn)定按下狀態(tài)(KS_PRESS)

松開抖動狀態(tài)(KS_RELEASE_SHAKE)

這里使用枚舉來定義這4個狀態(tài)。為了在調(diào)試時,能夠把對應(yīng)狀態(tài)名稱以字符串的形式打印出來,這里使用宏定義的一個小技巧:

#符號+自定義的枚舉名稱

即可自動轉(zhuǎn)變?yōu)樽址问?,再將這些字符串放到const char* key_status_name[]數(shù)組中,便可通過數(shù)組的形式訪問這些狀態(tài)的字符串名稱形式。

此外,為了不重復(fù)書寫枚舉名稱與對應(yīng)的枚舉字符串(#+枚舉名稱),進一步使用宏定義的方式,只定義一次狀態(tài),然后通過下面兩條宏定義,實現(xiàn)對枚舉項枚舉項對應(yīng)的字符串的分別獲?。?/p>

#define ENUM_ITEM(ITEM) ITEM,

#define ENUM_STRING(ITEM) #ITEM,

具體是宏定義、枚舉定義與枚舉名稱數(shù)組聲明如下:

#define ENUM_ITEM(ITEM) ITEM,
#define ENUM_STRING(ITEM) #ITEM,

#define KEY_STATUS_ENUM(STATUS)                   \
	STATUS(KS_RELEASE)       /*穩(wěn)定松開狀態(tài)*/       \
	STATUS(KS_PRESS_SHAKE)   /*按下抖動狀態(tài)*/       \
	STATUS(KS_PRESS)         /*穩(wěn)定按下狀態(tài)*/       \
	STATUS(KS_RELEASE_SHAKE) /*松開抖動狀態(tài)*/       \
	STATUS(KS_NUM)           /*狀態(tài)總數(shù)(無效狀態(tài))*/  \
	
typedef enum
{
	KEY_STATUS_ENUM(ENUM_ITEM)
}KEY_STATUS;

const char* key_status_name[] = {
	KEY_STATUS_ENUM(ENUM_STRING)
};

宏定義不便理解的,可以將宏定義分別帶入,轉(zhuǎn)為最終的結(jié)果,理解替代后的具體形式,比如下面的宏定義帶入替換示意:

/*
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 狀態(tài)機實現(xiàn)

下面是狀態(tài)機的具體實現(xiàn):

狀態(tài)機函數(shù)key_status_check在一個循環(huán)中,被每隔10ms調(diào)用一次

定義一個g_keyStatus表示狀態(tài)機所處的狀態(tài)

在每個循環(huán)中,switch根據(jù)當(dāng)前的狀態(tài),執(zhí)行對應(yīng)狀態(tài)所需要執(zhí)行的邏輯

定義一個g_DebounceCnt用于消抖時間計算,當(dāng)持續(xù)進入消抖狀態(tài),每次循環(huán)(10ms)中將此值加1,持續(xù)一定次數(shù)(5次,即50ms),認為是穩(wěn)定的按下或松開,消抖完成,跳轉(zhuǎn)到穩(wěn)定方向或穩(wěn)定松開狀態(tài)

在每個狀態(tài)的執(zhí)行邏輯中,當(dāng)檢測到某些條件滿足時,跳轉(zhuǎn)到其它的狀態(tài)

通過狀態(tài)的不斷跳轉(zhuǎn),實現(xiàn)狀態(tài)機的運行

此外,為方便觀察狀態(tài)機中狀態(tài)的變化,定義了一個g_lastKeyStatus表示前一狀態(tài),當(dāng)狀態(tài)發(fā)生變化時,可以將狀態(tài)名稱打印出來

KEY_STATUS g_keyStatus = KS_RELEASE; //當(dāng)前按鍵的狀態(tài)
KEY_STATUS g_lastKeyStatus = KS_NUM; //上一狀態(tài)
int g_DebounceCnt = 0; //消抖時間計數(shù)

void key_status_check()
{
	switch(g_keyStatus)
	{
		//按鍵釋放(初始狀態(tài))
		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;
		
		//穩(wěn)定按下
		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();	    //延時函數(shù)初始化	  
	KEY_Init();
	uart_init(115200);
	printf("hello\r\n");
	
	while(1)
	{
		key_status_check();
		delay_ms(10);
	}
}

注:本例程需要使用一個按鍵,需要初始化對應(yīng)的GPIO,這里不再貼代碼。

2.3 使用測試

將完整的代碼編譯后燒錄到板子中,連接串口,按下與松開按鍵,觀察串口輸出信息。

我的測試輸出信息如下:

pYYBAGMSCpeADWb3AACpThu0Gj0100.png

前兩次撥動按鍵模擬按鈕抖動的情況,可以看到串口打印出兩次從松開到按下抖動的狀態(tài)切換。

然后是按下按鍵,再松開按鍵,可以看到狀態(tài)的變化:松開 -> 按下抖動 -> 按下 -> 松開抖動 -> 松開

3 總結(jié)

本篇介紹了嵌入式軟件開發(fā)中常用的狀態(tài)機編程實現(xiàn),并通過按鍵消抖實例,以常用的switch-case形式,實現(xiàn)了對應(yīng)的狀態(tài)機編程代碼實現(xiàn),并通過測試,串口打印對應(yīng)狀態(tài),分析狀態(tài)機的狀態(tài)跳轉(zhuǎn)過程。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6023

    文章

    44376

    瀏覽量

    628304
  • 嵌入式
    +關(guān)注

    關(guān)注

    5045

    文章

    18816

    瀏覽量

    298459
  • STM32
    +關(guān)注

    關(guān)注

    2257

    文章

    10828

    瀏覽量

    352444
  • 狀態(tài)機
    +關(guān)注

    關(guān)注

    2

    文章

    489

    瀏覽量

    27391
收藏 人收藏

    評論

    相關(guān)推薦

    STM32按鍵狀態(tài)機2——狀態(tài)簡化與增加長按功能

    本篇繼續(xù)介紹狀態(tài)機的使用,在上篇的基礎(chǔ)上,通過簡化按鍵邏輯,并增加按鍵長按功能,進一步介紹狀態(tài)圖的修改與
    的頭像 發(fā)表于 09-03 21:26 ?3820次閱讀
    <b class='flag-5'>STM32</b><b class='flag-5'>按鍵</b><b class='flag-5'>狀態(tài)機</b>2——<b class='flag-5'>狀態(tài)</b>簡化與增加長按功能

    【連載視頻教程(八)】小梅哥FPGA設(shè)計思想與驗證方法視頻教程之基于狀態(tài)機的獨立按鍵

    ,主要通過獨立按鍵這樣一個實驗,來進一步舉例講解狀態(tài)機的設(shè)計思想,獨立按鍵
    發(fā)表于 09-29 14:19

    利用狀態(tài)機按鍵程序

    與過程分離. 把和按鍵狀態(tài)相關(guān)的東西統(tǒng)統(tǒng)塞到結(jié)構(gòu)里, 把的代碼放在一個函數(shù)中.//key.h 頭文件
    發(fā)表于 02-18 19:58

    怎樣去設(shè)計一個基于stm32標(biāo)準(zhǔn)庫獨立按鍵的多按鍵狀態(tài)機

    寫在前面??一般引用都寫在最后,但是這篇博文對我這個狀態(tài)機的影響很大,我這里有許多借鑒他的思維。所以寫在前面,如有侵權(quán)立即刪除簡單按鍵檢測??一開始學(xué)習(xí)單片的時候我接觸到
    發(fā)表于 12-09 07:48

    STM32單片按鍵和FPGA按鍵的相關(guān)資料分享

    寫在前面:STM32單片按鍵和FPGA按鍵
    發(fā)表于 01-18 06:39

    請問按鍵狀態(tài)機的長按和短按測試該怎樣去實現(xiàn)呢

    什么是按鍵呢?按鍵狀態(tài)機的長按和短按測試該怎樣去實現(xiàn)呢?
    發(fā)表于 01-20 06:45

    如何用狀態(tài)機法實現(xiàn)單片矩陣鍵盤的控制

    看了藍橋杯單片的培訓(xùn)視頻,發(fā)現(xiàn)用狀態(tài)機法實現(xiàn)矩陣鍵盤時左邊兩列沒有用,這是因為P36、P37口實際是P42、P44口,所以我們在用行列掃描法時會出錯。但習(xí)慣了
    發(fā)表于 02-17 07:47

    基于FPGA的按鍵電路設(shè)計

    采用了VHDL語言編程的設(shè)計方法,通過FPGA來實現(xiàn)按鍵的硬件電路。論述了基于計數(shù)器、RS觸發(fā)器和狀態(tài)機3種方法來實現(xiàn)按鍵
    發(fā)表于 12-05 14:13 ?224次下載

    利用狀態(tài)機按鍵程序

    利用狀態(tài)機按鍵程序講解,很好的資料下載吧。
    發(fā)表于 01-11 09:32 ?30次下載

    VHDL—按鍵

    達到去抖動的目的。本例中用狀態(tài)機實現(xiàn)了電路:端口描述:clk 輸入檢測時鐘;reset 復(fù)位信號;din 原始按鍵信號輸入; dout 去抖動輸出信號。
    發(fā)表于 11-11 17:17 ?2次下載

    使用狀態(tài)機按鍵處理方法電子鐘程序免費下載

    之前的電子鐘程序中,用的按鍵處理方法是10ms的延時,這種方法效率比較低所以現(xiàn)在利用狀態(tài)機原理重寫一下,效率很高啊 4個獨立按鍵中用到
    發(fā)表于 07-09 17:41 ?8次下載
    使用<b class='flag-5'>狀態(tài)機</b>的<b class='flag-5'>按鍵</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>處理方法電子鐘程序免費下載

    使用51單片實現(xiàn)按鍵的資料和程序免費下載

    抖動是機械按鍵存在的現(xiàn)象,是必須要進行處理的。一般處理有兩種方式:一種是硬件,另一種是軟件。硬件
    發(fā)表于 07-05 17:41 ?5次下載
    使用51單片<b class='flag-5'>機</b>實現(xiàn)<b class='flag-5'>按鍵</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>的資料和程序免費下載

    基于stm32標(biāo)準(zhǔn)庫獨立按鍵的多按鍵狀態(tài)機的實現(xiàn)

    寫在前面??一般引用都寫在最后,但是這篇博文對我這個狀態(tài)機的影響很大,我這里有許多借鑒他的思維。所以寫在前面,如有侵權(quán)立即刪除簡單按鍵檢測??一開始學(xué)習(xí)單片的時候我接觸到
    發(fā)表于 11-26 13:36 ?25次下載
    基于<b class='flag-5'>stm32</b>標(biāo)準(zhǔn)庫獨立<b class='flag-5'>按鍵</b>的多<b class='flag-5'>按鍵</b><b class='flag-5'>狀態(tài)機</b>的實現(xiàn)

    基于STM32按鍵的防和松開處理:狀態(tài)機

    用延時和while();去處理按鍵很浪費資源,這里我們用定時器來做一個按鍵的處理-狀態(tài)機;typedef enum {KEY_RELEASED,KEY_PRESSED,KEY_PROCESSED
    發(fā)表于 12-09 09:21 ?7次下載
    基于<b class='flag-5'>STM32</b><b class='flag-5'>按鍵</b>的防<b class='flag-5'>抖</b>和松開處理:<b class='flag-5'>狀態(tài)機</b>

    狀態(tài)機法實現(xiàn)藍橋杯單片矩陣鍵盤的控制

    看了藍橋杯單片的培訓(xùn)視頻,發(fā)現(xiàn)用狀態(tài)機法實現(xiàn)矩陣鍵盤時左邊兩列沒有用,這是因為P36、P37口實際是P42、P44口,所以我們在用行列掃描法時會出錯。但習(xí)慣了
    發(fā)表于 12-20 19:42 ?6次下載
    用<b class='flag-5'>狀態(tài)機</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>法實現(xiàn)藍橋杯單片<b class='flag-5'>機</b>矩陣鍵盤的控制