蓝桥杯篇---按键长按与双击

发布于:2025-03-20 ⋅ 阅读:(19) ⋅ 点赞:(0)


前言

本文简单介绍了一段代码,该代码是基于STC15F2K60S2单片机的按键检测程序,新增了长按、短按、双击、连击等功能,并采用状态机设计来适配原有的矩阵键盘扫描逻辑


1. 新增全局变量和宏定义

#include <STC15F2K60S2.H>

// 新增按键事件类型定义
#define KEY_EVENT_NONE    0
#define KEY_EVENT_SHORT   1  // 短按/单击
#define KEY_EVENT_LONG    2  // 长按
#define KEY_EVENT_DOUBLE  3  // 双击
#define KEY_EVENT_MULTI   4  // 连击(如3次)

// 时间阈值配置(单位:ms,需适配定时器频率)
#define DEBOUNCE_TIME   20   // 消抖时间
#define HOLD_TIME       1000 // 长按判定时间
#define DOUBLE_TIME     300  // 双击间隔
#define MULTI_TIME      500  // 连击间隔

// 全局按键状态变量
unsigned char Key_Val = 0;    // 当前键值
unsigned char Key_Old = 0;    // 上一次键值
unsigned char Key_Down = 0;   // 按下瞬间
unsigned char Key_Up = 0;     // 释放瞬间

volatile unsigned int sys_tick = 0;  // 系统时间戳(需定时器1ms中断更新)
unsigned char key_event = KEY_EVENT_NONE;  // 当前按键事件
unsigned char key_pressed = 0;        // 当前按下的键值
unsigned int key_press_start = 0;     // 按键按下开始时间
unsigned int key_last_release = 0;    // 按键最近释放时间
unsigned char click_cnt = 0;          // 连击次数

解释

1.1宏定义

KEY_EVENT_*

KEY_EVENT_*:定义了按键事件的类型,包括无事件、短按、长按、双击、连击

DEBOUNCE_TIME

DEBOUNCE_TIME:按键消抖时间,避免按键抖动误触发。

HOLD_TIME

HOLD_TIME:长按判定时间,超过该时间触发长按事件。

DOUBLE_TIME

DOUBLE_TIME:双击间隔时间,两次按下之间的时间小于该值则判定为双击。

MULTI_TIME

MULTI_TIME:连击间隔时间,多次按下之间的时间小于该值则判定为连击。

1.2全局变量

Key_Val

Key_Val:当前扫描到的键值

Key_Old

Key_Old:上一次扫描到的键值,用于检测按键状态变化。

Key_Down

Key_Down:按键按下瞬间的标志

Key_Up

Key_Up:按键释放瞬间的标志

sys_tick

sys_tick:系统时间戳,由定时器中断更新,用于时间相关的逻辑判断

key_event

key_event:当前检测到的按键事件

key_pressed

key_pressed:当前按下的键值。

key_press_start

key_press_start:按键按下的起始时间

key_last_release

key_last_release:按键最近一次释放的时间

click_cnt

click_cnt:连击次数计数器

2. 定时器初始化(1ms中断)

void Timer0_Init() {
    AUXR |= 0x80;       // 定时器0为1T模式
    TMOD &= 0xF0;       // 设置定时器模式
    TL0 = 0xCD;         // 1ms定时初值(11.0592MHz)
    TH0 = 0xD4;
    TF0 = 0;            // 清除标志
    TR0 = 1;            // 启动定时器
    ET0 = 1;            // 使能中断
    EA = 1;             // 全局中断使能
}

// 定时器0中断服务函数
void timer0_isr() interrupt 1 {
    sys_tick++;         // 系统时间戳自增
}

解释

2.1定时器初始化

配置定时器0为1T模式(1个时钟周期计数一次),设置初值以实现1ms定时。

启动定时器并开启中断。

2.2中断服务函数

1ms进入一次中断,sys_tick自增,用于记录系统运行时间。

3. 按键扫描与状态机修改

// 原有扫描函数(返回键值1~19,0表示无按键)
unsigned char ScanKey() {
    unsigned char temp = 0;
    P44=0; P42=P35=P34=1;
    if(P33==0) temp=4;  if(P32==0) temp=5;
    if(P31==0) temp=6;  if(P30==0) temp=7;
    P42=0; P44=P35=P34=1;
    if(P33==0) temp=8;  if(P32==0) temp=9;
    if(P31==0) temp=10; if(P30==0) temp=11;
    P35=0; P44=P42=P34=1;
    if(P33==0) temp=12; if(P32==0) temp=13;
    if(P31==0) temp=14; if(P30==0) temp=15;
    P34=0; P44=P42=P35=1;
    if(P33==0) temp=16; if(P32==0) temp=17;
    if(P31==0) temp=18; if(P30==0) temp=19;
    return temp;
}

