MCU独立按键驱动 - 长按连续触发

文摘   科技   2023-10-29 09:40   广东  

Keep Moving 

保持·热爱

按键驱动

应用实例

嵌入式C

独立按键、组合键



优化该按键驱动的状态机

增加长按连续触发功能

github.com/L231/ry_key

按键驱动的设计思路

后台回复开源获取全路径资料



概述

概述

当前版本解决了组合键与独立按键的事件冲突问题,新增长按连续触发功能。

与旧版本的结构差异,4个状态精简为2个状态:



状态机的优化

结构链表             

              链表构想

新的状态机

  • 两个状态

  • 新增长按连续触发模式


状态机的代码实现

uint8_t ry_key_state_machine(ry_key_t *key){    __key_level_scan(key);    /* 计时大于超长按阈值,就不要累加时间了 */    if(key->scan.tick <= __KEY_LONG_LONG_LIMIT)        key->scan.tick++;    switch(key->status)    {    case KEY_DOWN_STATUS :        /* 弹起事件 */        if(key->scan.level != key->scan.valid_level)        {            __key_event_mark(key, KEY_UP_EVENT);            key->status         = KEY_UP_STATUS;            /* 记录连击次数 */            if(key->scan.tick < __KEY_DOUBLE_CLICK_LIMIT)                key->scan.click_cnt++;        }        /* 时基 >= 长按连续触发的阈值 */        else if(key->scan.tick >= __KEY_LONG_PRESS_KEEP_TRIGGER_LIMIT)        {            /* 连续触发 */            if(key->long_press_keep_trigger)            {                __key_event_mark(key, KEY_DOWN_EVENT);                /* 时基清零,保证周期性触发 */                key->scan.tick  = 0;            }            /* 长按事件 */            else if(key->scan.tick == __KEY_LONG_LIMIT)                __key_event_mark(key, KEY_LONG_PRESS_EVENT);            /* 超长按事件 */            else if(key->scan.tick == __KEY_LONG_LONG_LIMIT)                __key_event_mark(key, KEY_LONG_LONG_PRESS_EVENT);            /* 长按,则连击事件应该消除 */            key->scan.click_cnt = 0;        }        break;    case KEY_UP_STATUS :        /* 按下事件 */        if(key->scan.level == key->scan.valid_level)        {            /* 先清零,表明是非连续触发的按下事件 */            key->scan.tick      = 0;            __key_event_mark(key, KEY_DOWN_EVENT);            key->status         = KEY_DOWN_STATUS;        }        /* 从按键按下开始计时,若时间超过连击时间阈值,则认为连击结束 */        else if(key->scan.tick > __KEY_DOUBLE_CLICK_LIMIT && key->scan.click_cnt > 0)        {            if(1 == key->scan.click_cnt)                __key_event_mark(key, KEY_SINGLE_CLICK_EVENT);            else if(2 == key->scan.click_cnt)                __key_event_mark(key, KEY_DOUBLE_CLICK_EVENT);            else if(3 == key->scan.click_cnt)                __key_event_mark(key, KEY_THREE_CLICK_EVENT);            key->scan.click_cnt = 0;        }        break;    default :        key->status             = KEY_UP_STATUS;        break;    }    __key_down_mark(key);    return key->event;}



实验效果

创建按键、扫描任务

场景说明:

  • 一个电源按键,注册长按事件

  • 一个功能按键,注册按压、连击事件

  • 组合键为“电源键” + “功能键”

void drv_key_create(void){    __KEY_REG(KEY_POWER, KEY_LONG_PRESS_NO_KEEP_TRIG);    __KEY_REG(KEY_CTRL, KEY_LONG_PRESS_KEEP_TRIG);    __KEY_COMPOUND_REG(KEY_COMPOUND1);    __KEY_COMPOUND_INSERT_SN(KEY_COMPOUND1, KEY_POWER);    __KEY_COMPOUND_INSERT_SN(KEY_COMPOUND1, KEY_CTRL);    /* 注册独立key事件 */    drv_set_key_event_handler(KEY_CTRL,  KEY_DOWN_EVENT,            __key_ctrl_down_callback);    drv_set_key_event_handler(KEY_CTRL,  KEY_SINGLE_CLICK_EVENT,    __key_ctrl_1_click_callback);    drv_set_key_event_handler(KEY_CTRL,  KEY_DOUBLE_CLICK_EVENT,    __key_ctrl_2_click_callback);    drv_set_key_event_handler(KEY_CTRL,  KEY_THREE_CLICK_EVENT,     __key_ctrl_3_click_callback);    drv_set_key_event_handler(KEY_POWER, KEY_LONG_PRESS_EVENT,      __key_power_long_press_callback);    drv_set_key_event_handler(KEY_POWER, KEY_LONG_LONG_PRESS_EVENT, __key_power_long_long_press_callback);    /* 注册组合键的事件 */    drv_set_key_compound_event_handler(KEY_COMPOUND1, __key_compound_callback);    /* 注册一个key扫描任务 */    ry_task_reg(&__taskKey,          /* 任务控制块 */                ry_key_scan,         /* 任务主体 */                RY_TASK_MODE_NORMAL, /* 普通模式,周期性运行即可 */                RY_TASK_READY,       /* 就绪态 */                "key",               /* 任务名字 */                KEY_SCAN_CYCLE);     /* 定时周期 */}


验证效果

功能键连击如下:


组合键、长按连续触发的效果如下:





-END-


碎片聚合
求真务实
 最新文章