mirror of
https://github.com/apache/nuttx.git
synced 2026-09-08 01:46:35 +00:00
The pins a board publishes are visible in /dev and each can be read through its own node, but surveying a whole board that way means an open and two ioctls per pin, and the signal and interrupt counters the upper half keeps are not reachable through any of them. Adds a list of registered pins and publishes it as /proc/gpio, behind GPIO_PROCFS: a quality of life view of the same kind as /proc/pinctrl and /proc/reset. Every common field comes from state the upper half already holds: the pin type, the value through go_read(), how many times the pin has been registered for signals, and how many interrupts it has taken. Lines carry the same key:value tokens in the same order, so the file is machine parseable. Lower halves may supply an optional go_describe() adding what only they can say, such as which pad carries the line or how its trigger is configured. It writes text into a caller supplied buffer and the upper half owns the line, so a lower half needs no procfs knowledge. A lower half without it is listed with the common tokens alone. The pin type index is bounded before use: it comes from the lower half and the name table cannot cover a type the enumeration does not define. A pin that cannot be read reports val:- rather than a zero that would read as a real level. procfs_register() appends without checking for duplicates, so the entry is claimed once for the lifetime of the system rather than whenever the list is empty; pins come and go at run time. The name is held in a buffer as long as the one gpio_pin_register() accepts, so a listing always names the same string as /dev. The pin type name table is declared without an explicit size so that the assertion beside it compares against the enumeration and can fail; sized as [GPIO_NPINTYPES] it would have been tautological. Documents the entry, its tokens, and how a lower half describes a pin. Off by default: with GPIO_PROCFS unset the list, the lock and the procfs entry are compiled out, and go_describe() is one more member at the end of a structure existing lower halves do not reach. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <justin@dynam.ac>
246 lines
8.4 KiB
ReStructuredText
246 lines
8.4 KiB
ReStructuredText
============
|
|
GPIO Drivers
|
|
============
|
|
|
|
- ``include/nuttx/ioexpander/gpio.h``. All structures and APIs needed
|
|
to work with GPIO pins as drivers are provided in this header file. This
|
|
header file includes:
|
|
|
|
#. Structures and interface descriptions needed to develop a
|
|
low-level, board-specific, GPIO driver.
|
|
#. To register the GPIO driver with a common GPIO character
|
|
driver.
|
|
#. Interfaces needed for interfacing user programs with the
|
|
common GPIO character driver.
|
|
|
|
- ``drivers/ioexpander/gpio.c``. The implementation of the common GPIO
|
|
character driver.
|
|
|
|
Application Programming Interface
|
|
=================================
|
|
|
|
The Application Programming Interface is included in the application with
|
|
the following header file.
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
#include <nuttx/ioexpander/gpio.h>
|
|
|
|
A GPIO pin is either an input pin or an output pin.
|
|
|
|
One GPIO pin is registered as a POSIX character device file into ``/dev``
|
|
namespace. It is necessary to open the device to get a file descriptor for
|
|
further operations. This can be done with standard POSIX ``open()`` call.
|
|
Only one pir per driver is supported by the peripheral.
|
|
|
|
Standard POSIX ``read()`` and ``write()`` operations are not allowed for GPIO
|
|
based drivers. All interface is routed through IOCTL calls. Following commands
|
|
are supported:
|
|
|
|
* :c:macro:`GPIOC_WRITE`
|
|
* :c:macro:`GPIOC_READ`
|
|
* :c:macro:`GPIOC_PINTYPE`
|
|
* :c:macro:`GPIOC_REGISTER`
|
|
* :c:macro:`GPIOC_UNREGISTER`
|
|
* :c:macro:`GPIOC_SETPINTYPE`
|
|
* :c:macro:`GPIOC_SETDEBOUNCE`
|
|
* :c:macro:`GPIOC_IRQ_SETMASK`
|
|
|
|
|
|
.. c:macro:: GPIOC_WRITE
|
|
|
|
The ``GPIOC_WRITE`` command sets the value of an output GPIO. The argument
|
|
is either 0 (set low value) or 1 (set high value). It is possible to write
|
|
to output pins only. Typical use case is:
|
|
|
|
.. code-block:: c
|
|
|
|
bool value = true;
|
|
int ret = ioctl(fd, GPIOC_WRITE, value);
|
|
|
|
|
|
.. c:macro:: GPIOC_READ
|
|
|
|
The ``GPIOC_READ`` command reads the value of a GPIO. The argument
|
|
is a pointer to a bool value to receive the result. The result is either 0
|
|
(low value) or 1 (high value). It is possible to read from both input and
|
|
output pins. The currently set value is returned in case the pin is output
|
|
pin. Typical use case is:
|
|
|
|
.. code-block:: c
|
|
|
|
bool value;
|
|
int ret = ioctl(fd, GPIOC_READ, (unsigned long)(uintptr_t)&value);
|
|
|
|
.. c:macro:: GPIOC_PINTYPE
|
|
|
|
The ``GPIOC_PINTYPE`` command gets the type of GPIO pin. The argument
|
|
is a pointer to an instance of type :c:enum:`gpio_pintype_e`.
|
|
|
|
.. code-block:: c
|
|
|
|
enum gpio_pintype_e
|
|
{
|
|
GPIO_INPUT_PIN = 0, /* float */
|
|
GPIO_INPUT_PIN_PULLUP,
|
|
GPIO_INPUT_PIN_PULLDOWN,
|
|
GPIO_OUTPUT_PIN, /* push-pull */
|
|
GPIO_OUTPUT_PIN_OPENDRAIN,
|
|
GPIO_INTERRUPT_PIN,
|
|
GPIO_INTERRUPT_HIGH_PIN,
|
|
GPIO_INTERRUPT_LOW_PIN,
|
|
GPIO_INTERRUPT_RISING_PIN,
|
|
GPIO_INTERRUPT_FALLING_PIN,
|
|
GPIO_INTERRUPT_BOTH_PIN,
|
|
GPIO_INTERRUPT_PIN_WAKEUP,
|
|
GPIO_INTERRUPT_HIGH_PIN_WAKEUP,
|
|
GPIO_INTERRUPT_LOW_PIN_WAKEUP,
|
|
GPIO_INTERRUPT_RISING_PIN_WAKEUP,
|
|
GPIO_INTERRUPT_FALLING_PIN_WAKEUP,
|
|
GPIO_INTERRUPT_BOTH_PIN_WAKEUP,
|
|
GPIO_NPINTYPES
|
|
};
|
|
|
|
.. c:macro:: GPIOC_REGISTER
|
|
|
|
The ``GPIOC_REGISTER`` command registers a pin to receive a signal whenever
|
|
there is an interrupt received on an input GPIO pin. This feature, of course,
|
|
depends upon interrupt GPIO support in the platform specific code. Please
|
|
refer to the documentation describing your target platform for further
|
|
information. The argument is the pointer to :c:type:`sigevent` value, a signal
|
|
to be generated when the interrupt occurs.
|
|
|
|
Typical use case is following:
|
|
|
|
.. code-block:: c
|
|
|
|
struct sigevent notify;
|
|
|
|
notify.sigev_notify = SIGEV_SIGNAL;
|
|
notify.sigev_signo = SIGUSR1;
|
|
|
|
int ret = ioctl(fd, GPIOC_REGISTER, (unsigned long)¬ify);
|
|
|
|
.. c:macro:: GPIOC_UNREGISTER
|
|
|
|
The ``GPIOC_UNREGISTER`` command unresigters a pin and stop receiving signals
|
|
for pin interrupt.
|
|
|
|
.. c:macro:: GPIOC_SETPINTYPE
|
|
|
|
The ``GPIOC_SETPINTYPE`` command can be used to change the GPIO pin type
|
|
(from input pin to output pin, changing interrupt edges and similar). The
|
|
types to set are listed in :c:enum:`gpio_pintype_e`.
|
|
|
|
.. c:macro:: GPIOC_SETDEBOUNCE
|
|
|
|
The ``GPIOC_SETDEBOUNCE`` command sets the debounce time for a GPIO input pin.
|
|
The argument is a pointer to an integer value, which specifies the debounce time in milliseconds.
|
|
This helps to filter out spurious transitions (noise) on the input pin.
|
|
|
|
Typical use case:
|
|
|
|
.. code-block:: c
|
|
|
|
int debounce_ms = 10;
|
|
int ret = ioctl(fd, GPIOC_SETDEBOUNCE, (unsigned long)(uintptr_t)&debounce_ms);
|
|
|
|
.. c:macro:: GPIOC_IRQ_SETMASK
|
|
|
|
The ``GPIOC_IRQ_SETMASK`` command sets the interrupt mask for a GPIO pin.
|
|
The argument is a pointer to an integer value, which specifies the mask to enable or disable specific interrupt types (such as rising/falling edge, level, etc).
|
|
The exact meaning of the mask depends on the platform implementation.
|
|
|
|
Typical use case:
|
|
|
|
.. code-block:: c
|
|
|
|
int irq_mask = /* platform-specific mask value */;
|
|
int ret = ioctl(fd, GPIOC_IRQ_SETMASK, (unsigned long)(uintptr_t)&irq_mask);
|
|
|
|
Application Example
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
An example application can be found in ``nuttx-apps`` repository under
|
|
path ``examples/gpio``. It is an example application that allows you
|
|
to read, write or configure GPIO pins.
|
|
|
|
/proc/gpio
|
|
==========
|
|
|
|
The pins are visible in ``/dev`` and each can be read through its own node
|
|
with the commands above, but doing that for a whole board means an open and
|
|
two ioctls per pin, and the signal and interrupt counters are not exposed
|
|
that way at all. ``CONFIG_GPIO_PROCFS`` adds ``/proc/gpio``, which puts
|
|
every registered pin in one place:
|
|
|
|
.. code-block:: text
|
|
|
|
gpio0 type:INPUT val:1 regs:0 ints:0 pad:GPIO27 port:A.27
|
|
gpio1 type:OUTPUT val:0 regs:0 ints:0 pad:GPIO28 port:A.28
|
|
gpio2 type:INT_BOTH val:1 regs:1 ints:42 pad:GPIO29 port:A.29 armed:both
|
|
|
|
Every line carries the same ``key:value`` tokens in the same order, so the
|
|
file can be parsed as well as read:
|
|
|
|
========== =============================================================
|
|
Token Meaning
|
|
========== =============================================================
|
|
``type`` The pin type, from ``enum gpio_pintype_e``
|
|
``val`` The pin's current value, or ``-`` if it cannot be read
|
|
``regs`` How many times the pin has been registered for signals
|
|
``ints`` How many interrupts the pin has taken
|
|
========== =============================================================
|
|
|
|
Anything after those comes from the pin's lower half.
|
|
|
|
Describing a pin from the lower half
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The upper half already knows a pin's type, value and counters, so a lower
|
|
half only supplies what it alone can say: which pad carries the line, how
|
|
the trigger is configured, and so on. That is the optional
|
|
``go_describe`` method, which writes further ``key:value`` fields and
|
|
leaves the rest of the line to the renderer:
|
|
|
|
.. code-block:: c
|
|
|
|
static int mychip_describe(FAR struct gpio_dev_s *dev, FAR char *extra,
|
|
size_t len)
|
|
{
|
|
FAR struct mychip_gpio_s *priv = (FAR struct mychip_gpio_s *)dev;
|
|
|
|
snprintf(extra, len, "pad:%u port:%c.%u", priv->pad,
|
|
'A' + priv->port, priv->pin);
|
|
return OK;
|
|
}
|
|
|
|
It is optional: a pin whose lower half omits it is listed with the common
|
|
tokens and nothing more. A lower half needs no procfs knowledge of its
|
|
own, since the renderer owns the line.
|
|
|
|
The option depends on ``FS_PROCFS_REGISTER`` and is off by default.
|
|
|
|
Configuration
|
|
=============
|
|
|
|
This section describes GPIO driver configuration in ``Kconfig``. The reader
|
|
should refer to target documentation for target specific configuration.
|
|
|
|
GPIO peripheral is enabled by ``CONFIG_DEV_GPIO``. Option
|
|
``CONFIG_DEV_NPOLLWAITERS`` is used to specify the maximum number of threads
|
|
that can be waiting on poll with default set to one. It is also possible
|
|
to register signals with the GPIO driver. The number of allowed signals
|
|
is configured with ``CONFIG_DEV_NSIGNALS``. ``CONFIG_GPIO_PROCFS`` adds
|
|
``/proc/gpio``, described above.
|
|
|
|
IO Expander Device Drivers
|
|
==========================
|
|
|
|
IO Expander device drivers are chips that provides more GPIO pins, usually
|
|
connected to the MCU with SPI or I2C bus. It is possible to register
|
|
individual GPIO pins of the expander as a separate pins if needed. This
|
|
option is enabled by ``CONFIG_GPIO_LOWER_HALF`` option. Please refer
|
|
to `ioexpander documentation <https://nuttx.apache.org/docs/latest/components/drivers/special/ioexpander.html>`_
|
|
for more description.
|