diff --git a/Documentation/components/drivers/special/ioexpander.rst b/Documentation/components/drivers/special/ioexpander.rst index 5a2f5134dd9..f52f019df43 100644 --- a/Documentation/components/drivers/special/ioexpander.rst +++ b/Documentation/components/drivers/special/ioexpander.rst @@ -2,29 +2,37 @@ IO Expander Device Drivers ========================== -- ``include/nuttx/ioexpander/ioexpander.h`` and ``include/nuttx/ioexpander/gpio.h``. - All structures and APIs needed to work with ioexpander drivers are provided in - this header file. +The IO Expander subsystem is defined in the following headers: -- ``struct ioexpander_ops_s``. Each ioexpand device driver must implement - an instance of ``struct ioexpander_ops_s``. That structure defines a - call table with the methods, and we also provide macros to help access methods. +- ``include/nuttx/ioexpander/ioexpander.h`` — defines the public IO expander + interface: macros, types, and helper access macros used by drivers and + consumers. +- ``include/nuttx/ioexpander/gpio.h`` — provides the "gpio lower half" + helper that allows registering an IO expander pin as a standard GPIO + character device (see ``gpio_lower_half`` and ``gpio_lower_half_byname``). -- we also provide method ``gpio_lower_half`` to make ioexpander compatible with normal gpio. +Each IO expander driver must implement an instance of ``struct +ioexpander_ops_s``. That structure defines the lower-half call table and +the operations a driver must provide; the public header also includes +helper macros that dispatch to the lower-half operations table. -- **Binding ioexpander Drivers**. ioexpander drivers are not normally directly - accessed by user code, we should always get lower level drivers, for example I2C, - and map extended gpio feature same asa normal gpio. See for example, - ``int nrf52_sx1509_initialize(void)`` - in ``boards/arm/nrf52/thingy52/src/nrf52_sx1509.c``. In general, the binding - sequence is: +The helper ``gpio_lower_half`` can be used to register individual expander +pins as standard GPIO devices so that upper-half GPIO consumers can access +expander pins through the common GPIO character driver. - #. Get an instance of ``struct i2c_master_s`` from the - hardware-specific I2C device driver, and - #. Provide that instance and configurations to the ioexpander initialization method - to get the ``struct ioexpander_dev_s`` ioe device instance. - #. Then use ioe device instance to do ioexpander operations, or use ``gpio_lower_half`` - to make ioexpand compatible with normal gpio. +**Binding IO expander drivers** + +IO expander drivers are usually bound by board-specific code rather than +accessed directly from application code. For I2C- or SPI-connected +expanders the typical sequence is: + +#. Obtain the bus instance (for example, a ``struct i2c_master_s *``) from + the hardware-specific bus driver. +#. Call the expander driver's initialization routine with the bus instance + and device-specific configuration; the init routine returns a + ``struct ioexpander_dev_s *`` instance. +#. Use the returned ``ioe`` instance directly, or register individual + expander pins with the upper-half GPIO driver via ``gpio_lower_half``. - **Examples**: ``drivers/ioexpander/pca9555.c``, @@ -33,3 +41,213 @@ IO Expander Device Drivers ``drivers/ioexpander/ioe_rpmsg.c``, ``boards/sim/sim/sim/src/sim_ioexpander.c``, ``boards/arm/nrf52/thingy52/src/nrf52_sx1509.c`` etc. + +Further details +=============== + +Header files +------------ + +The relevant header files are: + +- ``include/nuttx/ioexpander/ioexpander.h`` — defines macros, types and access + macros used to interact with IO expanders. +- ``include/nuttx/ioexpander/gpio.h`` — provides the "gpio lower half" helper + that allows registering an IO expander pin as a standard GPIO device. + +Overview of key macros and options +---------------------------------- + +The following is a concise reference of the important macros defined in the +header. These are the options you will typically use through ``IOEXP_SETOPTION`` +and the various access macros. The primary preprocessor definitions are +listed below (C syntax): + +.. code-block:: c + + /* Direction definitions */ + #define IOEXPANDER_DIRECTION_IN 0 /* float */ + #define IOEXPANDER_DIRECTION_IN_PULLUP 1 + #define IOEXPANDER_DIRECTION_IN_PULLDOWN 2 + #define IOEXPANDER_DIRECTION_OUT 3 /* push-pull */ + #define IOEXPANDER_DIRECTION_OUT_OPENDRAIN 4 + #define IOEXPANDER_DIRECTION_OUT_LED 5 /* LED output */ + + /* Pinset mask helpers */ + #define IOEXPANDER_PINMASK (((ioe_pinset_t)1 << CONFIG_IOEXPANDER_NPINS) - 1) + #define PINSET_ALL (~((ioe_pinset_t)0)) + + /* Common option values (used with IOEXP_SETOPTION) */ + /* Invert (active level) */ + #define IOEXPANDER_OPTION_INVERT 1 + #define IOEXPANDER_VAL_NORMAL 0 /* normal polarity */ + #define IOEXPANDER_VAL_INVERT 1 /* inverted polarity */ + + /* Interrupt configuration (level/edge and high/low/rising/falling/both) */ + #define IOEXPANDER_OPTION_INTCFG 2 + #define IOEXPANDER_VAL_DISABLE 0 /* 0000 disable interrupts */ + #define IOEXPANDER_VAL_LEVEL 1 /* xx01: level triggered */ + #define IOEXPANDER_VAL_EDGE 2 /* xx10: edge triggered */ + #define IOEXPANDER_VAL_HIGH 5 /* 0101: high level */ + #define IOEXPANDER_VAL_LOW 9 /* 1001: low level */ + #define IOEXPANDER_VAL_RISING 6 /* 0110: rising edge */ + #define IOEXPANDER_VAL_FALLING 10 /* 1010: falling edge */ + #define IOEXPANDER_VAL_BOTH 14 /* 1110: both edges */ + + /* LED configuration */ + #define IOEXPANDER_OPTION_LEDCFG 3 /* assign an LED number to a pin */ + + /* Non-generic (driver-specific) option */ + #define IOEXPANDER_OPTION_NONGENERIC 4 /* pass driver-specific struct */ + + /* Wakeup configuration (configure pin as SoC wake-up source) */ + #define IOEXPANDER_OPTION_WAKEUPCFG 5 + #define IOEXPANDER_WAKEUP_DISABLE 0 + #define IOEXPANDER_WAKEUP_ENABLE 1 + + /* Debounce and interrupt mask (recent additions) */ + #define IOEXPANDER_OPTION_SETDEBOUNCE 6 /* configure debounce */ + #define IOEXPANDER_DEBOUNCE_DISABLE 0 + #define IOEXPANDER_DEBOUNCE_ENABLE 1 + + #define IOEXPANDER_OPTION_SETMASK 7 /* control interrupt masking */ + #define IOEXPANDER_MASK_DISABLE 0 /* unmask (enable) interrupts */ + #define IOEXPANDER_MASK_ENABLE 1 /* mask (suppress) interrupts */ + +Access macros (API) +------------------- + +The header exposes a set of helper macros that dispatch to the underlying +driver operations table (``struct ioexpander_ops_s``): + +.. c:macro:: IOEXP_SETDIRECTION(dev, pin, dir) + + Set a pin direction (input, output, open-drain, LED, pull-up/down). + Returns 0 on success or a negative errno on failure. + +.. c:macro:: IOEXP_SETOPTION(dev, pin, opt, val) + + Generic option setting interface used to configure the options listed + above. Note that ``val`` is a ``void *``; drivers may accept an integer + casted to a pointer or a pointer to a driver-specific structure. + + Examples:: + + /* Invert pin polarity */ + IOEXP_SETOPTION(dev, 3, IOEXPANDER_OPTION_INVERT, + (FAR void *)IOEXPANDER_VAL_INVERT); + + /* Enable debounce on pin 2 */ + IOEXP_SETOPTION(dev, 2, IOEXPANDER_OPTION_SETDEBOUNCE, + (FAR void *)IOEXPANDER_DEBOUNCE_ENABLE); + + /* Mask interrupts for pin 5 */ + IOEXP_SETOPTION(dev, 5, IOEXPANDER_OPTION_SETMASK, + (FAR void *)IOEXPANDER_MASK_ENABLE); + +.. c:macro:: IOEXP_WRITEPIN(dev, pin, val) + + Set the pin level. Returns 0 on success or a negative errno on error. + +.. c:macro:: IOEXP_READPIN(dev, pin, valptr) + + Read the actual physical pin level. The value is returned via ``valptr``. + +.. c:macro:: IOEXP_READBUF(dev, pin, valptr) + + Read the buffered/register value cached by the expander. + + - ``IOEXP_WRITEPIN`` sets the pin level (TRUE typically means high). + Drivers handle polarity inversion if configured. + - ``IOEXP_READPIN`` reads the actual physical pin level. + - ``IOEXP_READBUF`` reads the buffered/register value cached by the + expander. + +Multi-pin operations +-------------------- + +When ``CONFIG_IOEXPANDER_MULTIPIN`` is enabled, batch operations are +available that may be more efficient than repeated single-pin calls: + +- ``IOEXP_MULTIWRITEPIN(dev, pins, vals, count)`` +- ``IOEXP_MULTIREADPIN(dev, pins, vals, count)`` +- ``IOEXP_MULTIREADBUF(dev, pins, vals, count)`` + +Interrupts and callbacks +------------------------ + +If ``CONFIG_IOEXPANDER_INT_ENABLE`` is enabled the header defines the +callback type and attach/detach helper macros. The callback signature +is:: + + typedef CODE int (*ioe_callback_t)(FAR struct ioexpander_dev_s *dev, + ioe_pinset_t pinset, FAR void *arg); + +The callback is invoked when events occur for the monitored pinset. The +attach/detach helpers are provided as macros that dispatch to the lower-half +driver when ``CONFIG_IOEXPANDER_INT_ENABLE`` is enabled: + +.. c:macro:: IOEP_ATTACH(dev, pinset, callback, arg) + + Attach and enable a pin interrupt callback. Returns a non-NULL opaque + handle on success. ``pinset`` selects which pin(s) will generate the + callback; ``callback`` is a function of type ``ioe_callback_t`` and + ``arg`` is passed through to the callback. + +.. c:macro:: IOEP_DETACH(dev, handle) + + Detach and disable a previously attached callback referenced by ``handle``. + +Note: when ``CONFIG_IOEXPANDER_NPINS`` > 64, ``ioe_pinset_t`` represents a +single interrupt pin number rather than a bitmask. + +Driver interface (lower-half) +----------------------------- + +Each IO expander driver must implement the operations table +``struct ioexpander_ops_s``. At minimum the driver should provide: + +- ``ioe_direction`` +- ``ioe_option`` +- ``ioe_writepin`` +- ``ioe_readpin`` +- ``ioe_readbuf`` + +Optional multi-pin and interrupt attach/detach methods should be provided +when the corresponding configuration options are enabled. + +Binding to the upper layer (gpio_lower_half) +-------------------------------------------- + +Applications normally do not access IO expander drivers directly. Typical +binding steps are: + +1. Obtain the bus instance (for example, ``struct i2c_master_s *``) from + the hardware-specific bus driver. +2. Call the expander driver's initialization routine with the bus instance + and device configuration to obtain a ``struct ioexpander_dev_s *``. +3. Use the returned ``ioe`` instance directly, or register individual + expander pins as standard GPIO devices via ``gpio_lower_half`` or + ``gpio_lower_half_byname``. + +Example (pseudocode):: + + /* Get the I2C bus */ + struct i2c_master_s *i2c = up_i2cinitialize(0); + + /* Initialize the expander (driver-specific init) */ + struct ioexpander_dev_s *ioe = pca9555_initialize(i2c, CONFIG_PCA9555_ADDR); + + /* Configure pin 0 as input with pull-up and enable debounce */ + IOEXP_SETDIRECTION(ioe, 0, IOEXPANDER_DIRECTION_IN_PULLUP); + IOEXP_SETOPTION(ioe, 0, IOEXPANDER_OPTION_SETDEBOUNCE, + (FAR void *)IOEXPANDER_DEBOUNCE_ENABLE); + +Examples and references +----------------------- + +See the following drivers and board examples for concrete usage: + +- ``drivers/ioexpander/pca9555.c`` — I2C IO expander implementation. +- ``drivers/ioexpander/ioe_rpmsg.c`` — RPMSG-based IO expander. +- ``boards/arm/nrf52/thingy52/src/nrf52_sx1509.c`` — binding example. diff --git a/include/nuttx/ioexpander/ioexpander.h b/include/nuttx/ioexpander/ioexpander.h index b2df1e4b31f..218bd3c34a6 100644 --- a/include/nuttx/ioexpander/ioexpander.h +++ b/include/nuttx/ioexpander/ioexpander.h @@ -83,7 +83,11 @@ #define IOEXPANDER_WAKEUP_DISABLE 0 /* Do not cfg the pin as wake up source */ #define IOEXPANDER_WAKEUP_ENABLE 1 /* Cfg the pin as wake up source */ #define IOEXPANDER_OPTION_SETDEBOUNCE 6 /* Configure debounce duration */ +# define IOEXPANDER_DEBOUNCE_DISABLE 0 /* Disable debounce */ +# define IOEXPANDER_DEBOUNCE_ENABLE 1 /* Enable debounce */ #define IOEXPANDER_OPTION_SETMASK 7 /* Mask the interrupter */ +# define IOEXPANDER_MASK_DISABLE 0 /* Unmask the interrupter */ +# define IOEXPANDER_MASK_ENABLE 1 /* Mask the interrupter */ /* Access macros ************************************************************/