本模块模仿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);
当按键按下,就可以在另外一个任务中去打印当前的按键事件了。
整体代码内容:
#include "bsp_includes.h"#include <string.h>static KeyInfo_Def *pHead_Node = NULL;/************************************************************************** * @brief 初始化链表头结点 **************************************************************************/void List_Init(void){ pHead_Node = NULL;}/************************************************************************** * @brief 获取按键当前触发的事件 **************************************************************************/u8 Get_KeyCurEvent(KeyInfo_Def *pHandle){ return (u8)(pHandle->byEvent);}/************************************************************************** * @brief 把新增的按键加入链表 **************************************************************************/int Add_KeyToList(KeyInfo_Def *pCurNode){ KeyInfo_Def *pTargetNode = pHead_Node; while (pTargetNode) { if (pTargetNode == pCurNode) { return -1; // already exist. } pTargetNode = pTargetNode->pNext; // find Null node } pCurNode->pNext = pHead_Node; pHead_Node = pCurNode; return 0; // add success}/************************************************************************** * @brief 注册按键信息 **************************************************************************/void Key_Attach(KeyInfo_Def *pHandle, GetIOStatus pFunc1, KeyEventProcess pFunc2, uint8_t byState){ memset(pHandle, 0, sizeof(KeyInfo_Def)); pHandle->dwPressedTicks = 0; pHandle->dwReleasedTicks = 0; pHandle->dwLongPressRepeat_Ticks = 0; pHandle->byEvent = KeyEvent_Idle; pHandle->byKeyStatus = Key_IDLE; pHandle->byDebounce_Count = 0; pHandle->byMultiplePressEnable = byState; pHandle->pGetIOLevel_Func = pFunc1; pHandle->pProcess_Func = pFunc2; pHandle->byKey_Level = pHandle->pGetIOLevel_Func(); Add_KeyToList(pHandle);}void KeyEvent_Process(KeyInfo_Def *handle, KeyEvent_Def keyEvent){ handle->byEvent = keyEvent; handle->pProcess_Func(handle, handle->byEvent);}/************************************************************************** * @brief 按键状态机 **************************************************************************/void Key_handler(KeyInfo_Def *pHandle){ uint8_t byRead_IO_Level = pHandle->pGetIOLevel_Func(); /*------------button debounce handle---------------*/ if (byRead_IO_Level != pHandle->byKey_Level) // not equal to prev one { // continue read 3 times same new level change if (++(pHandle->byDebounce_Count) >= DEBOUNCE_TICKS) { pHandle->byKey_Level = byRead_IO_Level; pHandle->byDebounce_Count = 0; if (pHandle->byKey_Level == Pressed) { KeyEvent_Process(pHandle, KeyEvent_PutDown); } else { KeyEvent_Process(pHandle, KeyEvent_RealeaseUp); } } } else { // leved not change ,counter reset. pHandle->byDebounce_Count = 0; } if (pHandle->dwReleasedTicks < 300000) // 300s pHandle->dwReleasedTicks++; if (pHandle->dwPressedTicks < 300000) pHandle->dwPressedTicks++; if (byRead_IO_Level != pHandle->byKey_Level) { if (byRead_IO_Level == Pressed) pHandle->dwPressedTicks = 0; else pHandle->dwReleasedTicks = 0; } switch (pHandle->byKeyStatus) { case Key_IDLE: if (pHandle->byKey_Level == Pressed) { if (pHandle->dwPressedTicks >= ShortPress_Ticks) { KeyEvent_Process(pHandle, KeyEvent_LongPressStart); pHandle->dwLongPressRepeat_Ticks = 0; pHandle->byKeyStatus = Key_LongPress; } else { pHandle->byKeyStatus = Key_ACK; } } else { pHandle->byKeyStatus = Key_IDLE; } break; case Key_ACK: if (pHandle->byKey_Level == Pressed) { if (pHandle->dwPressedTicks >= ShortPress_Ticks) { KeyEvent_Process(pHandle, KeyEvent_LongPressStart); pHandle->dwLongPressRepeat_Ticks = 0; pHandle->byKeyStatus = Key_LongPress; } } else { if (pHandle->byMultiplePressEnable == DPress_Disable) { KeyEvent_Process(pHandle, KeyEvent_Click); pHandle->byKeyStatus = Key_IDLE; } else { pHandle->byKeyStatus = Key_WaitDoublePress; } } break; case Key_WaitDoublePress: if (pHandle->byKey_Level == Pressed) { if (pHandle->dwReleasedTicks <= DoubleClickIdle_Ticks) { if (pHandle->byMultiplePressEnable == DPress_Enable) KeyEvent_Process(pHandle, KeyEvent_DoubleClick); else KeyEvent_Process(pHandle, KeyEvent_PutDown); pHandle->byKeyStatus = Key_WaitDoublePressIdle; } } else { if (pHandle->dwReleasedTicks > DoubleClickIdle_Ticks) { KeyEvent_Process(pHandle, KeyEvent_Click); pHandle->byKeyStatus = Key_IDLE; } } break; case Key_WaitDoublePressIdle: if (pHandle->byKey_Level == Released) { pHandle->byKeyStatus = Key_IDLE; } break; case Key_LongPress: if (pHandle->byKey_Level == Pressed) { if (pHandle->dwPressedTicks > Stuck_Ticks) { KeyEvent_Process(pHandle, KeyEvent_Stuck); pHandle->byKeyStatus = Key_STUCK; } else if (pHandle->dwLongPressRepeat_Ticks > LongPressRepeat_Ticks) { KeyEvent_Process(pHandle, KeyEvent_LongPressRepeat); pHandle->dwLongPressRepeat_Ticks = 0; } else { pHandle->dwLongPressRepeat_Ticks++; } } else { KeyEvent_Process(pHandle, KeyEvent_LongPressEnd); pHandle->byKeyStatus = Key_IDLE; } break; case Key_STUCK: if (pHandle->byKey_Level == Released) { KeyEvent_Process(pHandle, KeyEvent_Free); pHandle->byKeyStatus = Key_IDLE; } default: break; }}/************************************************************************** * @brief 移除按键节点 **************************************************************************/void Remove_Key(KeyInfo_Def *pTarget){ KeyInfo_Def **ppCur; KeyInfo_Def *entry = *ppCur; for (ppCur = &pHead_Node; (*ppCur) != NULL;) { if (entry == pTarget) { *ppCur = entry->pNext; // free(entry); } else { ppCur = &entry->pNext; } }}/************************************************************************** * @brief 毫秒处理函数 **************************************************************************/void Key_Ticks_1ms(void){ 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;/************************************************************************** * @brief 获取按键IO状态 **************************************************************************/uint8_t Read_Key1(void){ return (ReadGPIO(KEY1));}uint8_t Read_Key2(void){ return (ReadGPIO(KEY2));}uint8_t Read_Key3(void){ return (ReadGPIO(KEY3));}uint8_t Read_Key4(void){ return (ReadGPIO(KEY4));}uint8_t Read_Key5(void){ return (ReadGPIO(KEY5));}uint8_t Read_Key6(void){ return (ReadGPIO(KEY6));}/************************************************************************** * @brief 事件过滤 **************************************************************************/static void Keyx_ChoseEvent(uint8_t byEvent){ switch (byEvent) { case KeyEvent_PutDown: KeyData.byKey_event = KeyEvent_PutDown; break; case KeyEvent_RealeaseUp: KeyData.byKey_event = KeyEvent_RealeaseUp; break; case KeyEvent_Click: KeyData.byKey_event = KeyEvent_Click; break; case KeyEvent_DoubleClick: KeyData.byKey_event = KeyEvent_DoubleClick; break; case KeyEvent_LongPressStart: KeyData.byKey_event = KeyEvent_LongPressStart; break; case KeyEvent_LongPressRepeat: KeyData.byKey_event = KeyEvent_LongPressRepeat; break; case KeyEvent_LongPressEnd: KeyData.byKey_event = KeyEvent_LongPressEnd; break; case KeyEvent_Stuck: KeyData.byKey_event = KeyEvent_Stuck; break; case KeyEvent_Free: KeyData.byKey_event = KeyEvent_Free; break; default: break; }}/************************************************************************** * @brief 按键事件处理函数 **************************************************************************/static void Key1_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 1; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}static void Key2_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 2; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}static void Key3_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 3; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}static void Key4_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 4; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}static void Key5_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 5; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}static void Key6_Event_Process(void *btn, uint8_t event){ Keyx_ChoseEvent(event); KeyData.byKey_Num = 6; xQueueSend(xKeyInfoQueue, (void *)&KeyData, (TickType_t)10);}/************************************************************************** * @brief 按键模块初始化 **************************************************************************/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));}/************************************************************************** * @brief 队列接收,用于传递按键触发信息 **************************************************************************/uint8_t Get_KeyInfo(KeyProcessInfo_Def *ucQueueMsgValue, uint32_t xMaxBlockTime){ long byStatus; byStatus = xQueueReceive(xKeyInfoQueue, (void *)ucQueueMsgValue, (TickType_t)xMaxBlockTime); return (uint8_t)byStatus;}
#ifndef __BSP_KEY_H__#define __BSP_KEY_H__#include "bsp_includes.h"#define DEBOUNCE_TICKS 10#define ShortPress_Ticks 500#define DoubleClickIdle_Ticks 300#define Stuck_Ticks 20000#define LongPressRepeat_Ticks 200#define DPress_Enable 1#define DPress_Disable 0typedef uint8_t (*GetIOStatus)(void);typedef void (*KeyEventProcess)(void *, uint8_t);typedef enum{ Released = 1, Pressed = 0} IOStatus_Def;typedef enum{ Key_IDLE = 0, Key_ACK, Key_WaitDoublePress, Key_WaitDoublePressIdle, Key_LongPress, Key_STUCK} KeyStatus_Def;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; KeyEventProcess pProcess_Func;} KeyInfo_Def;/*----------------------------------------------------------------------*/typedef struct{ uint8_t byKey_Num; uint8_t byKey_event;} KeyProcessInfo_Def;void Key_Init(void);uint8_t Get_KeyInfo(KeyProcessInfo_Def *ucQueueMsgValue, uint32_t xMaxBlockTime);void Key_Ticks_1ms(void);#endif /* __BSP_KEY_H__ */
#include "bsp_includes.h"#define LED1_TASK_PRIO 1#define LED2_TASK_PRIO 2#define LED3_TASK_PRIO 2#define KEYDetect_TASK_PRIO 3#define LED1_STK_SIZE 128#define LED2_STK_SIZE 128#define LED3_STK_SIZE 128#define KEYDetect_STK_SIZE 128TaskHandle_t LED1Task_Handler;TaskHandle_t LED2Task_Handler;TaskHandle_t LED3Task_Handler;TaskHandle_t KEYDetectTask_Handler;/************************************************************************** * @brief led1_task**************************************************************************/void led1_task(void const *pvParameters){ 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); }}/************************************************************************** * @brief led2_task**************************************************************************/void led2_task(void const *pvParameters){ for(;;) { SetGPIO_Toggle(LED2); //printf("Task 2 \n\r"); vTaskDelay(1000); }}/************************************************************************** * @brief **************************************************************************/void led3_task(void const *pvParameters){ for(;;) { SetGPIO_Toggle(LED3); //printf("Task 3 \n\r"); vTaskDelay(1000); }}/************************************************************************** * @brief Set_PowerOn**************************************************************************/void KEYDetect_task(void const *pvParameters){ Key_Init(); for(;;) { Key_Ticks_1ms(); //printf("key Task \n\r"); vTaskDelay(1); }}/************************************************************************** * @brief Set_PowerOn**************************************************************************/void Set_PowerOn(void){ HAL_Delay(50); SetGPIO_High(POWER_ON);}/************************************************************************** * @brief AppTaskCreat_Init**************************************************************************/void AppTaskCreat_Init(void){ Set_PowerOn(); taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t) led1_task, (const char *) "led1_task", (uint16_t) LED1_STK_SIZE, (void *) NULL, (UBaseType_t) LED1_TASK_PRIO, (TaskHandle_t *) &LED1Task_Handler); xTaskCreate((TaskFunction_t) led2_task, (const char *) "led2_task", (uint16_t) LED2_STK_SIZE, (void *) NULL, (UBaseType_t) LED2_TASK_PRIO, (TaskHandle_t *) &LED2Task_Handler); xTaskCreate((TaskFunction_t) led3_task, (const char *) "led3_task", (uint16_t) LED3_STK_SIZE, (void *) NULL, (UBaseType_t) LED3_TASK_PRIO, (TaskHandle_t *) &LED3Task_Handler); xTaskCreate((TaskFunction_t) KEYDetect_task, (const char *) "KEYDetect_task", (uint16_t) KEYDetect_STK_SIZE, (void *) NULL, (UBaseType_t) KEYDetect_TASK_PRIO, (TaskHandle_t *) &KEYDetectTask_Handler); vTaskStartScheduler(); taskEXIT_CRITICAL();}