关于按键处理的破事

简介

LED、按键等这些东西是搞嵌入式的每个人都一定会打交道的东西,笔者当然也不例外。LED可以上电即用,但是按键相对而言就要复杂一些了。

众所周知,按键是有抖动的,所以在使用过程中不得不进行消抖操作,如:加电容进行硬件消抖以及延时进行软件消抖等。

按键抖动

但是,硬件消抖这件事不得不在画PCB时费心费力(虽然也没有很费心),而延时的消抖方式让MCU有了一段无意义的空等时间,而且出于消抖的需要,这段时间往往长达20ms左右。此外,如果想做一些小体积但是功能较多的项目的话,按键仅仅有单击的功能很可能是不够的,考虑到上述原因(其实就是闲的),笔者自行封装了一个按键检测封装,可以实现按键的单击连击以及长按检测

使用说明

要使用CommonKey按键检测封装,你需要在你的工程中做出如下处理:

  • 将文件加入你的工程路径中,按需修改时间阈值宏:

COMKEY_ClickThreshold:单击时间阈值,短于此时间视为抖动

COMKEY_HoldThreshold:长按时间阈值,长于此时间视为长按

COMKEY_HoldTriggerThreshold:长按触发阈值,长按时每隔该时间触发一次按键

COMKEY_IntervalVal:间隔时间阈值,短于此时间视为连按

  • 为您的每个按键定义一个comkey_t句柄

  • 每个按键调用ComKey_Init初始化函数,参数为按键句柄检测时间

  • 改写ComKey_SyncValue函数

  • 根据需要重写回调函数:

ComKey_LongHoldCallback长按回调函数,参数为按键句柄

ComKey_HoldTriggerCallback长按触发回调函数,参数为按键句柄

ComKey_MultipleClickCallback连点回调函数,参数为按键句柄

ComKey_KeyReleaseCallback按键松开回调函数,参数为按键句柄

ComKey_KeyPressCallback按键按下回调函数,参数为按键句柄

  • 以一个固定的间隔调用ComKey_Handler函数

  • 测试实际效果


一些说明与示例

接口函数的移植

库内部不提供对按键触发电平的检测,移植时请务必保证按键触发时传递到key->val的值为1;

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

此外请不要删除最后一句代码,这将影响程序的正常运行。

void ComKey_SyncValue(comkey_t *key) {
    //if your key was pressed,"key->val" should be 1.
    if (key == &key0) {
        key->val = !HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
    }
    if (key == &key1) {
        key->val = !HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
    }

    //IMPORTANT!!!DO NOT MODIFIED!!!
    key->preVal = key->val;
}

长按回调函数

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

此外,您可以通过按键句柄的holdTime成员来获取当次长按的持续时间(ms)供您使用.

void ComKey_LongHoldCallback(comkey_t *key) {
    if (key == &key0)
        printf("key0: ");
    if (key == &key1) {
        printf("key1: ");
    }
    printf("hold %ldms\n", key->holdTime);
}

长按触发回调函数

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

当您采用按键单击进行类似数据加减的任务时,若想通过长按来实现多次单击的效果,请使用此回调函数;

该函数将会以COMKEY_HoldTriggerThreshold的间隔被调用。

void ComKey_HoldTriggerCallback(comkey_t *key) {
    if (key == &key0)
        printf("key0: HoldTrigger\n");
    if (key == &key1) {
        printf("key1: HoldTrigger\n");
    }
}

连续点击回调函数

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

当您多次连续进行点击,会触发该回调函数,连续点击间隔时间阈值可通过COMKEY_IntervalVal宏来进行修改;

此外,您可以通过按键句柄的clickCnt成员来获得当次连按的次数以供您使用。

void ComKey_MultipleClickCallback(comkey_t *key) {
    if (key == &key0)
        printf("key0: ");
    if (key == &key1) {
        printf("key1: ");
    }
    printf("click: %d\n", key->clickCnt);
}

按键按下回调函数

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

任何按键按下的事件都会触发该回调函数。

void ComKey_KeyPressCallback(comkey_t *key) {
    if (key == &key0) {
        printf("key0: ");
    }
    if (key == &key1) {
        printf("key1: ");
    }
    printf("keyPress!\n");
}

按键释放回调函数

如果您有多个按键,请通过您自己定义的按键句柄来区分您的触发按键;

此外,您可以通过按键句柄的state成员的两个枚举值:

LongHoldMultiClick

来区分当次释放按键的事件是长按还是连击(单击)。

void ComKey_KeyReleaseCallback(comkey_t *key) {
    if (key == &key0) {
        printf("key0: ");
    }
    if (key == &key1) {
        printf("key1: ");
    }

    if (key->state == LongHold) {
        printf("LongHold");
    } else if (key->state == MultiClick) {
        printf("Click");
    }
    printf(" Release!\n");
}

实现思路

思路其实很简单,核心部分是两个计时器:按下计时器间隔计时器。通过有限状态机的方式来实现对各个功能的检测,状态转移图大概如下:

按键状态转移

仅作分享,欢迎提出优化意见以及建议🥰

上一篇
下一篇