Files
templates/c/keypad/keypad.c

180 lines
6.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Опрос кнопок с антидребезгом, удержанием и автоповтором. Сырые уровни и подтверждённые
* состояния хранятся раздельно; события поступают в ограниченную очередь, переполнение которой
* можно обнаружить отдельно.
*/
/**
* @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];
}