本模块模仿MultiButton实现的。GitHub:https://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 指长按下时的重发时间,一直长按会一直发。 以下为按键部分代码内容: #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); } } /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #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; 标签:Ticks,模块,KeyEvent,记录,pHandle,Key,按键,Level,Def From: https://www.cnblogs.com/cc-cnblogs/p/17839060.html