按鍵軟件消抖自我接觸單片機開始就已經存在這個問題了,網上的辦法無非是延時消抖和定時輪詢。對於寫裸機的我來說這兩種方法都不可避免的會有資源浪費掉,今天突然有了靈感,想到了一種相對高效的辦法來解決消抖問題。
硬件平臺:STM32F103RCT6開發板
開發環境:WIN7-64bit+MDK5+STD庫
按鍵消抖的必要性在此我就不多說了。直接步入正題。
在使用本方法前請注意,本方法需要一個全局時間戳的支持。
第一步:初始化全局時間戳的定時器,一般採用SysTick定時器來產生,每ms一次tick即可。
第二步:初始化按鍵對應的IO,複用爲邊沿觸發的外部中斷。
第三步:在外部中斷函數中添加按鍵事件處理函數。
代碼如下:
typedef struct _Key_t
{
u32 last_time;
enum
{
May_Press,
Release,
}private_state;
enum
{
No_Press,
Short_Press,
Long_Press,
}state;
}Key_t;
#define Is_ShortPress_Threshold 1500
簡單定義一個按鍵狀態的結構體,用於管理每個按鍵的狀態。順便再定義一個長短按的識別閾值,用於區分按鍵的長短按。
if(key_state.private_state==Release)
{
if(KEY==0)
{
key_state.private_state=May_Press;
key_state.last_time=course_ms();
}
}
else if(key_state.private_state==May_Press)
{
if(KEY==1)
{
if((course_ms()-key_state.last_time>10)&&(course_ms()-key_state.last_time<Is_ShortPress_Threshold))
{
key_state.state=Short_Press;
key_state.private_state=Release;
}
else if(course_ms()-key_state.last_time>Is_ShortPress_Threshold)
{
key_state.state=Long_Press;
key_state.private_state=Release;
}
else
key_state.private_state=Release;
}
}
以上爲需要添加到中斷處理函數的按鍵事件處理函數,算法的核心是一個狀態機。在本例中,按鍵被默認上拉,按下接地。course_ms()爲獲取全局時間戳的函數。
思路解釋如下:按鍵狀態結構體有一個用於識別的狀態位,默認處於Release,也就是釋放的狀態。一旦按鍵被按下,中斷觸發,此時檢查是否是Relase狀態,如果是就檢查按鍵是否被拉低,如果是,此時進入May_Press狀態,也就是可能是按下的,並且記錄此時的時間戳,這一步是消抖的關鍵。當按鍵被釋放,由於是邊沿觸發,會再次進行處理,此時檢查和上一次觸發之間的時間戳之差,如果小於10ms我們就認爲是抖動,此時不會對按鍵輸出狀態進行修改,而是直接將按鍵狀態置回Relase狀態,反之檢查差值和長短按閾值之間的關係,將state置位爲對應的狀態。消抖的核心在於記錄時間戳,而這只是一個簡單的賦值操作,並不耗費時間。
效率上來說,延時消抖花費時間在無意義延時上,而相對較好的定時輪詢還是不可避免的在輪詢,而現在這種方式完全是中斷性質的。唯一多出的開銷(全局時間戳)並不是只可以用於按鍵消抖,另外在HAL庫中存在直接獲取tick的函數,這樣實現就更方便了。經實際測試,消抖效果可以達到其他兩種消抖算法的水平。