一、文章概述
- 本文章将介绍基于蓝桥杯单片机实现独立按键的单击、长按、双击状态检测。主要思路为主函数不断扫描按键状态,若有按键按下,记录从按键按下到按键松开所需时间,如果时间超过1s即为长按。如果时间小于500ms则开启双击检测,启动双击间隔时间计时,如果经过230ms还没有按键按下则为单击,如果在230ms内检测到按键按下返回双击状态。
- 代码实现功能:蓝桥杯开发板跳帽接到独立按键时可支持键盘第一列四个独立按键的单击、长按、双击状态检测(采用switch语句实现多按键同时检测)。
二、代码解析
-
separate_key.c
#include "separate_key.h"
sbit S7=P3^0; //按键引脚定义
sbit S6=P3^1;
//================按键KEY7变量定义===================//
volatile bit KeyTimerState=0; //控制定时器是否开始计时按键按下时间
volatile bit KeyDoubleTimerState=0; //控制定时器是否开始计时双击间隔时间
volatile u16 PressTime=0; //按键按下时间
volatile u16 PressDoubleTime=0; //双击间隔时间
volatile bit PressState=1; //判断是否为误触
volatile bit KeyDoubleCount=0; //双击标志位,可防止双击检测过程循环执行时重复执行判断按键初次按下是否误触和第一次按下是否长按
//================按键KEY6变量定义===================//
volatile bit KeyTimerState1=0; //控制定时器是否开始计时按键按下时间
volatile bit KeyDoubleTimerState1=0; //控制定时器是否开始计时双击间隔时间
volatile u16 PressTime1=0; //按键按下时间
volatile u16 PressDoubleTime1=0; //双击间隔时间
volatile bit PressState1=1; //判断是否为误触
volatile bit KeyDoubleCount1=0; //双击标志位
extern void SmgDisplay(void);
void Delay2ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 24;
j = 85;
do
{
while (--j);
} while (--i);
}
void Timer0Init(void) //2毫秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x40; //设置定时初始值
TH0 = 0xA2; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
void Timer0(void) interrupt 1 //定时器0中断服务函数中实现按键按下时间、双击间隔时间和数码管显示
{
if(KeyTimerState==1)
PressTime++;
else
PressTime=0;
if(KeyDoubleTimerState==1)
PressDoubleTime++;
else
PressDoubleTime=0;
if(KeyTimerState1==1)
PressTime1++;
else
PressTime1=0;
if(KeyDoubleTimerState1==1)
PressDoubleTime1++;
else
PressDoubleTime1=0;
SmgDisplay();
}
u8 KeyScan(u8 key) //switch函数内部(KEY7和KEY6)即(case0和case1)的代码实现逻辑相同,以此类推将case后代码复制一次即可增加对一个按键的检测
{
switch(key)
{
case 0:if((S7==0)&&(KeyTimerState==0)&&(KeyDoubleCount==0))
{
Delay2ms(); //消抖
if(S7==0)
{
KeyTimerState=1; //开始计算按下时长
PressState=0; //按键按下标志
}
else
KeyTimerState=0;
}
if((PressState==0)&&(S7==1)&&(KeyDoubleCount==0)) //判断按键松开时间
{
if(PressTime<250) //500ms内松开为短按
{
KeyDoubleCount=1; //进入双击检测
PressState=1;
KeyTimerState=0;
KeyDoubleTimerState=1; //双击标志位置1,开始进入双击检测
}
if(PressTime>500) //超过500ms为长按
{
KeyTimerState=0;
PressState=1;
return KEY7_LONG;
}
}
if((KeyDoubleCount==1)&&(PressDoubleTime<115)&&(S7==0)) //如果双击间隔时间小于
230ms时有按键按下为双击
{
Delay2ms();
if(S7==0)
{
KeyDoubleCount=0;
KeyDoubleTimerState=0;
while(S7==0);
return KEY7_DOUBLE;
}
}
if(PressDoubleTime>115) //双击间隔时间超过230ms仍无按键按下为单击
{
KeyDoubleCount=0;
KeyDoubleTimerState=0;
return KEY7_SHORT;
}
break;
case 1:if((S6==0)&&(KeyTimerState1==0)&&(KeyDoubleCount1==0))
{
Delay2ms(); //消抖
if(S6==0)
{
KeyTimerState1=1; //开始计算按下时长
PressState1=0;
}
else
KeyTimerState1=0;
}
if((PressState1==0)&&(S6==1)&&(KeyDoubleCount1==0)) //判断按键松开时间
{
if(PressTime1<250) //500ms内松开为短按
{
KeyDoubleCount1=1; //进入双击检测
PressState1=1;
KeyTimerState1=0;
KeyDoubleTimerState1=1;
}
if(PressTime1>500)
{
KeyTimerState1=0;
PressState1=1;
return KEY6_LONG;
}
}
if((KeyDoubleCount1==1)&&(PressDoubleTime1<115)&&(S6==0))
{
Delay2ms();
if(S6==0)
{
KeyDoubleCount1=0;
KeyDoubleTimerState1=0;
while(S6==0);
return KEY6_DOUBLE;
}
}
if(PressDoubleTime1>115)
{
KeyDoubleCount1=0;
KeyDoubleTimerState1=0;
return KEY6_SHORT;
}
break;
}
return KEY_NO;
}
-
separate.h
#ifndef separate_key_H
#define separate_key_H
#include "stc15.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
enum KeyNum //按键键值
{
KEY7=0,
KEY6,
KEY5,
KEY4
};
enum KeyState //按键状态
{
KEY7_SHORT=0,
KEY7_LONG,
KEY7_DOUBLE,
KEY6_SHORT,
KEY6_LONG,
KEY6_DOUBLE,
KEY_NO
};
void Timer0Init(void);
u8 KeyScan(u8 key);
#endif
-
main.c
#include "stc15.h"
#include "intrins.h"
#include "separate_key.h"
#define HC138(x) P2=(P2&0x1F)|(x<<5)
u8 code smg_dm[]={0xc0, 0xf9, 0xa4, 0xb0,
0x99, 0x92, 0x82,0xf8,
0x80, 0x90,0xff};
u8 smg_num[]={10,10,10,10,10,10,10,10};
u8 com=0;
u8 KEY7_STATE=KEY_NO;
u8 KEY6_STATE=KEY_NO;
void SmgDisplay(void);
void Init(u8 i)
{
P2&=0x1F;
switch(i)
{
case 4:P0=0xFF;HC138(i);P2&=0x1F;P0=0x00;break;
case 5:HC138(i);break;
case 6:HC138(i);break;
case 7:P2&=0x1F;P0=0xFF;HC138(i);break;
}
P2&=0x1F;
}
void main(void)
{
Init(4);
Init(5);
Init(6);
Init(7);
Timer0Init();
while(1)
{
KEY7_STATE=KeyScan(KEY7); //读取按键状态
KEY6_STATE=KeyScan(KEY6);
//采用数码管第零位显示数值来演示按键状态
if(KEY7_STATE==KEY7_SHORT)
{
smg_num[0]=0;
}
if(KEY7_STATE==KEY7_LONG)
{
smg_num[0]=1;
}
if(KEY7_STATE==KEY7_DOUBLE)
{
smg_num[0]=2;
}
if(KEY6_STATE==KEY6_SHORT)
{
smg_num[0]=3;
}
if(KEY6_STATE==KEY6_LONG)
{
smg_num[0]=4;
}
if(KEY6_STATE==KEY6_DOUBLE)
{
smg_num[0]=5;
}
}
}
void SmgDisplay(void)
{
P0=0xFF;
HC138(6);
P0=(1<<com);
HC138(7);
P0=0xFF;
P0=smg_dm[smg_num[com]];
(com==7)?(com=0):(com++);
}
三:总结
上面所提供的代码可以实现对独立按键的单击、长按以及双击状态的检测,但其实时性和准确度仍有待提升,最后希望大家能从中有所收获,如有更好写法欢迎大家交流讨论,让我们共同学习进步!