**前言:**本文仅为个人简单记录,文章格式不做处理(等有时间再整理吧)。文章内容仅供参考,若有更好的想法,或者有疑问欢迎在评论留言。
正片:
关于按键扫描、按键处理网上有很多文章,本文提供了一个不同IO、按键判断方法不同的多按键扫描处理方法(有缺陷)。
应用场景:
1、按键数量不多,如4、5个。
2、按键IO口分布在不同P口(如P1、P2、PA、PB等)。
3、按键识别方式不一(上拉、下拉、ADC检测)。
4、按键短按、短按释放、长按、长按释放判断。
5、组合键判断。
如上述情景下,矩阵按键扫描显然不适用,每个按键单独消抖判段,程序又会庞杂,不精炼。
整体思想:
1、为每个按键定义一个键码值。键码值以8、4、2、1形式编码,
2、使用定时器,定时扫描按键,多次扫描消抖。
3、由于是定时扫描,则在按键扫描函数里计数,即可用来做按键按下时长判断,从而判断按键长按、短按。
下面提供按键处理参考代码(懒得区分文件了)。
//枚举按键键码
typedef enum ENUM_KEY_CODE
{
E_KEY_CODE_NONE = 0,
E_KEY_CODE_MENU = 0x01,
E_KEY_CODE_INC = 0x02,//加-上
E_KEY_CODE_DEC = 0x04,//减-下
E_KEY_CODE_OK = 0x08,
E_KEY_CODE_POWER = 0x10,
//。。。
} E_KEY_CODE;
//枚举按键状态
typedef enum ENUM_KEY_EVENT
{
E_KEY_EVT_NONE = 0,
E_KEY_EVT_SHORT_PRESS,
E_KEY_EVT_SHORT_RELEASE,
E_KEY_EVT_LONG_PRESS,
E_KEY_EVT_LONG_RELEASE,
E_KEY_EVT_TIMEOUT, //按键超时
} E_KEY_EVENT;
//全局变量
uint8_t KEY_Code = E_KEY_CODE_NONE; //按键键码
uint8_t KEY_Evt = E_KEY_EVT_NONE; //按键状态
void KeyScan()
{
// 20ms进行按键扫描
static uint8_t KeyCode_New = E_KEY_CODE_NONE; //新的按键值
static uint8_t KeyCode_Old = E_KEY_CODE_NONE; //旧的按键值
static uint8_t Key_count = 0; //消抖计数
static uint8_t KeyTimeCnt = 0; //按键按下时间计数
/*
对应按键按下或释放判断,可根据实际情况(上拉下拉,或者时ADC口),
按键按下则 对应键码置1,
按键释放则 对应键码清零
*/
//菜单键
if (MENU_KEY_TRUE)
KeyCode_New |= E_KEY_CODE_MENU;
else
KeyCode_New &= ~E_KEY_CODE_MENU;
// OK键
if (KEY_OK_TRUE)
KeyCode_New |= E_KEY_CODE_PTT;
else
KeyCode_New &= ~E_KEY_CODE_PTT;
//下
if (KEY_DOWN_TRUE)
KeyCode_New |= E_KEY_CODE_DEC;
else
KeyCode_New &= ~E_KEY_CODE_DEC;
//上
if (KEY_UP_TRUE)
KeyCode_New |= E_KEY_CODE_INC;
else
KeyCode_New &= ~E_KEY_CODE_INC;
// Power
if (KEY_POWER_TRUE)
KeyCode_New |= E_KEY_CODE_POWER;
else
KeyCode_New &= ~E_KEY_CODE_POWER;
//通过判断当前键码与上一次保存的键码是否相同来消抖,
//如果不同则更新键码值,清除计数变量,
if (KeyCode_New != KeyCode_Old)
{
KeyCode_Old = KeyCode_New;
Key_count = 0;
// KeyTimeCnt = 0;
}
else //键码值相同
{
Key_count++; //消抖计数
if (Key_count >= 3) //按键值相同,且连加3次 消抖时间3*20mS =60mS
{
Key_count = 3;
if (KeyCode_Old != E_KEY_CODE_NONE) //按键值不为空
{
if (KeyTimeCnt < 250)
KeyTimeCnt++; //按键时间计数
if (KeyTimeCnt == 1) // 立马指示按下,KEY_Evt 只置位一次
{
ioKeyEvt = 1;
KEY_Code = KeyCode_Old;
KEY_Evt = E_KEY_EVT_SHORT_PRESS; //按下
}
else
{
if (KeyTimeCnt == 33) //指示长按,KEY_Evt只置位一次
{
ioKeyEvt = 2;
KEY_Evt = E_KEY_EVT_LONG_PRESS; //长按
}
}
}
else //所有按键释放
{
if (ioKeyEvt == 1)
{
KEY_Evt = E_KEY_EVT_SHORT_RELEASE; //短按释放
}
else
{
if (ioKeyEvt == 2)
KEY_Evt = E_KEY_EVT_LONG_RELEASE; //长按释放
}
ioKeyEvt = 0;
KeyTimeCnt = 0;
}
}
}
}
//按键处理函数
void KEY_FUN()
{
switch (KEY_Code)
{
case E_KEY_CODE_NONE:
break;
case E_KEY_CODE_POWER:
if (KEY_Evt == E_KEY_EVT_SHORT_PRESS)
{
}
else if (KEY_Evt == E_KEY_EVT_SHORT_RELEASE) //按键短按释放
{
}
else if (KEY_Evt == E_KEY_EVT_LONG_PRESS) //按键长按
{
}
else //按键长按释放
{
}
break;
case E_KEY_CODE_MENU:
//同上
break;
case E_KEY_CODE_INC:
//同上上
break;
case E_KEY_CODE_DEC:
//同上上上
break;
case E_KEY_CODE_PTT:
//同上上上上
break;
default: //组合键,组合键功能可根据需要自行设置,可增加组合键的键码
if (KEY_Evt == E_KEY_EVT_SHORT_PRESS) //按键按下
{
}
else if (KEY_Evt == E_KEY_EVT_SHORT_RELEASE) //按键短按释放
{
}
else if (KEY_Evt == E_KEY_EVT_LONG_PRESS) //按键长按
{
}
else //按键长按释放
{
}
break;
}
}
uint8_t TIME_20MS_FLAG = 0;
//伪代码
void TIME_20mS_IRQ() //定时器中断函数
{
TIME_20MS_FLAG = 1;
}
void main()
{
//定时器初始化
while(1)
{
//定时中断中将标志置1,按键扫描一次时间 20MS
if(TIME_20MS_FLAG == 1)
{
KeyScan();
if (KEY_Evt != E_KEY_EVT_NONE)
{
KEY_FUN();
KEY_Evt = E_KEY_EVT_NONE;
}
TIME_20MS_FLAG = 0;
}
}
}
//上述方法还是有些缺陷的,不过已经能满足基本的按键长短按判断,及简单组合键的判断。
//最后,希望对看到本文的你有些帮助。