Device driver for Texas Instruments PCF857X I2C I/O expanders.
More...
Device driver for Texas Instruments PCF857X I2C I/O expanders.
driver for Texas Instruments PCF857X I2C I/O expanders
Overview
Texas Instruments PCF857X I2C I/O expanders provide general purpose I/O extension via I2C bus. The driver supports the following PCF857X I2C I/O expander variants:
| Expander | Type | Pseudomodule to be used |
| PCF8574 | 8-bit I2C I/O expander | pcf8574 |
| PCF8574A | 8-bit I2C I/O expander | pcf8574a |
| PCF8575 | 16-bit I2C I/O expander | pcf8575 |
For each of these PCF857X I2C I/O expanders variants, the driver defines a separate pseudomodule. Multiple PCF857X I2C I/O expanders and different variants can be used at the same time. Either the board definition or the application must specify used PCF857X I/O expander variants by a list of used pseudomodules. For example, to use a PCF8574A and a PCF8575 I/O expander in one application, the make command would be:
USEMODULE="pcf8574a pcf8575" make -C tests/drivers/pcf857x BOARD=...
At least one PCF857X I2C I/O expander variant has to be specified. The driver module pcf857x is then enabled implicitly.
- Note
- While PCF8575 is working in I2C fast mode with up to 400 kHz clock frequency, PCF8574 and PCF8574A are only specified for I2C normal mode with up to 100 kHz clock frequency. However, they seem also to work at 400 kHz clock frequency.
The driver interface is kept as compatible as possible with the peripheral GPIO interface. The only differences are that
- functions have the prefix
pcf857x_ and
- functions require an additional parameter, the pointer to the expander device of type pcf857x_t.
Defined pseudomodules
The functionality of the driver is controlled by the use of pseudomodules. The following pseudomodules are defined:
| Pseudomoule | Functionality |
pcf8574 | support of PCF8574 enabled |
pcf8574a | support of PCF8574A enabled |
pcf8575 | support of PCF8575 enabled |
pcf857x_irq | support of interrupts enabled with medium event priority |
pcf857x_irq_low | support of interrupts enabled with low event priority |
pcf857x_irq_medium | support of interrupts enabled with medium event priority |
pcf857x_irq_low | support of interrupts enabled with high event priority |
- Note
- At least one of the modules
pcf8574, pcf8574a and pcf8575 has to be used.
Expander GPIOs
The PCF857X expander devices provide a GPIO expansion over the I2C interface with either
- 1 port with 8 quasi-parallel input/output (I/O) pins (PCF8574, PCF8574A) or
- 2 ports with 8 quasi-parallel input/output (I/O) pins each (PCF8575).
Each quasi-bidirectional expander I/O pin can be used as an input or output without the use of a data-direction control signal. Output pins are latched and have high-current drive capability for directly driving LEDs. The quasi-bidirectional expander I/O pins without direction control work as follows:
- INPUT: Writing 1 to an expander pin configures the pin as an input, which is pulled up to HIGH by a very weak 100 μA pull-up. When reading the pin, its value then depends on the actual voltage. This corresponds to the behavior of the GPIO_IN_PU mode.
- OUTPUT: Writing 0 to an expander pin configures the pin as an output and actively drives the pin to LOW. This corresponds to the behavior of the GPIO_OD_PU mode.
- Note
- Since the expander I/O pins are quasi-bidirectional without direction control, the only actively driven level is the output LOW. Therefore the driver physically supports only the modes GPIO_IN_PU and GPIO_OD_PU. The other logically identical modes GPIO_IN, GPIO_OUT and GPIO_OD are emulated. Please keep this in mind when connecting these pins to other open-drain output pins that do not generate active signals. The GPIO_IN_PD mode is not supported.
After the initialization with the pcf857x_init function, all expander I/O pins are in input mode and pulled-up to HIGH.
The expander I/O pins can be addressed as GPIO pins using the following scheme:
Interrupts
PCF857X expanders have an open-drain, low-active interrupt (INT) signal, which generates an interrupt by any rising or falling edge of the expander pins in the input mode. Using this expander interrupt signal, the following features become available:
- An interrupt service function can be attached to an expander input pin with the pcf857x_gpio_init_int function. This interrupt service function is then called on any rising and/or falling edge of the expander input pin.
- In addition, the driver uses the interrupt on changes of an expander input pin to internally maintain the current state of all expander input pins. Using this internal current state of the expander input pins avoids reading all expander input pins via I2C every time the input value of a single expander GPIO pin is read with pcf857x_gpio_read.
Since interrupts are handled in the context of a separate event thread (see section The Interrupt Context Problem) enabling interrupts requires more RAM. Therefore interrupts have to be explicitly enabled with the module pcf857x_irq_<priority>. priority can be one of low, medium or highest, which correspond to the priority of the event thread that processes the interrupts. If only the module pcf857x_irq is used without specifying the priority, the interrupt handling is enabled with a medium priority of the event thread. For more information on the priorities check the Event Queue module.
Furthermore, the MCU GPIO pin to which the PCF857X INT signal is connected has to be defined by the default configuration parameter PCF857X_PARAM_INT_PIN (pcf857x_params_t::int_pin) either in the configuration parameter file or at the command line, for example:
CFLAGS="-DPCF857X_PARAM_INT_PIN=\(GPIO_PIN\(0,6\)\)" \
USEMODULE="pcf8575 pcf857x_irq_medium" make -C tests/drivers/pcf857x BOARD=...
- Note
- If an output of the expander is connected to an input of the same expander, there is no interrupt triggered by the input when the output changes. Therefore, a write operation to an output with pcf857x_gpio_write, pcf857x_gpio_clear, pcf857x_gpio_set or pcf857x_gpio_toggle leads to an additional read-after-write operation, if interrupts are used.
The use of interrupts therefore increases the read performance considerably, since I2C read operations are required only when the inputs change. But the write performance is reduced to the half.
The Interrupt Context Problem
Handling an interrupt of a PCF857x expander requires the driver to access the device directly via I2C. However, the mutex-based synchronization of I2C accesses does not work in the interrupt context. Therefore the ISR must not access the PCF857x expander device directly. Rather, the ISR must only indicate the occurrence of the interrupt which has to be handled asynchronously in thread context.
For this purpose an event thread module is used when interrupts are enabled by the module pcf857x_irq_<priority>. The driver then handles the interrupts in the context of the event thread with given priority. For more information on the priorities check the Event Queue module.
SAUL Capabilities
The driver provides SAUL capabilities that are compatible to the SAUL capabilities of peripheral GPIOs. Each PCF857X expander I/O pin can be mapped directly to SAUL by defining an according entry in PCF857X_SAUL_GPIO_PARAMS. Please refer file $RIOTBASE/drivers/pcf857x/include/pcf857x_params.h for an example.
- Note
- Module
saul_gpio has to be added to the project to enable SAUL capabilities of the PCF857X driver, e.g.: USEMODULE="pcf8575 saul_gpio" make -C tests/drivers/saul BOARD=...
Using Multiple Devices
It is possible to use multiple devices and different variants of PCF857X I/O expanders at the same time. Either the board definition or the application must specify used PCF857X I/O expander variants by a list of used pseudomodules. For example, to use a PCF8574A and a PCF8575 I/O expander in one application, the make command would be:
USEMODULE="pcf8574a pcf8575" make -C tests/drivers/pcf857x BOARD=...
Furthermore, used devices have to be configured by defining the configuration parameter set pcf857x_params of type pcf857x_params_t. The default configuration for one device is defined in drivers/pcf857x/pcf857x_params.h. Either the board definition or the application can override it by placing a file pcf857x_params.h in the board definition directory or the application directory $APPDIR. For example, the definition of the configuration parameter array for the two devices above could be:
static const pcf857x_params_t pcf857x_params[] = {
{
.dev = I2C_DEV(0),
.addr = 0,
.exp = PCF857X_EXP_PCF8574A,
.int_pin = GPIO_PIN(0, 1),
},
{
.dev = I2C_DEV(0),
.addr = 0,
.exp = PCF857X_EXP_PCF8575,
.int_pin = GPIO_PIN(0, 2),
},
};
- Author
- Gunar Schorcht gunar.nosp@m.@sch.nosp@m.orcht.nosp@m..net
|
| int | pcf857x_init (pcf857x_t *dev, const pcf857x_params_t *params) |
| | Initialize the PCF857X I/O expander.
|
| |
| int | pcf857x_gpio_init (pcf857x_t *dev, uint8_t pin, gpio_mode_t mode) |
| | Initialize a PCF857X pin.
|
| |
| int | pcf857x_gpio_init_int (pcf857x_t *dev, uint8_t pin, gpio_mode_t mode, gpio_flank_t flank, gpio_cb_t isr, void *arg) |
| | Initialize a PCF857X pin for external interrupt usage.
|
| |
| int | pcf857x_gpio_read (pcf857x_t *dev, uint8_t pin) |
| | Get the value from PCF857X input pin.
|
| |
| void | pcf857x_gpio_write (pcf857x_t *dev, uint8_t pin, int value) |
| | Write the value to PCF857X input pin.
|
| |
| void | pcf857x_gpio_clear (pcf857x_t *dev, uint8_t pin) |
| | Clear the PCF857X output pin.
|
| |
| void | pcf857x_gpio_set (pcf857x_t *dev, uint8_t pin) |
| | Set the PCF857X output pin.
|
| |
| void | pcf857x_gpio_toggle (pcf857x_t *dev, uint8_t pin) |
| | Toggle the value of the PCF857X output pin.
|
| |
| void | pcf857x_gpio_irq_enable (pcf857x_t *dev, uint8_t pin) |
| | Enable pin interrupt.
|
| |
| void | pcf857x_gpio_irq_disable (pcf857x_t *dev, uint8_t pin) |
| | Disable pin interrupt.
|
| |
|
PCF857X I2C slave addresses are defined as an offset to a base address, which depends on the expander used.
The address offset is in the range of 0 to 7.
|
| #define | PCF8575_BASE_ADDR (0x20) |
| | PCF8575 I2C slave base address.
|
| |
| #define | PCF8574_BASE_ADDR (0x20) |
| | PCF8574 I2C slave base address.
|
| |
| #define | PCF8574A_BASE_ADDR (0x38) |
| | PCF8574A I2C slave base address.
|
| |
◆ PCF8574_BASE_ADDR
| #define PCF8574_BASE_ADDR (0x20) |
PCF8574 I2C slave base address.
Addresses are then in range from 0x20 to 0x27
Definition at line 278 of file pcf857x.h.
◆ PCF8574_GPIO_PIN_NUM
| #define PCF8574_GPIO_PIN_NUM (8) |
PCF8574 has 8 I/O pins.
Definition at line 294 of file pcf857x.h.
◆ PCF8574A_BASE_ADDR
| #define PCF8574A_BASE_ADDR (0x38) |
PCF8574A I2C slave base address.
Addresses are then in range from 0x38 to 0x3f
Definition at line 283 of file pcf857x.h.
◆ PCF8574A_GPIO_PIN_NUM
| #define PCF8574A_GPIO_PIN_NUM (8) |
PCF8574A has 8 I/O pins.
Definition at line 295 of file pcf857x.h.
◆ PCF8575_BASE_ADDR
| #define PCF8575_BASE_ADDR (0x20) |
PCF8575 I2C slave base address.
Addresses are then in range from 0x20 to 0x27
Definition at line 273 of file pcf857x.h.
◆ PCF8575_GPIO_PIN_NUM
| #define PCF8575_GPIO_PIN_NUM (16) |
PCF8575 has 16 I/O pins.
Definition at line 293 of file pcf857x.h.
◆ PCF857X_GPIO_PIN
| #define PCF857X_GPIO_PIN |
( |
| x, |
|
|
| y ) |
Value:
conversion of (port x : pin y) to a pin number
Definition at line 299 of file pcf857x.h.
◆ PCF857X_GPIO_PIN_NUM
| #define PCF857X_GPIO_PIN_NUM (16) |
Maximum number of GPIO pins.
Defines the maximum number of GPIO pins of all PCF857X I/O expanders used. If a PCF8575 is used, the maximum number is 16 I/O pins.
Definition at line 313 of file pcf857x.h.
◆ pcf857x_data_t
Data type that can mask all expander pins.
If a PCF8575 is used, the 16 I/O pins have to be masked.
Definition at line 320 of file pcf857x.h.
◆ pcf857x_exp_t
Definition of PCF857X expander variants.
It is used in configuration parameters to specify the PCF857X expander used by device.
- Note
- Expander variants known by the driver depend on enabled pseudomodules
pcf8574, pcf8574a and pcf8575.
| Enumerator |
|---|
| PCF857X_EXP_PCF8574 | PCF8574 8 bit I/O expander used.
|
| PCF857X_EXP_PCF8574A | PCF8574A 8 bit I/O expander.
|
| PCF857X_EXP_PCF8575 | PCF8575 16 bit I/O expander.
|
Definition at line 339 of file pcf857x.h.
◆ pcf857x_gpio_clear()
| void pcf857x_gpio_clear |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Clear the PCF857X output pin.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to clear, use PCF857X_GPIO_PIN(x,y) to specify |
◆ pcf857x_gpio_init()
Initialize a PCF857X pin.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to initialize, use PCF857X_GPIO_PIN(x,y) to specify |
| [in] | mode | mode of the pin, see gpio_t |
- Note
- Since the expander I/O pins are quasi-bidirectional without direction control, the only actively driven level is the output LOW. Therefore the driver physically supports only the modes GPIO_IN_PU and GPIO_OD_PU. The other logically identical modes GPIO_IN, GPIO_OUT and GPIO_OD are emulated. For the GPIO_IN_PU mode the function returns with
-EINVAL.
- After initialization in GPIO_OUT mode the pin is actively driven LOW, after initialization in all other modes the pin is pulled-up to HIGH.
- Return values
-
| 0 | on success |
| <0 | a negative errno error code on error |
◆ pcf857x_gpio_init_int()
Initialize a PCF857X pin for external interrupt usage.
The registered callback function will be called in interrupt context every time the defined flank(s) are detected. Therefore, it MUST NOT be blocking or time-consuming.
The interrupt is activated automatically after the initialization.
- Precondition
- The MCU GPIO pin for the interrupt signal has to be defined by the default configuration parameter PCF857X_PARAM_INT_PIN (pcf857x_params_t::int_pin).
- Note
- This function is only available if interrupt handling is enabled by one of the modules
pcf857x_irq*
- Since the expander I/O pins are quasi-bidirectional without direction control, the only actively driven level is the output LOW. Therefore the driver physically supports only the modes GPIO_IN_PU and GPIO_OD_PU. The other logically identical modes GPIO_IN, GPIO_OUT and GPIO_OD are emulated. For the GPIO_IN_PU mode the function returns with
-EINVAL.
- After initialization in GPIO_OUT mode the pin is actively driven LOW, after initialization in all other modes the pin is pulled-up to HIGH.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to initialize, use PCF857X_GPIO_PIN(x,y) to specify |
| [in] | mode | mode of the pin, see gpio_t |
| [in] | flank | define the active flanks, see gpio_flank_t |
| [in] | isr | ISR that is called back from interrupt context |
| [in] | arg | optional argument passed to the callback |
- Return values
-
| 0 | on success |
| <0 | a negative errno error code on error |
◆ pcf857x_gpio_irq_disable()
| void pcf857x_gpio_irq_disable |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Disable pin interrupt.
- Note
- This function is only available if interrupt handling is enabled by one of the modules
pcf857x_irq*
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to enable the interrupt for |
◆ pcf857x_gpio_irq_enable()
| void pcf857x_gpio_irq_enable |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Enable pin interrupt.
- Note
- This function is only available if interrupt handling is enabled by one of the modules
pcf857x_irq*
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to enable the interrupt for |
◆ pcf857x_gpio_read()
| int pcf857x_gpio_read |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Get the value from PCF857X input pin.
- Note
- If the PCF857X interrupt is used, the read operation does not perform an I2C read operation since the last input pin value is already read.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to read, use PCF857X_GPIO_PIN(x,y) to specify |
◆ pcf857x_gpio_set()
| void pcf857x_gpio_set |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Set the PCF857X output pin.
- Parameters
-
◆ pcf857x_gpio_toggle()
| void pcf857x_gpio_toggle |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin ) |
Toggle the value of the PCF857X output pin.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to toggle, use PCF857X_GPIO_PIN(x,y) to specify |
◆ pcf857x_gpio_write()
| void pcf857x_gpio_write |
( |
pcf857x_t * | dev, |
|
|
uint8_t | pin, |
|
|
int | value ) |
Write the value to PCF857X input pin.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | pin | pin to write, use PCF857X_GPIO_PIN(x,y) to specify |
| [in] | value | value to write |
◆ pcf857x_init()
Initialize the PCF857X I/O expander.
All expander pins are set to be input and are pulled up.
- Parameters
-
| [in] | dev | descriptor of PCF857X I/O expander device |
| [in] | params | configuration parameters, see pcf857x_params_t |
- Precondition
- If the interrupt handling is enabled by one of the modules
pcf857x_irq*, the MCU GPIO pin for the interrupt signal has to be defined by the default configuration parameter PCF857X_PARAM_INT_PIN (pcf857x_params_t::int_pin).
- Return values
-
| 0 | on success |
| <0 | a negative errno error code on error |