按键是人机交互最简单也是最廉价的方式之一,要实现一个或者多个按键的有效扫描并处理,这里附上我修改过的代码:
实现的代码主要包含有四个部分:
第一部分:按键的初始化部分
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**********************************************/By Urien 2018年3月19日 10:47:36