273 lines
8.9 KiB
ReStructuredText
273 lines
8.9 KiB
ReStructuredText
.. _gpio-kbd:
|
|
|
|
GPIO Keyboard Matrix
|
|
####################
|
|
|
|
The :dtcompatible:`gpio-kbd-matrix` driver supports a large variety of keyboard
|
|
matrix hardware configurations and has numerous options to change its behavior.
|
|
This is an overview of some common setups and how they can be supported by the
|
|
driver.
|
|
|
|
The conventional configuration for all of these is that the driver reads on the
|
|
row GPIOs (inputs) and selects on the columns GPIOs (output).
|
|
|
|
Base use case, no isolation diodes, interrupt capable GPIOs
|
|
***********************************************************
|
|
|
|
This is the common configuration found on consumer keyboards with membrane
|
|
switches and flexible circuit boards, no isolation diodes, requires ghosting
|
|
detection (which is enabled by default).
|
|
|
|
.. figure:: no-diodes.svg
|
|
:align: center
|
|
:width: 50%
|
|
|
|
A 3x3 matrix, no diodes
|
|
|
|
The system must support GPIO interrupts, and the interrupt can be enabled on all
|
|
row GPIOs at the same time.
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
row-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
|
|
<&gpio0 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
|
|
<&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
|
col-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>,
|
|
<&gpio0 4 GPIO_ACTIVE_LOW>,
|
|
<&gpio0 5 GPIO_ACTIVE_LOW>;
|
|
};
|
|
|
|
In this configuration the matrix scanning library enters idle mode once all
|
|
keys are released, and the keyboard matrix thread only wakes up when a key has
|
|
been pressed.
|
|
|
|
GPIOs for columns that are not currently selected are configured in high
|
|
impedance mode. This means that the row state may need some time to settle to
|
|
avoid misreading the key state from a column to the following one. The settle
|
|
time can be tweaked by changing the ``settle-time-us`` property.
|
|
|
|
Isolation diodes
|
|
****************
|
|
|
|
If the matrix has isolation diodes for every key, then it's possible to:
|
|
|
|
- disable ghosting detection, allowing any key combination to be detected
|
|
- configuring the driver to drive unselected columns GPIO to inactive state
|
|
rather than high impedance, this allows to reduce the settle time
|
|
(potentially down to 0), and use the more efficient port wide GPIO read APIs
|
|
(happens automatically if the GPIO pins are sequential)
|
|
|
|
Matrixes with diodes going from rows to columns must use pull-ups on rows and
|
|
active low columns.
|
|
|
|
.. figure:: diodes-rc.svg
|
|
:align: center
|
|
:width: 50%
|
|
|
|
A 3x3 matrix with row to column isolation diodes.
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
row-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
|
|
<&gpio0 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
|
|
<&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
|
col-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>,
|
|
<&gpio0 4 GPIO_ACTIVE_LOW>,
|
|
<&gpio0 5 GPIO_ACTIVE_LOW>;
|
|
col-drive-inactive;
|
|
settle-time-us = <0>;
|
|
no-ghostkey-check;
|
|
};
|
|
|
|
Matrixes with diodes going from columns to rows must use pull-downs on rows and
|
|
active high columns.
|
|
|
|
.. figure:: diodes-cr.svg
|
|
:align: center
|
|
:width: 50%
|
|
|
|
A 3x3 matrix with column to row isolation diodes.
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
row-gpios = <&gpio0 0 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
|
|
<&gpio0 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>,
|
|
<&gpio0 2 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
|
|
col-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>,
|
|
<&gpio0 4 GPIO_ACTIVE_HIGH>,
|
|
<&gpio0 5 GPIO_ACTIVE_HIGH>;
|
|
col-drive-inactive;
|
|
settle-time-us = <0>;
|
|
no-ghostkey-check;
|
|
};
|
|
|
|
GPIO with no interrupt support
|
|
******************************
|
|
|
|
Some GPIO controllers have limitations on GPIO interrupts, and may not support
|
|
enabling interrupts on all row GPIOs at the same time.
|
|
|
|
In this case, the driver can be configured to not use interrupt at all, and
|
|
instead idle by selecting all columns and keep polling on the row GPIOs, which
|
|
is a single GPIO API operation if the pins are sequential.
|
|
|
|
This configuration can be enabled by setting the ``idle-mode`` property to
|
|
``poll``:
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
...
|
|
idle-mode = "poll";
|
|
};
|
|
|
|
GPIO multiplexer
|
|
****************
|
|
|
|
In more extreme cases, such as if the columns are using a multiplexer and it's
|
|
impossible to select all of them at the same time, the driver can be configured
|
|
to scan continuously.
|
|
|
|
This can be done by setting ``idle-mode`` to ``scan`` and ``poll-timeout-ms``
|
|
to ``0``.
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
...
|
|
poll-timeout-ms = <0>;
|
|
idle-mode = "scan";
|
|
};
|
|
|
|
Row and column GPIO selection
|
|
*****************************
|
|
|
|
If the row GPIOs are sequential and on the same gpio controller, the driver
|
|
automatically switches API to read from the whole GPIO port rather than the
|
|
individual pins. This is particularly useful if the GPIOs are not memory
|
|
mapped, for example on an I2C or SPI port expander, as this significantly
|
|
reduces the number of transactions on the corresponding bus.
|
|
|
|
The same is true for column GPIOs, but only if the matrix is configured for
|
|
``col-drive-inactive``, so that is only usable for matrixes with isolation
|
|
diodes.
|
|
|
|
16-bit row support
|
|
******************
|
|
|
|
The driver uses an 8-bit datatype to store the row state by default, which
|
|
limits the matrix row size to 8. This can be increased to 16 by enabling the
|
|
:kconfig:option:`CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW` option.
|
|
|
|
Actual key mask configuration
|
|
*****************************
|
|
|
|
If the key matrix is not complete, a map of the keys that are actually
|
|
populated can be specified using the ``actual-key-mask`` property. This allows
|
|
the matrix state to be filtered to remove keys that are not present before
|
|
ghosting detection, potentially allowing key combinations that would otherwise
|
|
be blocked by it.
|
|
|
|
For example for a 3x3 matrix missing a key:
|
|
|
|
.. figure:: no-sw4.svg
|
|
:align: center
|
|
:width: 50%
|
|
|
|
A 3x3 matrix missing a key.
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd-matrix {
|
|
compatible = "gpio-kbd-matrix";
|
|
...
|
|
actual-key-mask = <0x07 0x05 0x07>;
|
|
};
|
|
|
|
This would allow, for example, to detect pressing ``Sw1``, ``SW2`` and ``SW4``
|
|
at the same time without triggering anti ghosting.
|
|
|
|
The actual key mask can be changed at runtime by enabling
|
|
:kconfig:option:`CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC` and the using the
|
|
:c:func:`input_kbd_matrix_actual_key_mask_set` API.
|
|
|
|
Keymap configuration
|
|
********************
|
|
|
|
Keyboard matrix devices report a series of x/y/touch events. These can be
|
|
mapped to normal key events using the :dtcompatible:`input-keymap` driver.
|
|
|
|
For example, the following would setup a ``keymap`` device that take the
|
|
x/y/touch events as an input and generate corresponding key events as an
|
|
output:
|
|
|
|
.. code-block:: devicetree
|
|
|
|
kbd {
|
|
...
|
|
keymap {
|
|
compatible = "input-keymap";
|
|
keymap = <
|
|
MATRIX_KEY(0, 0, INPUT_KEY_1)
|
|
MATRIX_KEY(0, 1, INPUT_KEY_2)
|
|
MATRIX_KEY(0, 2, INPUT_KEY_3)
|
|
MATRIX_KEY(1, 0, INPUT_KEY_4)
|
|
MATRIX_KEY(1, 1, INPUT_KEY_5)
|
|
MATRIX_KEY(1, 2, INPUT_KEY_6)
|
|
MATRIX_KEY(2, 0, INPUT_KEY_7)
|
|
MATRIX_KEY(2, 1, INPUT_KEY_8)
|
|
MATRIX_KEY(2, 2, INPUT_KEY_9)
|
|
>;
|
|
row-size = <3>;
|
|
col-size = <3>;
|
|
};
|
|
};
|
|
|
|
Keyboard matrix shell commands
|
|
******************************
|
|
|
|
The shell command ``kbd_matrix_state_dump`` can be used to test the
|
|
functionality of any keyboard matrix driver implemented using the keyboard
|
|
matrix library. Once enabled it logs the state of the matrix every time it
|
|
changes, and once disabled it prints an or-mask of any key that has been
|
|
detected, which can be used to set the ``actual-key-mask`` property.
|
|
|
|
The command can be enabled using the
|
|
:kconfig:option:`CONFIG_INPUT_SHELL_KBD_MATRIX_STATE`.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: console
|
|
|
|
uart:~$ device list
|
|
devices:
|
|
- kbd-matrix (READY)
|
|
uart:~$ input kbd_matrix_state_dump kbd-matrix
|
|
Keyboard state logging enabled for kbd-matrix
|
|
[00:01:41.678,466] <inf> input: kbd-matrix state [01 -- -- --] (1)
|
|
[00:01:41.784,912] <inf> input: kbd-matrix state [-- -- -- --] (0)
|
|
...
|
|
press more buttons
|
|
...
|
|
uart:~$ input kbd_matrix_state_dump off
|
|
Keyboard state logging disabled
|
|
[00:01:47.967,651] <inf> input: kbd-matrix key-mask [07 05 07 --] (8)
|
|
|
|
Keyboard matrix library
|
|
***********************
|
|
|
|
The GPIO keyboard matrix driver is based on a generic keyboard matrix library,
|
|
which implements the core functionalities such as scanning delays, debouncing,
|
|
idle mode etc. This can be reused to implement other keyboard matrix drivers,
|
|
potentially application specific.
|
|
|
|
.. doxygengroup:: input_kbd_matrix
|