×
嵌入式 > 技术百科 > 详情

【单片机笔记】状态机效率地按键扫描、识别及检测方法

发布时间:2020-06-12 发布时间:
|

按键是人机交互最简单也是最廉价的方式之一,要实现一个或者多个按键的有效扫描并处理,这里附上我修改过的代码:


实现的代码主要包含有四个部分:


第一部分:按键的初始化部分


void Key_Configuration(void)

{

return;

}

这里需要根据所使用的IC来做不同的配置方式,我使用的是51内核,在初始化的过程I/O口默认做了准双向若上拉处理,按键低电平有效,所以就没有处理直接跳出去。


第二部分:按键的电平读取


//只读取初次按键电平状态,在状态机中进一步处理

static u8 Key_Read(void)

{

    if(!READ_KEY1)  

return KEY1_PRES;

    if(!READ_KEY2)  

return KEY2_PRES;       

    if(!READ_KEY3)  

return KEY3_PRES;

    if(!READ_KEY4)  

return KEY4_PRES;

    if(!READ_KEY5)  

return KEY5_PRES;


return KEY_NONE;

}

根据使用的具体环境及功能,这里每次读取电平只读取一个有效的电平并且有优先级,由代码可以看出优先级的顺序为:KEY1>KEY2>KEY3>KEY4>KEY5。当然需要使用多少个按键根据项目的需求来定,理论支持多少个独立按键都是可以的。


第三部分:状态机的按键判定部分


//状态机

static u8 Key_Scan(void)

{

static u8 state = 0; //按键初始化状态

static u8 KEY_LAST=0,KEY_NOW=0; //记录两次电平的状态

u8 KEY_VALUE=0;

 

KEY_NOW = Key_Read();//读按键值

   

switch(state)

{

case 0:

{

if(KEY_NOW != KEY_LAST) state = 1; //有按键按下

}break;

case 1: 

{

if(KEY_NOW == KEY_LAST) state = 2; //消斗之后按键有效

else state = 0; //认为误触

}break; 

case 2: //消斗之后

{

if(KEY_NOW == KEY_LAST) //还是按下的状态 

{

  state = 3;

}

else//松开了,短按

{

state = 0; 


KEY_VALUE = KEY_LAST|KEY_SHORT;  //返回键值短按

}

}break;


case 3: //判断长按短按

{

if(KEY_NOW == KEY_LAST) 

{

    static u8 cnt = 0;

if(cnt++ > 120) //1200ms

{

cnt = 0; 

state = 4;

KEY_VALUE = KEY_LAST|KEY_LONG; //返回键值长按

}   

}

else

{

state = 0;

KEY_VALUE = KEY_LAST|KEY_SHORT; //返回键值短按

}

}break;

case 4://长按松手检测

{

if(KEY_NOW != KEY_LAST) 

state = 0;

}break;

}//switch


KEY_LAST = KEY_NOW; //更新

return KEY_VALUE;

}

这部分也是整个的核心代码部分,首先定义了三个静态变量,按键的状态state,当前读取的键值KEY_NOW,上一次的键值KEY_LAST,以及返回的判定后的有效键值KEY_VALUE。接下来一步步研究:


state初始值为0,进入switch和case,每次进入case判断的时间间隔是由第四部分来确定的,这里给出我选用的是10ms


判定0,只要上一次记录的键值和本次读取到的键值不想等,则进入1。这里还有另一个作用,那就是短按的松手检测。这也是没有else的原因,具体如何实现请看状态4。


判定1,上一次和这一次的键值相等,注意case 0后KEY_LAST已经被更新,也就是说10ms后这次读取到的键值还等于上一次的键值,这里我们认为是有效的按下,而并非误触和干扰造成的,这种情况下进入2。否则就返回到0。


判定2,这里是从1过来的,也就是此时的按键键值是有效的,这里还是来判断上次更新的KEY_LAST及这次读取到的新的键值,如果不想等,证明手已经松开(单一按键的情况)。这样就识别成了短按,state重新回到0并返回短按的键值量,在0。如果按键还没有被释放那就有长按的趋势了,进入3。


