1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/matrix_keypad/matrix_keypad.c
Koen Zandberg 21ac46c89d
matrix_keypad: use gpio_is_valid
Bit of future proofing this module and use gpio_is_valid instead of
GPIO_UNDEF everywhere
2023-01-17 11:03:01 +01:00

136 lines
3.7 KiB
C

/*
* Copyright (C) 2021 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_matrix_keypad
* @{
*
* @file
* @brief Device driver implementation for the drivers
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#include <string.h>
#include "matrix_keypad.h"
#include "matrix_keypad_params.h"
#include "periph/gpio.h"
#include "ztimer.h"
static uint8_t _mask_bits(uint8_t bits)
{
return bits & MATRIX_KEYPAD_DEBOUNCE_MASK;
}
static void _flip_state(matrix_keypad_t *dev, size_t row, size_t column)
{
dev->state[row] ^= (1 << column);
}
static void _setup_columns(matrix_keypad_t *dev)
{
for (size_t i = 0; i < CONFIG_MATRIX_KEYPAD_NUM_COLUMNS; i++) {
gpio_t column = dev->params->columns[i];
if (gpio_is_valid(column)) {
gpio_init(column, GPIO_IN_PU);
}
}
}
static void _setup_rows(matrix_keypad_t *dev)
{
for (size_t i = 0; i < CONFIG_MATRIX_KEYPAD_NUM_ROWS; i++) {
gpio_t row = dev->params->rows[i];
if (gpio_is_valid(row)) {
gpio_init(row, MATRIX_KEYPAD_ROWS_GPIO_MODE); /* Open drain to ensure rows don't conflict */
gpio_set(row);
}
}
}
unsigned _update_key(matrix_keypad_t *dev,
size_t row, size_t column, bool status)
{
/* Pattern based debounce:
* https://hackaday.com/2015/12/10/embed-with-elliot-debounce-your-noisy-buttons-part-ii/
*/
/* get the current stored state */
bool state = dev->state[row] & (1 << column);
/* get the history of the pin */
uint8_t *debounce = &dev->debounce[row][column];
*debounce = (*debounce << 1) | status; /* Update state */
/* Pin history masking must match the begin pattern if it is currently
* pressed and match the end pattern if it is released */
uint8_t pattern = state ? CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN
: CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END;
if (_mask_bits(*debounce) == pattern) {
/* Changed */
*debounce = 0U - (unsigned)state; /* 0x0 if released, 0xFF if pressed */
_flip_state(dev, row, column);
dev->callback(dev->arg, row, column, status);
return 1;
}
return 0;
}
int matrix_keypad_init(matrix_keypad_t *dev, const matrix_keypad_params_t *params,
matrix_keypad_cb_t callback, void *arg)
{
static_assert(
(CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN & CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END) == 0,
"Debounce patterns must not overlap");
memset(dev, 0, sizeof(matrix_keypad_t));
dev->params = params;
dev->callback = callback;
dev->arg = arg;
_setup_columns(dev);
_setup_rows(dev);
return 0;
}
size_t matrix_keypad_scan(matrix_keypad_t *dev)
{
size_t res = 0;
/* Scan rows */
for (size_t i = 0; i < CONFIG_MATRIX_KEYPAD_NUM_ROWS; i++) {
gpio_t row = dev->params->rows[i];
if (!gpio_is_valid(row)) {
continue;
}
/* Pull the row low */
gpio_clear(row);
/* Wait for the row delay */
if (dev->params->row2col_delay) {
ztimer_sleep(ZTIMER_USEC, dev->params->row2col_delay);
}
/* Scan columns */
for (size_t j = 0; j < CONFIG_MATRIX_KEYPAD_NUM_COLUMNS; j++) {
gpio_t column = dev->params->columns[j];
if (!gpio_is_valid(column)) {
continue;
}
bool status = !gpio_read(column);
res += _update_key(dev, i, j, status);
}
/* Return the row to high-Z */
gpio_set(row);
}
return res;
}