2.使用队列传递按键信息
本模块模仿MultiButton实现的。GitHub:https://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;
我的按键是低电平有效