判定3,这里也很简单,判定键值有没有释放,每次进来就开始计次,10ms进入一次,这里计120次也就是需要1200ms的时间达到条件并且把返回的键值赋值成长按,同时进入4,反之没有达到时间就识别成短按并重新进入0。


判定4,这里代码的作用主要是作为长按的松手检测,道理也很简单,按键没有释放,那肯定历史键值和当前的键值相等并且不为0,等按键释放的后,读取的键值肯定为0,这就跳出了状态4。


第四部分:实体函数及被调用函数


static void KEY1_ShortHander(void)

{

 

}

static void KEY1_LongHander(void)

{

 

}

static void KEY2_ShortHander(void)

{

 

}

static void KEY2_LongHander(void)

{

 

}

static void KEY3_ShortHander(void)

{

 

}

static void KEY3_LongHander(void)

{

 

}

static void KEY4_ShortHander(void)

{

 

}

static void KEY4_LongHander(void)

{

 

}

static void KEY5_ShortHander(void)

{

 

}

static void KEY5_LongHander(void)

{

 

}

 

 

void Key_Hander(void) //按键处理函数

{

u8 KEY_NUM=0;

static u32 LAST=0;

if(Systick_ms-LAST<10) return;

LAST = Systick_ms;


KEY_NUM = Key_Scan();  //按键扫描值

if(KEY_NUM == KEY_NONE) return;


//有按键按下

if(KEY_NUM & KEY_SHORT) //短按

{    

if(KEY_NUM & KEY1_PRES)//KEY1_PRES

{

KEY1_ShortHander();

}

else if(KEY_NUM & KEY2_PRES)//KEY2_PRES

{

KEY2_ShortHander();

}

else if(KEY_NUM & KEY3_PRES)//KEY3_PRES

{

KEY3_ShortHander();

}

else if(KEY_NUM & KEY4_PRES)//KEY4_PRES

{

KEY4_ShortHander();

}

else if(KEY_NUM & KEY5_PRES)//KEY5_PRES

{

KEY5_ShortHander();

}

}

else if(KEY_NUM & KEY_LONG) //长按 

{

if(KEY_NUM & KEY1_PRES)//KEY1_PRES

{

KEY1_LongHander();

}

else if(KEY_NUM & KEY2_PRES)//KEY2_PRES

{

KEY2_LongHander();

}

else if(KEY_NUM & KEY3_PRES)//KEY3_PRES

{

KEY3_LongHander();

}

else if(KEY_NUM & KEY4_PRES)//KEY4_PRES

{

KEY4_LongHander();

}

else if(KEY_NUM & KEY5_PRES)//KEY5_PRES

{

KEY5_LongHander();

}

}

}

这部分代码是比较清晰的,通过前面3部分的分析,在这里调用前3部分的得到键值的结果,然后判定结果做相应的函数和功能处理,这里给的直接是函数体,用户可以直接在函数体里面添加响应的代码。但更有效的方式应该是把执行函数换成标记变量,这样程序执行会更加有条理并且不错误的占用时间片。


最后附上头文件:


#ifndef __FY_KEY_H

#define __FY_KEY_H

 

#include "fy_includes.h"

 

#define READ_KEY1 P30

#define READ_KEY2 P14

#define READ_KEY3 P13

#define READ_KEY4 P10

//#define READ_KEY5 P17

 

#define KEY1_PRES 0x01

#define KEY2_PRES 0x02

#define KEY3_PRES 0x04

#define KEY4_PRES 0x08

#define KEY5_PRES 0x10

 

#define KEY_SHORT  0x40

#define KEY_LONG   0x80

#define KEY_NONE      0

 

#define HOT_KEY_PRES KEY1_PRES

#define PWR_KEY_PRES KEY2_PRES

#define H2_KEY_PRES KEY3_PRES

#define MP3_KEY_PRES KEY4_PRES

 

void Key_Configuration(void);

void Key_Hander(void);

 

#endif

 

/*********************************************END OF FILE**********************************************/



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
NTMD6N03R2G的技术参数