/** * @file keypad.c * @brief Антидребезг, автоповтор и очередь событий шести кнопок. * * Опрос программный: уровни читаются обратным вызовом порта, состояние * подтверждается выдержкой debounce_ms, а автоповтор и удержание отсчитываются * от момента подтверждённого нажатия. Прерывания не используются, поэтому * библиотека одинаково работает с выводами МК, расширителями и матрицами. */ #include "keypad.h" /** Значения по умолчанию для незаданных параметров. */ #define KEYPAD_DEFAULT_DEBOUNCE_MS 20U #define KEYPAD_DEFAULT_REPEAT_DELAY_MS 400U #define KEYPAD_DEFAULT_REPEAT_PERIOD_MS 120U #define KEYPAD_DEFAULT_LONG_PRESS_MS 800U /** * @brief Кладёт событие в очередь, отбрасывая его при переполнении. * * @param keypad Состояние клавиатуры. * @param key Кнопка. * @param type Вид события. */ static void keypad_push(Keypad *keypad, uint8_t key, Keypad_EventType type) { uint8_t tail; if (keypad->count >= (uint8_t)KEYPAD_QUEUE_SIZE) { keypad->overflow = 1U; return; } tail = (uint8_t)((keypad->head + keypad->count) % (uint8_t)KEYPAD_QUEUE_SIZE); keypad->queue[tail].key = (Keypad_Key)key; keypad->queue[tail].type = type; keypad->count++; } uint8_t Keypad_Init(Keypad *keypad, const Keypad_Config *config) { uint8_t index; if ((keypad == 0) || (config == 0) || (config->read == 0)) { return 0U; } keypad->config = *config; if (keypad->config.debounce_ms == 0U) { keypad->config.debounce_ms = KEYPAD_DEFAULT_DEBOUNCE_MS; } if (keypad->config.repeat_delay_ms == 0U) { keypad->config.repeat_delay_ms = KEYPAD_DEFAULT_REPEAT_DELAY_MS; } if (keypad->config.repeat_period_ms == 0U) { keypad->config.repeat_period_ms = KEYPAD_DEFAULT_REPEAT_PERIOD_MS; } if (keypad->config.long_press_ms == 0U) { keypad->config.long_press_ms = KEYPAD_DEFAULT_LONG_PRESS_MS; } if (keypad->config.repeat_mask == 0U) { keypad->config.repeat_mask = KEYPAD_MASK_ARROWS; } for (index = 0U; index < (uint8_t)KEYPAD_KEY_COUNT; index++) { keypad->keys[index].raw = 0U; keypad->keys[index].stable = 0U; keypad->keys[index].long_sent = 0U; keypad->keys[index].changed_ms = 0U; keypad->keys[index].pressed_ms = 0U; keypad->keys[index].repeated_ms = 0U; } keypad->head = 0U; keypad->count = 0U; keypad->overflow = 0U; return 1U; } void Keypad_Poll(Keypad *keypad, uint32_t now_ms) { uint8_t index; if ((keypad == 0) || (keypad->config.read == 0)) { return; } for (index = 0U; index < (uint8_t)KEYPAD_KEY_COUNT; index++) { Keypad_KeyState *state = &keypad->keys[index]; const uint8_t level = (uint8_t)((keypad->config.read(keypad->config.context, index) != 0U) ? 1U : 0U); if (level != state->raw) { /* Уровень изменился: отсчёт выдержки начинается заново. */ state->raw = level; state->changed_ms = now_ms; continue; } if (level == state->stable) { /* Подтверждённое нажатие: автоповтор и порог удержания. */ if (state->stable == 0U) { continue; } if ((state->long_sent == 0U) && ((now_ms - state->pressed_ms) >= keypad->config.long_press_ms)) { state->long_sent = 1U; keypad_push(keypad, index, KEYPAD_EVENT_LONG); } if ((((keypad->config.repeat_mask >> index) & 1U) != 0U) && ((now_ms - state->pressed_ms) >= keypad->config.repeat_delay_ms) && ((now_ms - state->repeated_ms) >= keypad->config.repeat_period_ms)) { state->repeated_ms = now_ms; keypad_push(keypad, index, KEYPAD_EVENT_REPEAT); } continue; } if ((now_ms - state->changed_ms) < keypad->config.debounce_ms) { continue; } state->stable = level; if (level != 0U) { state->pressed_ms = now_ms; state->repeated_ms = now_ms; state->long_sent = 0U; keypad_push(keypad, index, KEYPAD_EVENT_PRESS); } else { keypad_push(keypad, index, KEYPAD_EVENT_RELEASE); } } } uint8_t Keypad_GetEvent(Keypad *keypad, Keypad_Event *event) { if ((keypad == 0) || (event == 0) || (keypad->count == 0U)) { return 0U; } *event = keypad->queue[keypad->head]; keypad->head = (uint8_t)((keypad->head + 1U) % (uint8_t)KEYPAD_QUEUE_SIZE); keypad->count--; return 1U; } uint8_t Keypad_IsDown(const Keypad *keypad, Keypad_Key key) { if ((keypad == 0) || (key >= KEYPAD_KEY_COUNT)) { return 0U; } return keypad->keys[key].stable; } uint8_t Keypad_Overflow(Keypad *keypad) { uint8_t flag; if (keypad == 0) { return 0U; } flag = keypad->overflow; keypad->overflow = 0U; return flag; } const char *Keypad_KeyName(Keypad_Key key) { static const char *const names[KEYPAD_KEY_COUNT] = { "UP", "DOWN", "LEFT", "RIGHT", "ENTER", "BACK" }; if (key >= KEYPAD_KEY_COUNT) { return "?"; } return names[key]; }