简介
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
成员的两个枚举值:
LongHold
和MultiClick
来区分当次释放按键的事件是长按还是连击(单击)。
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");
}
实现思路
思路其实很简单,核心部分是两个计时器:按下计时器和间隔计时器。通过有限状态机的方式来实现对各个功能的检测,状态转移图大概如下:
仅作分享,欢迎提出优化意见以及建议🥰