文章目录
- 前言
- 1. 新增全局变量和宏定义
- 解释
- 1.1宏定义
- KEY_EVENT_*
- DEBOUNCE_TIME
- HOLD_TIME
- DOUBLE_TIME
- MULTI_TIME
- 1.2全局变量
- Key_Val
- Key_Old
- Key_Down
- Key_Up
- sys_tick
- key_event
- key_pressed
- key_press_start
- key_last_release
- click_cnt
- 2. 定时器初始化(1ms中断)
- 解释
- 2.1定时器初始化
- 2.2中断服务函数
- 3. 按键扫描与状态机修改
- 解释
- 3.1ScanKey函数
- 3.2Key_Loop函数
- 4. 主函数使用示例
- 解释
- 5. 关键逻辑说明
- 5.1消抖处理
- 5.2长按检测
- 5.3双击检测
- 5.4连击检测
- 6. 优化建议
- 6.1矩阵键盘冲突
- 6.2低功耗优化
- 6.3事件调回
前言
本文简单介绍了一段代码,该代码是基于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函数
-
检测按键按下和释放的边沿(Key_Down和Key_Up)。
-
无按键时,记录释放时间并计算连击次数。
-
按键按下时,记录按下的键值和时间。
-
长按检测:按下时间超过HOLD_TIME则触发长按事件。
-
短按和双击检测:在释放时判断按下时间是否小于HOLD_TIME,并结合DOUBLE_TIME判断是否为双击。
-
连击检测:在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;}}
}
解释
-
初始化定时器并进入主循环。
-
调用Key_Loop检测按键事件。
-
根据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事件调回
事件回调:通过函数指针绑定事件处理函数,提高代码灵活性。