1. 程式人生 > 實用技巧 >按鍵的狀態機方法實現

按鍵的狀態機方法實現

  按鍵的狀態機方法實現 為什麼要使用狀態機?使用狀態機有啥好處?是不是裝13? 要我說,很簡單的一個按鍵,可以不必使用,要是你有很多按鍵,或者需要判斷長按,短按,雙擊666,那麼,狀態機方法可以讓你設計簡潔,功能穩定,不會出現你自己測試好了,別人測試就會出問題;或者今天測試沒有問題,第二天不穩定了,是不是很糟心?功能複雜勢必會宣告很多個變數一大丟,然後把功能也 寫進按鍵處理裡面,是不是很亂,不好讀懂?按鍵識別就是按鍵部分,不要有任何功能上面的處理,要通過介面去傳遞。這麼說,本文主要就是兩個思想:狀態機和介面。懂得人自然懂。下面詳細說說,例項上菜。 開局一張圖,內容全靠編。

正確,合理的狀態劃分是做好任何功能的前提,根據按鍵全生命週期的發生順序,我劃分了上面4個狀態。去抖大家都清楚,按鍵值持續2-30Ms才進入正式的識別按鍵值狀態,否則退回idle狀態。我說的是持續,不是間隔2--30Ms再次去確認一次。好了,我把程式裡面需要用到的一些資料型別都貼出來吧:

  1 enum KEYDOWNSTATE
  2 {//按鍵狀態機
  3 MKEY_IDLE, ///<待機狀態
  4 MKEY_DELAY, ///<去抖狀態
  5 MKEY_GETVALUE,///<取值狀態
  6 MKEY_UNFIX ///<取值成功狀態
  7 };
  8 typedef struct
  9 {
 10 unsigned char modeKey : 1;
 11 unsigned char levelKey : 1;
 12 unsigned char powerKey : 1;
 13 unsigned char datb3 : 1;
 14 unsigned char
datb4 : 1; 15 unsigned char datb5 : 1; 16 unsigned char datb6 : 1; 17 unsigned char datb7 : 1; 18 }datbit; //使用位段 19 typedef union { 20 unsigned char bytedat; 21 datbit data_bits; 22 }ByteChar; //非常方便採集資料和取值的一個聯合體 23 enum EKEYVALUE 24 {//按鍵值 25 MMODE = 0XFE, 26 MLEVEL = 0XFD, 27 MREUSE = 0XFC, 28
MPOWERON = 0XFB, 29 MTEST = 0X07 30 }; //無論是獨立式按鍵還是adc方式,都會有個值或者範圍 31 enum EKEYTYPE 32 { 33 MNONE, 34 MSHORTKEY = 0X13, //短按,隨意定的一個值 35 M2SKEY, //長按 36 M10KEY, //復位按 37 }; //代表著按鍵長按,短按,復位 38 typedef struct { 39 bit getkeyFlag; //按鍵值獲取成功 40 ByteChar gkeyValueIn; //按鍵值 41 UINT8 gDelayType; //根據按鍵delay長短確定的型別 42 }keyInterface_t; 43 keyInterface_t keyInterface; //按鍵驅動模組與外部的介面 44 /************************************************************************************************************ 45 * void keyDriver(void) 46 * 注意:間隔10ms左右呼叫一下本函式 47 * 本函式採用狀態機方式程式設計,適合於獨立按鍵,可以識別出長按,短按,超長10S按。 48 ************************************************************************************************************/ 49 static void keyDriver(void) 50 { 51 ByteChar keyValueIn; 52 keyValueIn.bytedat = 0xFF; 53 keyValueIn.data_bits.modeKey = MODEKEY; 54 keyValueIn.data_bits.levelKey = LevelKEY; 55 keyValueIn.data_bits.powerKey = PowerKEY; 56 switch (keyState) 57 { 58 case MKEY_IDLE: /* constant-expression */ 59 keyDownConts = 0; 60 keyInterface.gDelayType = MNONE; 61 if (keyValueIn.bytedat != 0xFF) 62 { 63 keyInterface.gkeyValueIn.bytedat = keyValueIn.bytedat; 64 keyState = MKEY_DELAY; 65 keyDownConts = 0; 66 } 67 break; 68 case MKEY_DELAY: /* 去抖動 */ 69 if (keyInterface.gkeyValueIn.bytedat ==keyValueIn.bytedat) 70 { 71 keyDownConts++; 72 } 73 else 74 { 75 keyDownConts = 0; 76 keyState = MKEY_IDLE; 77 } 78 if (keyDownConts > 3) 79 { 80 keyState = MKEY_GETVALUE; 81 } 82 break; 83 case MKEY_GETVALUE: /* 進入判斷按鍵 */ 84 keyDownConts++; 85 if (keyDownConts > KEY10s) //如果是復原按,則無需等待鬆開按鍵 86 { 87 keyInterface.gDelayType = M10KEY; //復位按 88 keyState = MKEY_UNFIX; 89 } 90 if (keyDownConts > KEY2s) //如果是長按,則無需等待鬆開按鍵 91 { 92 keyInterface.gDelayType = M2SKEY; //長按 93 keyState = MKEY_UNFIX; 94 } 95 if (keyValueIn.bytedat == 0xFF) 96 { 97 keyUpConts++; 98 } 99 else 100 { 101 keyUpConts = 0; 102 } 103 if (keyUpConts > 2) 104 { 105 if (keyDownConts > KEYSHORTMIN &&keyDownConts < KEYSHORTMAX) //如果是短按,則需等待鬆開按鍵 106 { 107 keyInterface.gDelayType = MSHORTKEY; //短按 108 keyState = MKEY_UNFIX; 109 } 110 else 111 { 112 keyState = MKEY_IDLE; 113 } 114 } 115 break; 116 case MKEY_UNFIX: /* 此狀態代表按鍵可以正常取值 */ 117 _nop_(); 118 break; 119 default: 120 _nop_(); 121 break; 122 } 123 }

再次總結一下,keyState變數就是交際花,在不同的狀態切換;按鍵驅動模組與外部的介面keyInterface_t keyInterface ,使用一個結構體變數,實現了模組化。

針對本文中所有的錯誤和不足,歡迎交流,交流技術和專案合作均可。等你來撩我哦!加我請說明來意,謝謝。