mirror of
https://github.com/apache/nuttx.git
synced 2026-08-07 13:37:18 +00:00
This documentation helps users understand when and how to use IO expander
pins for implementing additional I2C buses in their applications.
Related commit: 06099d492e (drivers/i2c: Add IO expander-based I2C bit-bang)
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
212 lines
7.1 KiB
ReStructuredText
212 lines
7.1 KiB
ReStructuredText
==================
|
|
I2C Device Drivers
|
|
==================
|
|
|
|
- ``include/nuttx/i2c/i2c_master.h``
|
|
All structures and APIs needed to work with I2C drivers are provided in
|
|
this header file.
|
|
|
|
- ``struct i2c_ops_s``. Each I2C device driver must implement
|
|
an instance of ``struct i2c_ops_s``. That structure defines a
|
|
call table with the following methods:
|
|
|
|
- **Binding I2C Drivers**. I2C drivers are not normally directly
|
|
accessed by user code, but are usually bound to another, higher
|
|
level device driver. In general, the binding sequence is:
|
|
|
|
#. Get an instance of ``struct i2c_master_s`` from the
|
|
hardware-specific I2C device driver, and
|
|
#. Provide that instance to the initialization method of the
|
|
higher level device driver.
|
|
|
|
- **Examples**: ``arch/z80/src/ez80/ez80_i2c.c``,
|
|
``arch/z80/src/z8/z8_i2c.c``, etc.
|
|
|
|
|
|
=======================
|
|
I2C Bit-Bang Driver
|
|
=======================
|
|
|
|
The I2C bit-bang driver provides a software implementation of the I2C
|
|
protocol using GPIO pins. This is useful when hardware I2C peripherals
|
|
are not available or when additional I2C buses are needed.
|
|
|
|
Overview
|
|
--------
|
|
|
|
- ``include/nuttx/i2c/i2c_bitbang.h``
|
|
Generic upper-half I2C bit-bang driver interface.
|
|
|
|
- ``drivers/i2c/i2c_bitbang.c``
|
|
Generic upper-half implementation that handles the I2C protocol timing.
|
|
|
|
- Platform-specific lower-half drivers control the GPIO pins (SDA and SCL).
|
|
|
|
IO Expander-Based I2C Bit-Bang
|
|
-------------------------------
|
|
|
|
A generic lower-half implementation is provided for systems using IO expanders
|
|
to control GPIO pins. This eliminates the need for platform-specific code when
|
|
implementing I2C bit-bang via IO expander pins.
|
|
|
|
Configuration
|
|
~~~~~~~~~~~~~
|
|
|
|
- ``CONFIG_I2C_BITBANG`` - Enable I2C bit-bang driver framework
|
|
- ``CONFIG_I2C_BITBANG_IOEXPANDER`` - Enable IO expander-based lower-half
|
|
(depends on ``CONFIG_IOEXPANDER``)
|
|
|
|
Header Files
|
|
~~~~~~~~~~~~
|
|
|
|
- ``include/nuttx/i2c/i2c_bitbang_ioexpander.h``
|
|
IO expander-based lower-half driver interface.
|
|
|
|
API
|
|
~~~
|
|
|
|
.. c:function:: FAR struct i2c_master_s *i2c_bitbang_ioexpander_initialize(FAR struct ioexpander_dev_s *ioe, int scl_pin, int sda_pin, int busnum);
|
|
|
|
Initialize an I2C bit-bang driver using IO expander pins.
|
|
|
|
:param ioe: Pointer to the IO expander device
|
|
:param scl_pin: IO expander pin number for SCL (clock line)
|
|
:param sda_pin: IO expander pin number for SDA (data line)
|
|
:param busnum: I2C bus number to register (use negative value to skip registration)
|
|
|
|
:return: Pointer to ``struct i2c_master_s`` on success, NULL on failure
|
|
|
|
The pins will be configured as open-drain outputs, which is required
|
|
for proper I2C operation. If busnum >= 0, the I2C bus is automatically
|
|
registered and accessible via standard I2C APIs.
|
|
|
|
Usage Example
|
|
~~~~~~~~~~~~~
|
|
|
|
.. code-block:: c
|
|
|
|
#include <nuttx/ioexpander/ioexpander.h>
|
|
#include <nuttx/i2c/i2c_bitbang_ioexpander.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
|
|
/* Assume we have an IO expander device */
|
|
FAR struct ioexpander_dev_s *ioe = /* ... get IO expander ... */;
|
|
FAR struct i2c_master_s *i2c;
|
|
|
|
/* Initialize I2C bit-bang using IO expander pins 10 (SCL) and 11 (SDA) */
|
|
/* Register as I2C bus 0 */
|
|
i2c = i2c_bitbang_ioexpander_initialize(ioe, 10, 11, 0);
|
|
if (i2c == NULL)
|
|
{
|
|
/* Initialization failed */
|
|
return -1;
|
|
}
|
|
|
|
/* Now use the I2C master device normally */
|
|
/* For example, with I2C character driver or directly */
|
|
|
|
/* If registered (busnum >= 0), can also access via /dev/i2c0 */
|
|
|
|
Use Cases
|
|
~~~~~~~~~
|
|
|
|
- **GPIO Expansion**: When using I2C or SPI IO expanders for GPIO expansion,
|
|
and need to implement additional I2C buses using those expanded pins.
|
|
|
|
- **Multi-Master Scenarios**: Software bit-bang can be useful in multi-master
|
|
configurations where hardware I2C has limitations.
|
|
|
|
- **Pin Flexibility**: Implement I2C on any GPIO pins, not limited to
|
|
hardware I2C peripheral pins.
|
|
|
|
- **Testing and Debugging**: Use IO expander pins for I2C communication
|
|
during prototyping and debugging.
|
|
|
|
- **Hardware I2C Unavailable**: When hardware I2C peripherals are exhausted
|
|
or not available on specific pins.
|
|
|
|
Features
|
|
~~~~~~~~
|
|
|
|
- Uses standard IO expander API (``IOEXP_WRITEPIN``, ``IOEXP_READPIN``)
|
|
- Automatic open-drain configuration
|
|
- Supports clock stretching (via pin reading)
|
|
- Platform-independent implementation
|
|
- Works with any IO expander that implements the standard interface
|
|
- Automatic I2C bus registration
|
|
|
|
Limitations
|
|
~~~~~~~~~~~
|
|
|
|
- Software timing (slower than hardware I2C)
|
|
- Timing accuracy depends on system load and IO expander response time
|
|
- Limited to standard I2C speeds (fast-mode and high-speed may not be reliable)
|
|
|
|
Implementation Details
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The IO expander-based implementation provides these callbacks:
|
|
|
|
- ``initialize``: Configures SCL and SDA pins as open-drain outputs
|
|
- ``set_scl/set_sda``: Controls pin output values
|
|
- ``get_scl/get_sda``: Reads current pin states (for clock stretching detection)
|
|
|
|
The driver automatically manages the IO expander pin states and handles
|
|
the bit-bang protocol timing according to I2C specifications.
|
|
|
|
|
|
========================
|
|
I2C Slave Device Drivers
|
|
========================
|
|
|
|
- ``include/nuttx/i2c/i2c_slave.h``
|
|
Declaration of all macros and ops and ``int i2c_slave_register``,
|
|
used to bind the lowerhalf driver to the upper one and register an
|
|
I2C slave device.
|
|
|
|
- **Binding I2C Slave Drivers**. Use ``int i2c_slave_register`` in your BSP
|
|
to register your slave device. Before that, you need to get the instance
|
|
of ``struct i2c_slave_s`` from the hardware-specific I2C Slave driver.
|
|
|
|
- **Using I2C Slave in your Application**. I2C slave drivers are normally directly
|
|
accessed by user code, We can read and write to device nodes using posix
|
|
interfaces. The device is registered as ``/dev/i2cslv%d``, where ``%d``
|
|
is a number provided in the BSP initialization phase.
|
|
|
|
- **BSP initialization example (STM32 I2C Slave)**:
|
|
|
|
.. code-block:: c
|
|
|
|
int stm32_i2cs_setup(void)
|
|
{
|
|
i2cs = stm32_i2cbus_slaveinitialize(1);
|
|
if (i2cs != NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
return i2c_slave_register(i2cs, 1, 0x01, 7);
|
|
}
|
|
|
|
I2C Slave Driver API
|
|
--------------------
|
|
|
|
.. c:function:: int i2c_slave_register(FAR struct i2c_slave_s *dev, int bus, int addr, int nbit);
|
|
|
|
Bind the lowerhalf ``dev`` driver to the upperhalf driver and register
|
|
a device. The name of the device is ``/dev/i2cslv%d``, where ``%d`` is
|
|
bus. The address the I2C slave is specified in ``addr``.
|
|
``nbit`` is either 7 or 10 and it specifies the address format.
|
|
|
|
|
|
- ``struct i2c_slaveops_s``. Each I2C slave lowerhalf driver must implement
|
|
an instance of ``struct i2c_slaveops_s``. That structure defines a call
|
|
table with the following methods:
|
|
|
|
- ``setownaddress``: the address the slave responds to,
|
|
- ``write``: sets the tx buffer pointer,
|
|
- ``read``: sets the rx buffer pointer,
|
|
- ``registercallback``: registers the callback function which should
|
|
be called from a service routine. Signals the received or fully transferred
|
|
I2C packet.
|
|
- ``setup``: initializes the peripheral,
|
|
- ``shutdown``: shutdowns the peripheral.
|