记录一个按键处理模块

本模块模仿MultiButton实现的。GitHubhttps://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;