STM32按键状态机短按、长按和连击操作

文章介绍了如何使用状态机设计一个STM32按键处理函数,通过GPIO状态和定时器判断来识别和响应不同类型的按键操作,如短按、长按和连击。代码示例展示了如何配置定时器和实现状态转换逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当使用状态机来处理按键操作时,我们将按键的不同状态抽象为状态机中的各个状态,并根据按键的状态转换进行相应的处理。以下是对上述示例代码中函数的详细注释和状态机思想的解释:

// 定义按键状态
typedef enum {
  BUTTON_STATE_IDLE,    // 按键空闲状态
  BUTTON_STATE_PRESSED, // 按键按下状态
  BUTTON_STATE_SHORT,   // 短按状态
  BUTTON_STATE_LONG,    // 长按状态
  BUTTON_STATE_DOUBLE   // 连击状态
} ButtonState;
上述代码定义了按键的不同状态,包括空闲状态(BUTTON_STATE_IDLE)、按下状态(BUTTON_STATE_PRESSED)、短按状态(BUTTON_STATE_SHORT)、长按状态(BUTTON_STATE_LONG)和连击状态(BUTTON_STATE_DOUBLE)。


void Button_Process(void) {
  static uint8_t buttonPressed = 0;
  static uint8_t buttonReleased = 0;
  static uint16_t pressCounter = 0;
  static uint16_t releaseCounter = 0;

  switch (buttonState) {
    case BUTTON_STATE_IDLE:
      if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
        buttonPressed = 1;
        buttonReleased = 0;
        pressCounter = 0;
        releaseCounter = 0;

        buttonState = BUTTON_STATE_PRESSED;
      }
      break;

    case BUTTON_STATE_PRESSED:
      if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
        if (buttonPressed) {
          pressCounter++;
          if (pressCounter >= 10) {
            buttonState = BUTTON_STATE_LONG;
          }
        }
      } else {
        buttonReleased = 1;
        releaseCounter = 0;
        buttonState = BUTTON_STATE_IDLE;
      }
      break;

    case BUTTON_STATE_SHORT:
      // 处理短按操作
      break;

    case BUTTON_STATE_LONG:
      // 处理长按操作
      break;

    case BUTTON_STATE_DOUBLE:
      // 处理连击操作
      break;
  }

  // 检查定时器溢出标志
  if (timerOverflow) {
    timerOverflow = 0;

    if (buttonState == BUTTON_STATE_PRESSED && buttonPressed) {
      pressCounter++;
      if (pressCounter >= 10) {
        buttonState = BUTTON_STATE_LONG;
      }
    }

    if (buttonState == BUTTON_STATE_LONG && buttonReleased) {
      releaseCounter++;
      if (releaseCounter >= 10) {
        buttonState = BUTTON_STATE_SHORT;
      }
    }

    if (buttonState == BUTTON_STATE_SHORT && buttonReleased) {
      buttonState = BUTTON_STATE_DOUBLE;
    }

    if (buttonState == BUTTON_STATE_DOUBLE) {
      // 连击操作处理完成后,返回空闲状态
      buttonState = BUTTON_STATE_IDLE;
    }
  }
}

以上是按键处理函数的具体实现。该函数通过轮询GPIO状态和定时器溢出来处理按键操作。

在函数开始时,我们定义了几个静态变量,用于记录按键的按下和释放状态以及按下和释放的计数器。

接下来,通过switch语句根据当前的按键状态进行处理。

在空闲状态(BUTTON_STATE_IDLE)下,检测到按键按下后,将按钮按下状态设置为1,重置释放状态和计数器,并将状态转换为按下状态(BUTTON_STATE_PRESSED)。

在按下状态(BUTTON_STATE_PRESSED)下,如果按键仍然被按下,会增加按下计数器。如果按下计数器达到一定阈值(例如10),则将状态转换为长按状态(BUTTON_STATE_LONG)。如果检测到按键释放,则设置释放状态为1,重置释放计数器,并将状态转换回空闲状态(BUTTON_STATE_IDLE)。

在其他状态下(BUTTON_STATE_SHORT、BUTTON_STATE_LONG、BUTTON_STATE_DOUBLE),可以添加相应的处理逻辑来处理短按、长按和连击操作。

在函数的最后,我们检查定时器溢出标志,并根据当前的按键状态和按键的按下和释放状态进行相应的处理。

如果按键处于按下状态(BUTTON_STATE_PRESSED)且按键仍然被按下,递增按下计数器。如果按下计数器达到一定阈值(例如10),将状态转换为长按状态(BUTTON_STATE_LONG)。

如果按键处于长按状态(BUTTON_STATE_LONG)且按键已释放,递增释放计数器。如果释放计数器达到一定阈值(例如10),将状态转换为短按状态(BUTTON_STATE_SHORT)。

如果按键处于短按状态(BUTTON_STATE_SHORT)且按键已释放,将状态转换为连击状态(BUTTON_STATE_DOUBLE)。

