按键状态机

原工程地址:https://github.com/candylife9/state_machine_example

视频:C语言之状态机编程_02_状态机使用案例分析_哔哩哔哩_bilibili

我觉得讲的挺好的。

来自豆包封装的通用接口

头文件

/**
 * @file key_state_machine.h
 * @brief 通用按键状态机接口
 */

#ifndef KEY_STATE_MACHINE_H
#define KEY_STATE_MACHINE_H

#include <stdint.h>
#include <stdbool.h>

/** 按键状态枚举 */
typedef enum {
    KEY_STATE_IDLE,           // 空闲状态
    KEY_STATE_PRESS_DEBOUNCE, // 按下消抖状态
    KEY_STATE_SHORT_PRESS,    // 短按状态
    KEY_STATE_LONG_PRESS,     // 长按状态
    KEY_STATE_RELEASE_DEBOUNCE // 释放消抖状态
} KeyState;

/** 按键事件回调函数类型 */
typedef void (*KeyEventCallback)(void* context, uint8_t key_id, bool is_long_press);

/** 按键配置结构体 */
typedef struct {
    uint8_t key_id;                  // 按键ID
    uint32_t debounce_time_ms;       // 消抖时间(ms)
    uint32_t long_press_threshold_ms; // 长按阈值(ms)
    uint32_t long_press_interval_ms;  // 长按连续触发间隔(ms)
    bool active_low;                 // 是否低电平有效
    bool (*read_pin)(void);          // 读取引脚电平的函数指针
    KeyEventCallback event_callback; // 事件回调函数
    void* callback_context;          // 回调函数上下文
} KeyConfig;

/** 按键状态机实例结构体 */
typedef struct {
    const KeyConfig* config;         // 按键配置
    KeyState state;                  // 当前状态
    uint8_t debounce_counter;        // 消抖计数器
    uint32_t check_time;             // 检测时间戳
    uint32_t old_time;               // 旧时间戳
    bool press_detected;             // 短按检测标志
    bool long_press_detected;        // 长按检测标志
    uint32_t long_press_trigger_time; // 长按触发时间
} KeyStateMachine;

/**
 * @brief 初始化按键状态机
 * @param key 按键状态机实例指针
 * @param config 按键配置指针
 */
void Key_Init(KeyStateMachine* key, const KeyConfig* config);

/**
 * @brief 按键状态机处理函数,需定期调用
 * @param key 按键状态机实例指针
 */
void Key_Process(KeyStateMachine* key);

/**
 * @brief 获取按键当前状态
 * @param key 按键状态机实例指针
 * @return 当前状态
 */
KeyState Key_GetState(const KeyStateMachine* key);

/**
 * @brief 检查按键是否被按下(短按或长按)
 * @param key 按键状态机实例指针
 * @return true: 按下, false: 未按下
 */
bool Key_IsPressed(const KeyStateMachine* key);

/**
 * @brief 检查按键是否处于长按状态
 * @param key 按键状态机实例指针
 * @return true: 长按, false: 非长按
 */
bool Key_IsLongPressed(const KeyStateMachine* key);

#endif // KEY_STATE_MACHINE_H

C文件 

/**
 * @file key_state_machine.c
 * @brief 通用按键状态机实现
 */

#include "key_state_machine.h"
#include "stm32fxxx_hal.h" // 假设使用STM32系列,需要包含HAL库头文件

/**
 * @brief 判断引脚电平是否为有效电平
 * @param key 按键状态机实例指针
 * @return true: 有效电平, false: 无效电平
 */
static bool IsKeyLevelActive(const KeyStateMachine* key) {
    bool pin_state = key->config->read_pin();
    return (key->config->active_low) ? (pin_state == false) : (pin_state == true);
}

void Key_Init(KeyStateMachine* key, const KeyConfig* config) {
    if (!key || !config) return;
    
    key->config = config;
    key->state = KEY_STATE_IDLE;
    key->debounce_counter = 0;
    key->check_time = HAL_GetTick();
    key->old_time = HAL_GetTick();
    key->press_detected = false;
    key->long_press_detected = false;
    key->long_press_trigger_time = 0;
}

