
[![Arduino CI](https://github.com/RobTillaart/TCA6408A_RT/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/TCA6408A_RT/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/TCA6408A_RT/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/TCA6408A_RT/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/TCA6408A_RT/actions/workflows/jsoncheck.yml)
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/TCA6408A_RT.svg)](https://github.com/RobTillaart/TCA6408A_RT/issues)

[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/TCA6408A_RT/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/TCA6408A_RT.svg?maxAge=3600)](https://github.com/RobTillaart/TCA6408A_RT/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/TCA6408A.svg)](https://registry.platformio.org/libraries/RobTillaart/TCA6408A_RT)


# TCA6408A

Arduino library for TCA6408A I2C 8 bits IO expander.


## Description

**Experimental**

This library is to use the TCA6408A I2C 8 bit IO expander. 
This device supports a voltage range of 1.65 .. 5.5 Volt.
It allows bidirectional voltage-level translation and gpio expansion 
between 1.8-V, 2.5-V, 3.3-V, and 5-V I2C bus and P-ports.

Every IO pin can be configured as INPUT or OUTPUT, with optional
polarity inversion.

The TCA6408A has a active LOW RESET pin to reset the device,
see reset section below.

The TCA6408A supports an active LOW interrupt on changing INPUT pins, 
see interrupt section below.

As always, feedback is welcome.


### Hardware

See datasheet section 5 Pin Configuration and Functions for details.

The pin numbering differs per type chip TSOP, VFQN, UFQN.

Note the device has two voltage pins - see also 8.3.1 Voltage Translation
- VCCI = voltage of I2C bus.
- VCCP = voltage of IO pins. 


### Interrupts

From datasheet 8.3.3 + drawing 9.2 Typical Application 

_An interrupt is generated by any rising or falling edge of the port inputs in the input mode. 
Resetting the interrupt circuit is achieved when data on the port is changed to the original
setting or when data is read from the port that generated the interrupt. 
A pin configured as an output cannot cause an interrupt. 
Changing an I/O from an output to an input may cause a false interrupt to occur if the
state of the pin does not match the contents of the Input Port register.
The INT output has an open-drain structure and requires pull-up resistor._

The library does not support interrupt handling, the user should implement this.


### Reset

From datasheet 8.3.4 + drawing 9.2 Typical Application 

_The RESET input can be asserted to initialize the system while keeping the VCCP at its operating level.
A reset can be accomplished by holding the RESET pin low for a minimum of tW. 
The TCA6408A registers are changed to their default state once RESET is low (0). 
When RESET is high (1), the I/O levels at the P-port can be changed externally or through the controller. 
This RESET requires a pull-up resistor to VCCI, if no active connection is used._

The current version of the library does not support a reset() function yet.


### Related

- https://github.com/RobTillaart/TCA6408A_RT - 8 bits I2C IO expander
- https://github.com/RobTillaart/TCA6416A_RT - 16 bits I2C IO expander
- https://github.com/RobTillaart/TCA6424A_RT - 24 bits I2C IO expander
- https://github.com/RobTillaart/PCF8574 - 8 bits I2C IO expander
- https://github.com/RobTillaart/PCF8575 - 16 bits I2C IO expander

datasheet
- https://www.ti.com/lit/ds/symlink/tca6408a.pdf


### Tested

Thanks to Dave Williams for testing the library with hardware.

## I2C

### I2C Address

The device has one address pin to set address to 0x20 or 0x21. (32/33 decimal)


### I2C multiplexing

Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up 
to eight channels (think of it as I2C subnets) which can use the complete 
address range of the device. 

Drawback of using a multiplexer is that it takes more administration in 
your code e.g. which device is on which channel. 
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices 
too if they are behind the multiplexer.

- https://github.com/RobTillaart/TCA9548


### I2C Performance

The TCA6408A supports I2C clocks up to 400 kHz (datasheet), 
higher clocks are not tested.

|  Clock     |  time (us)  |  Notes  |
|:----------:|:-----------:|:--------|
|   100 KHz  |             |  default 
|   200 KHz  |             |
|   300 KHz  |             |
|   400 KHz  |             |


TODO: create and run performance sketch on hardware.


## Interface

```cpp
#include "TCA6408A.h"
```

### Constructor

- **TCA6408A(uint8_t address, TwoWire \*wire = &Wire)** optional select I2C bus.
- **bool begin()** checks if the device is visible on the I2C bus.
- **bool isConnected()** Checks if the device address can be found on the I2C bus.
- **uint8_t getAddress()** Returns the address configured in constructor (convenience).

### PinMode

mask = 0x00..0xFF, pin = 0..7  

- **void setPinMode8(uint8_t mask)** per bit 1 = INPUT 0 = OUTPUT.
- **uint8_t getPinMode8()** returns the set mask.
- **void setPinMode1(uint8_t pin, uint8_t value)** set the pin to INPUT / OUTPUT.
- **uint8_t getPinMode1(uint8_t pin)** returns the pinMode of the pin.

### Polarity

mask = 0x00..0xFF, pin = 0..7  

- **void setPolarity8(uint8_t mask)** per bit 1 = inverted, 0 = normal.
- **uint8_t getPolarity8()** returns the set mask.
- **void setPolarity1(uint8_t pin, uint8_t value)** set the pin to 1 = inverted, 0 = normal.
- **uint8_t getPolarity1(uint8_t pin)** returns the polarity of the pin.

### Core IO

mask = 0x00..0xFF, pin = 0..7

- **void digitalWrite8(uint8_t mask)** write all the pins at once.
- **uint8_t digitalRead8()** read all the pins at once.
- **void digitalWrite1(uint8_t pin, uint8_t value)** set the pin to HIGH or LOW.
- **uint8_t digitalRead1(uint8_t pin)** returns the value of the pin.

### Debug

- **int getLastError()** returns last error of low level communication.


## Future

#### Must

- improve documentation

#### Should

- investigate optimization 
  - multi-read without setting register again
  - cache register values (not in 0.1.x)
- implement RESET support
  - reset pin configuration (begin?)
  - reset pin example
- improve error handling
  - range checking parameters
  - pass parameter by reference and return error flag.

#### Could

- add examples
- add unit tests

#### Wont


## Support

If you appreciate my libraries, you can support the development and maintenance.
Improve the quality of the libraries by providing issues and Pull Requests, or
donate through PayPal or GitHub sponsors.

Thank you,


