标题:
51通用的按键长按单击双击单击读写方式
[打印本页]
作者:
千早爱音爱玩51
时间:
2025-7-3 15:10
标题:
51通用的按键长按单击双击单击读写方式
本代码参考了
https://blog.csdn.net/m0_52596850/article/details/126776765#
,在原代码的基础上按我的代码风格重写了,添加了三击功能。
具体原理在注释中已经给出,通读一遍便能理解,简单的概括就是通过switch在各种不同的状态之间切换实现按键的消抖,长短按识别,相当的巧妙。其中消抖直接使用了定时器中断的间隔进行消抖,我认为这是这个方案最巧思的一点。
如果要复用,直接根据你的按键IO口改变第一行的keyinput就行了,其他的不需要改变,然后在主循环中或者定时器中断调用就可以了。目前为P32低电平为有效按键输入,如有需求可以改为高电平。
#define KEYINPUT P32//按键输入为P32
#define NOKEY 0//无
#define SINGLEKEY 1//单键
#define DOUBLEKEY 2//双键
#define TRIPLEKEY 3//三键
#define LONGKEY 4//长键
#define KEYSTATE0 0
#define KEYSTATE1 1
#define KEYSTATE2 2
#define KEYSTATE3 3
unsigned char KEY_DRIVER(void){
static unsigned char keystate = KEYSTATE0;
static unsigned char keytime = 0;
unsigned char keypress;
unsigned char keyreturn = NOKEY;
keypress = KEYINPUT;//读取P32电平
switch(keystate){
case KEYSTATE0://按键初始状态,按下后转换到消抖与确认态,用定时器中断间隔实现消抖
if(!keypress){//P32==0
keystate = KEYSTATE1;//如果无按键按下就始终返回为NOKEY
}
break;
case KEYSTATE1:
if(!keypress){//P32==0
keytime = 0;
keystate = KEYSTATE2;}//按键仍然处于按下,消抖完成,状态转换到计时
else{
keystate = KEYSTATE0;//低电平持续时间过小,只有一个定时器间隔
} //认为是无效按键,清零状态,实现消抖
break;
case KEYSTATE2:
if(keypress){//P32==1,按键释放,且间隔2个定时器中断,认为是无抖动的按键输入。
keyreturn = SINGLEKEY;//返回单击
keystate = KEYSTATE0;//清空状态
}
else if(++keytime >= 64){//P32=0,继续按下,计时加一个定时器中断间隔时间,
keyreturn = LONGKEY;//在下次定时器中断直接输出为长按,不需要等待
keystate = KEYSTATE3;//进入状态3,等待按键释放
}
break;
case KEYSTATE3://等待按键释放,释放后清空状态
if(keypress){//P32==1,按键已经抬起
keystate = KEYSTATE0;//清空状态
}
break;
}
return keyreturn;
}
//////////////////////////////////////////////////
unsigned char KEY_READ(void){
static unsigned char key1 = KEYSTATE0;
static unsigned char keytime1 = 0;//多次按键计数器
unsigned char keyreturn = NOKEY;
unsigned char keytemp;
keytemp = KEY_DRIVER();//读取按键状态
switch(key1){
case KEYSTATE0:
if(keytemp == SINGLEKEY){
keytime1 = 0;//第一次单击,无返回值,到下个状态判断之后是否有再次单击
key1 = KEYSTATE1;//切换单击
}
else{
keyreturn = keytemp;//对于无键,长按时间返回原事件
}
break;
case KEYSTATE1:
if(keytemp == SINGLEKEY){//再次单击,间隔小于640ms
key1 = KEYSTATE2;//切换到状态3,等待三击
}//不清空计数器,因为要实现总间隔检测
else{
if(++keytime1 >= 32){//在这里实现等待双击
keyreturn = SINGLEKEY;//返回单击
key1 = KEYSTATE0;//清空状态
}
}
break;
case KEYSTATE2:
if(keytemp == SINGLEKEY){//第三次单击,总间隔小于640ms,沿用state1中的计数器
keyreturn = TRIPLEKEY;//输出为三击
key1 = KEYSTATE0;//返回初始状态
}
else{
if(++keytime1 >= 32){//沿用之前的计数器值,继续计数
keyreturn = DOUBLEKEY;//超时,输出双击
key1 = KEYSTATE0;
}
}
}
return keyreturn;
}
下面是测试代码及调用功能示范,长按带点亮led,三击熄灭led,可以用于测试功能,需要手动
bit timer20msok = 0;
void TIMER0_ROUTINE(void) interrupt 1{
timer20msok = 1;
}
unsigned char keyevent = NOKEY;
void main(void){
TH0 = 0XD8;
TL0 = 0XF0;
IE = 0X8F;//允许中断
TR0 = 1;//打开定时器电源
while(1){
if(timer20msok){
timer20msok = 0;
keyevent = KEY_READ();
if(keyevent == LONGKEY){
P30 = 0;
}
else if(keyevent == TRIPLEKEY)
P30 = 1;
}
}
}
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1