原工程地址: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; // 按鍵IDuint32_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);
}