本模块模仿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 nr”);
vTaskDelay(1);
}
}
在另外一个任务中获取该信息放到keyInfo中:
KeyProcessInfo_Def keyInfo;
for(;;)
{
if(Get_KeyInfo(&keyInfo,1000) == 0)
{
memset(&keyInfo, 0, sizeof(KeyProcessInfo_Def));
//printf(“QUEUE_EMPTYnr”);
}
if(keyInfo.byKey_Num ==3)
{
if(keyInfo.byKey_event==KeyEvent_Click)
{
printf(“Key 3 Clicknr”);
}
else if(keyInfo.byKey_event==KeyEvent_PutDown)
{
printf(“Key 3 PutDownnr”);
}
else if(keyInfo.byKey_event==KeyEvent_RealeaseUp)
{
printf(“Key 3 RealeaseUpnr”);
}
else if(keyInfo.byKey_event==KeyEvent_DoubleClick)
{
printf(“Key 3 DoubleClicknr”);
}
else if(keyInfo.byKey_event==KeyEvent_LongPressStart)
{
printf(“Key 3 LongPressStartnr”);
}
else if(keyInfo.byKey_event==KeyEvent_LongPressRepeat)
{
printf(“Key 3 LongPressRepeatnr”);
}
else if(keyInfo.byKey_event==KeyEvent_LongPressEnd)
{
printf(“Key 3 LongPressEndnr”);
}
else if(keyInfo.byKey_event==KeyEvent_Free)
{
printf(“Key 3 Freenr”);
}
else if(keyInfo.byKey_event==KeyEvent_Stuck)
{
printf(“Key 3 Stucknr”);
}
}
SetGPIO_Toggle(LED1);
//printf(“Task 1 nr”);
//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 0
typedef 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__ */