STM32按键识别 状态机( 单击 双击 长按)可以根据情况自己在添加多击 长按

STM32按键识别 状态机( 单击 双击 长按)可以根据情况自己在添加多击 长按

在做嵌入式产品时按键是最常用的输入信号了,按键多的时候我们可以实现很多的功能,有时候资源不够,但又需要实现较多的功能时,这就需要按键复用了,常常一个按键包含短按,长按,或者双击,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初始化代码 和 定时器初始化代码这方面的代码,很多大佬理解的比我还好,我就不献丑了

读取按键的状态

/****************************************************************************

  • 名 称: GetKEY(void)
  • 功 能:取得按键GPIO的值
  • 入口参数:无
  • 出口参数:返回按键值
  • 说 明:如果读到低电平则说明有按键按下,不同的CPU只要更改if里面的读取方式即可
  • 调用方法:key_driver 函数调用 用于判断是否有按键按下
    增加按键或者删除按键 需要对应增加或者删除
    ***************************************************************************/
    uint8_t GetKEY(void)
    {
    if(GPIO_ReadInputDataBit(GPIO_PORT_K1, GPIO_PIN_K1) == PRESS) /
    读取按键 2 的状态 /
    {
    return KEY_K1;//按键被按下 返回1
    }
    if(GPIO_ReadInputDataBit(GPIO_PORT_K2, GPIO_PIN_K2) == PRESS) /
    读取按键 2 的状态 /
    {
    return KEY_K2;//按键被按下 返回2
    }
    if(GPIO_ReadInputDataBit(GPIO_PORT_K3, GPIO_PIN_K3) == PRESS) /
    读取按键 2 的状态 */
    {
    return KEY_K3;//按键被按下 返回3
    }
    return KEY_NO_DOWN;
    }

实现 单击 和 长按

/****************************************************************************

  • 名 称: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; // 返回 按键值
    }

定时器调用函数

/*


  • 函 数 名: GENERAL_TIM3_IRQHandler
  • 功能说明: 定时器 的中断处理函数
  • 形 参: 无
  • 返 回 值: 无

/
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_TIME
10MS

// 按键值 增加按键或者删除按键 需要对应增加或者删除
#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

  • 10
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值