This guide will get you up and running with the PCAL95555 driver in just a few steps.
Prerequisites
Minimal Example
Here's a complete working example:
#include "pcal95555.hpp"
class MyI2c : public pcal95555::I2cInterface<MyI2c> {
public:
bool Write(uint8_t addr, uint8_t reg, const uint8_t *data, size_t len) {
return true;
}
bool Read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) {
return true;
}
};
MyI2c i2c;
pcal95555::PCAL95555<MyI2c> gpio(&i2c, 0x20);
if (!gpio.EnsureInitialized()) {
return;
}
gpio.ResetToDefault();
if (gpio.HasAgileIO()) {
} else {
}
gpio.SetPinDirection(0, pcal95555::PCAL95555<MyI2c>::GPIODir::Output);
gpio.WritePin(0, true);
bool input = gpio.ReadPin(1);
Step-by-Step Explanation
Step 1: Include the Header
Step 2: Implement the I2C Interface
You need to implement the I2cInterface for your platform. See Platform Integration for detailed examples for ESP32, STM32, and Arduino.
The interface requires two methods:
Write(uint8_t addr, uint8_t reg, const uint8_t *data, size_t len) - Write data to a register
Read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) - Read data from a register
Step 3: Create Driver Instance
You can create the driver instance in two ways:
Option 1: Using I2C address directly (recommended)
MyI2c i2c;
pcal95555::PCAL95555<MyI2c> gpio(&i2c, 0x20);
Option 2: Using address pin levels
MyI2c i2c;
pcal95555::PCAL95555<MyI2c> gpio(&i2c, false, false, false);
Constructor Parameters:
Using address (Option 1):
- Pointer to your I2C interface implementation
- I2C address (0x20 to 0x27). Address bits are calculated automatically.
Using pin levels (Option 2):
- Pointer to your I2C interface implementation
- A0 pin level (true = HIGH/VDD, false = LOW/GND)
- A1 pin level (true = HIGH/VDD, false = LOW/GND)
- A2 pin level (true = HIGH/VDD, false = LOW/GND)
The I2C address is calculated automatically: base address 0x20 + (A2<<2 | A1<<1 | A0). For example:
- Address 0x20 → A0=LOW, A1=LOW, A2=LOW (default)
- Address 0x21 → A0=HIGH, A1=LOW, A2=LOW
Step 4: Initialize
The driver uses **lazy initialization** - it initializes automatically on first use. However, you can explicitly initialize:
if (!gpio.EnsureInitialized()) {
return;
}
gpio.ResetToDefault();
**Lazy Initialization:**
- The driver initializes automatically when you call any method that requires I2C communication
- Initialization includes setting address pins, verifying I2C communication, **auto-detecting the chip variant** (PCA9555 vs PCAL9555A), and initializing internal state
- If initialization fails, methods return `false` or appropriate error values
- You can call `EnsureInitialized()` explicitly to verify initialization before use
**Chip Variant Detection:**
- The driver automatically probes the Agile I/O register bank to determine if the chip is a PCA9555 or PCAL9555A
- Use `HasAgileIO()` to check if PCAL9555A features are available
- Use `GetChipVariant()` to get the specific variant enum
- PCAL9555A-only methods return `false` and set `Error::UnsupportedFeature` on a PCA9555
`ResetToDefault()` puts the device in a known state (all pins as inputs with pull-ups enabled on PCAL9555A).
Step 5: Configure and Use Pins
gpio.SetPinDirection(0, pcal95555::PCAL95555<MyI2c>::GPIODir::Output);
gpio.WritePin(0, true);
bool value = gpio.ReadPin(1);
Expected Output
When running this example:
- Pin 0 should be set high (if connected to an LED, it should light up)
- Pin 1 value should be read correctly
- No error messages should appear
Troubleshooting
If you encounter issues:
- Compilation errors: Check that you've implemented all required I2C interface methods
- Initialization fails: Verify hardware connections and I2C address
- No response: Check I2C bus and pull-up resistors
- See: Troubleshooting for common issues
Next Steps