功能有如下几个方面:
1、多键扫描处理,提高处理速度
2、支持长按处理(单次触发或一直触发)
3、支持按下、弹起、按住、松开、长按的事件
4、项目中只需要修改扫描键值,然后在各事件中判断对应键值(单键或多键)
主要编程思路如下:
1、变量的说明
//长按键的时长
#define longkey_times 2000
//长按单次模式定义,如果要长按时一直执行,请注释下一行
#define LONG_PROCESS_ONCE
uint8_t KEY_PRESS; //当前按下的键值
uint8_t KEY_NOT_PRESS; //当前未被按的键值
uint8_t KEY_LAST; //上一次的键值
uint8_t KEY_LONG; //长按的键值
uint8_t KEY_DOWN; //按下的键值
uint8_t KEY_UP; //弹起的键值
uint8_t KEY_UP_NL; //弹起的键值不带长按键
uint32_t KEY_TICKS; //按键时间,用于长按计时
2、按键相关函数说明
//按键处理程序
void JUDGE_KEY(bool SINGLE_KEY); //键值扫描及逻辑处理
void KEY_LONG_PROCESS(void); //长按事件
void KEY_PRESS_PROCESS(void); //按住状态事件
void KEY_NOT_PRESS_PROCESS(void); //松开状态事件
void KEY_DOWN_PROCESS(void); //按下事件
void KEY_UP_PROCESS(void); //弹起事件
3、按键扫描及逻辑处理思路
//bool SINGLE_KEY 防抖开关,True时打开
void JUDGE_KEY(bool SINGLE_KEY)
{
uint8_t TEMP_KEY; //临时的键值缓存
TEMP_KEY = PIND & 0x0C; //批量扫描IO,并生成键值,用户需结合项目自已修改,PIND
//此处表示PD0-7的端口,不同单片机不一样,0x0C只取出
//PD2 PD3的值
TEMP_KEY ^= 0x0C; //此处主要是把键值取反,如果你的按键是低电平触发的话
//如果你的按键是高电平触发,则删除此行,不需要取反
if(TEMP_KEY > 0) //键值大于0,表示有按键按着
{
delay(10); //防抖延时
//以下再一次批量扫描键值
KEY_PRESS = PIND & 0x0C;
KEY_PRESS ^= 0x0C;
//如果防抖开关有效且两次键值不一致,返回不处理
if(TEMP_KEY!=KEY_PRESS && SINGLE_KEY)
{
return;
}
}
else //无按键动作,当前按下的键值=0
{
KEY_PRESS=0;
}
//核心按键逻辑判断
KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS; //按下的键值
KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//弹起的键值(包含长按键)
KEY_UP_NL=(~KEY_LONG) & KEY_UP; //弹起的键值(不包含长按键)
KEY_NOT_PRESS=~KEY_PRESS; //未按的键状态值
if(KEY_LONG & KEY_UP)
{
KEY_LONG=0;
}
if(KEY_PRESS > 0) //当前有按键值按下
{
if(KEY_LAST & KEY_PRESS) //如果当前的值与上次按下的值有相同的地方
//表示有键一直按着,否则可能只是切换了其他按键
{
//millis()函数是Arduino的开机时间毫秒计数,其他单片机自己实现
if(millis() - KEY_TICKS > longkey_times) //按键时间大于长按时间
{
KEY_LONG =KEY_LAST & KEY_PRESS; //长按键值等于一直按住的值
KEY_LONG_PROCESS(); //长按键处理
#ifdef LONG_PROCESS_ONCE //如果是长按单次处理
KEY_TICKS=millis(); //更新长按时间标记,避免进入长按判断
#endif
}
}
else
{
KEY_TICKS=millis(); //切换了其他键,更新长按时间标记,避免进入长按判断
}
}
else //当前无按键按下
{
KEY_TICKS=millis(); //更新长按时间标记,避免进入长按判断
}
if(KEY_UP > 0) //如果有弹起的按键值
{
KEY_UP_PROCESS(); //按键弹起时处理
KEY_UP = 0; //复位弹起的键值
}
if(KEY_DOWN > 0)
{
KEY_DOWN_PROCESS(); //按键按下时处理
}
if(KEY_PRESS > 0)
{
KEY_PRESS_PROCESS(); //按键按着状态处理
}
if(KEY_NOT_PRESS)
{
KEY_NOT_PRESS_PROCESS(); //按键弹起状态处理
}
KEY_LAST=KEY_PRESS; //更新上一次的键值
}
4、按键逻辑处理算法详解 4.1首次按下的键,先用异或^进行上次扫描键值和本次扫描键值计算,取得不一样的键位,不一样的键位和本次扫描键位相同,则表示首次按下。 如 0000 0010表示上次扫描的键,第1位是按下的状态 0000 0110 表示本次扫描的键,第1位和第2位是按下的, 我们要算出第2位是首次按下,则 0000 0010 ^ 0000 0110=0000 0100 0000 0100 & 0000 0110=0000 0100
又如 000 0010表示上次扫描的键,第1位是按下的 0000 0100表示本次扫描的键,第2位是按下的,第1位已经松开 我们要算出第2位是首次按下,则 0000 0010 ^ 0000 0100=0000 0110 0000 0110 & 0000 0100=0000 0100 (所以与本次扫描的键值与,可以得到首次按下的键值) KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS; //按下的键值
4.2弹起的键值 与按下的原理一样,不同的是要和上次扫描的键值相与 如 0000 0010表示上次扫描的键,第1位是按下的状态 0000 0100 表示本次扫描的键,第2位是按下的, 我们要算出第1位是弹起,则 0000 0010 ^ 0000 0100=0000 0110 0000 0110 & 0000 0010=0000 0010
KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//弹起的键值(包含长按键) 4.3长按键一般单独处理,弹起时如果要排除,避免多次触发事件,需要计算出 不包含长按键的键值,用如下公式 KEY_UP_NL=(~KEY_LONG) & KEY_UP; //弹起的键值(不包含长按键) 4.4 长按键的计算逻辑,见程序注释 5、如何使用
5.1 设置好长按的时间 #define longkey_times 2000 //这里表示2秒 5.2 修改扫描键值 TEMP_KEY = PIND & 0x0C; //批量扫描IO,并生成键值,用户需结合项目自已修改,PIND //此处表示PD0-7的端口,不同单片机不一样,0x0C只取出 PD2 PD3的值 TEMP_KEY ^= 0x0C; //此处主要是把键值取反,如果你的按键是低电平触发的话 //还有一处地方也要一起改 KEY_PRESS = PIND & 0x0C; KEY_PRESS ^= 0x0C;
注意:51或其他单片机中,如果按键不在同一序列,比如P01 P03 P14 P16,则可以如下设置 TEMP_KEY = P0 & 0x0A; //取出 P01 P03 TEMP_KEY |=(P1 & 0x50); //取出 P14 P16
TEMP_KEY ^= (0x0A|0x50); //此处主要是把键值取反,如果你的按键是低电平触发的话, //如果你的按键是高电平触发,则删除此行,不需要取反 //还有一处地方也要一起改 KEY_PRESS = P0 & 0x0A; //取出 P01 P03 KEY_PRESS |=(P1 & 0x50); //取出 P14 P16 KEY_PRESS ^= (0x0A|0x50); //此处主要是把键值取反,如果你的按键是低电平触发的话, //如果你的按键是高电平触发,则删除此行,不需要取反 为了编程方便,尽量使用同一序列的口,如果不同序列的口,那端口号也要能错开,如用了P01,就不要用P11了。 这样的话,才能方便计算,提高扫描效率,如果非要用,只能通过移位处理 如51或其他单片机中,想判断 P01 P02 P12 P13的键 TEMP_KEY = P1 & 0x0C; //取出 P12 P13 TEMP_KEY =TEMP_KEY<<1; //左移1位,避开P12和P02交叉重叠 TEMP_KEY |= (P0 & 0x06); //取出 P01 P02 TEMP_KEY ^= (0x18|0x06); //此处主要是把键值取反,如果你的按键是低电平触发的话 //如果你的按键是高电平触发,则删除此行,不需要取反 这样键值里,0x02表示P01,0x04表示P02,0x08表示P12,0x10表示P13
5.3在单片机循环程序或定时器里,周期性调用扫描程序
|