非阻塞全功能单按键状态查询(1~254连击及长按识别)
毕业设计中按键检测程序改了很久,分享给大家。支持单击、双击、三连击、四连击、五连击、六连击、七连击、八连击、九连击、十连击、十一连击、十二连击、十三连击、十四击、十五击、十六连击……254连击以及无阻塞长按识别。程序与流程图略有不同,如有BUG或者更好建议,欢迎指正!
一、按键查询程序流程图

1.流程说明:
- 开始:根据传入的按键索引
keyn读取对应引脚电平。
- 释放处理:若引脚为高(释放),则增加释放消抖T1计数;连续10次检测到释放后,确认释放,重置相关变量并启动连击窗口T3计时。
- 按下处理:若引脚为低(按下),则增加长按T2计时、清除释放消抖T1计数;若之前为释放状态,则更新状态state、连击次数N1加1并重置连击窗口T3计时。
- 长按判断:若长按T2计时达到360ms,则将连击次数N1设为255(长按标志),置位长按标志B_L;否则清除长按标志B_L,并检查连击窗口是否结束(90ms),若结束则输出当前连击次数N1到N2(
key_mode[])并清空连击计数N1。
- 键值快速清零:若N2(
key_mode[])非零,则启动快速清零计时,超过1ms后将N2(key_mode)清零,确保输出仅维持短暂时间。
二、视频演示
**1.视频说明:**数码管高四位显示按键P4^7连击数,数码管低四位显示按键P3^3连击数; 长按流水灯循环,长按释放后数码管显示255,开机时数码管显示12345678。
三、按键状态查询代码
1.h文件
#ifndef __KEY_QUERY_H__
#define __KEY_QUERY_H__
#include <STC8051U.H>
#include <AI_USB.H>
#define KEY_Number 2 //物理按键数量
//按键引脚定义
sbit Key0 = P4^7;
sbit Key1 = P3^3;
//sbit Key2 = P3^6;
void KeyRead(u8 keyn);//查询按键状态(放入1ms中断中)
#endif
2.c文件
#include "key_query.h"
// 输出变量(供外部调用)
u16 xdata key_hold_timer[KEY_Number] = {0}; // 长按计时
u8 xdata key_nobl_long[KEY_Number] = {0}; // 非阻塞长按标志
u8 xdata key_mode[KEY_Number] = {0}; // 最终按键类型
// 按键扫描函数(需放入1ms中断中)
void KeyRead(u8 keyn)
{
// 内部状态(静态局部,仅在此函数内使用)
static u8 key_state[KEY_Number] = {0}; // 当前按下状态
static u8 key_combinations[KEY_Number] = {0}; // 实时连击次数
static u8 key_debounce_timer[KEY_Number] = {0}; // 释放消抖计时
static u8 key_response_delay[KEY_Number] = {0}; // 连击窗口计时
static u8 key_res_timer[KEY_Number] = {0}; // 键值快速清零计时
bit key_pin; // 当前按键引脚电平
// 根据索引读取对应引脚
switch (keyn)
{
case 0: key_pin = Key0; break;
case 1: key_pin = Key1; break;
// case 2: key_pin = Key2; break;
default:{key_pin = 1;} break; // 无效索引,视为高电平
}
// ---------- 按键按下/释放处理 ----------
if (key_pin == 0) // 按下(低电平有效)
{
key_hold_timer[keyn]++; // 长按计时递增
key_debounce_timer[keyn] = 0; // 清除释放消抖计时
if (key_state[keyn] == 0) // 之前是释放状态,表示一次新的按下
{
key_state[keyn] = 1;
key_combinations[keyn]++; // 连击次数加1
if (key_combinations[keyn] == 255) key_combinations[keyn] = 0; // 防止长按释放后误判为单击
key_response_delay[keyn] = 0; // 清除连击窗口计时
}
}
else // 释放(高电平)
{
// 释放消抖:连续10次检测到高电平才确认为释放
if (++key_debounce_timer[keyn] >= 10)
{
key_state[keyn] = 0;
key_debounce_timer[keyn] = 0;
key_hold_timer[keyn] = 0; // 清除长按计时
key_response_delay[keyn]++; // 启动连击窗口计时
}
}
// ---------- 长按判断及连击输出 ----------
if (key_hold_timer[keyn] >= 360) // 长按阈值360ms
{
key_combinations[keyn] = 255; // 长按标记
key_nobl_long[keyn] = 1; // 置位长按标志
}
else
{
key_nobl_long[keyn] = 0; // 可注释该行,在按键处理中手动清除长按标志
// 连击窗口结束(90ms内无新按下),输出当前连击次数
if (key_response_delay[keyn] >= 9)
{
key_response_delay[keyn] = 0;
key_mode[keyn] = key_combinations[keyn];
key_combinations[keyn] = 0; // 清空连击计数
}
}
// ---------- 键值快速清零(使key_mode仅保持约1ms)----------
if (key_mode[keyn] != 0)
{
if (++key_res_timer[keyn] > 1)
{
key_res_timer[keyn] = 0;
key_mode[keyn] = 0;
}
}
}
四、应用范例
1.键值查询处理代码
按键处理程序按实际需求移植,请忽略已注释代码。以下为视频演示中按键处理的代码。
(1).h
#ifndef __KEY_MANAGE_H__
#define __KEY_MANAGE_H__
#include <STC8051U.H>
#include <AI_USB.H>
#include <intrins.h>
void Key_Manage(void); //键值处理程序
#endif
(2).C
#include "key_manage.h"
extern u8 xdata key_mode[]; //键值
extern u16 xdata key_hold_timer[]; //按键长按计时
extern u8 xdata key_nobl_long[]; //非阻塞按键长按标志位(用户手动消除)
extern u8 xdata Led_buf; //LED流水灯缓存
extern u16 xdata display_buf1, display_buf2; //数码管显示数据十进制缓存
void Key_Manage(void)
{
//系统按键操作
if(key_mode[0] != 0) display_buf1 = key_mode[0], Display_Led_0123(); //刷新显示按键1连击数
if(key_mode[1] != 0) display_buf2 = key_mode[1], Display_Led_4567(); //刷新显示按键2连击数
//按键1处理
if(key_mode[0] == 1) //单击
{
Led_buf |= 1; //LED0亮灯
}
else if(key_mode[0] == 2) //双击
{
Led_buf |= 2; //LED1亮灯
}
else if(key_mode[0] == 3) //三连击
{
Led_buf |= 4; //LED2亮灯
}
else if(key_mode[0] == 4) //四连击
{
Led_buf |= 8; //LED3亮灯
}
else if(key_mode[0] == 5) //五连击
{
Led_buf |= 16; //LED4亮灯
}
else if(key_mode[0] == 6) //六连击
{
Led_buf |= 32; //LED5亮灯
}
else if(key_mode[0] == 7) //七连击
{
Led_buf |= 64; //LED6亮灯
}
else if(key_mode[0] == 8) //八连击
{
Led_buf |= 128; //LED7亮灯
}
else if(key_nobl_long[0] == 1 && key_hold_timer[0]%160 == 0)//长按连加连减效果
{
Led_buf = _crol_(Led_buf, 1);//LED流水灯循环左移
}
//按键2处理
if(key_mode[1] == 1) //单击
{
Led_buf &= ~1; //LED0熄灭
}
else if(key_mode[1] == 2) //双击
{
Led_buf &= ~2; //LED1熄灭
}
else if(key_mode[1] == 3) //三连击
{
Led_buf &= ~4; //LED2熄灭
}
else if(key_mode[1] == 4) //四连击
{
Led_buf &= ~8; //LED3熄灭
}
else if(key_mode[1] == 5) //五连击
{
Led_buf &= ~16; //LED4熄灭
}
else if(key_mode[1] == 6) //六连击
{
Led_buf &= ~32; //LED5熄灭
}
else if(key_mode[1] == 7) //七连击
{
Led_buf &= ~64; //LED6熄灭
}
else if(key_mode[1] == 8) //八连击
{
Led_buf &= ~128; //LED7熄灭
}
else if(key_nobl_long[1] == 1 && key_hold_timer[1]%160 == 0)//长按连加连减效果
{
Led_buf = _cror_(Led_buf, 1);//LED流水灯循环右移
}
// //按键2处理
// if(key_mode[2] == 1)
// {
// Led_buf = 0; //LDE 全部熄灭
// }
P0 = ~Led_buf; //刷新流水灯
}
五、工程文件(按键控制流水灯视频)
AI8051U试验箱 V1.2 USB下载可用(CPU指令模式32位,IRC=40MHz)
附件:多功能单按键流水灯加数码管演示.rar