/** * @file keypad.h * @brief Портируемый опрос шести кнопок навигации с антидребезгом. * * Библиотека не знает ни о портах, ни о таймерах: уровни кнопок она получает * через обратный вызов порта, а время — параметром Keypad_Poll(). События * складываются в короткую очередь, поэтому быстрое нажатие не теряется, даже * если приложение перерисовывает экран. * * Кнопок ровно шесть: четыре направления, ввод и возврат. Такой набор * покрывает навигацию по меню без матрицы и сдвиговых регистров. */ #ifndef KEYPAD_H #define KEYPAD_H #include /** @brief Кнопки клавиатуры; порядок задаёт индексы для обратного вызова. */ typedef enum { KEYPAD_KEY_UP = 0, /**< Вверх: предыдущий пункт меню. */ KEYPAD_KEY_DOWN, /**< Вниз: следующий пункт меню. */ KEYPAD_KEY_LEFT, /**< Влево: уменьшение значения. */ KEYPAD_KEY_RIGHT, /**< Вправо: увеличение значения. */ KEYPAD_KEY_ENTER, /**< Ввод: вход в пункт или подтверждение. */ KEYPAD_KEY_BACK, /**< Назад: возврат на предыдущий экран. */ KEYPAD_KEY_COUNT /**< Число кнопок; не является кнопкой. */ } Keypad_Key; /** @brief Вид события клавиатуры. */ typedef enum { KEYPAD_EVENT_PRESS = 0, /**< Нажатие после подтверждения антидребезгом. */ KEYPAD_EVENT_REPEAT, /**< Автоповтор при удержании. */ KEYPAD_EVENT_LONG, /**< Удержание дольше порога, выдаётся один раз. */ KEYPAD_EVENT_RELEASE /**< Отпускание кнопки. */ } Keypad_EventType; /** @brief Событие очереди клавиатуры. */ typedef struct { Keypad_Key key; /**< Кнопка, вызвавшая событие. */ Keypad_EventType type; /**< Вид события. */ } Keypad_Event; /** Глубина очереди событий; хватает на любой темп нажатий вручную. */ #define KEYPAD_QUEUE_SIZE 8U /** Маска кнопок направлений для поля repeat_mask. */ #define KEYPAD_MASK_ARROWS ((uint8_t)((1U << KEYPAD_KEY_UP) | (1U << KEYPAD_KEY_DOWN) \ | (1U << KEYPAD_KEY_LEFT) | (1U << KEYPAD_KEY_RIGHT))) /** Маска всех кнопок клавиатуры. */ #define KEYPAD_MASK_ALL ((uint8_t)((1U << KEYPAD_KEY_COUNT) - 1U)) /** * @brief Параметры клавиатуры; нулевые поля заменяются значениями по умолчанию. */ typedef struct { /** Читает состояние кнопки: 1 — нажата. Индекс — значение Keypad_Key. */ uint8_t (*read)(void *context, uint8_t key); void *context; /**< Указатель платы для обратного вызова. */ uint16_t debounce_ms; /**< Время подтверждения уровня, по умолчанию 20. */ uint16_t repeat_delay_ms; /**< Пауза до автоповтора, по умолчанию 400. */ uint16_t repeat_period_ms;/**< Период автоповтора, по умолчанию 120. */ uint16_t long_press_ms; /**< Порог удержания, по умолчанию 800. */ uint8_t repeat_mask; /**< Кнопки с автоповтором, по умолчанию стрелки. */ } Keypad_Config; /** @brief Состояние одной кнопки. */ typedef struct { uint8_t raw; /**< Последний прочитанный уровень. */ uint8_t stable; /**< Подтверждённое состояние: 1 — нажата. */ uint8_t long_sent; /**< Признак выданного события удержания. */ uint32_t changed_ms; /**< Момент последнего изменения сырого уровня. */ uint32_t pressed_ms; /**< Момент подтверждённого нажатия. */ uint32_t repeated_ms; /**< Момент последнего события автоповтора. */ } Keypad_KeyState; /** @brief Состояние клавиатуры; принадлежит вызывающему коду. */ typedef struct { Keypad_Config config; /**< Копия параметров. */ Keypad_KeyState keys[KEYPAD_KEY_COUNT]; /**< Состояния кнопок. */ Keypad_Event queue[KEYPAD_QUEUE_SIZE]; /**< Кольцевая очередь событий. */ uint8_t head; /**< Индекс чтения очереди. */ uint8_t count; /**< Число событий в очереди. */ uint8_t overflow; /**< Признак потери событий. */ } Keypad; /** * @brief Готовит клавиатуру к работе и подставляет значения по умолчанию. * * Состояние всех кнопок считается отпущенным, поэтому зажатая при включении * кнопка даёт обычное событие нажатия после выдержки антидребезга. * * @param keypad Состояние клавиатуры. * @param config Параметры; обязателен только обратный вызов чтения. * @return 1 при успешной настройке, 0 если обратный вызов не задан. */ uint8_t Keypad_Init(Keypad *keypad, const Keypad_Config *config); /** * @brief Опрашивает кнопки и наполняет очередь событий. * * Вызывается из главного цикла с любым темпом: шаг антидребезга задаётся не * периодом вызова, а метками времени. * * @param keypad Состояние клавиатуры. * @param now_ms Текущее монотонное время в миллисекундах. */ void Keypad_Poll(Keypad *keypad, uint32_t now_ms); /** * @brief Забирает следующее событие из очереди. * * @param keypad Состояние клавиатуры. * @param event Приёмник события. * @return 1, если событие получено, 0 если очередь пуста. */ uint8_t Keypad_GetEvent(Keypad *keypad, Keypad_Event *event); /** * @brief Сообщает подтверждённое состояние кнопки. * * @param keypad Состояние клавиатуры. * @param key Опрашиваемая кнопка. * @return 1, если кнопка нажата. */ uint8_t Keypad_IsDown(const Keypad *keypad, Keypad_Key key); /** * @brief Возвращает и сбрасывает признак переполнения очереди. * * @param keypad Состояние клавиатуры. * @return 1, если события терялись с прошлого вызова. */ uint8_t Keypad_Overflow(Keypad *keypad); /** * @brief Даёт короткое имя кнопки для отладочных сообщений и экранов. * * @param key Кнопка. * @return Строка вида "UP"; для неизвестного кода — "?". */ const char *Keypad_KeyName(Keypad_Key key); #endif /* KEYPAD_H */