/** * @file btn_fsm.c * @brief 按键 FSM 实现 — 参考 MultiButton 设计 * * 5 状态 FSM: * IDLE → PRESS → RELEASE → IDLE (单击/双击) * → LONG_HOLD → IDLE (长按) * * 驱动方式: 外部 5ms 定时器调用 Btn_Tick() * Btn_Tick() 遍历全局链表,对每个按键执行 handler */ #include "btn_fsm.h" #include /* ======== 全局链表头 ======== */ static Btn_FSM* g_head = (void*)0; /* ======== 内部: 触发回调 ======== */ static inline void btn_fire(Btn_FSM* btn, BtnEvent_E evt) { btn->event = (uint8_t)evt; if (btn->cb[evt] != (void*)0) { btn->cb[evt](btn, btn->user_data); } } /* ======== 内部: 单按键 FSM 处理 ======== */ static void btn_handler(Btn_FSM* btn) { uint8_t read_gpio = btn->hal_read(btn->button_id); /* 非 IDLE 状态时递增节拍 */ if (btn->state != BTN_STATE_IDLE) { if (btn->ticks < 0xFFFF) { btn->ticks++; } } /* ---- 去抖处理 ---- */ if (read_gpio != btn->button_level) { btn->debounce_cnt++; if (btn->debounce_cnt >= BTN_DEBOUNCE_TICKS) { btn->button_level = read_gpio; btn->debounce_cnt = 0; } } else { btn->debounce_cnt = 0; } /* ---- FSM 状态机 ---- */ switch ((BtnState_E)btn->state) { case BTN_STATE_IDLE: if (btn->button_level == btn->active_level) { /* 检测到有效按下 */ btn->ticks = 0; btn->repeat = 1; btn_fire(btn, BTN_EVT_PRESS_DOWN); btn->state = BTN_STATE_PRESS; } break; case BTN_STATE_PRESS: if (btn->button_level != btn->active_level) { /* 按键释放 */ btn->ticks = 0; btn_fire(btn, BTN_EVT_PRESS_UP); btn->state = BTN_STATE_RELEASE; } else if (btn->ticks >= BTN_LONG_TICKS) { /* 长按 */ btn->ticks = 0; btn_fire(btn, BTN_EVT_LONG_PRESS_START); btn->state = BTN_STATE_LONG_HOLD; } break; case BTN_STATE_RELEASE: if (btn->button_level == btn->active_level) { /* 短时间内再次按下 → 连击/双击 */ btn->ticks = 0; btn->repeat++; btn_fire(btn, BTN_EVT_PRESS_DOWN); btn->state = BTN_STATE_REPEAT; } else if (btn->ticks >= BTN_SHORT_TICKS) { /* 超时未再次按下 → 判定单击或双击 */ if (btn->repeat == 1) { btn_fire(btn, BTN_EVT_SINGLE_CLICK); } else if (btn->repeat == 2) { btn_fire(btn, BTN_EVT_DOUBLE_CLICK); } btn->state = BTN_STATE_IDLE; } break; case BTN_STATE_REPEAT: if (btn->button_level != btn->active_level) { /* 再次释放 */ btn->ticks = 0; btn_fire(btn, BTN_EVT_PRESS_UP); btn->state = BTN_STATE_RELEASE; } else if (btn->ticks >= BTN_SHORT_TICKS) { /* 超时 → 重新进入 PRESS 态 */ btn->state = BTN_STATE_PRESS; } break; case BTN_STATE_LONG_HOLD: if (btn->button_level != btn->active_level) { /* 长按释放 */ btn->ticks = 0; btn_fire(btn, BTN_EVT_PRESS_UP); btn->state = BTN_STATE_IDLE; } else { /* 持续触发长按保持 */ if (btn->ticks >= BTN_LONG_TICKS) { btn->ticks = 0; btn_fire(btn, BTN_EVT_LONG_PRESS_HOLD); } } break; } } /* ======== 公开 API ======== */ void Btn_Init(Btn_FSM* btn, uint8_t (*hal_read)(uint8_t button_id), uint8_t button_id, uint8_t active_level) { if (btn == (void*)0 || hal_read == (void*)0) return; memset(btn, 0, sizeof(Btn_FSM)); btn->hal_read = hal_read; btn->button_id = button_id; btn->active_level = active_level; btn->button_level = (active_level == 0) ? 1 : 0; /* 初始为未按下 */ btn->state = BTN_STATE_IDLE; btn->next = (void*)0; } void Btn_Attach(Btn_FSM* btn, BtnEvent_E event, BtnCallback cb, void* user_data) { if (btn == (void*)0 || event >= BTN_EVT_COUNT) return; btn->cb[event] = cb; btn->user_data = user_data; } void Btn_Start(Btn_FSM* btn) { if (btn == (void*)0) return; /* 头插法加入链表 */ btn->next = g_head; g_head = btn; } void Btn_Stop(Btn_FSM* btn) { if (btn == (void*)0) return; /* 二级指针遍历摘除 */ Btn_FSM** cur = &g_head; while (*cur != (void*)0) { if (*cur == btn) { *cur = btn->next; btn->next = (void*)0; return; } cur = &(*cur)->next; } } void Btn_Tick(void) { Btn_FSM* btn = g_head; while (btn != (void*)0) { btn_handler(btn); btn = btn->next; } } BtnState_E Btn_GetState(const Btn_FSM* btn) { if (btn == (void*)0) return BTN_STATE_IDLE; return (BtnState_E)btn->state; }