// 按键处理主逻辑(循环调用)
void Key_Loop() {
    Key_Val = ScanKey();
    Key_Down = Key_Val & (Key_Old ^ Key_Val);  // 检测下降沿
    Key_Up = ~Key_Val & (Key_Old ^ Key_Val);   // 检测上升沿
    Key_Old = Key_Val;

    // 无按键时重置状态
    if (Key_Val == 0) {
        if (key_pressed != 0) {
            // 记录释放时间并计算连击
            if (sys_tick - key_last_release < MULTI_TIME) {
                click_cnt++;
            } else {
                click_cnt = 1;  // 超过连击间隔则重置
            }
            key_last_release = sys_tick;
            key_pressed = 0;
        }
        return;
    }

    // 处理按键按下
    if (Key_Down != 0) {
        key_pressed = Key_Val;          // 记录当前按键
        key_press_start = sys_tick;    // 记录按下时间
        click_cnt = 0;                 // 连击计数重置
    }

    // 处理长按检测
    if (key_pressed != 0 && (sys_tick - key_press_start > HOLD_TIME)) {
        key_event = KEY_EVENT_LONG;     // 触发长按事件
        key_pressed = 0;               // 长按后标记处理完成
    }

    // 处理按键释放
    if (Key_Up != 0 && key_pressed != 0) {
        // 短按判定(未达到长按时间)
        if (sys_tick - key_press_start < HOLD_TIME) {
            // 双击检测:在DOUBLE_TIME内检测第二次按下
            if (sys_tick - key_last_release < DOUBLE_TIME) {
                key_event = KEY_EVENT_DOUBLE;
            } else {
                key_event = KEY_EVENT_SHORT;
            }
        }
    }

    // 连击检测(需配合释放后的时间窗口)
    if (click_cnt >= 2) {
        key_event = KEY_EVENT_MULTI;
        click_cnt = 0;  // 连击后重置
    }
}

解释

3.1ScanKey函数

扫描矩阵键盘,返回当前按下的键值**(1~19)**,0表示无按键。

3.2Key_Loop函数

  1. 检测按键按下和释放的边沿(Key_Down和Key_Up)。

  2. 无按键时,记录释放时间并计算连击次数

  3. 按键按下时,记录按下的键值和时间

  4. 长按检测:按下时间超过HOLD_TIME则触发长按事件

  5. 短按和双击检测:在释放时判断按下时间是否小于HOLD_TIME,并结合DOUBLE_TIME判断是否为双击

  6. 连击检测:在MULTI_TIME内多次按下同一键则触发连击事件。

4. 主函数使用示例

void main() {
    Timer0_Init();      // 初始化定时器
    while (1) {
        Key_Loop();     // 循环调用按键检测

        // 处理按键事件
        switch (key_event) {
            case KEY_EVENT_SHORT:
                printf("Key %d: 短按\r\n", key_pressed);
                key_event = KEY_EVENT_NONE;
                break;
            case KEY_EVENT_LONG:
                printf("Key %d: 长按\r\n", key_pressed);
                key_event = KEY_EVENT_NONE;
                break;
            case KEY_EVENT_DOUBLE:
                printf("Key %d: 双击\r\n", key_pressed);
                key_event = KEY_EVENT_NONE;
                break;
            case KEY_EVENT_MULTI:
                printf("Key %d: 连击%d次\r\n", key_pressed, click_cnt + 1);
                key_event = KEY_EVENT_NONE;
                break;
        }
    }
}

解释

  1. 初始化定时器并进入主循环。

  2. 调用Key_Loop检测按键事件。

  3. 根据key_event的值处理不同的按键事件,并输出相应的信息。

5. 关键逻辑说明

5.1消抖处理

消抖处理:通过Key_Down和Key_Up检测边沿,隐式消抖。

5.2长按检测

长按检测:按下时间超过HOLD_TIME触发长按事件

5.3双击检测

双击检测:在DOUBLE_TIME内检测第二次按下

5.4连击检测

连击检测:在MULTI_TIME检测多次按下

6. 优化建议

6.1矩阵键盘冲突

矩阵键盘冲突:改进ScanKey函数以支持多键同时按下

6.2低功耗优化

低功耗优化:在空闲时进入休眠模式,通过按键唤醒。

6.3事件调回

事件回调:通过函数指针绑定事件处理函数,提高代码灵活性。