feat(keypad): опрос шести кнопок с антидребезгом и автоповтором
Перенесён из KONOR_ds18b20/lib/keypad; в OpticalTester лежала такая же копия. О портах и таймерах библиотека не знает: уровни приходят обратным вызовом read(), время — параметром Keypad_Poll(). События складываются в очередь на восемь штук, поэтому быстрое нажатие не теряется, пока приложение перерисовывает экран.
This commit is contained in:
145
c/keypad/keypad.h
Normal file
145
c/keypad/keypad.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @file keypad.h
|
||||
* @brief Портируемый опрос шести кнопок навигации с антидребезгом.
|
||||
*
|
||||
* Библиотека не знает ни о портах, ни о таймерах: уровни кнопок она получает
|
||||
* через обратный вызов порта, а время — параметром Keypad_Poll(). События
|
||||
* складываются в короткую очередь, поэтому быстрое нажатие не теряется, даже
|
||||
* если приложение перерисовывает экран.
|
||||
*
|
||||
* Кнопок ровно шесть: четыре направления, ввод и возврат. Такой набор
|
||||
* покрывает навигацию по меню без матрицы и сдвиговых регистров.
|
||||
*/
|
||||
|
||||
#ifndef KEYPAD_H
|
||||
#define KEYPAD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** @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 */
|
||||
Reference in New Issue
Block a user