独立按键的长按、短按与双击的判断,运用了状态的分析,短按和长按的区别为按键按下的时间不同所以从短按状态到长按状态的条件为按键按下的时间超过设定的长按时间,而二者的共同点为:按键只按下一次。
而双击与前者的不同在于双击按键按下了两次。
我在这先识别【单击】和【长按】然后在有一次按键按下的基础下,且必须是【单击】的情况下再去在规定的间隔内检测【双击】。
由于在我们按键按下的时候会有抖动,实际中会有这种情况:按键松开了但没松完,或者按键没有完全按下,只是很轻的按单片机也会判断为【单击】,所以我们在这需要设置判断按键是否有效的状态。
所以我们在判断【单击】和【长按】时设置四个状态:
一、基础状态:无按键按下的状态,或者等待按键按下的状态。
二、判断有效按键状态:判断按键按下的时间是否超过预定的有效按键时间
三、判断是否是【长按】状态:判断按键按下的时间是否超过长按预定时间
四、等待按键松开状态:长按效果实现状态,如果按键在长按状态一直按下,则一直实行长按功能,松开就回到基础状态。
#include "key.h"
// ----------------------------------- key_driver --------------------------
unsigned char key_driver (unsigned char KEY_INPUT)
{
static unsigned char key_state=0;
static unsigned int key_time=0; //key_time 的范围:0~3误触 >3单击 >300长按
unsigned char key_press,key_return;
key_return = KEY_NONE; //清除返回键值,用来达到数据更新和无按键按下的效果
key_press = KEY_INPUT; //读取当前键值
switch(key_state)
{
case KEY_STATE_0: //状态0:判断是否有按键按下
if(!key_press) //如果有按键按下
{
key_time = 0; //清零时间间隔计数
key_state = KEY_STATE_1; //然后进入状态1
}
break;
case KEY_STATE_1: //按键状态1:软件消抖(确定按键是否有 效,而不是误触)。
按键有效的定义:按键持续按下超过规定时间
if(!key_press)
{
key_time++; //用定时器10ms调用一次key_driver函数,key_time+1表示10ms
if(key_time >= SINGLE_KEY_TIME) //消抖时间为:SINGLE_KEY_TIME*10ms = 30ms;
{
key_state = KEY_STATE_2;//如果按键时间超过消抖时间,即判断为按下的按键有效,并进入状态2
}
}
else
{
key_state = KEY_STATE_0;//如果按键事件没有超过,判定为误触,按键无效,返回状态0,等待按键按下
}
break;
case KEY_STATE_2: //按键状态2:判断按键有效的种类:1.单击。
2、长按
if(key_press) //如果按键在设定的长按时间内释放,则判定为单击
{
key_return = KEY_SHORT;//如果在规定时间内释放则判定为单击
key_state = KEY_STATE_0;//去状态0:等待按键按下
}
else
{
key_time++;
if(key_time >= LONG_KEY_TIME)//如果按键事件超过设定时间(LONG_KEY_TIME*10ms=300*10ms=3000ms),则判断为长按
{
key_return = KEY_LONG; //返回 有效键值:长按
key_state = KEY_STATE_3;//去状态3:等待按键释放
}
}
break;
case KEY_STATE_3:
if(key_press)
{
key_state = KEY_STATE_0;
}
else
{
key_return = KEY_LONG;
}
break;
default:
key_state = KEY_STATE_0;
break;
}
return key_return;
}
// ----------------------------------- key_read --------------------------------
unsigned char key_read(unsigned char KEY_INPUT)
{
static unsigned char key_state1 = 0, key_time1 = 0;
unsigned char key_return, key_temp;
key_return = KEY_NONE; //清零返回按键值
key_temp = key_driver(KEY_INPUT); //读取按键值
switch(key_state1)
{
case KEY_STATE_0: //按键状态0:等待有效按键(通过key_driver返回的有效键值)
if(key_temp == KEY_SHORT) //如果是【单击】,不马上返回单击按键值,先进入按键状态1,判断是否为【双击】
{
key_time1 = 0; //清零计时
key_state1 = KEY_STATE_1; //进入状态1:判断是否为双击
}
else
{
key_return = key_temp;
}
break;
case KEY_STATE_1: //按键状态1:判断是否有【双击】
//等待按键再次按下
if(key_temp == KEY_SHORT) //有【单击】后,如果在设定时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有【单击】,则为【双击】,先进入状态2,判断是否有【三击】
{
key_return = KEY_DOUBLE; //返回有效键值:【双击】
key_state1 = KEY_STATE_0; //返回按键状态0,等待有新的按键
}
else // // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]
{
key_time1++;
if(key_time1 >= KEY_INTERVAL)
{
key_return = KEY_SHORT;
key_state1 = KEY_STATE_0; //返回按键状态0:等待新的有效按键
}
}
break;
default: // 特殊情况:key_state是其他值得情况,清零key_state。
这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
key_state1 = KEY_STATE_0;
break;
}
return key_return;
}
在判断【双击】的时候,基础状态就是单击状态了,那么只需要在规定时间内按键再次按下就可以进入双击状态。
但是这一个代码也有他的缺点:这个代码因为用了static变量,所以只能用于扫描一个按键。 如果扫描多个按键的话,就会出现state混乱的现象。 例如当扫描完按键1时,进行扫描按键2,如果按键2没有按下则state就扫描为无按键按下的状态,因为用的是一个函数去扫描。
当然我也试着去改一下这个代码,去实现多按键的单击双击长按功能,我添加了两个数组分别来存储四个键的状态和按下时间
#include "key.h"
unsigned char key_state[]={0,0,0,0};
unsigned int key_time[]={0,0,0,0};//key_time 的范围:0~3误触 >3单击 >300长按
unsigned char key_state1[]={0,0,0,0};
unsigned char key_time1[]={0,0,0,0};
// ----------------------------------- key_driver --------------------------
unsigned char key_driver (unsigned char i)
{
unsigned char key_press,key_return;
key_return = KEY_NONE; //清除返回键值,用来达到数据更新和无按键按下的效果
switch(i)
{
case 0: key_press = KEY1;break; //读取当前键值
case 1: key_press = KEY2;break;
case 2: key_press = KEY3;break;
case 3: key_press = KEY4;break;
}
switch(key_state[i])
{
case KEY_STATE_0: //状态0:判断是否有按键按下
if(!key_press) //如果有按键按下
{
key_time[i] = 0; //清零时间间隔计数
key_state[i] = KEY_STATE_1; //然后进入状态1
}
break;
case KEY_STATE_1: //按键状态1:软件消抖(确定按键是否有效,而不是误触)。
按键有效的定义:按键持续按下超过规定时间
if(!key_press)
{
key_time[i]+=speed; //用定时器10ms调用一次key_driver函数,key_time+1表示10ms
if(key_time[i] >= SINGLE_KEY_TIME) //消抖时间为:SINGLE_KEY_TIME*10ms = 30ms;
{
key_state[i] = KEY_STATE_2;//如果按键时间超过消抖时间,即判断为按下的按键有效,并进入状态2
}
}
else
{
key_state[i] = KEY_STATE_0;//如果按键事件没有超过,判定为误触,按键无效,返回状态0,等待按键按下
}
break;
case KEY_STATE_2: //按键状态2:判断按键有效的种类:1.单击。
2、长按
if(key_press) //如果按键在设定的长按时间内释放,则判定为单击
{
key_return = KEY_SHORT;//如果在规定时间内释放则判定为单击
key_state[i] = KEY_STATE_0;//去状态0:等待按键按下
}
else
{
key_time[i]+=speed;
if(key_time[i] >= LONG_KEY_TIME)//如果按键事件超过设定时间(LONG_KEY_TIME*10ms=300*10ms=3000ms),则判断为长按
{
key_return = KEY_LONG; //返回 有效键值:长按
key_state[i] = KEY_STATE_3;//去状态3:等待按键释放
}
}
break;
case KEY_STATE_3:
if(key_press)
{
key_state[i] = KEY_STATE_0;
}
else
{
key_return = KEY_LONG;
}
break;
default:
key_state[i] = KEY_STATE_0;
break;
}
return key_return;
}
// ----------------------------------- key_read --------------------------------
unsigned char key_read(unsigned char i)
{
unsigned char key_return, key_temp;
key_return = KEY_NONE; //清零返回按键值
key_temp = key_driver(i); //读取按键值
switch(key_state1[i])
{
case KEY_STATE_0: //按键状态0:等待有效按键(通过key_driver返回的有效键值)
if(key_temp == KEY_SHORT) //如果是【单击】,不马上返回单击按键值,先进入按键状态1,判断是否为【双击】
{
key_time1[i] = 0; //清零计时
key_state1[i] = KEY_STATE_1; //进入状态1:判断是否为双击
}
else
{
key_return = key_temp;
}
break;
case KEY_STATE_1: //按键状态1:判断是否有【双击】
//等待按键再次按下
if(key_temp == KEY_SHORT) //有【单击】后,如果在设定时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有【单击】,则为【双击】,先进入状态2,判断是否有【三击】
{
key_return = KEY_DOUBLE; //返回有效键值:【双击】
key_state1[i] = KEY_STATE_0; //返回按键状态0,等待有新的按键
}
else // // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]
{
key_time1[i]+=speed;
if(key_time1[i] >= KEY_INTERVAL)
{
key_return = KEY_SHORT;
key_state1[i] = KEY_STATE_0; //返回按键状态0:等待新的有效按键
}
}
break;
default: // 特殊情况:key_state是其他值得情况,清零key_state。
这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
key_state1[i] = KEY_STATE_0;
break;
}
return key_return;
}
#include
#include "key.h"
#include "timer0.h"
#include "Delay.h"
unsigned char g_flag_10ms_key; //10ms 计时标志
unsigned char g_Keymode,Keynum; //按键值
void main()
{
time0_init();
time0_start();
while(1)
{
if(g_flag_10ms_key) //等待10ms,定时完成
{
g_flag_10ms_key = 0; //清零10ms定时标志
g_Keymode=key_read(0);
switch(g_Keymode)
{
case KEY_SHORT:P2_0=0;break;
case KEY_DOUBLE:P2_0=1;break;
case KEY_LONG:P2_0=!P2_0;Delay(100);break;
}
}
}
}
void Timer0_Routine () interrupt 1
{
static unsigned char count=0;
TL0=0x18;
TH0=0xfc;
count++;
if(count>=10)
{
g_flag_10ms_key=1;
count=0;
}
}
但是我发现没有现象,可能是我的方法不行,希望大家有些好的见解,帮忙修改一下,可以有偿!!!
下面是我写的多按键的单击和长按功能:
#include
#include "typedef.h"
#include "keytest.h"
//定义并初始化全局变量
uint8 keyFlag1 = 0; //这四个为按键计时数
uint8 keyFlag2 = 0;
uint8 keyFlag3 = 0;
uint8 keyFlag4 = 0;
uint8 KeyShort[4]= 0; //短按状态
uint8 KeyLong[4]= 0; //长按状态
uint8 KeyMulti[4] = 0; //连发状态
//定时器0初始化程序
void time0Init(){
TMOD = 0x01; //定时器0工作方式1
TH0 = (65536-10000)/256; //定时10ms
TL0 = (65536-10000)%256;
//打开中断开关
EA = 1;
ET0 = 1;
//启动定时器0
TR0 = 1;
}
//定时器0中断程序
void time0() interrupt 1{
TH0 = (65536-10000)/256; //定时10ms
TL0 = (65536-10000)%256;
//在按键正在按下的情况下
if (key1 == 0)
keyFlag1++;
if (key2 == 0)
keyFlag2++;
if (key3 == 0)
keyFlag3++;
if (key4 == 0)
keyFlag4++;
}
uint8 keyScan4(){
uint16 key=0; //按键返回值
//第一按键
if (key1 == 0){ //如果key1正在按下
if (keyFlag1 >= SwDelay && keyFlag1 < ShotToLong) //软件延时0.1s(消抖+连发间隔)
KeyShort[0] = 1; //短按状态,KeyShort置1
else if (keyFlag1 >= ShotToLong && keyFlag1 =LongToMul){ //如果按键超过1s,进入连发状态
KeyMulti[0] = 1; //KeyMulti置1
KeyLong[0] = 0;
}
}
//如果key1已释放
if (key1 != 0 && KeyShort[0] == 1){
if (KeyLong[0] == 1 && KeyMulti[0] == 0)
key = 12; //第一按键长按状态
else
key = 11; //第一按键短按状态
keyFlag1 = KeyShort[0] = KeyLong[0] = 0;
}
//进入连发状态
if (KeyMulti[0] == 1 && KeyShort[0] == 1){
key = 11;
keyFlag1 = KeyShort[0] = 0;
}
//key1未按下或已释放,则给连发状态置0
if (key1 != 0)
KeyMulti[0] = 0;
/
//第二按键
if (key2 == 0){ //如果key2正在按下
if (keyFlag2 >= SwDelay && keyFlag2 < ShotToLong) //软件延时0.1s(消抖+连发间隔)
KeyShort[1] = 1; //短按状态,KeyShort置1
else if (keyFlag2 >= ShotToLong && keyFlag2 =LongToMul){ //如果按键超过1s,进入连发状态
KeyMulti[1] = 1; //KeyMulti置1
KeyLong[1] = 0;
}
}
//如果key2已释放
if (key2 != 0 && KeyShort[1] == 1){
if (KeyLong[1] == 1 && KeyMulti[1] == 0)
key = 22; //第一按键长按状态
else
key = 21; //第一按键短按状态
keyFlag2 = KeyShort[1] = KeyLong[1] = 0;
}
//进入连发状态
if (KeyMulti[1] == 1 && KeyShort[1] == 1){
key = 21;
keyFlag2 = KeyShort[1] = 0;
}
//key2未按下或已释放,则给连发状态置0
if (key2 != 0)
KeyMulti[1] = 0;
/
//第三按键
if (key3 == 0){ //如果key3正在按下
if (keyFlag3 >= SwDelay && keyFlag3 < ShotToLong) //软件延时0.1s(消抖+连发间隔)
KeyShort[2] = 1; //短按状态,KeyShort置1
else if (keyFlag3 >= ShotToLong && keyFlag3 =LongToMul){ //如果按键超过1s,进入连发状态
KeyMulti[2] = 1; //KeyMulti置1
KeyLong[2] = 0;
}
}
//如果key3已释放
if (key3 != 0 && KeyShort[2] == 1){
if (KeyLong[2] == 1 && KeyMulti[2] == 0)
key = 32; //第一按键长按状态
else
key = 31; //第一按键短按状态
keyFlag3 = KeyShort[2] = KeyLong[2] = 0;
}
//进入连发状态
if (KeyMulti[2] == 1 && KeyShort[2] == 1){
key = 31;
keyFlag3 = KeyShort[2] = 0;
}
//key3未按下或已释放,则给连发状态置0
if (key3 != 0)
KeyMulti[2] = 0;
/
//第四按键
if (key4 == 0){ //如果key4正在按下
if (keyFlag4 >= SwDelay && keyFlag4 < ShotToLong) //软件延时0.1s(消抖+连发间隔)
KeyShort[3] = 1; //短按状态,KeyShort置1
else if (keyFlag4 >= ShotToLong && keyFlag4 =LongToMul){ //如果按键超过1s,进入连发状态
KeyMulti[3] = 1; //KeyMulti置1
KeyLong[3] = 0;
}
}
//如果key4已释放
if (key4 != 0 && KeyShort[3] == 1){
if (KeyLong[3] == 1 && KeyMulti[3] == 0)
key = 42; //第一按键长按状态
else
key = 41; //第一按键短按状态
keyFlag4 = KeyShort[3] = KeyLong[3] = 0;
}
//进入连发状态
if (KeyMulti[3] == 1 && KeyShort[3] == 1){
key = 41;
keyFlag4 = KeyShort[3] = 0;
}
//key4未按下或已释放,则给连发状态置0
if (key4 != 0)
KeyMulti[3] = 0;
/
return key;
}
如果想让长按的功能实现速度增快,只需要将SwDelay减低就可以。
希望有大佬可以指点一下那个状态代码如何实现多按键功能!有偿!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)