在STM32的HAL中实现单按,长按和双按功能 [英] Implementing a single press, long press and a double press function in HAL for STM32

查看:164
本文介绍了在STM32的HAL中实现单按,长按和双按功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现单按,双按和长按功能,以执行不同的功能.到目前为止,我已经了解了单按和长按的逻辑,但是我不知道如何检测到双按.至于代码,我已经使用一个计数器实现了单按和长按,但是代码只在第一个if条件下停留.

I'm trying to implement a single press, double press and long press function to perform different functions. So far I've understood the logic for a single press and long press but I cant figure out how to detect a double press. As for the code, I've implemented the single press and long press using a counter but the code only stays on the first if condition.

          bool single_press = false;
      bool long_press = false;

      if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13))
      {

          HAL_TIM_Base_Start(&htim2);
          if ((TIM2->CNT == 20) && (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)))
          {
              single_press = true;
              long_press = false;
          }
          else if ((TIM2->CNT == 799) && (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)))
          {
              single_press = true;
              long_press = true;
          }
          HAL_TIM_Base_Stop(&htim2);
      }

      if (single_press == true && long_press == false)
      {
          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, 1);
          HAL_Delay(1000);
          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, 0);
      }
      else if (single_press == true && long_press == true)
      {
          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
          HAL_Delay(1000);
          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
      }
  }

我正在尝试实现一种情况,如果我按下按键20毫秒(单按),PB0会变高一秒钟,如果我按下按键800毫秒,PB7会变高一秒钟.但是,在运行程序时,当我按下按钮时,无论我按住按钮多长时间,PB0都会变高,而PB7会保持低电平.所以我想我有两个问题:

I'm trying to implement a case where if I press the key for 20 ms (single press) PB0 will go high for a second and if I press the key for 800 ms PB7 will go high for a second. However, on running the program, when I press the button, PB0 goes high regardless of how long I hold the button and PB7 stays low. So I guess I have two questions:

  • 如何编辑代码,使单按PB0变高,而长按PB7变高?
  • 一个人如何实现双击功能?

谢谢!

推荐答案

请勿将延迟用于开始.一直以来,您都无法看到按钮在做什么(或做任何其他有用的事情).相反,您需要连续轮询(或使用中断)按钮状态,并在状态发生变化时加上时间戳,并根据时间做出操作决定.

Do not use delays for a start. All the while you are in the delay, you are not seeing what the button is doing (or doing anything else useful for that matter). Instead you need to continuously poll (or use interrupts) for the button state and when the state changes, time-stamp it, and make your action decisions based on the timing.

首先,您将需要具有防抖功能的可靠按钮状态检测.有很多方法.一个例子:

First of all you will need a robust button state detection with debouncing. There are a number of approaches. An example:

bool buttonState()
{
    static const uint32_t DEBOUNCE_MILLIS = 20 ;
    static bool buttonstate = HAL_GPIO_ReadPin( GPIOC, GPIO_PIN_13 ) == GPIO_PIN_SET ;
    static uint32_t buttonstate_ts = HAL_GetTick() ;

    uint32_t now = HAL_GetTick() ;
    if( now - buttonstate_ts > DEBOUNCE_MILLIS )
    {
        if( buttonstate != HAL_GPIO_ReadPin( GPIOC, GPIO_PIN_13 ) == GPIO_PIN_SET )
        {
            buttonstate = !buttonstate ;
            buttonstate_ts = now ;
        }
    }
    return buttonsate ;
}

因此, buttonState()总是立即返回-没有延迟,但是在状态更改后,重新读取按钮将保持20ms,以防止误解开关在多个状态变化时的反弹.

So buttonState() always returns immediately - no delays, but re-reading the button is held off for 20ms after a state change to prevent misinterpreting switch bounce as multiple state changes.

然后,您需要一个按钮状态轮询功能,该功能可以检测按钮按下和按钮按下事件的时间.这样:

Then you need a button state polling function that detects the timing of button down and button up events. Such that:

     ____________________________
____|                            |_____________
     <----long press min-->
                           ^
                           |_Long press detected
     ______     _____
____|      |___|     |_________________________
                     ^
                     |_Double press detected
     ______
____|      |___________________________________
           <------->
              ^    ^
              |    |_Single press detected
              |_ Double press gap max.

请注意,在按下按钮后过了很长时间后才检测到单按,因此将其视为双按.以下可能需要一些调试(未经试验)作为说明:

Note that the single press is detected after too long has passed after button-up for it to be a double press. The following may need some debugging (untested) treat as illustrative:

typedef enum
{
    NO_PRESS,
    SINGLE_PRESS,
    LONG_PRESS,
    DOUBLE_PRESS
} eButtonEvent ;

eButtonEvent getButtonEvent()
{
    static const uint32_t DOUBLE_GAP_MILLIS_MAX = 250 ;
    static const uint32_t LONG_MILLIS_MIN = 800 ;
    static uint32_t button_down_ts = 0 ;
    static uint32_t button_up_ts = 0 ;
    static bool double_pending = false ;
    static bool long_press_pending = false ;
    static bool button_down = false ; ;

    eButtonEvent button_event = NO_PRESS ;
    uint32_t now = HAL_GetTick() ;

    // If state changed...
    if( button_down != buttonState() )
    {
        button_down = !button_down ;
        if( button_down )
        {
            // Timestamp button-down
            button_down_ts = now ;
        }
        else
        {
            // Timestamp button-up
            button_up_ts = now ;

            // If double decision pending...
            if( double_pending )
            {
                button_event = DOUBLE_PRESS ;
                double_pending = false ;
            }
            else
            {
                double_pending = true ;
            }

            // Cancel any long press pending
            long_press_pending = false ;
        }
    }

    // If button-up and double-press gap time expired, it was a single press
    if( !button_down && double_pending && now - button_up_ts > DOUBLE_GAP_MILLIS_MAX )
    {
        double_pending = false ;
        button_event = SINGLE_PRESS ;
    }
    // else if button-down for long-press...
    else if( !long_press_pending && button_down && now - button_down_ts > LONG_MILLIS_MIN )
    {
        button_event = LONG_PRESS ;
        long_press_pending = false ;
        double_pending = false ;

    }

    return button_event ;
}

然后最后您需要频繁轮询

int main()
{
    for(;;)
    {
        // Check for button events
        switch( getButtonEvent() )
        {
            case NO_PRESS :     { ... } break ;
            case SINGLE_PRESS : { ... } break ;
            case LONG_PRESS :   { ... } break ;
            case DOUBLE_PRESS : { ... } break ;
        }

        // Do other work...
    }
}

查看没有延迟的情况,使您可以检查按钮事件并实时进行其他工作.显然,其他工作"是指其他工作".还必须执行过多的延迟,否则会搞乱您的按钮事件计时.因此,例如,要在单按中实现1秒的输出,您可能会:

See how there are no delays, allowing you to check for button events and do other work in real-time. Obviously, the "other work" must also execute with excessive delays otherwise it would mess up your button event timing. So for example to implement your 1 second output on single press you might have:

            case SINGLE_PRESS :
            {
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
                single_press_ts = now ;

            } break ;

然后在切换/案例之后:

then later in after the switch/case:

    if( now - single_press_ts > 1000 )
    {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
    }

如果这是一个问题,您将需要考虑将中断用于按钮事件处理-将其与反跳处理结合使用,或者使用RTOS调度程序轮询任务中的按钮事件.

If that is a problem they you would need to consider using interrupts for the button event processing - combining it with the de-bounce processing, or use an RTOS scheduler and poll for button events in a task.

这篇关于在STM32的HAL中实现单按,长按和双按功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