記錄一個按鍵處理模組

2023-11-17 18:01:23

本模組模仿MultiButton實現的。GitHubhttps://github.com/0x1abin/MultiButton

按鍵狀態參考DALI協定301部分按鍵狀態。

分享測試檔案:

連結:https://pan.baidu.com/s/1dqXc-_ycR-Tl-KQtsxJs4A
提取碼:1234

 

按鍵狀態分為以下狀態:

typedef enum
{
    KeyEvent_Idle = 0,
    KeyEvent_PutDown,
    KeyEvent_RealeaseUp,
    KeyEvent_Click,
    KeyEvent_DoubleClick,
    KeyEvent_LongPressStart,
    KeyEvent_LongPressRepeat,
    KeyEvent_LongPressEnd,
    KeyEvent_Stuck,
    KeyEvent_Free
} KeyEvent_Def;
有按下,彈起,單擊,雙擊,長按,卡死等。
大致邏輯就是濾波+狀態機+連結串列管理;
 
 
按下時間小於ShortPress_Ticks 就是短按,大於這個時間進入長按;
第一次按下之後的空閒時間 < DoubleClickIdle_Ticks 就是一個雙擊有效的區域,超過這個時間就預設是一個短按
Stuck_Ticks 是指按鍵卡死所需的時間,如果按鍵按鍵20s以上,就認為是一個卡死事件
LongPressRepeat_Ticks 指長按下時的重發時間,一直長按會一直髮。
 
 
 
 
 
 
以下為按鍵部分程式碼內容:
 
 
  1 #include "bsp_includes.h"
  2 #include <string.h>
  3 
  4 static KeyInfo_Def *pHead_Node = NULL;
  5 /**************************************************************************
  6  * @brief 初始化連結串列頭結點
  7  **************************************************************************/
  8 void List_Init(void)
  9 {
 10     pHead_Node = NULL;
 11 }
 12 /**************************************************************************
 13  * @brief 獲取按鍵當前觸發的事件
 14  **************************************************************************/
 15 u8 Get_KeyCurEvent(KeyInfo_Def *pHandle)
 16 {
 17     return (u8)(pHandle->byEvent);
 18 }
 19 /**************************************************************************
 20  * @brief 把新增的按鍵加入連結串列
 21  **************************************************************************/
 22 int Add_KeyToList(KeyInfo_Def *pCurNode)
 23 {
 24     KeyInfo_Def *pTargetNode = pHead_Node;
 25     while (pTargetNode)
 26     {
 27         if (pTargetNode == pCurNode)
 28         {
 29             return -1; // already exist.
 30         }
 31         pTargetNode = pTargetNode->pNext; // find Null node
 32     }
 33 
 34     pCurNode->pNext = pHead_Node;
 35     pHead_Node = pCurNode;
 36     return 0; // add success
 37 }
 38 
 39 /**************************************************************************
 40  * @brief 註冊按鍵資訊
 41  **************************************************************************/
 42 void Key_Attach(KeyInfo_Def *pHandle, GetIOStatus pFunc1, KeyEventProcess pFunc2, uint8_t byState)
 43 {
 44     memset(pHandle, 0, sizeof(KeyInfo_Def));
 45     pHandle->dwPressedTicks = 0;
 46     pHandle->dwReleasedTicks = 0;
 47     pHandle->dwLongPressRepeat_Ticks = 0;
 48     pHandle->byEvent = KeyEvent_Idle;
 49     pHandle->byKeyStatus = Key_IDLE;
 50     pHandle->byDebounce_Count = 0;
 51     pHandle->byMultiplePressEnable = byState;
 52     pHandle->pGetIOLevel_Func = pFunc1;
 53     pHandle->pProcess_Func = pFunc2;
 54     pHandle->byKey_Level = pHandle->pGetIOLevel_Func();
 55 
 56     Add_KeyToList(pHandle);
 57 }
 58 
 59 void KeyEvent_Process(KeyInfo_Def *handle, KeyEvent_Def keyEvent)
 60 {
 61     handle->byEvent = keyEvent;
 62     handle->pProcess_Func(handle, handle->byEvent);
 63 }
 64 /**************************************************************************
 65  * @brief 按鍵狀態機
 66  **************************************************************************/
 67 void Key_handler(KeyInfo_Def *pHandle)
 68 {
 69     uint8_t byRead_IO_Level = pHandle->pGetIOLevel_Func();
 70 
 71     /*------------button debounce handle---------------*/
 72     if (byRead_IO_Level != pHandle->byKey_Level) // not equal to prev one
 73     {
 74         // continue read 3 times same new level change
 75         if (++(pHandle->byDebounce_Count) >= DEBOUNCE_TICKS)
 76         {
 77             pHandle->byKey_Level = byRead_IO_Level;
 78             pHandle->byDebounce_Count = 0;
 79             if (pHandle->byKey_Level == Pressed)
 80             {
 81                 KeyEvent_Process(pHandle, KeyEvent_PutDown);
 82             }
 83             else
 84             {
 85                 KeyEvent_Process(pHandle, KeyEvent_RealeaseUp);
 86             }
 87         }
 88     }
 89     else
 90     { // leved not change ,counter reset.
 91         pHandle->byDebounce_Count = 0;
 92     }
 93 
 94     if (pHandle->dwReleasedTicks < 300000) // 300s
 95         pHandle->dwReleasedTicks++;
 96     if (pHandle->dwPressedTicks < 300000)
 97         pHandle->dwPressedTicks++;
 98 
 99     if (byRead_IO_Level != pHandle->byKey_Level)
100     {
101         if (byRead_IO_Level == Pressed)
102             pHandle->dwPressedTicks = 0;
103         else
104             pHandle->dwReleasedTicks = 0;
105     }
106 
107     switch (pHandle->byKeyStatus)
108     {
109     case Key_IDLE:
110         if (pHandle->byKey_Level == Pressed)
111         {
112             if (pHandle->dwPressedTicks >= ShortPress_Ticks)
113             {
114                 KeyEvent_Process(pHandle, KeyEvent_LongPressStart);
115                 pHandle->dwLongPressRepeat_Ticks = 0;
116                 pHandle->byKeyStatus = Key_LongPress;
117             }
118             else
119             {
120                 pHandle->byKeyStatus = Key_ACK;
121             }
122         }
123         else
124         {
125             pHandle->byKeyStatus = Key_IDLE;
126         }
127         break;
128     case Key_ACK:
129         if (pHandle->byKey_Level == Pressed)
130         {
131             if (pHandle->dwPressedTicks >= ShortPress_Ticks)
132             {
133                 KeyEvent_Process(pHandle, KeyEvent_LongPressStart);
134                 pHandle->dwLongPressRepeat_Ticks = 0;
135                 pHandle->byKeyStatus = Key_LongPress;
136             }
137         }
138         else
139         {
140             if (pHandle->byMultiplePressEnable == DPress_Disable)
141             {
142                 KeyEvent_Process(pHandle, KeyEvent_Click);
143                 pHandle->byKeyStatus = Key_IDLE;
144             }
145             else
146             {
147                 pHandle->byKeyStatus = Key_WaitDoublePress;
148             }
149         }
150         break;
151     case Key_WaitDoublePress:
152         if (pHandle->byKey_Level == Pressed)
153         {
154             if (pHandle->dwReleasedTicks <= DoubleClickIdle_Ticks)
155             {
156                 if (pHandle->byMultiplePressEnable == DPress_Enable)
157                     KeyEvent_Process(pHandle, KeyEvent_DoubleClick);
158                 else
159                     KeyEvent_Process(pHandle, KeyEvent_PutDown);
160                 pHandle->byKeyStatus = Key_WaitDoublePressIdle;
161             }
162         }
163         else
164         {
165             if (pHandle->dwReleasedTicks > DoubleClickIdle_Ticks)
166             {
167                 KeyEvent_Process(pHandle, KeyEvent_Click);
168                 pHandle->byKeyStatus = Key_IDLE;
169             }
170         }
171         break;
172     case Key_WaitDoublePressIdle:
173         if (pHandle->byKey_Level == Released)
174         {
175             pHandle->byKeyStatus = Key_IDLE;
176         }
177         break;
178     case Key_LongPress:
179         if (pHandle->byKey_Level == Pressed)
180         {
181             if (pHandle->dwPressedTicks > Stuck_Ticks)
182             {
183                 KeyEvent_Process(pHandle, KeyEvent_Stuck);
184                 pHandle->byKeyStatus = Key_STUCK;
185             }
186             else if (pHandle->dwLongPressRepeat_Ticks > LongPressRepeat_Ticks)
187             {
188                 KeyEvent_Process(pHandle, KeyEvent_LongPressRepeat);
189                 pHandle->dwLongPressRepeat_Ticks = 0;
190             }
191             else
192             {
193                 pHandle->dwLongPressRepeat_Ticks++;
194             }
195         }
196         else
197         {
198             KeyEvent_Process(pHandle, KeyEvent_LongPressEnd);
199             pHandle->byKeyStatus = Key_IDLE;
200         }
201         break;
202 
203     case Key_STUCK:
204         if (pHandle->byKey_Level == Released)
205         {
206             KeyEvent_Process(pHandle, KeyEvent_Free);
207             pHandle->byKeyStatus = Key_IDLE;
208         }
209     default:
210         break;
211     }
212 }
213 /**************************************************************************
214  * @brief 移除按鍵節點
215  **************************************************************************/
216 void Remove_Key(KeyInfo_Def *pTarget)
217 {
218     KeyInfo_Def **ppCur;
219     KeyInfo_Def *entry = *ppCur;
220     for (ppCur = &pHead_Node; (*ppCur) != NULL;)
221     {
222         if (entry == pTarget)
223         {
224             *ppCur = entry->pNext;
225             // free(entry);
226         }
227         else
228         {
229             ppCur = &entry->pNext;
230         }
231     }
232 }
233 /**************************************************************************
234  * @brief 毫秒處理常式
235  **************************************************************************/
236 void Key_Ticks_1ms(void)
237 {
238     KeyInfo_Def *pTarget;
239     for (pTarget = pHead_Node; pTarget; pTarget = pTarget->pNext)
240     {
241         if (pTarget == NULL)
242             return;
243         Key_handler(pTarget);
244     }
245 }

 

 
 1 #ifndef __BSP_KEY_H__
 2 #define __BSP_KEY_H__
 3 
 4 #include "bsp_includes.h"
 5 
 6 #define DEBOUNCE_TICKS 10
 7 
 8 #define ShortPress_Ticks         500
 9 #define DoubleClickIdle_Ticks     300
