1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/matrix_keypad.h

230 lines
6.9 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.
*/
/**
* @defgroup drivers_matrix_keypad Matrix Keypad
* @ingroup drivers_sensors
* @brief Matrix keypad driver for row/column keypads
*
* This module implements a simple matrix keypad driver where keys are connected
* between GPIO columns and rows. It works best with diodes in series with the
* switches to prevent key ghosting, but it can be used without these diodes.
* @ref CONFIG_MATRIX_KEYPAD_ROWS_USE_OPEN_DRAIN can be enabled when the keypad
* doesn't use diodes in the switches.
*
* The keypad works by configuring the column GPIOs as input with pull-ups. Each
* row is configured as open drain with pull-up. One by one the rows are set to
* pull their output low. For each row the column GPIOs are read and the state
* is checked. When a key is pressed the column GPIO of that switch will read
* low as soon as the row it is on is pulled low.
*
* The debouncing algorithm is a pattern style debounce where the switch must be
* in one position for a number of samples, then a set of "don't care" samples
* and then in the other position for a number of samples. The samples in the
* middle allow for a period where the switch can be either low or high without
* affecting the transition. The exact pattern is determined by
* @ref CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN and
* @ref CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END. These are used as mask where
* the switch must be in a determined state. The bits where neither pattern is
* set is used as the "don't care" set of samples.
*
* A full scan of the matrix keypad is done via the @ref matrix_keypad_scan
* function. This function iterates over all rows and columns to update the
* stored history of every pin.
*
* When a state change is detected on a switch, the @ref matrix_keypad_cb_t
* callback is called with the row and column number together with the new state
* of the switch (pressed or not pressed).
*
* @{
*
* @file
* @brief Interface definition for the matrix keypad
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef MATRIX_KEYPAD_H
#define MATRIX_KEYPAD_H
#include <stdint.h>
#include <stdbool.h>
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum number of rows
*/
#ifndef CONFIG_MATRIX_KEYPAD_NUM_ROWS
#define CONFIG_MATRIX_KEYPAD_NUM_ROWS 2
#endif
/**
* @brief Maximum number of columns
*/
#ifndef CONFIG_MATRIX_KEYPAD_NUM_COLUMNS
#define CONFIG_MATRIX_KEYPAD_NUM_COLUMNS 2
#endif
/**
* @brief Debounce pattern high to low bits
*
* This pattern should consist of bits set on the most significant bits of the
* value. The number of bits set determines the number of scans the key must be
* in the current state before a change is processed.
*/
#ifndef CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN
#define CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN 0xC0
#endif
/**
* @brief Debounce pattern low to high bits
*
* This pattern should consist of bits set on the least significant bits of the
* value. The number of bits set determines the number of scans the key must be
* in the next state before a change is processed.
*/
#ifndef CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END
#define CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END 0x7
#endif
/**
* @brief Use open drain GPIO mode
*/
#ifndef CONFIG_MATRIX_KEYPAD_ROWS_USE_OPEN_DRAIN
#define CONFIG_MATRIX_KEYPAD_ROWS_USE_OPEN_DRAIN 0
#endif
/**
* @brief GPIO mode used for the row GPIOs
*/
#if CONFIG_MATRIX_KEYPAD_ROWS_USE_OPEN_DRAIN
#define MATRIX_KEYPAD_ROWS_GPIO_MODE GPIO_OD_PU
#else
#define MATRIX_KEYPAD_ROWS_GPIO_MODE GPIO_OUT
#endif
/**
* @brief Debounce pattern mask
*/
#define MATRIX_KEYPAD_DEBOUNCE_MASK \
(CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_BEGIN | CONFIG_MATRIX_KEYPAD_DEBOUNCE_PATTERN_END)
/**
* @brief Type definition for a full row (all columns) state, variable width
* depending on the number of columns.
*/
#if CONFIG_MATRIX_KEYPAD_NUM_COLUMNS <= 8
typedef uint8_t matrix_keypad_state_row_t;
#elif CONFIG_MATRIX_KEYPAD_NUM_COLUMNS <= 16
typedef uint16_t matrix_keypad_state_row_t;
#elif CONFIG_MATRIX_KEYPAD_NUM_COLUMNS <= 32
typedef uint32_t matrix_keypad_state_row_t;
#elif CONFIG_MATRIX_KEYPAD_NUM_COLUMNS <= 64
typedef uint64_t matrix_keypad_state_row_t;
#else
#error Too many columns on matrix keypad.
#endif
/**
* @brief Device initialization parameters
*/
typedef struct {
/**
* @brief GPIO pin array for the rows. GPIO_UNDEF is allowed and scanning
* is skipped for these.
*/
gpio_t rows[CONFIG_MATRIX_KEYPAD_NUM_ROWS];
/**
* @brief GPIO pin array for the columns. GPIO_UNDEF is allowed and scanning
* is skipped for these.
*/
gpio_t columns[CONFIG_MATRIX_KEYPAD_NUM_COLUMNS];
/**
* @brief Delay in microseconds between configuring the row gpio and reading
* out the column. Can be zero to skip the delay.
*/
uint32_t row2col_delay;
} matrix_keypad_params_t;
/**
* @brief Callback for key state changes
*
* @param arg callback context
* @param row Row that changed
* @param column Column that changed
* @param state New state of the key, 1 = pressed, 0 = released
*/
typedef void (*matrix_keypad_cb_t)(void *arg, size_t row, size_t column, bool state);
/**
* @brief Device descriptor for the driver
*/
typedef struct {
/**
* @brief Device initialization parameters
*/
const matrix_keypad_params_t *params;
/**
* @brief Debounce history
*/
uint8_t debounce[CONFIG_MATRIX_KEYPAD_NUM_ROWS][CONFIG_MATRIX_KEYPAD_NUM_COLUMNS];
/**
* @brief Current button state
*/
matrix_keypad_state_row_t state[CONFIG_MATRIX_KEYPAD_NUM_ROWS];
/**
* @brief callback context
*/
void *arg;
/**
* @brief Callback called when a key changes state
*/
matrix_keypad_cb_t callback;
} matrix_keypad_t;
/**
* @brief Initialize the given device
*
* @param[inout] dev Device descriptor of the driver
* @param[in] params Initialization parameters
* @param[in] callback Callback to call on state changes
* @param[in] arg Context argument for the callback
*
* @return 0 on success
*/
int matrix_keypad_init(matrix_keypad_t *dev,
const matrix_keypad_params_t *params,
matrix_keypad_cb_t callback,
void *arg);
/**
* @brief Scan through the keypad matrix
*
* This updates the state of the device descriptor, calling
* @ref matrix_keypad_cb_t when a key press or release has been detected
*
* @param[inout] dev Device descriptor of the driver
*
* @return Number of keys that changed state
*/
size_t matrix_keypad_scan(matrix_keypad_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* MATRIX_KEYPAD_H */
/** @} */