void Key_Process(KeyStateMachine* key) {
    if (!key || !key->config) return;
    
    uint32_t current_time = HAL_GetTick();
    
    switch (key->state) {
        case KEY_STATE_IDLE:
            if (current_time - key->old_time > 1000) {
                // 空闲时,每间隔1秒输出等待按键提示信息(可通过回调函数实现)
                if (key->config->event_callback) {
                    key->config->event_callback(key->config->callback_context, key->config->key_id, false);
                }
                key->old_time = current_time;
            }
            
            if (IsKeyLevelActive(key)) {
                // 检测到有效电平,开始消抖
                key->check_time = current_time;
                key->debounce_counter = 5; // 默认5次检测
                key->state = KEY_STATE_PRESS_DEBOUNCE;
            }
            break;
            
        case KEY_STATE_PRESS_DEBOUNCE:
            if (key->debounce_counter > 0) {
                if (current_time - key->check_time > key->config->debounce_time_ms) {
                    if (!IsKeyLevelActive(key)) {
                        // 检测到无效电平,抖动,返回空闲状态
                        key->old_time = current_time;
                        key->state = KEY_STATE_IDLE;
                    } else {
                        // 仍然是有效电平,继续消抖
                        key->check_time = current_time;
                        key->debounce_counter--;
                    }
                }
            } else {
                // 消抖完成,确认按下
                key->press_detected = true;
                key->old_time = current_time;
                key->long_press_trigger_time = current_time;
                key->state = KEY_STATE_SHORT_PRESS;
                
                // 触发短按事件回调
                if (key->config->event_callback) {
                    key->config->event_callback(key->config->callback_context, key->config->key_id, false);
                }
            }
            break;
            
        case KEY_STATE_SHORT_PRESS:
            key->press_detected = false; // 重置短按标志
            
            if (current_time - key->old_time > key->config->long_press_threshold_ms) {
                // 达到长按阈值,进入长按状态
                key->long_press_detected = true;
                key->old_time = current_time;
                key->state = KEY_STATE_LONG_PRESS;
                
                // 触发长按事件回调
                if (key->config->event_callback) {
                    key->config->event_callback(key->config->callback_context, key->config->key_id, true);
                }
            } else if (!IsKeyLevelActive(key)) {
                // 短按状态下检测到释放,开始释放消抖
                key->check_time = current_time;
                key->debounce_counter = 5;
                key->state = KEY_STATE_RELEASE_DEBOUNCE;
            }
            break;
            
        case KEY_STATE_LONG_PRESS:
            if (current_time - key->old_time > key->config->long_press_interval_ms) {
                // 长按连续触发
                key->old_time = current_time;
                
                // 触发长按连续事件回调
                if (key->config->event_callback) {
                    key->config->event_callback(key->config->callback_context, key->config->key_id, true);
                }
            }
            
            if (!IsKeyLevelActive(key)) {
                // 长按状态下检测到释放,开始释放消抖
                key->check_time = current_time;
                key->debounce_counter = 5;
                key->state = KEY_STATE_RELEASE_DEBOUNCE;
            }
            break;
            
        case KEY_STATE_RELEASE_DEBOUNCE:
            if (key->debounce_counter > 0) {
                if (current_time - key->check_time > key->config->debounce_time_ms) {
                    if (IsKeyLevelActive(key)) {
                        // 检测到有效电平,抖动,返回之前的状态
                        key->state = (key->long_press_detected) ? KEY_STATE_LONG_PRESS : KEY_STATE_SHORT_PRESS;
                    } else {
                        // 仍然是无效电平,继续消抖
                        key->check_time = current_time;
                        key->debounce_counter--;
                    }
                }
            } else {
                // 消抖完成,确认释放
                key->long_press_detected = false;
                key->old_time = current_time;
                key->state = KEY_STATE_IDLE;
            }
            break;
            
        default:
            key->state = KEY_STATE_IDLE;
            break;
    }
}

KeyState Key_GetState(const KeyStateMachine* key) {
    return (key) ? key->state : KEY_STATE_IDLE;
}

bool Key_IsPressed(const KeyStateMachine* key) {
    if (!key) return false;
    
    KeyState state = key->state;
    return (state == KEY_STATE_SHORT_PRESS || state == KEY_STATE_LONG_PRESS);
}

bool Key_IsLongPressed(const KeyStateMachine* key) {
    return (key && key->state == KEY_STATE_LONG_PRESS);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值