按键检测是做项目时经常遇到的问题,很多时候缺乏经验容易顾此失彼。个人梳理了一下技术点,水平有限,不足之处希望指出,希望能和大家共同进步。

1.按键检测

我们这里以默认低电平,按键按下读取到高电平来叙述,按键按下检测比较稳妥的是边沿读取,即读取电平之间的切换,单纯的读取电平容易受肌肉抖动影响,故现在按下读取应该读取低电平变成高电平这一过程

uint8_t key_last = 0;//上次电平
uint8_t key_now = 0;

if(key_last == 0 && key_now == 1)
{

}

2.按键消抖

手指按下的时候会有抖动,所以应该消抖,消抖延时时间一般在20ms左右。注意这里最好采用非阻塞延时,否则会影响外设上的其他功能,tick放在中断中执行,我这里中断是1ms执行1次,根据需要调整就可以。另外MS_TO_TICK(ms)可以根据需要灵活调整

#define MS_TO_TICK(ms)   ((ms) * 1)

uint32_t data tick = 0;    /* 系统时间戳,单位1ms */
uint32_t xdata data_return = 0;
uint16_t xdata now;
/* 判断是否过了指定毫秒数 */
uint8_t elapsed_ms(uint32_t start, uint32_t ms)
{
    now = tick;
    if(now > start)
    {
        data_return = now - start;
    }
    if(now < start)
    {
        data_return = 0xFFFF - start + now + 1;
    }
        return (data_return >= MS_TO_TICK(ms)) ? 1 : 0;
}

3.单击和长按的功能逻辑

大家最想看的还是整体代码

这里采用switch-case的结构,有些地方应该没有定义,在上面定义一下即可

typedef enum {
    KEY_IDLE,
    KEY_DEBOUNCE,
    KEY_PRESS,
    KEY_HOLD
} State;



uint8_t Key_Scan(void)
{
    uint8_t key_now = KEY_PIN;

    switch(State)
    {
        case KEY_IDLE:
        {
            // 上升沿:检测按下
            if(key_last == 0 && key_now == 1)
            {
                debounce_tick = tick;
                state = KEY_DEBOUNCE;
            }
        }
        break;

        case KEY_DEBOUNCE:
        {
            // 必须保持稳定高电平
            if(key_now == 1)
            {
                if(elapsed_ms(debounce_tick, 20))
                {
                    press_tick = tick;
                    state = KEY_PRESS;
                }
            }
            else
            {
                state = KEY_IDLE;
            }
        }
        break;

        case KEY_PRESS:
        {
            // 下降沿:松开
            if(key_last == 1 && key_now == 0)
            {
                state = KEY_IDLE;

                if(!long_flag)
                {
                    key_last = key_now;
                    return KEY_SHORT;
                }
            }

            // 长按判断(持续状态,不依赖边沿)
            if(elapsed_ms(press_tick, LONG_PRESS_MS))
            {
                long_flag = 1;
                return KEY_LONG;
            }
        }
        break;
    }

    key_last = key_now;
    return KEY_NONE;
}

4.功能拓展

其实还可以在这个架构上增加例如双击之类的功能,如果有需要可以自己尝试增加功能,代码仅供参考,如有问题欢迎提出

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