记录一个按键处理模块

发布时间 2023-11-17 16:58:59作者: cc_record

本模块模仿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;