在做嵌入式产品时按键是最常用的输入信号了,按键多的时候我们可以实现很多的功能,有时候资源不够,但又需要实现较多的功能时,这就需要按键复用了,常常一个按键包含短按,长按,或者双击,N击,赋予不同的功能,这和具体项目有关。我们通常做的方法是识别I/O引脚电平,然后加以适当防抖处理,那么简单的按键单击功能就可以实现了。
用户基本操作定义:
1。短按操作:按键按下,按下时间<1s,属于一次短按操作
2。长按操作:按键按下,按下时间>1.5s,属于一次长按操作
在正常0.5s内无按键操作为启始按键扫描条件下,扫描按键将产生以下3种按键事件:
1。长按事件:任何1次出现的长按操作都属于长按事件
2。单击事件:1次短按操作后,间隔0.5内没有短按操作
3。双击事件:2次短按操作间隔时间<0.5s,则2次短按操作为1次双击事件,且2次短按都取消
对按键操作者的建议:
由于按键的多功能性质,建议操作者每次在单击/长按/双击按键事件发生后,隔0.5s后再进行下一次的按键操作。因为在特别操作情况下,程序是保证按定义进行判断和处理的,主要是怕操作者自己记不清楚导致操作失误。
STM32的IO初始化代码 和 定时器初始化代码这方面的代码,很多大佬理解的比我还好,我就不献丑了
/****************************************************************************
/****************************************************************************
名 称:unsigned char key_driver(void)
功 能:状态机检测按键
入口参数:无
出口参数:返回按键值
调用方法:该函数实现按键的单击长按和没有按键按下的检测
添加或者删除独立按键不需要更改此处
****************************************************************************/
unsigned char key_driver(void)
{
static unsigned char key_state = 0; // 按键状态变量
static unsigned int key_time = 0; // 按键计时变量
static unsigned char Key_LaetgValue = 0;//上一次扫描的键值
unsigned char Key_LaetPress,key_press, key_return;//当前按键键值
key_return = N_KEY; // 清除 返回按键值
Key_LaetPress = key_press = GetKEY(); // 读取当前键值
if( Key_LaetPress ) //保存按键的的键值只在非 0 的状态才改变其按键值
{
Key_LaetgValue = key_press;
}
switch (key_state)
{
case KEY_STATE_A: // 按键状态A:判断有无按键按下
if ( ( key_press != 0 ) & (key_press == Key_LaetPress) ) // 有按键按下
{
key_time = 0; // 清零时间间隔计数
key_state = KEY_STATE_B; // 然后进入 按键状态B
}
break;
case KEY_STATE_B: // 按键状态B:软件消抖(确定按键是否有效,而不是误触)。按键有效的定义:按键持续按下超过设定的消抖时间。
if (key_press == Key_LaetPress)
{
key_time++; // 一次10ms
if(key_time>=SINGLE_KEY_TIME) // 消抖时间为:SINGLE_KEY_TIME*10ms;
{
key_state = KEY_STATE_C; // 如果按键时间超过 消抖时间,即判定为按下的按键有效。按键有效包括两种:单击或者长按,进入 按键状态2, 继续判定到底是那种有效按键
}
}
else
{
key_state = KEY_STATE_A; // 如果按键时间没有超过,判定为误触,按键无效,返回 按键状态0,继续等待按键
}
break;
case KEY_STATE_C: // 按键状态C:判定按键有效的种类:是单击,还是长按
if( key_press == 0 ) // 如果按键在 设定的长按时间 内释放,则判定为单击
{
key_return = (Key_LaetgValue | KEY_STATE_C ); // 返回 有效按键值:单击
key_state = KEY_STATE_A; // 返回 按键状态A,继续等待按键
}
else
{
key_time++;
if(key_time >= LONG_KEY_TIME) // 如果按键时间超过 设定的长按时间(LONG_KEY_TIME10ms=20010ms=2000ms), 则判定为 长按
{
key_return = (Key_LaetgValue | KEY_STATE_E ); // 返回 有效键值值:长按
key_state = KEY_STATE_F; // 去等待释放状态
}
}
break;
case KEY_STATE_F: // 按键状态C:按键释放
if ( key_press == 0 )
{
key_state = KEY_STATE_A; // 按键释放后,进入 按键状态A ,进行下一次按键的判定
}
break;
default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
key_state = KEY_STATE_A;
break;
}
return key_return; // 返回 按键值
}
/****************************************************************************
名 称:char key_read(void)
功 能:状态机检测按键
入口参数:无
出口参数:返回按键值
调用方法:该函数是对 key_driver 函数的调用 用于实现按键的 双击 和 单击 检测
****************************************************************************/
unsigned char key_read(void)
{
static unsigned char KeyState = 0; // 初始化状态机的状态
static unsigned int KeyTime = 0; // 按键计时变量
static unsigned char LaetgKeyValue = 0;//上一次扫描的键值
unsigned char KeyValue,KeyReturnValue;//当前按键键值
KeyReturnValue = N_KEY; // 清除 返回按键值
KeyValue = key_driver(); // 读取当前键值的状态
if( KeyValue ) //保存按键的的键值只在非 0 的状态才改变其按键值
{
LaetgKeyValue = KeyValue & VALUE; //取出按键值 去掉按键的状态编号
}
switch (KeyState)
{
case KEY_STATE_A: // 按键状态0:判断有无按键按下
if ( ( KeyValue & STATE ) == KEY_STATE_C ) // 判断是否有单击按键
{
KeyTime = 0; // 清零时间间隔计数
KeyState = KEY_STATE_D; // 然后进入 按键状态D
}
else // 对于无键、长键,返回原事件
{
KeyReturnValue = KeyValue;
}
break;
case KEY_STATE_D: // 按键状态D:双击状态
if ( ( KeyValue & STATE ) == KEY_STATE_C )
{
KeyReturnValue = (LaetgKeyValue | KEY_STATE_D ); // 返回 有效按键值:双击
KeyState = KEY_STATE_F; // 然后进入 按键状态F
}
else if ( ++KeyTime >= DOUBLE_KEY_TIME ) //DOUBLE_KEY_TIME*10ms时间内没有再次单击 视为单击
{
KeyReturnValue = (LaetgKeyValue | KEY_STATE_C ); //返回 有效按键值:单击
KeyState = KEY_STATE_A; // 然后进入 按键状态F
}
break;
case KEY_STATE_F: // 等待按键释放
if ( KeyValue == 0 )
{
KeyState = KEY_STATE_A; // 按键释放后,进入 按键状态0 ,进行下一次按键的判定
}
break;
default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候
KeyState = KEY_STATE_A;
break;
}
return KeyReturnValue; // 返回 按键值
}
/*
/
uint16_t Number;
extern uint8_t g_u8_KeyValue;
void GENERAL_TIM3_IRQHandler (void)
{
if ( TIM_GetITStatus( GENERAL_TIM3_TIM, TIM_IT_Update) != RESET )
{
TIM_ClearITPendingBit(GENERAL_TIM3_TIM, TIM_IT_Update);
/ 中断任务执行*/
g_u8_KeyValue = key_read();
switch(g_u8_KeyValue)
{
case Key1_S:
printf(" This is 11 SINGLE key ! \r\n" );
break;
case Key1_D:
printf(" This is 11 DOUBLE key ! \r\n" );
break;
case Key1_L:
printf(" This is 11 LONG key ! \r\n" );
break;
case Key2_S:
printf(" This is 22 SINGLE key ! \r\n" );
break;
case Key2_D:
printf(" This is 22 DOUBLE key ! \r\n" );
break;
case Key2_L:
printf(" This is 22 LONG key ! \r\n" );
break;
case Key3_S:
printf(" This is 33 SINGLE key ! \r\n" );
break;
case Key3_D:
printf(" This is 33 DOUBLE key ! \r\n" );
break;
case Key3_L:
printf(" This is 33 LONG key ! \r\n" );
break;
default:
break;
}
}
}
//按键状态
#define KEY_STATE_A 0xA0 // 无
#define KEY_STATE_B 0xB0 // 软件消抖
#define KEY_STATE_C 0xC0 // 单击
#define KEY_STATE_D 0xD0 // 双击
#define KEY_STATE_E 0xE0 // 长按
#define KEY_STATE_F 0xF0 // 等待按键释放
//用于识别按键的状态 用它与按键的状态相与即可区分
#define STATE 0xF0 // 等待按键释放
#define VALUE 0x0F // 存放按键值
//用于识别按键按下的状态识别
#define PRESS RESET //低电平识别
//按键时间调整
#define SINGLE_KEY_TIME 3 // 消抖时间为 SINGLE_KEY_TIME10MS
#define DOUBLE_KEY_TIME 50 / 双击间隔时间 DOUBLE_KEY_TIME10MS 改变这个数值可以增加按键的灵敏度 数值越小 灵敏度越高
此数值定时器定时时间要大于消抖时间 小于长按按键的时间 /
#define LONG_KEY_TIME 150 // 长按时间 LONG_KEY_TIME10MS
// 按键值 增加按键或者删除按键 需要对应增加或者删除
#define KEY_NO_DOWN 0x00 //无按键按下
#define KEY_K1 0x01 // 第一个按键按下
#define KEY_K2 0x02 // 第二个按键按下
#define KEY_K3 0x03 // 第三个按键按下
//按键返回值 增加按键或者删除按键 需要对应增加或者删除
#define N_KEY 0 // no click
#define Key1_S (KEY_STATE_C | KEY_K1 ) // single click
#define Key1_D (KEY_STATE_D | KEY_K1 ) // double press
#define Key1_L (KEY_STATE_E | KEY_K1 ) // long press
#define Key2_S (KEY_STATE_C | KEY_K2 ) // single click
#define Key2_D (KEY_STATE_D | KEY_K2 ) // double press
#define Key2_L (KEY_STATE_E | KEY_K2 ) // long press
#define Key3_S (KEY_STATE_C | KEY_K3 ) // single click
#define Key3_D (KEY_STATE_D | KEY_K3 ) // double press
#define Key3_L (KEY_STATE_E | KEY_K3 ) // long press
完整工程链接
参考教程
参考博客 https://blog.csdn.net/u010360138/article/details/79148966
FSM状态机按键功能解析 http://www.doc88.com/p-8252759803281.html