如果按键处于连击状态(BUTTON_STATE_DOUBLE),可以在此处添加处理连击操作的逻辑。处理完成后,将状态转换回空闲状态(BUTTON_STATE_IDLE)。

状态机的思想是将一个系统或组件的运行过程划分为不同的状态,并根据输入和当前状态来决定下一个状态和相应的行为。在本例中,按键的不同状态对应于不同的按键操作,通过状态转换和计数器的判断,可以准确地识别和处理短按、长按和连击操作。

使用状态机的好处是可以提高代码的可读性和可维护性,使逻辑更清晰、结构更简洁。每个状态都有明确定义的行为,易于理解和修改。此外,状态机还能够方便地扩展和添加新的状态和操作,以适应不同的需求和场景。

以下是一个基于状态机的按键处理程序的示例,用于处理短按、长按和连击操作。这个示例基于STM32F103微控制器,并使用HAL库进行GPIO和定时器的配置。

首先,需要在STM32CubeMX或其他工具中配置GPIO引脚和定时器,以适应按键的连接和需求。假设按键连接在GPIOA的Pin 0引脚上。

#include "stm32f1xx_hal.h"

// 定义按键状态
typedef enum {
  BUTTON_STATE_IDLE,    // 按键空闲状态
  BUTTON_STATE_PRESSED, // 按键按下状态
  BUTTON_STATE_SHORT,   // 短按状态
  BUTTON_STATE_LONG,    // 长按状态
  BUTTON_STATE_DOUBLE   // 连击状态
} ButtonState;

// 定义按键处理函数
void Button_Process(void);

// 按键状态变量
ButtonState buttonState = BUTTON_STATE_IDLE;

// 定时器溢出标志
volatile uint8_t timerOverflow = 0;

// 定时器溢出中断处理函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2) {
    timerOverflow = 1;
  }
}

int main(void) {
  // STM32初始化代码

  // 配置定时器
  TIM_HandleTypeDef htim2;
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 7200 - 1; // 定时器时钟预分频,72MHz / 7200 = 10kHz
  htim2.Init.Period = 1000 - 1;    // 定时器溢出时间,10kHz / 1000 = 10Hz
  HAL_TIM_Base_Init(&htim2);
  HAL_TIM_Base_Start_IT(&htim2);

  while (1) {
    Button_Process();
  }
}

void Button_Process(void) {
  static uint8_t buttonPressed = 0;
  static uint8_t buttonReleased = 0;
  static uint16_t pressCounter = 0;
  static uint16_t releaseCounter = 0;

  switch (buttonState) {
    case BUTTON_STATE_IDLE:
      if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
        buttonPressed = 1;
        buttonReleased = 0;
        pressCounter = 0;
        releaseCounter = 0;

        buttonState = BUTTON_STATE_PRESSED;
      }
      break;

    case BUTTON_STATE_PRESSED:
      if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
        if (buttonPressed) {
          pressCounter++;
          if (pressCounter >= 10) {
            buttonState = BUTTON_STATE_LONG;
          }
        }
      } else {
        buttonReleased = 1;
        releaseCounter = 0;
        buttonState = BUTTON_STATE_IDLE;
      }
      break;

    case BUTTON_STATE_SHORT:
      // 处理短按操作
      break;

    case BUTTON_STATE_LONG:
      // 处理长按操作
      break;

    case BUTTON_STATE_DOUBLE:
      // 处理连击操作
      break;
  }

  // 检查定时器溢出标志
  if (timerOverflow) {
    timerOverflow = 0;

    if (buttonState == BUTTON_STATE_PRESSED && buttonPressed) {
      pressCounter++;
      if (pressCounter >= 10) {
        buttonState = BUTTON_STATE_LONG;
      }
    }

    if (buttonState == BUTTON_STATE_LONG && buttonReleased) {
      releaseCounter++;
      if (releaseCounter >= 10) {
        buttonState = BUTTON_STATE_SHORT;
      }
    }

    if (buttonState == BUTTON_STATE_SHORT && buttonReleased) {
      buttonState = BUTTON_STATE_DOUBLE;
    }

    if (buttonState == BUTTON_STATE_DOUBLE) {
      // 连击操作处理完成后,返回空闲状态
      buttonState = BUTTON_STATE_IDLE;
    }
  }
}

在上述示例中,按键状态机通过轮询GPIO状态和定时器溢出来处理按键操作。根据按键的按下和释放时间,可以识别短按、长按和连击操作。可以根据需要在相应的状态处理代码中添加具体的按键操作逻辑。

需要注意的是,示例代码中的定时器配置和中断处理函数是基于STM32F103和HAL库的的用法。如果使用其他型号的STM32微控制器或其他库,可能需要进行适当的修改。

请确保正确配置GPIO引脚和定时器,并根据实际需求调整定时器的预分频和溢出时间。另外,示例代码中的按键状态转换阈值(如短按、长按的计数值)可以根据需要进行调整。

最后,根据自己的应用需求,在相应的状态处理代码中添加适当的按键操作逻辑,例如触发事件、发送消息等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章鱼哥嵌入式开发

坚持不易,你们的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值