TA的每日心情 | 开心 昨天 22:03 |
---|
签到天数: 110 天 [LV.6]常住居民II
高级会员
- 积分
- 599
|
楼主 |
发表于 2024-4-18 23:28:36
|
显示全部楼层
按键状态机
按键按状态来分:按键按下、按键松开
按电平来分:低电平、高电平
其中按键按下时按低电平时长分为5种状态:
<30 ms 消抖
=30 ms 单击
<3000ms 单击结束
=3000ms 长按3秒
>3000ms 长按结束
可以看到时间是在一直累加的,当到达其中1个时间点时我们就执行这一步的程序,比如在30ms以内,我们认为按键是消抖状态,不进行任何操作,正好等于30ms时,执行单击程序,大于30ms小于3000ms这个区间是认为在长按过程,正好等于3000ms时认为达到长按的判定。超过3000ms,可以判定为长按结束。
这里引进了一个状态机的概念,状态机教程里说的是条件转移,即这个变量是按键按下后累计的时长ms,随着时间轴的进行,等于或满足哪个条件时就进行运行状态转移,就比如上面的消抖,单击,长按,他是要满足时间条件后才能运行对应的步。
下面分析下程序:
计时数组 u16 count[8] = {0,0,0,0,0,0,0,0}; //一个8位元素的数组,每个元素对应1个P口的8位IO口,用于记录每个按键按下后的时间,数据类型要为16位整型。比如count[0]用于记录P3.0按下的时长
u8 LastState = 0; //8位整型变量用于记录上次哪个按键按下
检查按键状态函数,放在主程序中每10ms执行一次
void KEY_Deal(void)
{
u8 i = 0;
for(i=0; i<8; i++) //循环8次,用于判定P30~P37有无按键按下
{
if(~P3 & (1<<i)) //假设i= 0时,P30有按下为低电平,~P30 = ^(1111 1110)=0000 0001,1<<0 = 0000 0001,两者相与后为0000 0001,如果持续有P30按下,这个条件是一直满足的。
{
if(count[i]<60000) //当有按键按下时,并且count[i]的值小于60000时,对应的count[i]就会一直进行累加,用于记录对应的按键按下时间。
count[i]++;
}
else //当按键松开后,执行else里的语句
{
if(count[i]>0) //当按键松开时,此时count[i]是大于0的,会把按键状态更新给LastState,LastState用来记录哪个按键按下了
{
LastState |= (1<<i); //这个按键有按下过,假设是P35,i=5,LastState = 0000 0000,1<<5 = 0010 0000,更新后LastState = 0010 0000
}
else
{
LastState &= ~(1<<i); //两者相与,比如上面是P35按下,在i=0到4时,0010 0000 & 1111 1110,还是保持0010 0000不变,只有在i=5时才会将LastState清零,
} //所以在这段函数运行时,会记录下哪个按键有按下,按下了多长时间,并将按键状态传递给LastState。
count[i] = 0; //计数变量count[i]清零
}
}
}
读取指定的按键 状态
/**************变量声明**************/
#define KEY_NOPRESS 0 // 按键未按下
#define KEY_FILCKER 1 // 消抖
#define KEY_PRESS 2 // 单击
#define KEY_PRESSOVER 3 // 单击结束
#define KEY_LONGPRESS 4 // 长按3秒
#define KEY_LONGOVER 5 // 长按结束
#define KEY_RELAX 6 // 按键松开
u8 KEY_Readstate(u8 keynum) //keynum = 0~7;10ms执行一次
{
if(coount[keynum]>0) //按键有按下时,count[i]有计数,各种状态处理
{
if(count[keynum]<3) //1~29ms按键消抖
{ return KEY_FILCKER; }
else if (count[keynum]==3) //超过30ms,返回单击
{ return KEY_PRESS; }
else if (count[keynum]<300) //31~2999ms,返回单击结束
{ return KEY_PRESSOVER; }
else if (count[keynum]==300) //正好3000ms时,返回长按
{ return KEY_LONGPRESS; }
else // 大于3000ms,返回长按结束
{ return KEY_LONGOVER; }
}
else //按键松开后,进行判断按键之前有无按下过
{
if(LastState & (1<<keynum)) //假设之前P3.5有按下过,LastState会记忆P3.5的状态值,当P3.5再次按下,等其松开后就会返回”KEY_RELAX“,表示这个键上次有按下过
{ return KEY_RELAX; }
else
{ return KEY_NOPRESS; } //按键之前没有被按下过,比如上次是按的P3.5,这次是按的P3.0,两者不相等,就会返回这里语句的值。
}
}
以上,是对第13集多任务处理中,按键状态机的程序解读,LastState这个变量是用于检查按键检查函数中用于区分”按键松开“与”没有按下“。
通过计数数组count[i]来统计按键按下时长,根据count[i]的值来判定程序应该执行到哪一步,满足了某个条件就进行转移,这就是状态机,这也是程序中一种常用用法。后面还会经常运用到。
|
|