2.使用队列传递按键信息

本模块模仿MultiButton实现的。GitHubhttps://github.com/0x1abin/MultiButton

Freertos学习项目来自B站up主:https://www.bilibili.com/video/BV13R4y177jU/?spm_id_from=333.999.0.0

 

分享测试文件:

链接: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;
 
 
 然后为每一个按键都设置一个结构体,通过链表连接:
typedef struct Key
{
    struct Key *pNext;  //指向下一个按键结构体
    uint32_t    dwPressedTicks;//按下的时长
    uint32_t    dwReleasedTicks;//弹起后的时长
    uint32_t    dwLongPressRepeat_Ticks;//长按下的时长,用于重发事件
    uint8_t     byDebounce_Count;//按键消抖计数
    uint8_t     byEvent;//触发的事件
    uint8_t     byKey_Level;//按键的电平
    uint8_t     byKeyStatus;//按键的状态
    uint8_t     byMultiplePressEnable;//双击开启标志
    GetIOStatus pGetIOLevel_Func;//获取GPIO电平的函数
    KeyEventProcess pProcess_Func;//按键事件处理函数
} KeyInfo_Def;
 
 
整个模块大致逻辑:获取输入,根据状态输出事件。
输入:uint8_t byRead_IO_Level = pHandle->pGetIOLevel_Func();
输出:KeyEvent_Process(pHandle, KeyEvent_RealeaseUp);
 
 
通过把每一个按键结构体通过链表连接起来,再定时去轮询按键的状态,然后触发事件。
加入按键:int Add_KeyToList(KeyInfo_Def *pCurNode)
轮询按键:
KeyInfo_Def *pTarget;
    for (pTarget = pHead_Node; pTarget; pTarget = pTarget->pNext)
    {
        if (pTarget == NULL)
            return;
        Key_handler(pTarget);
    }
 
 
使用时:先创建一个队列句柄和几个按键结构体以及我们需要传递的信息(通过队列在任务之间通信)
static QueueHandle_t xKeyInfoQueue;
static KeyProcessInfo_Def KeyData;
static KeyInfo_Def key1;
static KeyInfo_Def key2;
static KeyInfo_Def key3;
static KeyInfo_Def key4;
static KeyInfo_Def key5;
static KeyInfo_Def key6;
 
 
初始化链表,再把按键挂到链表上,并创建一个队列:
void Key_Init(void)
{
    List_Init();

    // 注册按键
    Key_Attach(&key1, Read_Key1, Key1_Event_Process, DPress_Enable);
    Key_Attach(&key2, Read_Key2, Key2_Event_Process, DPress_Enable);
    Key_Attach(&key3, Read_Key3, Key3_Event_Process, DPress_Enable);
    Key_Attach(&key4, Read_Key4, Key4_Event_Process, DPress_Enable);
    Key_Attach(&key5, Read_Key5, Key5_Event_Process, DPress_Enable);
    Key_Attach(&key6, Read_Key6, Key6_Event_Process, DPress_Enable);

    xKeyInfoQueue = xQueueCreate(2, sizeof(KeyProcessInfo_Def));
}
 
在按键检测任务中每1ms轮询一次,并检测按键状态,触发事件,并把信息KeyData传递给队列:
void KEYDetect_task(void const *pvParameters)
{
    Key_Init();
    for(;;)
    {
        Key_Ticks_1ms();
        //printf("key  Task \n\r");
        vTaskDelay(1);
    }
}
 
 
在另外一个任务中获取该信息放到keyInfo中:
KeyProcessInfo_Def keyInfo;
    for(;;)
    {
        if(Get_KeyInfo(&keyInfo,1000) == 0)
        {
            memset(&keyInfo, 0, sizeof(KeyProcessInfo_Def));
            //printf("QUEUE_EMPTY\n\r");
        }
        if(keyInfo.byKey_Num ==3)
        {
            if(keyInfo.byKey_event==KeyEvent_Click)
            {
                printf("Key 3 Click\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_PutDown)
            {
                printf("Key 3 PutDown\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_RealeaseUp)
            {
                printf("Key 3 RealeaseUp\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_DoubleClick)
            {
                printf("Key 3 DoubleClick\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_LongPressStart)
            {
                printf("Key 3 LongPressStart\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_LongPressRepeat)
            {
                printf("Key 3 LongPressRepeat\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_LongPressEnd)
            {
                printf("Key 3 LongPressEnd\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_Free)
            {
                printf("Key 3 Free\n\r");
            }
            else if(keyInfo.byKey_event==KeyEvent_Stuck)
            {
                printf("Key 3 Stuck\n\r");
            }
        }
        SetGPIO_Toggle(LED1);
        //printf("Task 1 \n\r");
        //vTaskDelay(1000);

当按键按下,就可以在另外一个任务中去打印当前的按键事件了。



以下为按键部分代码内容:
 
 
  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;
我的按键是低电平有效

热门相关:试婚老公,用点力!   龙骑战机   苍穹龙骑   苍穹龙骑   至尊剑皇