单片机按键检测与长短按识别实现

最近一个项目中,需要实现单键多功能控制:

单击执行某个动作; 双击切换模式; 长按执行开关机。

下面是具体实现方式分享

一、按键

#define KEY_ONOFF PORTA0 //此IO内部开启上拉

二、按键结构体定义

按键设计了一个结构体 `KEY_HandleTypeDef`, 用来存储每个按键的各种状态信息:

typedef struct

{

u8 state; // 当前按键状态(0=松开,1=按下)

u8 pressed_ct; // 短按计数

u16 long_press_ct; // 长按计数

u8 release_time; // 松开延时检测

u8 click; // 按键事件标志:1=单击,2=双击,3=长按

u8 longpress_flag; // 长按触发标志(防止重复触发)

u16 longpress_cnt; // 长按连续动作计数器

} KEY_HandleTypeDef;

KEY_HandleTypeDef key0 = {0};

注:可支持后续拓展更多,比如3击,4击,或者长按连发、多键组合等。

三、核心扫描函数 k0_scan()

该函数建议每 10ms 调用一次,负责检测按键按下/松开状态,并进行计数和事件识别。

复制代码
void k0_scan(void)
{
    // ① 按下检测
    if (KEY_ONOFF == 0)
    {
        if (key0.state == 0)
        {
            key0.state = 1;
            if (key0.pressed_ct < 100) key0.pressed_ct++;
            key0.release_time = 30; // 释放延时(30*10ms=300ms)
        }

        // 长按计数
        if (key0.long_press_ct < 301) key0.long_press_ct++;

        // 超过3秒判定为长按
        if (key0.long_press_ct == 300)
        {
            key0.long_press_ct = 300;
            key0.click = 3;  // 标记长按事件
            key0.pressed_ct = 0;
            key0.longpress_cnt = 0;

            if (!key0.longpress_flag)
            {
                key0.longpress_flag = 1;
                //长按
            }
        }
    }
    // ② 松开检测
    else
    {
        key0.state = 0;
        key0.long_press_ct = 0;
        if (key0.release_time) key0.release_time--;
    }

    // ③ 松开后判断单击/双击
    if (key0.release_time == 0)
    {
        if (!key0.longpress_flag)
        {
            if (key0.pressed_ct == 1)
            {
                key0.click = 1; // 单击
                key0.pressed_ct = 0;
            }
            else if (key0.pressed_ct == 2)
            {
                key0.click = 2; // 双击
                key0.pressed_ct = 0;
            }
        }
        else
        {
            // 长按松开后清除标志
            key0.longpress_flag = 0;
            key0.pressed_ct = 0;
            key0.click = 0;
            key0.longpress_cnt = 0;
        }
    }
}

四、逻辑说明

功能 条件 判定逻辑 执行动作
单击 按下并快速松开一次 在 300ms 内没有再次按下 key0.click = 1
双击 在第一次松开后, 300ms 内再次按下 第二次松开时检测到 `pressed_ct == 2 key0.cl``ick = 2
长按 按住时间 ≥ 3 秒 long_press_ct` 计数达到 300(10ms × 300 = 3s) key0.click = 3,触发开关机逻辑

五、事件处理函数

在主循环中调用 KEY_handle() 进行按键事件分发处理:

复制代码
void KEY_handle(void)
{
    // 单击事件
    if (key0.click == 1)
    {
        key0.click = 0;
        // 执行单击动作
        // 比如:切换模式 / 调节亮度 / 唤醒设备 等
    }
    if (key0.click == 2) // 双击
    {

    }

    if (key0.click == 3) // 长按
    {
    
    }
}
相关推荐
H_oRIZoN_1 分钟前
Linux入门DAY23(C语言树形结构)
linux·c语言·数据结构
千秋岁rr1 小时前
5.0 TIM
单片机·嵌入式硬件
caimouse3 小时前
ReactOS 窗口系统分析(25):标题栏显示与系统按钮 — nonclient.c 标题栏专题
c语言·开发语言
2601_966799044 小时前
CH398 技术解析:USB 3.0转千兆以太网单芯片方案与内置电源设计要点
单片机·嵌入式硬件
caimouse4 小时前
ReactOS 窗口系统分析(14):计时器/属性/加速键/热键 — timer.c + prop.c + accelerator.c + hotkey.c
c语言·开发语言·reactos
水饺编程4 小时前
第5章,[Win32 章节] :贝塞尔样条曲线(一)
c语言·c++·windows·visual studio
zlinear数据采集卡5 小时前
数据采集卡从入门到精通(38):上位机开发实战——Python/QT/LabVIEW的技术选型与分层架构
python·单片机·嵌入式硬件·qt·fpga开发·开源·labview
-凌凌漆-5 小时前
【C语言】结构体typedef struct与struct的区别
c语言·开发语言
嵌入式阿蔡5 小时前
项目实战 02:智能家居环境网关——ESP32 做 BLE 中心 + 本地联动
网络·单片机·嵌入式硬件·esp32·智能家居·嵌入式实时数据库
caimouse5 小时前
ReactOS 窗口系统分析(24):输入法 — ime.c
c语言·reactos