input: add keyboard matrix driver

Add a generic kmatrix lower-half with polling/debounce, STM32 board adapters, Kconfig options, a public API header, and a test example/documentation.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>

decouple kbd / keypad.

Fix some comments

add documentation

fix rule issues

Update CMakeLists.txt

update documentation.

improve documentation
This commit is contained in:
Felipe Moura
2026-02-08 11:46:26 -03:00
committed by Alan C. Assis
parent 6c37217c36
commit ef71625ede
19 changed files with 1950 additions and 123 deletions
@@ -0,0 +1 @@
<binary image placeholder>
@@ -5,6 +5,7 @@ Input Devices
.. toctree::
:caption: Supported Drivers
keypad-keyboard.rst
keypad.rst
sbutton.rst
@@ -0,0 +1,143 @@
=======================
Keyboard/Keypad Drivers
=======================
**Keypads vs. Keyboards** Keyboards and keypads are really the same
devices for NuttX. A keypad is thought of as simply a keyboard with
fewer keys.
**Special Commands**. In NuttX, a keyboard/keypad driver is simply
a character driver that may have an (optional) encoding/decoding
layer on the data returned by the character driver. A keyboard may
return simple text data (alphabetic, numeric, and punctuation) or
control characters (enter, control-C, etc.) when a key is pressed.
We can think about this the "normal" keyboard data stream.
However, in addition, most keyboards support actions that cannot
be represented as text or control data. Such actions include
things like cursor controls (home, up arrow, page down, etc.),
editing functions (insert, delete, etc.), volume controls, (mute,
volume up, etc.) and other special functions. In this case, some
special encoding may be required to multiplex the normal text data
and special command key press data streams.
**Key Press and Release Events** Sometimes the time that a key is
released is needed by applications as well. Thus, in addition to
normal and special key press events, it may also be necessary to
encode normal and special key release events.
**Encoding/Decoding** Layer. An optional encoding/decoding layer
can be used with the basic character driver to encode the keyboard
events into the text data stream. The function interfaces that
comprise that encoding/decoding layer are defined in the header
file ``include/nuttx/input/kbd_code.h``. These functions provide
a matched set of (a) driver encoding interfaces, and (b)
application decoding interfaces.
#. **Driver Encoding Interfaces**. These are interfaces used by
the keyboard/keypad driver to encode keyboard events and data.
- ``kbd_press()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``ch``: The character to be added to the output stream.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
- ``kbd_release()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``ch``: The character associated with the key that was
released.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
- ``kbd_specpress()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``keycode``: The command to be added to the output
stream. The enumeration ``enum kbd_keycode_e keycode``
identifies all commands known to the system.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
- ``kbd_specrel()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``keycode``: The command to be added to the output
stream. The enumeration ``enum kbd_keycode_e keycode``
identifies all commands known to the system.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
#. **Application Decoding Interfaces**. These are user interfaces
to decode the values returned by the keyboard/keypad driver.
- ``kbd_decode()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``stream``: An instance of ``lib_instream_s`` to perform
the actual low-level get operation.
- ``pch``: The location to save the returned value. This
may be either a normal, character code or a special
command (i.e., a value from ``enum kbd_getstate_s``.
- ``state``: A user provided buffer to support parsing.
This structure should be cleared the first time that
``kbd_decode()`` is called.
**Returned Value:**
- ``KBD_PRESS`` (0)**: Indicates the successful receipt
of normal, keyboard data. This corresponds to a keypress
event. The returned value in ``pch`` is a simple byte of
text or control data.
- ``KBD_RELEASE`` (1)**: Indicates a key release event.
The returned value in ``pch`` is the byte of text or
control data corresponding to the released key.
- ``KBD_SPECPRESS`` (2)**: Indicates the successful
receipt of a special keyboard command. The returned value
in ``pch`` is a value from ``enum kbd_getstate_s``.
- ``KBD_SPECREL`` (3)**: Indicates a special command key
release event. The returned value in ``pch`` is a value
from ``enum kbd_getstate_s``.
- ``KBD_ERROR`` (``EOF``)**: An error has getting the
next character (reported by the ``stream``). Normally
indicates the end of file.
**I/O Streams**. Notice the use of the abstract I/O streams in
these interfaces. These stream interfaces are defined in
``include/nuttx/streams.h``.
@@ -1,142 +1,83 @@
=======================
Keyboard/Keypad Drivers
Matrix Keypad (KMATRIX)
=======================
**Keypads vs. Keyboards** Keyboards and keypads are really the
same devices for NuttX. A keypad is thought of as simply a
keyboard with fewer keys.
**What is a Keypad?**
A keypad is a small keyboard with a limited set of keys, typically
arranged in a matrix. It is commonly used for numeric input, access
control, or simple user interfaces.
**Special Commands**. In NuttX, a keyboard/keypad driver is simply
a character driver that may have an (optional) encoding/decoding
layer on the data returned by the character driver. A keyboard may
return simple text data (alphabetic, numeric, and punctuation) or
control characters (enter, control-C, etc.) when a key is pressed.
We can think about this the "normal" keyboard data stream.
However, in addition, most keyboards support actions that cannot
be represented as text or control data. Such actions include
things like cursor controls (home, up arrow, page down, etc.),
editing functions (insert, delete, etc.), volume controls, (mute,
volume up, etc.) and other special functions. In this case, some
special encoding may be required to multiplex the normal text data
and special command key press data streams.
For example, a typical 12-key numeric keypad looks like this:
**Key Press and Release Events** Sometimes the time that a key is
released is needed by applications as well. Thus, in addition to
normal and special key press events, it may also be necessary to
encode normal and special key release events.
.. image:: images/keypad-example.png
:alt: Example of a 12-key matrix keypad
:align: center
:width: 200px
**Encoding/Decoding** Layer. An optional encoding/decoding layer
can be used with the basic character driver to encode the keyboard
events into the text data stream. The function interfaces that
comprise that encoding/decoding layer are defined in the header
file ``include/nuttx/input/kbd_code.h``. These functions provide
an matched set of (a) driver encoding interfaces, and (b)
application decoding interfaces.
**Purpose**. The KMATRIX driver provides a generic keypad
implementation for boards that expose a switch matrix through GPIOs.
It periodically scans rows and columns, detects state changes with a
simple debounce, and emits keyboard events through the common keyboard
upper-half. This makes the device available as a character driver
(e.g., ``/dev/keypad0``) using the standard keyboard
interfaces.
#. **Driver Encoding Interfaces**. These are interfaces used by
the keyboard/keypad driver to encode keyboard events and data.
**Why Polling**. This first version uses polling to be broadly usable
on any board with available GPIOs, without requiring per-board IRQ
wiring, pin interrupt capabilities, or expander-specific interrupt
support. Polling also simplifies early bring-up and makes the driver
predictable while the keymap and GPIO configuration are validated.
Future iterations are expected to add interrupt-driven scanning and
I2C expander variants; the GPIO polling path remains a good baseline
and fallback.
- ``kbd_press()``
**Driver Overview**. The KMATRIX lower-half scans the matrix and calls
``keyboard_event()`` when it detects a press or release. The keyboard
upper-half registers the character device at the requested ``devpath``
and stores events in a circular buffer. Applications read
``struct keyboard_event_s`` from the device or use the optional
kbd-codec layer.
**Function Prototype:**
**Board Support**. To support KMATRIX, a board must provide:
**Description:**
#. **GPIO Definitions**
**Input Parameters:**
- Define the row and column GPIOs (arrays of pins).
- Provide a keymap array indexed by ``row * ncols + col``.
- ``ch``: The character to be added to the output stream.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
#. **Configuration Callbacks**
**Returned Value:**
- ``config_row(pin)``: Configure a row GPIO as output.
- ``config_col(pin)``: Configure a column GPIO as input with pull-up
or pull-down consistent with the wiring.
- ``row_set(pin, active)``: Drive a row active/inactive. For the
STM32F4Discovery example, rows are driven low to activate.
- ``col_get(pin)``: Read a column and return ``true`` when pressed.
- ``kbd_release()``
#. **Registration Hook**
**Function Prototype:**
- Implement ``board_kmatrix_initialize(const char *devpath)`` to
call ``kmatrix_register(&config, devpath)``.
- Invoke the board hook during bring-up (for example,
``board_kmatrix_initialize("/dev/keypad0")``).
**Description:**
**Reference Implementation (STM32F4Discovery)**. The current reference
is in ``boards/arm/stm32/common/src/stm32_kmatrix_gpio.c``:
**Input Parameters:**
- Rows: ``BOARD_KMATRIX_ROW0..3`` (outputs)
- Columns: ``BOARD_KMATRIX_COL0..2`` (inputs with pull-up)
- Keymap: 4x3 phone keypad layout
- Callbacks: ``km_stm32_config_row``, ``km_stm32_config_col``,
``km_stm32_row_set``, ``km_stm32_col_get``
- Registration: ``board_kmatrix_initialize()`` calls
``kmatrix_register()``
- ``ch``: The character associated with the key that was
released.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Data Path Summary**.
**Returned Value:**
- ``kbd_specpress()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``keycode``: The command to be added to the output
stream. The enumeration ``enum kbd_keycode_e keycode``
identifies all commands known to the system.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
- ``kbd_specrel()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``keycode``: The command to be added to the output
stream. The enumeration ``enum kbd_keycode_e keycode``
identifies all commands known to the system.
- ``stream``: An instance of ``lib_outstream_s`` to perform
the actual low-level put operation.
**Returned Value:**
#. **Application Decoding Interfaces**. These are user interfaces
to decode the values returned by the keyboard/keypad driver.
- ``kbd_decode()``
**Function Prototype:**
**Description:**
**Input Parameters:**
- ``stream``: An instance of ``lib_instream_s`` to perform
the actual low-level get operation.
- ``pch``: The location to save the returned value. This
may be either a normal, character code or a special
command (i.e., a value from ``enum kbd_getstate_s``.
- ``state``: A user provided buffer to support parsing.
This structure should be cleared the first time that
``kbd_decode()`` is called.
**Returned Value:**
- ``KBD_PRESS`` (0)**: Indicates the successful receipt
of normal, keyboard data. This corresponds to a keypress
event. The returned value in ``pch`` is a simple byte of
text or control data.
- ``KBD_RELEASE`` (1)**: Indicates a key release event.
The returned value in ``pch`` is the byte of text or
control data corresponding to the released key.
- ``KBD_SPECPRESS`` (2)**: Indicates the successful
receipt of a special keyboard command. The returned value
in ``pch`` is a value from ``enum kbd_getstate_s``.
- ``KBD_SPECREL`` (3)**: Indicates a special command key
release event. The returned value in ``pch`` is a value
from ``enum kbd_getstate_s``.
- ``KBD_ERROR`` (``EOF``)**: An error has getting the
next character (reported by the ``stream``). Normally
indicates the end of file.
**I/O Streams**. Notice the use of the abstract I/O streams in
these interfaces. These stream interfaces are defined in
``include/nuttx/streams.h``.
- Board calls ``board_kmatrix_initialize("/dev/keypad0")``
- ``kmatrix_register()`` configures GPIOs and calls
``keyboard_register(&lower, devpath, buflen)``
- The upper-half registers the device node at ``devpath``
- ``kmatrix_scan_worker()`` calls ``keyboard_event()`` on press/release
- Applications read events from the device node
@@ -0,0 +1,87 @@
/****************************************************************************
* boards/arm/stm32/common/include/stm32_kmatrix_gpio.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_GPIO_H
#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_GPIO_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Type Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: board_kmatrix_initialize
*
* Description:
* This function is called by application-specific setup logic to
* configure the keyboard matrix device.
*
* Input Parameters:
* devpath - The device path, typically "/dev/kbd0"
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int board_kmatrix_initialize(const char *devpath);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_GPIO_H */
@@ -0,0 +1,87 @@
/****************************************************************************
* boards/arm/stm32/common/include/stm32_kmatrix_i2c.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_I2C_H
#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_I2C_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Type Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: board_kmatrix_i2c_initialize
*
* Description:
* This function is called by application-specific setup logic to
* configure the keyboard matrix device using an I2C GPIO expander.
*
* Input Parameters:
* devpath - The device path, typically "/dev/kbd0"
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int board_kmatrix_i2c_initialize(const char *devpath);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_KMATRIX_I2C_H */
@@ -158,4 +158,12 @@ if(CONFIG_INPUT_SBUTTON)
list(APPEND SRCS stm32_sbutton.c)
endif()
if(CONFIG_INPUT_KMATRIX)
list(APPEND SRCS stm32_kmatrix_gpio.c)
endif()
if(CONFIG_INPUT_KMATRIX_I2C)
list(APPEND SRCS stm32_kmatrix_i2c.c)
endif()
target_sources(board PRIVATE ${SRCS})
+8
View File
@@ -166,6 +166,14 @@ ifeq ($(CONFIG_INPUT_SBUTTON),y)
CSRCS += stm32_sbutton.c
endif
ifeq ($(CONFIG_INPUT_KMATRIX),y)
CSRCS += stm32_kmatrix_gpio.c
endif
ifeq ($(CONFIG_INPUT_KMATRIX_I2C),y)
CSRCS += stm32_kmatrix_i2c.c
endif
DEPPATH += --dep-path src
VPATH += :src
CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src
@@ -0,0 +1,355 @@
/****************************************************************************
* boards/arm/stm32/common/src/stm32_kmatrix_gpio.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <stdio.h>
#include <errno.h>
#include <debug.h>
#include <unistd.h>
#include <nuttx/board.h>
#include <arch/board/board.h>
#include <nuttx/input/kmatrix.h>
#include "stm32.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
typedef uint32_t kmatrix_pin_t;
struct stm32_kmatrix_gpio_config_s
{
/* Configuration structure as seen by the kmatrix driver */
struct kmatrix_config_s config;
/* Additional private definitions only known to this driver */
void *arg; /* Argument to pass if needed */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void km_stm32_config_row(kmatrix_pin_t pin);
static void km_stm32_config_col(kmatrix_pin_t pin);
static void km_stm32_row_set(kmatrix_pin_t pin, bool active);
static bool km_stm32_col_get(kmatrix_pin_t pin);
/****************************************************************************
* Private Data
****************************************************************************/
/* Row and column GPIO pin definitions for 4x3 keypad matrix on
* STM32F4Discovery
* Rows: PB0-PB3 (outputs)
* Columns: PC0-PC2 (inputs with pull-up)
*/
static const kmatrix_pin_t g_km_rows[] =
{
BOARD_KMATRIX_ROW0,
BOARD_KMATRIX_ROW1,
BOARD_KMATRIX_ROW2,
BOARD_KMATRIX_ROW3,
};
static const kmatrix_pin_t g_km_cols[] =
{
BOARD_KMATRIX_COL0,
BOARD_KMATRIX_COL1,
BOARD_KMATRIX_COL2,
};
/* Keymap for 4x3 matrix - Standard phone keypad layout
* Rows: 0-3, Columns: 0-2
*/
static const uint32_t g_km_keymap[] =
{
'1', '2', '3', /* Row 0 */
'4', '5', '6', /* Row 1 */
'7', '8', '9', /* Row 2 */
'*', '0', '#', /* Row 3 */
};
/* A reference to a structure of this type must be passed to the kmatrix
* driver. This structure provides information about the configuration
* of the keypad matrix and provides GPIO callbacks.
*
* Memory for this structure is provided by the caller. It is not copied
* by the driver and is presumed to persist while the driver is active.
*/
static struct stm32_kmatrix_gpio_config_s g_km_config =
{
.config =
{
.nrows = 4,
.ncols = 3,
.rows = g_km_rows,
.cols = g_km_cols,
.keymap = g_km_keymap,
.poll_interval_ms = CONFIG_INPUT_KMATRIX_POLL_MS,
.config_row = km_stm32_config_row,
.config_col = km_stm32_config_col,
.row_set = km_stm32_row_set,
.col_get = km_stm32_col_get,
},
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: km_stm32_config_row
*
* Description:
* Configure a row GPIO pin as an output
*
****************************************************************************/
static void km_stm32_config_row(kmatrix_pin_t pin)
{
iinfo("Configuring row pin as output\n");
stm32_configgpio(pin);
stm32_gpiowrite(pin, true); /* Initialize to inactive (high) */
}
/****************************************************************************
* Name: km_stm32_config_col
*
* Description:
* Configure a column GPIO pin as an input with pull-up
*
****************************************************************************/
static void km_stm32_config_col(kmatrix_pin_t pin)
{
iinfo("Configuring column pin as input\n");
stm32_configgpio(pin);
}
/****************************************************************************
* Name: km_stm32_row_set
*
* Description:
* Activate or deactivate a row (logic: active=true sets to 0/low to
* activate the row, active=false sets to 1/high to deactivate)
*
****************************************************************************/
static void km_stm32_row_set(kmatrix_pin_t pin, bool active)
{
/* With diodes, we drive rows low to activate.
* active=true -> write 0 (low)
* active=false -> write 1 (high)
*/
stm32_gpiowrite(pin, active ? 0 : 1);
}
/****************************************************************************
* Name: km_stm32_col_get
*
* Description:
* Read the state of a column GPIO pin
*
****************************************************************************/
static bool km_stm32_col_get(kmatrix_pin_t pin)
{
/* With pull-up resistors:
* Key pressed -> column goes low (0) when row is driven low
* Key released -> column stays high (1)
* Return true when pressed (low), false when released (high)
*/
return !stm32_gpioread(pin);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: board_kmatrix_initialize
*
* Description:
* This function is called by application-specific setup logic to
* configure the keyboard matrix device.
*
* Input Parameters:
* devpath - The device path, typically "/dev/keypad0"
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int board_kmatrix_initialize(const char *devpath)
{
iinfo("Initializing keyboard matrix at %s\n", devpath);
/* Register the keyboard matrix with the generic driver */
return kmatrix_register(&g_km_config.config, devpath);
}
int board_kmatrix_diag(int loops, int delay_ms)
{
int iter = 0;
uint8_t last_bits[4] =
{
0xff, 0xff, 0xff, 0xff
};
const useconds_t pulse_us = 200000;
iinfo("KMATRIX diag: pin identify pulses (disconnect keypad)\n");
for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
{
iinfo("Pulse ROW%u\n", r + 1);
stm32_configgpio(g_km_rows[r]);
stm32_gpiowrite(g_km_rows[r], true);
usleep(pulse_us);
stm32_gpiowrite(g_km_rows[r], false);
usleep(pulse_us);
stm32_gpiowrite(g_km_rows[r], true);
usleep(pulse_us);
}
iinfo("KMATRIX diag: column pulses require BOARD_KMATRIX_COLx_OUT\n");
#ifdef BOARD_KMATRIX_COL0_OUT
iinfo("Pulse COL1 (output mode)\n");
stm32_configgpio(BOARD_KMATRIX_COL0_OUT);
stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, true);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, false);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL0_OUT, true);
usleep(pulse_us);
#endif
#ifdef BOARD_KMATRIX_COL1_OUT
iinfo("Pulse COL2 (output mode)\n");
stm32_configgpio(BOARD_KMATRIX_COL1_OUT);
stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, true);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, false);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL1_OUT, true);
usleep(pulse_us);
#endif
#ifdef BOARD_KMATRIX_COL2_OUT
iinfo("Pulse COL3 (output mode)\n");
stm32_configgpio(BOARD_KMATRIX_COL2_OUT);
stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, true);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, false);
usleep(pulse_us);
stm32_gpiowrite(BOARD_KMATRIX_COL2_OUT, true);
usleep(pulse_us);
#endif
for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
{
km_stm32_config_row(g_km_rows[r]);
stm32_gpiowrite(g_km_rows[r], true);
}
for (unsigned int c = 0; c < g_km_config.config.ncols; c++)
{
km_stm32_config_col(g_km_cols[c]);
}
iinfo("KMATRIX diag: loops=%d delay_ms=%d\n", loops, delay_ms);
while (loops <= 0 || iter < loops)
{
for (unsigned int r = 0; r < g_km_config.config.nrows; r++)
{
for (unsigned int rr = 0; rr < g_km_config.config.nrows; rr++)
{
stm32_gpiowrite(g_km_rows[rr], true);
}
stm32_gpiowrite(g_km_rows[r], false);
usleep(1000);
if (g_km_config.config.ncols == 3)
{
bool b0 = stm32_gpioread(g_km_cols[0]);
bool b1 = stm32_gpioread(g_km_cols[1]);
bool b2 = stm32_gpioread(g_km_cols[2]);
uint8_t bits = (b0 ? 1 : 0) |
(b1 ? 2 : 0) |
(b2 ? 4 : 0);
if (bits != last_bits[r])
{
iinfo("ROW=%u COLS(raw)=%d%d%d\n",
r + 1, b0 ? 1 : 0, b1 ? 1 : 0, b2 ? 1 : 0);
last_bits[r] = bits;
}
}
for (unsigned int c = 0; c < g_km_config.config.ncols; c++)
{
bool pressed = !stm32_gpioread(g_km_cols[c]);
if (pressed)
{
iinfo("ROW=%u COL=%u\n", r + 1, c + 1);
while (!stm32_gpioread(g_km_cols[c]))
{
usleep(1000);
}
}
}
}
if (delay_ms > 0)
{
usleep(delay_ms * 1000);
}
iter++;
}
return OK;
}
@@ -0,0 +1,210 @@
/****************************************************************************
* boards/arm/stm32/common/src/stm32_kmatrix_i2c.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/input/kmatrix.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/ioexpander/ioexpander.h>
#include <arch/board/board.h>
#include "stm32.h"
#include "stm32_i2c.h"
#ifdef CONFIG_IOEXPANDER_MCP23X08
# include <nuttx/ioexpander/mcp23x08.h>
#endif
#ifdef CONFIG_IOEXPANDER_PCA9538
# include <nuttx/ioexpander/pca9538.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
typedef uint32_t kmatrix_pin_t;
/****************************************************************************
* Private Data
****************************************************************************/
/* Row and column pin definitions for 4x3 keypad matrix via I2C expander
*
* For MCP23X08/PCA9538 I2C expanders, pins are numbered 0-7.
*
* Example mapping:
* Rows (outputs): Pins 0-3
* Columns (inputs): Pins 4-6 (with pull-ups)
*/
static const kmatrix_pin_t g_km_rows[] =
{
0, 1, 2, 3, /* Row 0-3: Output pins on expander */
};
static const kmatrix_pin_t g_km_cols[] =
{
4, 5, 6, /* Col 0-2: Input pins on expander (with pull-up) */
};
/* Keymap for 4x3 matrix - Standard phone keypad layout
* Rows: 0-3, Columns: 0-2
*/
static const uint32_t g_km_keymap[] =
{
'1', '2', '3', /* Row 0 */
'4', '5', '6', /* Row 1 */
'7', '8', '9', /* Row 2 */
'*', '0', '#', /* Row 3 */
};
/* Get callbacks from I2C driver */
extern FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void);
/* Keyboard matrix configuration structure
* Callbacks are set in board_kmatrix_i2c_initialize.
*/
static struct kmatrix_config_s g_km_i2c_config =
{
.nrows = 4,
.ncols = 3,
.rows = g_km_rows,
.cols = g_km_cols,
.keymap = g_km_keymap,
.poll_interval_ms = CONFIG_INPUT_KMATRIX_POLL_MS,
};
/* IO expander configuration */
#ifdef CONFIG_IOEXPANDER_MCP23X08
static struct mcp23x08_config_s g_mcp23x08_config =
{
.address = CONFIG_STM32_KMATRIX_I2C_ADDR,
.frequency = CONFIG_STM32_KMATRIX_I2C_FREQ,
};
#endif
#ifdef CONFIG_IOEXPANDER_PCA9538
static struct pca9538_config_s g_pca9538_config =
{
.address = CONFIG_STM32_KMATRIX_I2C_ADDR,
.frequency = CONFIG_STM32_KMATRIX_I2C_FREQ,
};
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/**
* Name: board_kmatrix_i2c_initialize
*
* Description:
* Initialize keyboard matrix driver using I2C GPIO expander.
* This function is called by stm32_bringup.c during initialization.
*
* Input Parameters:
* devpath - Device path (e.g., "/dev/kbd0")
*
* Returned Value:
* Zero on success; negated errno on failure.
*/
int board_kmatrix_i2c_initialize(const char *devpath)
{
FAR struct i2c_master_s *i2c;
FAR struct ioexpander_dev_s *ioe;
FAR struct kmatrix_callbacks_s *callbacks;
int ret;
iinfo("Initializing keyboard matrix via I2C expander\n");
/* Initialize I2C bus */
i2c = stm32_i2cbus_initialize(CONFIG_STM32_KMATRIX_I2C_BUS);
if (i2c == NULL)
{
ierr("ERROR: Failed to initialize I2C bus %d\n",
CONFIG_STM32_KMATRIX_I2C_BUS);
return -ENODEV;
}
/* Initialize IO expander */
#ifdef CONFIG_IOEXPANDER_MCP23X08
ioe = mcp23x08_initialize(i2c, &g_mcp23x08_config);
if (ioe == NULL)
{
ierr("ERROR: Failed to initialize MCP23X08\n");
stm32_i2cbus_uninitialize(i2c);
return -ENODEV;
}
iinfo("MCP23X08 initialized at 0x%02x\n", CONFIG_STM32_KMATRIX_I2C_ADDR);
#elif defined(CONFIG_IOEXPANDER_PCA9538)
ioe = pca9538_initialize(i2c, &g_pca9538_config);
if (ioe == NULL)
{
ierr("ERROR: Failed to initialize PCA9538\n");
stm32_i2cbus_uninitialize(i2c);
return -ENODEV;
}
iinfo("PCA9538 initialized at 0x%02x\n", CONFIG_STM32_KMATRIX_I2C_ADDR);
#else
# error "No IO expander configured"
#endif
/* Get callbacks from I2C driver */
callbacks = kmatrix_i2c_get_callbacks();
g_km_i2c_config.config_row = callbacks->config_row;
g_km_i2c_config.config_col = callbacks->config_col;
g_km_i2c_config.row_set = callbacks->row_set;
g_km_i2c_config.col_get = callbacks->col_get;
/* Register keyboard matrix driver */
ret = kmatrix_i2c_register(ioe, &g_km_i2c_config, devpath);
if (ret < 0)
{
ierr("ERROR: Failed to register keyboard matrix: %d\n", ret);
stm32_i2cbus_uninitialize(i2c);
return ret;
}
iinfo("Keyboard matrix I2C driver registered at %s\n", devpath);
return OK;
}
+19
View File
@@ -120,4 +120,23 @@ config PM_SLEEP_WAKEUP_NSEC
Number of additional nanoseconds to wait in PM_SLEEP before going to
PM_STANDBY mode.
if INPUT_KMATRIX_I2C
config STM32_KMATRIX_I2C_BUS
int "I2C Bus Number"
default 1
---help---
I2C bus number to use for the keyboard matrix GPIO expander.
Common values: 1 or 2 (depends on available I2C interfaces).
config STM32_KMATRIX_I2C_ADDR
hex "I2C Slave Address of GPIO Expander"
default 0x20
---help---
I2C slave address of the GPIO expander (PCF8574 or MCP23017).
PCF8574/MCP23017 default addresses (7-bit):
0x20-0x27: varies with A0-A2 pins (default is 0x20)
endif # INPUT_KMATRIX_I2C
endif
@@ -472,4 +472,76 @@
#define BOARD_XEN1210_PWMTIMER 1
/* Keyboard Matrix Configuration */
/* Define keyboard matrix row pins (outputs) */
#define GPIO_KMATRIX_ROW0 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN7)
#define GPIO_KMATRIX_ROW1 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN8)
#define GPIO_KMATRIX_ROW2 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN9)
#define GPIO_KMATRIX_ROW3 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN10)
/* Row pins as inputs with pull-up for early diagnostics */
#define GPIO_KMATRIX_ROW0_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN7)
#define GPIO_KMATRIX_ROW1_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN8)
#define GPIO_KMATRIX_ROW2_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN9)
#define GPIO_KMATRIX_ROW3_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN10)
/* Define keyboard matrix column pins (inputs) */
#define GPIO_KMATRIX_COL0 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN11)
#define GPIO_KMATRIX_COL1 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN13)
#define GPIO_KMATRIX_COL2 (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|\
GPIO_PORTE|GPIO_PIN14)
/* Column pins as outputs for diagnostics only */
#define GPIO_KMATRIX_COL0_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN11)
#define GPIO_KMATRIX_COL1_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN13)
#define GPIO_KMATRIX_COL2_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN14)
/* Board-level KMATRIX pin definitions */
#define BOARD_KMATRIX_ROW0 GPIO_KMATRIX_ROW0
#define BOARD_KMATRIX_ROW1 GPIO_KMATRIX_ROW1
#define BOARD_KMATRIX_ROW2 GPIO_KMATRIX_ROW2
#define BOARD_KMATRIX_ROW3 GPIO_KMATRIX_ROW3
#define BOARD_KMATRIX_ROW0_IN GPIO_KMATRIX_ROW0_IN
#define BOARD_KMATRIX_ROW1_IN GPIO_KMATRIX_ROW1_IN
#define BOARD_KMATRIX_ROW2_IN GPIO_KMATRIX_ROW2_IN
#define BOARD_KMATRIX_ROW3_IN GPIO_KMATRIX_ROW3_IN
#define BOARD_KMATRIX_COL0 GPIO_KMATRIX_COL0
#define BOARD_KMATRIX_COL1 GPIO_KMATRIX_COL1
#define BOARD_KMATRIX_COL2 GPIO_KMATRIX_COL2
#define BOARD_KMATRIX_COL0_OUT GPIO_KMATRIX_COL0_OUT
#define BOARD_KMATRIX_COL1_OUT GPIO_KMATRIX_COL1_OUT
#define BOARD_KMATRIX_COL2_OUT GPIO_KMATRIX_COL2_OUT
#ifdef CONFIG_INPUT_KMATRIX
int board_kmatrix_diag(int loops, int delay_ms);
#endif
/* Keyboard Matrix I2C Configuration */
#define CONFIG_STM32_KMATRIX_I2C_BUS 1 /* I2C1 */
#define CONFIG_STM32_KMATRIX_I2C_ADDR 0x20 /* MCP23X08/PCA9538 address */
#define CONFIG_STM32_KMATRIX_I2C_FREQ 400000 /* 400 kHz */
#endif /* __BOARDS_ARM_STM32_STM32F4DISCOVERY_INCLUDE_BOARD_H */
@@ -102,6 +102,14 @@
#include "board_sbutton.h"
#endif
#ifdef CONFIG_INPUT_KMATRIX
#include "stm32_kmatrix_gpio.h"
#endif
#ifdef CONFIG_INPUT_KMATRIX_I2C
#include "stm32_kmatrix_i2c.h"
#endif
#ifdef CONFIG_SENSORS_ZEROCROSS
#include "stm32_zerocross.h"
#endif
@@ -414,6 +422,27 @@ int stm32_bringup(void)
}
#endif
#ifdef CONFIG_INPUT_KMATRIX
/* Initialize and register the keyboard matrix driver */
ret = board_kmatrix_initialize(CONFIG_INPUT_KMATRIX_DEVPATH);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: board_kmatrix_initialize() failed: %d\n", ret);
}
#endif
#ifdef CONFIG_INPUT_KMATRIX_I2C
/* Initialize and register the keyboard matrix driver via I2C expander */
ret = board_kmatrix_i2c_initialize("/dev/kbd0");
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: board_kmatrix_i2c_initialize() failed: %d\n",
ret);
}
#endif
#ifdef CONFIG_INPUT_NUNCHUCK
/* Register the Nunchuck driver */
+8
View File
@@ -106,6 +106,14 @@ if(CONFIG_INPUT)
list(APPEND SRCS keyboard_upper.c)
endif()
if(CONFIG_INPUT_KMATRIX)
list(APPEND SRCS kmatrix.c)
endif()
if(CONFIG_INPUT_KMATRIX_I2C)
list(APPEND SRCS kmatrix_i2c.c)
endif()
if(CONFIG_INPUT_SBUTTON)
list(APPEND SRCS sbutton.c)
endif()
+67 -1
View File
@@ -723,7 +723,7 @@ config INPUT_SPQ10KBD
select I2C
---help---
Enable the Solder Party Q10 BlackBerry Keyboard support. This
exposes itself as a standard keyboard at /dev/kbdN.
exposes itself as a standard keyboard at /dev/keypadN.
This keyboard exists both as a standalone module and integrated
into the Solder Party Keyboard FeatherWing. Information on this
can be found at https://www.solder.party/docs/keyboard-pmod/
@@ -749,4 +749,70 @@ config SPQ10KBD_NPOLLWAITERS
endif # INPUT_SPQ10KBD
config INPUT_KMATRIX
bool "Keyboard Matrix Driver"
default n
select INPUT_KEYBOARD
---help---
Enable support for keyboard matrix input devices with polling-based
scanning. This driver supports NxM key matrices with debounce and
anti-ghosting using diodes.
if INPUT_KMATRIX
config INPUT_KMATRIX_BUFSIZE
int "Keyboard matrix buffer size"
default 64
---help---
Size of the keyboard event buffer for each open file descriptor.
config INPUT_KMATRIX_POLL_MS
int "Polling interval (milliseconds)"
default 10
---help---
Time interval in milliseconds between matrix scans. Smaller values
provide lower latency but consume more CPU. Default: 10ms.
config INPUT_KMATRIX_DEBOUNCE
int "Debounce threshold (scan cycles)"
default 3
---help---
Number of consecutive scan cycles a key must maintain the same state
before generating a press/release event. Higher values provide more
robust debounce but increase latency. Default: 3 cycles.
config INPUT_KMATRIX_DEVPATH
string "Device path"
default "/dev/keypad0"
---help---
Path where the keyboard matrix device will be registered. Default: /dev/keypad0
config INPUT_KMATRIX_I2C
bool "Keyboard Matrix via I2C GPIO Expander"
depends on INPUT_KMATRIX && I2C
default n
---help---
Enable keyboard matrix driver using I2C GPIO expander (PCF8574 or MCP23017).
Requires I2C support to be enabled.
if INPUT_KMATRIX_I2C
config INPUT_KMATRIX_I2C_PCF8574
bool "Use PCF8574 I2C Expander"
default n
---help---
Use PCF8574 I2C GPIO expander (8-bit, quasi-bidirectional I/O).
This is a simple, low-cost 8-bit I/O expander commonly used in hobbyist projects.
config INPUT_KMATRIX_I2C_MCP23017
bool "Use MCP23017 I2C Expander"
default n
---help---
Use MCP23017 I2C GPIO expander (16-bit, configurable I/O directions).
This offers more features and flexibility than PCF8574.
endif # INPUT_KMATRIX_I2C
endif # INPUT_KMATRIX
endif # INPUT
+8
View File
@@ -126,6 +126,14 @@ ifeq ($(CONFIG_INPUT_SPQ10KBD),y)
CSRCS += spq10kbd.c
endif
ifeq ($(CONFIG_INPUT_KMATRIX),y)
CSRCS += kmatrix.c
endif
ifeq ($(CONFIG_INPUT_KMATRIX_I2C),y)
CSRCS += kmatrix_i2c.c
endif
ifeq ($(CONFIG_INPUT_GOLDFISH_EVENTS),y)
CSRCS += goldfish_events.c
endif
+362
View File
@@ -0,0 +1,362 @@
/****************************************************************************
* drivers/input/kmatrix.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <stdio.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <nuttx/input/kmatrix.h>
#include <nuttx/fs/fs.h>
#include <nuttx/clock.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/wqueue.h>
#include <nuttx/semaphore.h>
#include <nuttx/input/keyboard.h>
#include <nuttx/input/kbd_codec.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct kmatrix_dev_s
{
FAR const struct kmatrix_config_s *config; /* Board configuration data */
mutex_t lock; /* Exclusive access to device */
struct work_s work; /* Work queue for polling */
uint16_t poll_interval; /* Polling interval in milliseconds */
/* Current and previous state of the matrix (bitfield) */
FAR uint8_t *state; /* Current state bitmap */
FAR uint8_t *debounce; /* Debounce counter */
/* Keyboard lower-half registration */
struct keyboard_lowerhalf_s lower;
};
/****************************************************************************
* Static Function Prototypes
****************************************************************************/
static void kmatrix_scan_worker(FAR void *arg);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: kmatrix_get_state
*
* Description:
* Get the current state of a key at position (row, col)
*
****************************************************************************/
static bool kmatrix_get_state(FAR struct kmatrix_dev_s *priv,
uint8_t row, uint8_t col)
{
uint16_t idx = row * priv->config->ncols + col;
uint16_t byte_idx = idx / 8;
uint8_t bit_idx = idx % 8;
return (priv->state[byte_idx] >> bit_idx) & 1;
}
/****************************************************************************
* Name: kmatrix_set_state
*
* Description:
* Set the current state of a key at position (row, col)
*
****************************************************************************/
static void kmatrix_set_state(FAR struct kmatrix_dev_s *priv,
uint8_t row, uint8_t col, bool pressed)
{
uint16_t idx = row * priv->config->ncols + col;
uint16_t byte_idx = idx / 8;
uint8_t bit_idx = idx % 8;
if (pressed)
{
priv->state[byte_idx] |= (1 << bit_idx);
}
else
{
priv->state[byte_idx] &= ~(1 << bit_idx);
}
}
/****************************************************************************
* Name: kmatrix_inc_debounce
*
* Description:
* Increment debounce counter for a key
*
****************************************************************************/
static void kmatrix_inc_debounce(FAR struct kmatrix_dev_s *priv,
uint8_t row, uint8_t col)
{
uint16_t idx = row * priv->config->ncols + col;
if (priv->debounce[idx] < CONFIG_INPUT_KMATRIX_DEBOUNCE)
{
priv->debounce[idx]++;
}
}
/****************************************************************************
* Name: kmatrix_reset_debounce
*
* Description:
* Reset debounce counter for a key
*
****************************************************************************/
static void kmatrix_reset_debounce(FAR struct kmatrix_dev_s *priv,
uint8_t row, uint8_t col)
{
uint16_t idx = row * priv->config->ncols + col;
priv->debounce[idx] = 0;
}
/****************************************************************************
* Name: kmatrix_scan_worker
*
* Description:
* Periodic worker that scans the keyboard matrix and detects key presses
* and releases.
*
****************************************************************************/
static void kmatrix_scan_worker(FAR void *arg)
{
FAR struct kmatrix_dev_s *priv = (FAR struct kmatrix_dev_s *)arg;
uint8_t row;
uint8_t col;
bool pressed;
bool old_state;
uint32_t keycode;
int ret;
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return;
}
/* Scan each row */
for (row = 0; row < priv->config->nrows; row++)
{
/* Activate this row */
priv->config->row_set(priv->config->rows[row], true);
/* Read each column */
for (col = 0; col < priv->config->ncols; col++)
{
pressed = priv->config->col_get(priv->config->cols[col]);
old_state = kmatrix_get_state(priv, row, col);
/* Check if state changed */
if (pressed != old_state)
{
kmatrix_inc_debounce(priv, row, col);
/* After debounce threshold is reached, update state */
if (priv->debounce[row * priv->config->ncols + col] >=
CONFIG_INPUT_KMATRIX_DEBOUNCE)
{
kmatrix_set_state(priv, row, col, pressed);
kmatrix_reset_debounce(priv, row, col);
/* Generate keyboard event */
keycode = priv->config->keymap[
row * priv->config->ncols + col];
keyboard_event(&priv->lower, (uint16_t)keycode,
pressed ? KEYBOARD_PRESS :
KEYBOARD_RELEASE);
iinfo("Key [%d,%d]: %s (code %lu)\n", row, col,
pressed ? "PRESS" : "RELEASE",
(unsigned long)keycode);
}
}
else
{
kmatrix_reset_debounce(priv, row, col);
}
}
/* Deactivate this row */
priv->config->row_set(priv->config->rows[row], false);
}
nxmutex_unlock(&priv->lock);
/* Reschedule the worker */
work_queue(LPWORK, &priv->work, kmatrix_scan_worker, priv,
MSEC2TICK(priv->poll_interval));
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: kmatrix_register
*
* Description:
* Configure and register a keyboard matrix device.
*
****************************************************************************/
int kmatrix_register(FAR const struct kmatrix_config_s *config,
FAR const char *devpath)
{
FAR struct kmatrix_dev_s *priv;
int ret;
uint16_t state_size;
uint16_t debounce_size;
uint16_t keys;
iinfo("Registering keypad matrix: %dx%d at %s\n", config->nrows,
config->ncols, devpath);
/* Validate configuration */
DEBUGASSERT(config != NULL);
DEBUGASSERT(devpath != NULL);
DEBUGASSERT(config->rows != NULL);
DEBUGASSERT(config->cols != NULL);
DEBUGASSERT(config->keymap != NULL);
DEBUGASSERT(config->config_row != NULL);
DEBUGASSERT(config->config_col != NULL);
DEBUGASSERT(config->row_set != NULL);
DEBUGASSERT(config->col_get != NULL);
/* Allocate driver instance */
priv = kmm_zalloc(sizeof(struct kmatrix_dev_s));
if (!priv)
{
ierr("ERROR: kmm_zalloc(%zu) failed\n", sizeof(struct kmatrix_dev_s));
return -ENOMEM;
}
/* Calculate bitmap sizes */
keys = config->nrows * config->ncols;
state_size = (keys + 7) / 8;
debounce_size = keys;
/* Allocate state and debounce bitmaps */
priv->state = kmm_zalloc(state_size);
if (!priv->state)
{
ierr("ERROR: Failed to allocate state bitmap\n");
kmm_free(priv);
return -ENOMEM;
}
priv->debounce = kmm_zalloc(debounce_size);
if (!priv->debounce)
{
ierr("ERROR: Failed to allocate debounce bitmap\n");
kmm_free(priv->state);
kmm_free(priv);
return -ENOMEM;
}
/* Initialize device structure */
priv->config = config;
priv->poll_interval = config->poll_interval_ms > 0 ?
config->poll_interval_ms :
CONFIG_INPUT_KMATRIX_POLL_MS;
nxmutex_init(&priv->lock);
/* Configure all GPIO pins */
for (int i = 0; i < config->nrows; i++)
{
config->config_row(config->rows[i]);
}
for (int i = 0; i < config->ncols; i++)
{
config->config_col(config->cols[i]);
}
/* Register as keyboard device */
ret = keyboard_register(&priv->lower, devpath,
CONFIG_INPUT_KMATRIX_BUFSIZE);
if (ret < 0)
{
ierr("ERROR: keyboard_register() failed: %d\n", ret);
goto errout_with_priv;
}
/* Start the scanning worker */
work_queue(LPWORK, &priv->work, kmatrix_scan_worker, priv,
MSEC2TICK(priv->poll_interval));
iinfo("Keypad matrix registered as %s\n", devpath);
return OK;
errout_with_priv:
nxmutex_destroy(&priv->lock);
kmm_free(priv->debounce);
kmm_free(priv->state);
kmm_free(priv);
return ret;
}
+262
View File
@@ -0,0 +1,262 @@
/****************************************************************************
* drivers/input/kmatrix_i2c.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <stdio.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/input/kmatrix.h>
#include <nuttx/ioexpander/ioexpander.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Verify IO expander support is enabled */
#if !defined(CONFIG_IOEXPANDER_PCA9538) && !defined(CONFIG_IOEXPANDER_MCP23X08)
# error "Either CONFIG_IOEXPANDER_PCA9538 or " \
"CONFIG_IOEXPANDER_MCP23X08 must be enabled"
#endif
/****************************************************************************
* Private Types
****************************************************************************/
typedef uint32_t kmatrix_pin_t;
struct kmatrix_i2c_dev_s
{
FAR struct ioexpander_dev_s *ioe; /* IO expander device */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void km_i2c_config_row(kmatrix_pin_t pin);
static void km_i2c_config_col(kmatrix_pin_t pin);
static void km_i2c_row_set(kmatrix_pin_t pin, bool active);
static bool km_i2c_col_get(kmatrix_pin_t pin);
/****************************************************************************
* Private Data
****************************************************************************/
/* Global I2C device instance (simplified - one per board) */
static struct kmatrix_i2c_dev_s g_km_i2c_dev;
/****************************************************************************
* I2C Keyboard Matrix Callbacks
****************************************************************************/
/**
* Name: km_i2c_config_row
*
* Description:
* Configure row pins as outputs using IO expander API.
*/
static void km_i2c_config_row(kmatrix_pin_t pin)
{
int ret;
iinfo("I2C: Configuring pin %lu as output (row)\n", (unsigned long)pin);
ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
IOEXPANDER_DIRECTION_OUT);
if (ret < 0)
{
ierr("ERROR: Failed to configure row pin %lu: %d\n",
(unsigned long)pin, ret);
}
}
/**
* Name: km_i2c_config_col
*
* Description:
* Configure column pins as inputs with pull-up using IO expander API.
*/
static void km_i2c_config_col(kmatrix_pin_t pin)
{
int ret;
iinfo("I2C: Configuring pin %lu as input (column)\n", (unsigned long)pin);
ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
IOEXPANDER_DIRECTION_IN_PULLUP);
if (ret < 0)
{
/* PCA9538 does not support IN_PULLUP; fall back to plain input. */
iinfo("I2C: IN_PULLUP not supported for pin %lu, falling back to IN\n",
(unsigned long)pin);
ret = IOEXP_SETDIRECTION(g_km_i2c_dev.ioe, (uint8_t)pin,
IOEXPANDER_DIRECTION_IN);
if (ret < 0)
{
ierr("ERROR: Failed to configure col pin %lu: %d\n",
(unsigned long)pin, ret);
}
}
}
/**
* Name: km_i2c_row_set
*
* Description:
* Control row output (active-low for matrix with diodes).
*/
static void km_i2c_row_set(kmatrix_pin_t pin, bool active)
{
int ret;
/* For active-low: active=true means write LOW (false) */
ret = IOEXP_WRITEPIN(g_km_i2c_dev.ioe, (uint8_t)pin, !active);
if (ret < 0)
{
ierr("ERROR: Failed to set row pin %lu: %d\n",
(unsigned long)pin, ret);
}
iinfo("I2C: Row set pin %lu to %d\n", (unsigned long)pin, active ? 0 : 1);
}
/**
* Name: km_i2c_col_get
*
* Description:
* Read column input (active-low with pull-up).
*/
static bool km_i2c_col_get(kmatrix_pin_t pin)
{
bool value;
int ret;
ret = IOEXP_READPIN(g_km_i2c_dev.ioe, (uint8_t)pin, &value);
if (ret < 0)
{
ierr("ERROR: Failed to read col pin %lu: %d\n",
(unsigned long)pin, ret);
return false;
}
/* Return inverted: true = active (low), false = inactive (high) */
bool result = !value;
iinfo("I2C: Col get pin %lu = %d\n", (unsigned long)pin, result);
return result;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/**
* Name: kmatrix_i2c_get_callbacks
*
* Description:
* Get the I2C callback functions to use in keyboard matrix config.
* This is called by board adapters to populate the callbacks.
*
* Returned Value:
* Structure with the callback function pointers.
*/
static struct kmatrix_callbacks_s g_km_i2c_callbacks =
{
.config_row = km_i2c_config_row,
.config_col = km_i2c_config_col,
.row_set = km_i2c_row_set,
.col_get = km_i2c_col_get,
};
FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void)
{
return &g_km_i2c_callbacks;
}
/**
* Name: kmatrix_i2c_register
*
* Description:
* Register keyboard matrix driver using I2C GPIO expander.
* The IO expander device must already be initialized.
*
* Input Parameters:
* ioe_dev - IO expander device (from mcp23x08_initialize or
* pca9538_initialize)
* config - Keyboard matrix configuration (with callbacks set)
* devpath - Device path (e.g., "/dev/kbd0")
*
* Returned Value:
* Zero on success; negated errno on failure.
*/
int kmatrix_i2c_register(FAR struct ioexpander_dev_s *ioe_dev,
FAR const struct kmatrix_config_s *config,
FAR const char *devpath)
{
int ret;
if (ioe_dev == NULL)
{
ierr("ERROR: IO expander device is NULL\n");
return -EINVAL;
}
iinfo("Initializing keyboard matrix via I2C IO expander\n");
/* Store IO expander device in global for callbacks */
g_km_i2c_dev.ioe = ioe_dev;
/* Register the keyboard matrix driver with provided config
* (which must have callbacks already set by the board adapter)
*/
ret = kmatrix_register(config, devpath);
if (ret < 0)
{
ierr("ERROR: kmatrix_register failed: %d\n", ret);
return ret;
}
iinfo("Keyboard matrix I2C driver registered successfully\n");
return OK;
}
+160
View File
@@ -0,0 +1,160 @@
/****************************************************************************
* include/nuttx/input/kmatrix.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_INPUT_KMATRIX_H
#define __INCLUDE_NUTTX_INPUT_KMATRIX_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/input/keyboard.h>
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
typedef uint32_t kmatrix_pin_t;
/* Keyboard matrix configuration structure passed to kmatrix_register() */
struct kmatrix_config_s
{
uint8_t nrows; /* Number of rows */
uint8_t ncols; /* Number of columns */
FAR const kmatrix_pin_t *rows; /* Array of row GPIO pins */
FAR const kmatrix_pin_t *cols; /* Array of column GPIO pins */
/* Keymap: keycode[row * cols + col] */
FAR const uint32_t *keymap;
uint16_t poll_interval_ms; /* Polling interval in milliseconds */
/* GPIO callback functions specific to the SoC/board */
void (*config_row)(kmatrix_pin_t pin);
void (*config_col)(kmatrix_pin_t pin);
void (*row_set)(kmatrix_pin_t pin, bool active);
bool (*col_get)(kmatrix_pin_t pin);
};
#ifdef CONFIG_INPUT_KMATRIX_I2C
/* Keyboard matrix callback structure for I2C expanders */
struct kmatrix_callbacks_s
{
void (*config_row)(kmatrix_pin_t pin);
void (*config_col)(kmatrix_pin_t pin);
void (*row_set)(kmatrix_pin_t pin, bool active);
bool (*col_get)(kmatrix_pin_t pin);
};
#endif /* CONFIG_INPUT_KMATRIX_I2C */
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
/****************************************************************************
* Name: kmatrix_register
*
* Description:
* Configure and register a keyboard matrix device. This will create the
* /dev/keypadN device node and enable keyboard scanning.
*
* Input Parameters:
* config - The keyboard matrix configuration. This structure is not
* copied; it must persist for the lifetime of the driver.
* devpath - The device path for the /dev/keypadN device.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int kmatrix_register(FAR const struct kmatrix_config_s *config,
FAR const char *devpath);
#ifdef CONFIG_INPUT_KMATRIX_I2C
/* Forward declaration */
struct ioexpander_dev_s;
/****************************************************************************
* Name: kmatrix_i2c_register
*
* Description:
* Register keyboard matrix driver using I2C GPIO expander.
* The IO expander device must already be initialized using
* mcp23x08_initialize() or pca9538_initialize().
*
* Input Parameters:
* ioe_dev - IO expander device (from mcp23x08_initialize or
* pca9538_initialize)
* config - The keyboard matrix configuration (with callbacks set)
* devpath - The device path for the /dev/keypadN device
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int kmatrix_i2c_register(FAR struct ioexpander_dev_s *ioe_dev,
FAR const struct kmatrix_config_s *config,
FAR const char *devpath);
/****************************************************************************
* Name: kmatrix_i2c_get_callbacks
*
* Description:
* Get the I2C callback functions to use in keyboard matrix config.
* This is called by board adapters to populate the callbacks.
*
* Returned Value:
* Structure with the callback function pointers.
*
****************************************************************************/
FAR struct kmatrix_callbacks_s *kmatrix_i2c_get_callbacks(void);
#endif /* CONFIG_INPUT_KMATRIX_I2C */
#ifdef __cplusplus
}
#endif
#endif /* __INCLUDE_NUTTX_INPUT_KMATRIX_H */