STM32F103按鍵操作的另一種實(shí)現(xiàn)——狀態(tài)機(jī)
#ifndef?_KEY_H_ #define?_KEY_H_ #include?"HAL_gpio.h"?//?換成STM32F103對(duì)應(yīng)的GPIO庫 #include?"type.h"?????//?type.h主要是一些類型的重命名 #define?KEY_UP_GRP?????????GPIOA #define?KEY_UP_IDX?????????GPIO_Pin_9 #define?KEY_UP_IS_DOWN()???GPIO_ReadInputDataBit(KEY_UP_GRP,?KEY_UP_IDX) #define?KEY_UP_CONFIG()????GPIOConfig(KEY_UP_GRP,?KEY_UP_IDX,?GPIO_Mode_IPU)?//?這個(gè)函數(shù)我在之前帖子里面寫過 #define?KEY_DOWN_GRP???????GPIOA #define?KEY_DOWN_IDX???????GPIO_Pin_10 #define?KEY_DOWN_IS_DOWN()?GPIO_ReadInputDataBit(KEY_DOWN_GRP,?KEY_DOWN_IDX) #define?KEY_DOWN_CONFIG()??GPIOConfig(KEY_DOWN_GRP,?KEY_DOWN_IDX,?GPIO_Mode_IPU) #define?KEY_FUNC_GRP???????GPIOA #define?KEY_FUNC_IDX???????GPIO_Pin_11 #define?KEY_FUNC_IS_DOWN()?GPIO_ReadInputDataBit(KEY_FUNC_GRP,?KEY_FUNC_IDX) #define?KEY_FUNC_CONFIG()??GPIOConfig(KEY_FUNC_GRP,?KEY_FUNC_IDX,?GPIO_Mode_IPU)? #define?KEY_TURN_GRP???????GPIOA #define?KEY_TURN_IDX???????GPIO_Pin_12?|?GPIO_Pin_13 #define?KEY_TURN_IS_DOWN()?GPIO_ReadInputDataBit(KEY_TURN_GRP,?KEY_TURN_IDX) #define?KEY_TURN_CONFIG()??GPIOConfig(KEY_TURN_GRP,?KEY_TURN_IDX,?GPIO_Mode_IPU) //==================================================================================== typedef?enum { ??CONFIRM_KEY?=?1, ??FUNC_KEY, ??UP_KEY, ??DOWN_KEY }?key_event_t; #define?state_keyUp?????????0???????//初始狀態(tài),未按鍵 #define?state_keyDown???????1???????//鍵被按下 #define?state_keyLong???????2???????//長按 #define?state_keyTime???????3???????//按鍵計(jì)時(shí)態(tài) #define?return_keyUp????????0x00????//初始狀態(tài) #define?return_keyPressed???0x01????//鍵被按過,普通按鍵 #define?return_keyLong??????0x02????//長按 #define?return_keyAuto??????0x04????//自動(dòng)連發(fā) #define?key_down????????????0???????//按下 #define?key_up??????????????0xf0????//未按時(shí)的key有效位鍵值 #define?key_longTimes???????100?????//10ms一次,200次即2秒,定義長按的判定時(shí)間 #define?key_autoTimes???????20??????//連發(fā)時(shí)間定義,20*10=200,200毫秒發(fā)一次 #define?KEYS1_VALUE?????????0xe0????//keyS1?按下 #define?KEYS2_VALUE?????????0xd0????//keyS2?按下 #define?KEYS3_VALUE?????????0xb0????//keyS3?按下 #define?KEYS4_5_VALUE???????0x70????//keyS4_5?按下 //==================================================================================== void?KeyProcess(void);??//T0定時(shí)器調(diào)用的工作函數(shù) void?KeyTimerInit(void); #endif?/*?_KEY_H_?*/
#include#include?"key.h" #include?"timer.h"?//?STM32F103定時(shí)器的配置 static?uint8_t key_get(void)???????//獲取P3口值 { ??if(KEY_UP_IS_DOWN()?==?key_down) ??{ ????return?KEYS1_VALUE; ??} ??if(KEY_DOWN_IS_DOWN()?==?key_down) ??{ ????return?KEYS2_VALUE; ??} ??if(KEY_FUNC_IS_DOWN()?==?key_down) ??{ ????return?KEYS3_VALUE; ??} ??if(KEY_TURN_IS_DOWN()?==?key_down) ??{ ????return?KEYS4_5_VALUE; ??} ??return?key_up?;????//0xf0??沒有任何按鍵 } //函數(shù)每20ms被調(diào)用一次,而我們彈性按鍵過程時(shí)一般都20ms以上 //所以每次按鍵至少調(diào)用本函數(shù)2次 static?uint8_t key_read(uint8_t*?pKeyValue) { ??static?uint8_t??s_u8keyState?=?0;????????//未按,普通短按,長按,連發(fā)等狀態(tài) ??static?uint16_t?s_u16keyTimeCounts?=?0;??//在計(jì)時(shí)狀態(tài)的計(jì)數(shù)器 ??static?uint8_t??s_u8LastKey?=?key_up?;?//保存按鍵釋放時(shí)的P3口數(shù)據(jù) ??uint8_t?keyTemp?=?0;??????????????//鍵對(duì)應(yīng)io口的電平 ??int8_t?key_return?=?0;??????????//函數(shù)返回值 ??keyTemp?=?key_up?&?key_get();??//提取所有的key對(duì)應(yīng)的io口 ??switch(s_u8keyState)???????????//這里檢測(cè)到的是先前的狀態(tài) ??{ ??case?state_keyUp:???//如果先前是初始態(tài),即無動(dòng)作 ??{ ????if(key_up?!=?keyTemp)?//如果鍵被按下 ????{ ??????s_u8keyState?=?state_keyDown;?//更新鍵的狀態(tài),普通被按下 ????} ??} ??break; ??case?state_keyDown:?//如果先前是被按著的 ??{ ????if(key_up?!=?keyTemp)?//如果現(xiàn)在還被按著 ????{ ??????s_u8keyState?=?state_keyTime;?//轉(zhuǎn)換到計(jì)時(shí)態(tài) ??????s_u16keyTimeCounts?=?0; ??????s_u8LastKey?=?keyTemp;?????//保存鍵值 ????} ????else ????{ ??????s_u8keyState?=?state_keyUp;?//鍵沒被按著,回初始態(tài),說明是干擾 ????} ??} ??break; ??case?state_keyTime:??//如果先前已經(jīng)轉(zhuǎn)換到計(jì)時(shí)態(tài)(值為3) ??{ ????//如果真的是手動(dòng)按鍵,必然進(jìn)入本代碼塊,并且會(huì)多次進(jìn)入 ????if(key_up?==?keyTemp)?//如果未按鍵 ????{ ??????s_u8keyState?=?state_keyUp; ??????key_return?=?return_keyPressed;????//返回1,一次完整的普通按鍵 ??????//程序進(jìn)入這個(gè)語句塊,說明已經(jīng)有2次以上10ms的中斷,等于已經(jīng)消抖 ??????//那么此時(shí)檢測(cè)到按鍵被釋放,說明是一次普通短按 ????} ????else??//在計(jì)時(shí)態(tài),檢測(cè)到鍵還被按著 ????{ ??????if(++s_u16keyTimeCounts?>?key_longTimes)?//時(shí)間達(dá)到2秒 ??????{ ????????s_u8keyState?=?state_keyLong;??//進(jìn)入長按狀態(tài) ????????s_u16keyTimeCounts?=?0;??????//計(jì)數(shù)器清空,便于進(jìn)入連發(fā)重新計(jì)數(shù) ????????key_return?=?return_keyLong;???//返回state_keyLong ??????} ??????//代碼中,在2秒內(nèi)如果我們一直按著key的話,返回值只會(huì)是0,不會(huì)識(shí)別為短按或長按的 ????} ??} ??break; ??case?state_keyLong:??//在長按狀態(tài)檢測(cè)連發(fā)??,每0.2秒發(fā)一次 ??{ ????if(key_up?==?keyTemp) ????{ ??????s_u8keyState?=?state_keyUp; ????} ????else?//按鍵時(shí)間超過2秒時(shí) ????{ ??????if(++s_u16keyTimeCounts?>?key_autoTimes)//10*20=200ms ??????{ ????????s_u16keyTimeCounts?=?0; ????????key_return?=?return_keyAuto;??//每0.2秒返回值的第2位置位(1<<2) ??????}//連發(fā)的時(shí)候,肯定也伴隨著長按 ????} ????key_return?|=?return_keyLong;??//0x02是肯定的,0x04|0x02是可能的 ??} ??break; ??default: ????break; ??} ??*pKeyValue?=?s_u8LastKey?;?//返回鍵值 ??return?key_return; }
//?這個(gè)函數(shù)就是要在中斷中調(diào)用的。主要是使用事件隊(duì)列的方式。 void KeyProcess(void) { ??uint8_t?key_stateValue; ??uint8_t?keyValue?=?0; ??uint8_t*?pKeyValue?=?&keyValue; ??key_stateValue?=?key_read(pKeyValue); ??if?((return_keyPressed?==?key_stateValue)?&&?(*pKeyValue?==?KEYS1_VALUE)) ??{ ????//短按keyS1時(shí)改變對(duì)時(shí)狀態(tài),將其加入隊(duì)列,隊(duì)列的基本操作在將隊(duì)列的時(shí)候?qū)戇^。 ????if(QueueEventIsEmpty(g_state_manager.process->key_event)?|| ????????(!QueueEventIsEmpty(g_state_manager.process->key_event)?&& ?????????!QUEUE_EVENT_IS_EQUEL(g_state_manager.process->key_event,?UP_KEY))) ????{ ??????QueueEventPush(g_state_manager.process->key_event,?UP_KEY); ????} ??} } //====================================================================================== void KeyTimerInit(void) { ??KEY_UP_CONFIG(); ??KEY_DOWN_CONFIG(); ??KEY_FUNC_CONFIG(); ??KEY_TURN_CONFIG(); ??//?20ms ??TimerConfig(KEY_TIMER,?KEY_TIMER_DIV,?KEY_TIMER_PERIOD); ??TimerDisable(KEY_TIMER); ??TimerEnable(KEY_TIMER); }
主要是描述一下按鍵狀態(tài)機(jī)的思維,使用定時(shí)器中斷的方法,按鍵按下將其加入隊(duì)列中,在主函數(shù)的循環(huán)中實(shí)現(xiàn)出隊(duì)。親測(cè)可用。