10 #define Stuck_Ticks             20000
11 #define LongPressRepeat_Ticks     200
12 
13 #define DPress_Enable    1
14 #define DPress_Disable    0
15 
16 typedef uint8_t (*GetIOStatus)(void);
17 typedef void (*KeyEventProcess)(void *, uint8_t);
18 
19 typedef enum
20 {
21     Released = 1,
22     Pressed = 0
23 } IOStatus_Def;
24 
25 typedef enum
26 {
27     Key_IDLE = 0,
28     Key_ACK,
29     Key_WaitDoublePress,
30     Key_WaitDoublePressIdle,
31     Key_LongPress,
32     Key_STUCK
33 } KeyStatus_Def;
34 
35 typedef enum
36 {
37     KeyEvent_Idle = 0,
38     KeyEvent_PutDown,
39     KeyEvent_RealeaseUp,
40     KeyEvent_Click,
41     KeyEvent_DoubleClick,
42     KeyEvent_LongPressStart,
43     KeyEvent_LongPressRepeat,
44     KeyEvent_LongPressEnd,
45     KeyEvent_Stuck,
46     KeyEvent_Free
47 } KeyEvent_Def;
48 
49 typedef struct Key
50 {
51     struct Key *pNext;
52     uint32_t     dwPressedTicks;
53     uint32_t     dwReleasedTicks;
54     uint32_t    dwLongPressRepeat_Ticks;
55     uint8_t     byDebounce_Count;
56     uint8_t     byEvent;
57     uint8_t     byKey_Level;
58     uint8_t     byKeyStatus;
59     uint8_t     byMultiplePressEnable; 
60     GetIOStatus pGetIOLevel_Func;
61     KeyEventProcess pProcess_Func;
62 } KeyInfo_Def;

 

 測試結果:

 如需要移植需注意:

typedef enum
{
    Released = 1,
    Pressed = 0
} IOStatus_Def;
我的按鍵是低電平有效