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