/****************************************************************************** * @file key.c * @brief 按键状态机实现 * @author cyWu <1917507415@qq.com> * @date 2026.08.25 * @version V1.0.0 * @history * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按键短按/长按/释放状态机 ******************************************************************************/ #include "key.h" #include static uint32_t s_key_count_time; static uint8_t s_key_num = USR_KEY_COUNT; keyCategory_t keyTable[USR_KEY_COUNT]; /** * @brief 读按键电平到 fsm * @param key 状态机 * @return 1=已读,0=被屏蔽 */ static uint8_t get_key_level(keyFSM_t *key) { if (key->keyShield == KEY_DISABLE) { return 0; } if (key->keyReadValue == NULL) { return 0; } key->keyLevel = (key->keyReadValue() == key->keyDownLevel) ? Bit_SET : Bit_RESET; return 1; } /** * @brief 单击 / 双击间隔判定 * @param click 状态机 * @return 1=可以出事件 */ static uint8_t key_click_frequency(keyFSM_t *click) { click->keyInterval++; switch (click->keyFrequency) { case CLICK: if (click->keyInterval >= (KEY_INTERVAL / (DEBOUNCE_TIME * KEY_TIMER_MS))) { click->eventType = SHORT_Event; return 1; } break; case DbLCLICK: if (click->keyInterval >= (KEY_INTERVAL / (DEBOUNCE_TIME * KEY_TIMER_MS))) { click->eventType = DBCL_Event; return 1; } break; default: return 1; } return 0; } /** * @brief 按键状态机步进 * @param key_buf 状态机 * @return 1=已处理 */ static uint8_t read_key_status(keyFSM_t *key_buf) { if (!get_key_level(key_buf)) { return 0; } switch (key_buf->keyStatus) { case KEY_ST_NULL: if (key_buf->keyLevel == Bit_SET) { key_buf->keyStatus = KEY_ST_SURE; } break; case KEY_ST_SURE: if (key_buf->keyLevel == Bit_SET) { key_buf->eventType = DOWN_Event; key_buf->keyStatus = KEY_ST_DOWN; key_buf->keyCount = 0; key_buf->keyLongFlag = 1; } else { key_buf->keyStatus = KEY_ST_NULL; } break; case KEY_ST_DOWN: if (key_buf->keyLevel != Bit_SET) { if (key_buf->keyShortFlag == 0) { key_buf->keyShortFlag = 1; key_buf->keyFrequency++; key_buf->keyInterval = 0; } if (key_click_frequency(key_buf)) { key_buf->keyFrequency = 0; key_buf->keyStatus = KEY_ST_NULL; } if ((key_buf->keyCount >= key_buf->keyLongTime / DEBOUNCE_TIME) && (key_buf->keyCount < key_buf->keyLastTime / DEBOUNCE_TIME)) { key_buf->keyFrequency = 0; key_buf->keyStatus = KEY_ST_NULL; key_buf->eventType = RELEASE_Event; } key_buf->keyCount = 0; key_buf->keyLongFlag = 1; } else { if ((++key_buf->keyCount >= key_buf->keyLastTime / DEBOUNCE_TIME)) { key_buf->keyCount = 0; key_buf->keyFrequency = 0; key_buf->keyStatus = KEY_ST_LONG; key_buf->eventType = LAST_Event; key_buf->keyLongFlag = 1; } if ((key_buf->keyCount >= key_buf->keyLongTime / DEBOUNCE_TIME) && (key_buf->keyCount < key_buf->keyLastTime / DEBOUNCE_TIME)) { key_buf->keyFrequency = 0; if (key_buf->keyLongFlag == 1) { key_buf->eventType = LONG_Event; key_buf->keyLongFlag = 0; } } key_buf->keyShortFlag = 0; } break; case KEY_ST_LONG: if (key_buf->keyLevel != Bit_SET) { key_buf->keyStatus = KEY_ST_NULL; key_buf->eventType = RELEASE_Event; } break; default: break; } return 1; } /** * @brief 扫描全部按键状态机 * @return 无 */ static void key_event_process(void) { size_t i; for (i = 0; i < s_key_num; i++) { (void)read_key_status(&keyTable[i].fsm); } } /** * @brief 1ms 调用一次;满 DEBOUNCE_TIME 才真正采样 * @return 无 */ void keyCheckProcess(void) { s_key_count_time++; if (s_key_count_time >= (DEBOUNCE_TIME / KEY_TIMER_MS)) { s_key_count_time = 0; key_event_process(); } } /** * @brief 拷贝产品按键表 * @param keys 按键数组,长度 USR_KEY_COUNT * @return 无 */ void keyParaInit(keyCategory_t *keys) { if (keys == NULL) { return; } if (USR_KEY_COUNT >= KEY_MAX_NUMBER) { s_key_num = KEY_MAX_NUMBER; } memcpy(keyTable, keys, sizeof(keyCategory_t) * s_key_num); } /** * @brief 运行中改长按/连按时长并复位状态 * @param key_event 按键索引 * @param key_param 新参数 * @return 无 */ void setKeyEventParams(uint8_t key_event, keyCategory_t key_param) { if (key_event >= s_key_num) { return; } keyTable[key_event].fsm.keyShield = key_param.fsm.keyShield; keyTable[key_event].fsm.keyLongTime = key_param.fsm.keyLongTime; keyTable[key_event].fsm.keyLastTime = key_param.fsm.keyLastTime; keyTable[key_event].fsm.keyStatus = KEY_ST_NULL; keyTable[key_event].fsm.eventType = NULL_Event; keyTable[key_event].fsm.keyCount = 0; } /** * @brief 派发按键事件回调 * @return 无 */ void keyHandle(void) { size_t i; for (i = 0; i < s_key_num; i++) { if (keyTable[i].fsm.eventType == NULL_Event) { continue; } switch (keyTable[i].fsm.eventType) { case RELEASE_Event: if (keyTable[i].func.releasePressCb != NULL) { keyTable[i].func.releasePressCb(); } keyTable[i].fsm.eventType = NULL_Event; break; case SHORT_Event: if (keyTable[i].func.ShortPressCb != NULL) { keyTable[i].func.ShortPressCb(); } keyTable[i].fsm.eventType = NULL_Event; break; case DOWN_Event: if (keyTable[i].func.downPressCb != NULL) { keyTable[i].func.downPressCb(); } keyTable[i].fsm.eventType = NULL_Event; break; case LONG_Event: if (keyTable[i].func.longPressCb != NULL) { keyTable[i].func.longPressCb(); } keyTable[i].fsm.eventType = NULL_Event; break; case LAST_Event: if (keyTable[i].func.lastPressCb != NULL) { keyTable[i].func.lastPressCb(); } break; case DBCL_Event: if (keyTable[i].func.dbclPressCb != NULL) { keyTable[i].func.dbclPressCb(); } keyTable[i].fsm.eventType = NULL_Event; break; default: break; } } } /** * @brief 复位指定按键状态机 * @param key_index 按键索引 * @return 无 */ void reset_key_Status(keyList key_index) { if (key_index >= USR_KEY_COUNT) { return; } keyTable[key_index].fsm.keyStatus = KEY_ST_NULL; keyTable[key_index].fsm.eventType = NULL_Event; keyTable[key_index].fsm.keyCount = 0; }