/** * @file btn_fsm.h * @brief 按键 FSM — OOPC 开放结构体模式(Open Struct) * * 设计原则(来自 multibutton_state_machine.md): * - 开放结构体: 外部可直接初始化 Btn_FSM btn = {0}; * - HAL 解耦: 通过函数指针读 GPIO, 不依赖具体硬件 * - 事件回调: 每种事件注册独立的回调函数 * - 链表管理: 多按键通过 next 指针串联 * - 非阻塞: 由 5ms 定时器节拍驱动, 软件去抖 * * UML 状态图 (5 状态 FSM): * [*] --> IDLE * IDLE --> PRESS : 检测到有效按下(去抖后) * PRESS --> RELEASE : 检测到释放 * PRESS --> LONG_HOLD : 持续按压超过 LONG_TICKS * RELEASE --> IDLE : 超时未再次按下 → 触发 CLICK/DOUBLE_CLICK * RELEASE --> PRESS : 短时间内再次按下 → 触发 REPEAT * LONG_HOLD --> IDLE : 释放 → 触发 LONG_PRESS_UP */ #ifndef BTN_FSM_H #define BTN_FSM_H #include /* ======== 配置参数 ======== */ #define BTN_TICKS_INTERVAL 5 /* 节拍周期 ms */ #define BTN_DEBOUNCE_TICKS 3 /* 去抖: 3×5ms = 15ms */ #define BTN_SHORT_TICKS 60 /* 短按/双击判定: 60×5ms = 300ms */ #define BTN_LONG_TICKS 200 /* 长按判定: 200×5ms = 1000ms */ /* ======== 事件类型 ======== */ typedef enum { BTN_EVT_NONE = 0, BTN_EVT_PRESS_DOWN, /* 按下瞬间 */ BTN_EVT_PRESS_UP, /* 释放瞬间 */ BTN_EVT_SINGLE_CLICK, /* 单击(释放后无二次按下) */ BTN_EVT_DOUBLE_CLICK, /* 双击(SHORT_TICKS 内二次按下) */ BTN_EVT_LONG_PRESS_START, /* 长按开始 */ BTN_EVT_LONG_PRESS_HOLD, /* 长按持续 */ BTN_EVT_COUNT /* 回调数组大小 */ } BtnEvent_E; /* ======== FSM 内部状态 ======== */ typedef enum { BTN_STATE_IDLE = 0, BTN_STATE_PRESS, BTN_STATE_RELEASE, BTN_STATE_REPEAT, BTN_STATE_LONG_HOLD } BtnState_E; /* ======== 前向声明 ======== */ typedef struct Btn_FSM Btn_FSM; /* ======== 回调函数类型 ======== */ typedef void (*BtnCallback)(Btn_FSM* btn, void* user_data); /* ======== 开放结构体(外部可直接初始化) ======== */ struct Btn_FSM { uint16_t ticks; /* 节拍计数器 */ uint8_t repeat : 4; /* 连击计数 */ uint8_t event : 4; /* 当前事件 */ uint8_t state : 3; /* FSM 状态 */ uint8_t debounce_cnt : 3; /* 去抖计数 */ uint8_t active_level : 1; /* 有效按压电平 (0=低有效, 1=高有效) */ uint8_t button_level : 1; /* 当前实际电平 */ uint8_t button_id; /* 按键 ID */ /* HAL 层: 读电平函数指针 */ uint8_t (*hal_read)(uint8_t button_id); /* 事件回调数组 */ BtnCallback cb[BTN_EVT_COUNT]; void* user_data; /* 链表指针(支持多按键串联) */ Btn_FSM* next; }; /* ======== 公开 API ======== */ /** * @brief 初始化按键实例 * @param btn 按键实例指针(栈分配或静态分配均可) * @param hal_read HAL 读电平函数 * @param button_id 按键 ID(传给 hal_read) * @param active_level 有效按压电平 */ void Btn_Init(Btn_FSM* btn, uint8_t (*hal_read)(uint8_t button_id), uint8_t button_id, uint8_t active_level); /** * @brief 注册事件回调 */ void Btn_Attach(Btn_FSM* btn, BtnEvent_E event, BtnCallback cb, void* user_data); /** * @brief 启动按键(加入全局链表) */ void Btn_Start(Btn_FSM* btn); /** * @brief 停止按键(从全局链表移除) */ void Btn_Stop(Btn_FSM* btn); /** * @brief 节拍驱动(在 5ms 定时器中断中调用) * 遍历全局链表,处理所有按键的 FSM */ void Btn_Tick(void); /** * @brief 获取按键当前 FSM 状态 */ BtnState_E Btn_GetState(const Btn_FSM* btn); #endif /* BTN_FSM_H */