找回密码
 立即注册
首页 业界区 安全 STM32按键扫描

STM32按键扫描

僚娥 2025-11-23 12:45:00
外部中断


  • 此方法需注意Timer base(也就是HAL_Delay)的优先级,要低于外部中断的优先级,否则会卡死,推荐下面状态机实现的方法。

  • 上拉输入:
  1. if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET) {  // 按下
  2.             OSTimeDly(20,OS_OPT_TIME_DLY,&err); // 去抖
  3.             if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET) {
  4.                 /* 用户代码 */
  5.             }
  6.         }
复制代码

  • 长按短按:
  1. if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET) {  // 按下
  2.         HAL_Delay(20);  // 去抖
  3.         if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET) {  
  4.             HAL_Delay(500);  // 判断长按
  5.             if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET) {  
  6.                 /* 长按 */
  7.             }
  8.             else {
  9.                 /* 短按 */
  10.             }
  11.         }
  12.     }
复制代码
状态机实现多按键短按长按

key.c
  1. #include "key.h"
  2. #define KEY_NUM 3
  3. #define LONG_PRESS_TIME 1000
  4. #define DEBOUNCE_TIME 20
  5. Key_t keys[KEY_NUM];
  6. /**
  7. * @brief 初始化按键接口参数
  8. */
  9. void Key_Init(void)
  10. {
  11.     keys[0].port = KEY_L_GPIO_Port;
  12.     keys[0].pin = KEY_L_Pin;
  13.     keys[0].active_level = 1;
  14.     keys[0].state = KEY_IDLE;
  15.     keys[0].last_level = 0;
  16.     keys[1].port = KEY_M_GPIO_Port;
  17.     keys[1].pin = KEY_M_Pin;
  18.     keys[1].active_level = 0;
  19.     keys[1].state = KEY_IDLE;
  20.     keys[1].last_level = 1;
  21.     keys[2].port = KEY_R_GPIO_Port;
  22.     keys[2].pin = KEY_R_Pin;
  23.     keys[2].active_level = 0;
  24.     keys[2].state = KEY_IDLE;
  25.     keys[2].last_level = 1;
  26. }
  27. /**
  28. * @brief 按键扫描函数
  29. * @retval result.key_id    按键id
  30. * @retval result.event     事件类型
  31. */
  32. KeyEvent_t Key_Scan(void)
  33. {
  34.     KeyEvent_t result = {0, KEY_EVENT_NONE};
  35.     for(uint8_t i = 0; i < KEY_NUM; i++)
  36.     {
  37.         bool current_level;
  38.         if (HAL_GPIO_ReadPin(keys[i].port, keys[i].pin) == keys[i].active_level)
  39.         {
  40.             current_level = 1;
  41.         }
  42.         else current_level = 0;
  43.         switch (keys[i].state)
  44.         {
  45.         case KEY_IDLE:
  46.             if (current_level && !keys[i].last_level)
  47.             {
  48.                 keys[i].state = KEY_PRESSED;
  49.                 keys[i].timer = HAL_GetTick();
  50.             }
  51.             break;
  52.            
  53.         case KEY_PRESSED:
  54.             if (current_level)
  55.             {
  56.                 if (HAL_GetTick() - keys[i].timer >= DEBOUNCE_TIME)
  57.                 {
  58.                     keys[i].state = KEY_HOLD;
  59.                 }
  60.             }
  61.             else keys[i].state = KEY_IDLE;
  62.             break;
  63.         case KEY_HOLD:
  64.             if (current_level)
  65.             {
  66.                 if (HAL_GetTick() - keys[i].timer >= LONG_PRESS_TIME)
  67.                 {
  68.                     result.key_id = i;
  69.                     result.event = KEY_EVENT_LONG;
  70.                     keys[i].state = KEY_RELEASED;
  71.                     keys[i].last_level = current_level;
  72.                     return result;  // 长按事件
  73.                 }
  74.             }
  75.             else
  76.             {
  77.                 if (HAL_GetTick() - keys[i].timer >= DEBOUNCE_TIME)
  78.                 {
  79.                     result.key_id = i;
  80.                     result.event = KEY_EVENT_SHORT;
  81.                     keys[i].state = KEY_IDLE;
  82.                     keys[i].last_level = current_level;
  83.                     return result;  // 短按事件
  84.                 }
  85.                 else keys[i].state = KEY_RELEASED;
  86.             }
  87.             break;
  88.         
  89.         case KEY_RELEASED:
  90.             if (!current_level && (HAL_GetTick() - keys[i].timer >= DEBOUNCE_TIME))
  91.             {
  92.                 keys[i].state = KEY_IDLE;
  93.             }
  94.             break;
  95.         
  96.         
  97.         default:
  98.             break;
  99.         }
  100.         keys[i].last_level = current_level;
  101.     }
  102.     return result;
  103. }
复制代码
key.h
  1. #ifndef __KEY_H
  2. #define __KEY_H
  3. #include "main.h"
  4. #include <stdbool.h>
  5. // 按键状态枚举
  6. typedef enum {
  7.     KEY_IDLE,      // 空闲
  8.     KEY_PRESSED,   // 按下(去抖中)
  9.     KEY_HOLD,      // 持续按下
  10.     KEY_RELEASED   // 释放(去抖中)
  11. } KeyState;
  12. // 按键事件枚举
  13. typedef enum {
  14.     KEY_EVENT_NONE,   // 无事件
  15.     KEY_EVENT_SHORT,  // 短按
  16.     KEY_EVENT_LONG    // 长按
  17. } KeyEvent;
  18. // 按键事件返回结构体
  19. typedef struct {
  20.     uint8_t key_id;   // 按键ID
  21.     KeyEvent event;   // 事件类型
  22. } KeyEvent_t;
  23. // 按键结构体
  24. typedef struct {
  25.     GPIO_TypeDef* port; // GPIO端口
  26.     uint16_t pin;       // GPIO引脚
  27.     bool active_level;       // 按下时的有效电平(true: 高电平, false: 低电平)
  28.     KeyState state;          // 当前状态
  29.     uint32_t timer;          // 计时器
  30.     bool last_level;         // 上次电平
  31. } Key_t;
  32. void Key_Init(void);
  33. KeyEvent_t Key_Scan(void);
  34. #endif
复制代码
实现方法
在main.c文件中:
  1. #include "key.h"
  2. KeyEvent_t key;
  3. void main(void)
  4. {
  5.   Key_Init();
  6.   while (1)
  7.   {
  8.     key = Key_Scan();
  9.     if (key.event != KEY_EVENT_NONE)
  10.     {
  11.       switch (key.event)
  12.       {
  13.       case KEY_EVENT_SHORT:
  14.         printf("key %d short press\n", key.key_id);
  15.         break;
  16.       case KEY_EVENT_LONG:
  17.         printf("key %d long press\n", key.key_id);
  18.         break;
  19.       default:
  20.         break;
  21.       }
  22.     }
  23.   }
  24. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

7 天前

举报

您需要登录后才可以回帖 登录 | 立即注册