按键驱动状态机实现

发布时间:2026/7/1 19:26:04

按键驱动状态机实现 1给按键状态分为4种空闲按下确认按下松开确认松开空闲状态说明IDLE空闲无按键动作等待有效电平通常为0即按下接地。PRESS_SHAKE按下防抖首次检测到有效电平启动定时器等待电平稳定。PRESSED确认按下电平稳定执行按下回调可在此区分短按/长按。RELEASE_SHAKE松开防抖检测到无效电平启动定时器等待电平稳定。CONFIRM_RELEASE确认松开电平稳定执行松开回调返回 IDLE即“确认松开空闲”。2. 轮询模式与中断模式应用场景触发方式实现方式优点缺点典型场景轮询在系统任务中循环调用Key_Scan()周期 10~50ms。逻辑简单无中断干扰便于调试。占用 CPU 资源响应有延迟取决于轮询周期。系统已有 20ms 的 RTOS 心跳节拍简单控制产品如家电面板。中断配置 EXTI 边沿触发上升沿/下降沿在 ISR 中调用Key_Scan()。响应极快CPU 空闲时可进入休眠低功耗。需处理抖动中断可能多次触发需注意 ISR 中避免耗时操作。电池供电设备需唤醒 CPU需要快速响应按键如音量键。2.1 结构体定义#include los_timer.h #include los_task.h #include stm32f4xx_hal.h /*---------- 按键硬件配置 ----------*/ #define KEY_PORT GPIOA #define KEY_PIN GPIO_PIN_0 #define KEY_PRESS_LEVEL GPIO_PIN_RESET // 低电平有效按下为0 #define KEY_RELEASE_LEVEL GPIO_PIN_SET // 高电平有效松开为1 /*---------- 按键状态枚举 ----------*/ typedef enum { KEY_IDLE 0, KEY_PRESS_SHAKE, KEY_PRESSED, KEY_RELEASE_SHAKE, KEY_CONFIRM_RELEASE } Key_State_t; /*---------- 按键结构体 ----------*/ typedef struct { Key_State_t state; UINT32 timer_id; // LiteOS 定时器 ID GPIO_TypeDef *port; uint16_t pin; uint32_t debounce_ms; // 防抖延时 (ms) void (*on_press)(void); // 按下回调函数指针 void (*on_release)(void); // 松开回调函数指针 } Key_Handle_t; static Key_Handle_t g_key {0};2.2 GPIO 初始化输入模式 上拉void Key_GPIO_Init(void) { GPIO_InitTypeDef gpio_init {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); gpio_init.Pin KEY_PIN; gpio_init.Mode GPIO_MODE_INPUT; gpio_init.Pull GPIO_PULLUP; // 内部上拉确保按键未按下时读取高电平 gpio_init.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(KEY_PORT, gpio_init); }2.3 按键状态机核心逻辑定时器回调/*---------- 按键定时器回调防抖处理 ----------*/ VOID Key_Timer_Callback(UINT32 arg) { GPIO_PinState level HAL_GPIO_ReadPin(g_key.port, g_key.pin); switch (g_key.state) { case KEY_PRESS_SHAKE: if (level KEY_PRESS_LEVEL) { // 电平稳定确认按下 g_key.state KEY_PRESSED; if (g_key.on_press) g_key.on_press(); } else { // 抖动返回空闲 g_key.state KEY_IDLE; } break; case KEY_RELEASE_SHAKE: if (level KEY_RELEASE_LEVEL) { // 电平稳定确认松开 g_key.state KEY_CONFIRM_RELEASE; if (g_key.on_release) g_key.on_release(); g_key.state KEY_IDLE; // 回归空闲 } else { // 抖动回到按下状态 g_key.state KEY_PRESSED; } break; default: break; } } /*---------- 启动防抖定时器 ----------*/ static void Key_StartTimer(void) { if (g_key.timer_id ! 0xFFFFFFFF) { LOS_TimerDelete(g_key.timer_id); } // 创建单次定时器防抖延时 LOS_TimerCreate(g_key.timer_id, LOS_HW_MODE_SINGLE, Key_Timer_Callback, NULL, g_key.debounce_ms); LOS_TimerStart(g_key.timer_id); }2.4 外部触发接口中断调用/*---------- 外部检测触发由定时器轮询或中断调用 ----------*/ void Key_Scan(void) { GPIO_PinState level HAL_GPIO_ReadPin(g_key.port, g_key.pin); switch (g_key.state) { case KEY_IDLE: if (level KEY_PRESS_LEVEL) { g_key.state KEY_PRESS_SHAKE; Key_StartTimer(); // 启动防抖 } break; case KEY_PRESSED: if (level KEY_RELEASE_LEVEL) { g_key.state KEY_RELEASE_SHAKE; Key_StartTimer(); // 启动松开防抖 } break; default: // 防抖中不做任何事等待定时器回调 break; } }2.5 用户回调与初始化/*---------- 用户回调函数举例 ----------*/ void User_Key_Press(void) { printf(按键确认按下!\n); // 可在此触发任务信号量或事件 } void User_Key_Release(void) { printf(按键确认松开!\n); } void Key_Init(void) { Key_GPIO_Init(); g_key.port KEY_PORT; g_key.pin KEY_PIN; g_key.state KEY_IDLE; g_key.debounce_ms 30; // 30ms 防抖 g_key.on_press User_Key_Press; g_key.on_release User_Key_Release; g_key.timer_id 0xFFFFFFFF; }3 中断模式具体实现EXTI 示例/*---------- 中断初始化以 PA0 为例 ----------*/ void Key_EXTI_Init(void) { GPIO_InitTypeDef gpio_init {0}; EXTI_HandleTypeDef exti_init {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SYSCFG_CLK_ENABLE(); // 1. GPIO 复用为 EXTI gpio_init.Pin KEY_PIN; gpio_init.Mode GPIO_MODE_IT_FALLING; // 下降沿触发按下瞬间 gpio_init.Pull GPIO_PULLUP; HAL_GPIO_Init(KEY_PORT, gpio_init); // 2. 配置中断优先级 HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); } /*---------- 中断服务函数在中断向量表中调用 ----------*/ void EXTI0_IRQHandler(void) { // 清除中断标志必须 if (__HAL_GPIO_EXTI_GET_IT(KEY_PIN) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(KEY_PIN); } // 调用按键扫描仅做状态切换和启动定时器无耗时操作 Key_Scan(); } /*---------- 回调函数在中断中调用需快速处理 ----------*/ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin KEY_PIN) { // 关键在中断中仅触发检测不直接运行定时器创建耗时 Key_Scan(); // 该函数内会启动软件定时器将后续防抖交给定时器 } }#include los_hwi.h /*---------- 中断初始化LiteOS 方式 ----------*/ void Key_Interrupt_Init(void) { UINT32 ret; HWI_PRIOR_T hwiPrio 3; HWI_MODE_T mode 0; HWI_ARG_T arg 0; // 1. 配置 GPIO 为中断输入模式下降沿触发 GPIO_InitTypeDef gpio_init {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SYSCFG_CLK_ENABLE(); gpio_init.Pin KEY_PIN; gpio_init.Mode GPIO_MODE_IT_FALLING; // 下降沿触发 gpio_init.Pull GPIO_PULLUP; gpio_init.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(KEY_PORT, gpio_init); // 2. 注册中断处理函数到 LiteOS 内核 // 参数中断号、优先级、模式、回调函数、参数 ret LOS_HwiCreate(KEY_IRQn, hwiPrio, mode, (HWI_PROC_FUNC)EXTI0_IRQHandler, arg); if (ret ! LOS_OK) { printf(Key interrupt create failed! ret%u\n, ret); return; } // 3. 使能中断LiteOS 已自动使能但可显式调用 // LOS_HwiEnable(KEY_IRQn); printf(Key interrupt registered successfully!\n); }

相关新闻