HF-PCAL95555 0.1.0-dev
Loading...
Searching...
No Matches
HF-PCAL95555 Driver

Hardware-agnostic C++ driver for the NXP PCA9555 and PCAL9555A 16-bit I/O expanders

C++ License CI Docs

Overview

📖 📚🌐 Live Complete Documentation - Interactive guides, examples, and step-by-step tutorials

The PCA9555 and PCAL9555A are 16-bit I/O expanders from NXP Semiconductors that communicate via I2C. They provide 16 GPIO pins organized into two 8-bit ports (PORT_0: pins 0-7, PORT_1: pins 8-15), expanding your microcontroller's I/O capabilities over a simple two-wire bus.

This driver provides a single, unified C++ class that supports both chip variants. The chip type is auto-detected during initialization by probing the extended register bank. Features exclusive to the PCAL9555A degrade gracefully when a standard PCA9555 is detected.

The driver is hardware-agnostic – it uses the CRTP (Curiously Recurring Template Pattern) for zero-overhead I2C abstraction. You only need to implement a small I2cInterface class for your platform.

Features

  • Dual-chip support: Auto-detects PCA9555 vs PCAL9555A at runtime
  • 16 GPIO Pins: Two 8-bit ports (PORT_0: pins 0-7, PORT_1: pins 8-15)
  • Per-pin configuration: Direction, pull-up/pull-down, drive strength, polarity
  • Interrupt support: Per-pin interrupt masking with edge-detection callbacks
  • Hardware agnostic: CRTP-based I2C interface for platform independence
  • Modern C++17: Template-based design with std::initializer_list multi-pin APIs
  • Zero overhead: CRTP for compile-time polymorphism – no virtual calls
  • Lazy initialization: No I2C traffic in the constructor; init on first use
  • Kconfig integration: Optional compile-time configuration via ESP-IDF Kconfig
  • Robust error handling: Bitmask error flags, configurable I2C retries
  • Graceful degradation: PCAL9555A features fail cleanly on PCA9555

Quick Start

1. Implement the I2C interface for your platform

#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) {
// Your platform I2C write implementation
}
bool Read(uint8_t addr, uint8_t reg, uint8_t* data, size_t len) {
// Your platform I2C read implementation
}
bool SetAddressPins(bool a0, bool a1, bool a2) {
return false; // Return false if address pins are hardwired
}
};

2. Create the driver and use it

{% raw %}

MyI2c i2c;
// Option A: Address pin levels (A0=LOW, A1=LOW, A2=LOW -> 0x20)
pcal95555::PCAL95555<MyI2c> gpio(&i2c, false, false, false);
// Option B: Direct I2C address
pcal95555::PCAL95555<MyI2c> gpio(&i2c, 0x20);
// Option C: Force chip variant (skip auto-detection)
pcal95555::PCAL95555<MyI2c> gpio(&i2c, 0x20, pcal95555::ChipVariant::PCA9555);
// Use the driver
gpio.SetPinDirection(0, GPIODir::Output);
gpio.WritePin(0, true); // Turn on pin 0
bool state = gpio.ReadPin(1); // Read pin 1
// Check chip variant
if (gpio.HasAgileIO()) {
gpio.SetDriveStrength(0, DriveStrength::Level3);
gpio.SetPullEnable(1, true);
gpio.SetPullDirection(1, true); // pull-up
}
// Multi-pin operations
gpio.WritePins({{0, true}, {1, false}, {2, true}});
gpio.SetDirections({{0, GPIODir::Output}, {1, GPIODir::Input}});

{% endraw %}

Building and Flashing (ESP32)

Prerequisites

  • ESP-IDF v5.5+
  • ESP32-S3 development board with PCA9555 or PCAL9555A connected via I2C

Build

cd examples/esp32
# List available apps
./scripts/build_app.sh list
# Build the comprehensive test suite
./scripts/build_app.sh pcal95555_comprehensive_test Debug
# Build the LED animation demo
./scripts/build_app.sh pcal95555_led_animation Debug

Flash and Monitor

# Flash and open serial monitor
./scripts/flash_app.sh flash_monitor pcal95555_comprehensive_test Debug
# Flash only
./scripts/flash_app.sh flash pcal95555_comprehensive_test Debug
# Monitor only (if already flashed)
./scripts/flash_app.sh monitor

Note (ESP32-S3 native USB): If using the USB-SERIAL-JTAG port (/dev/ttyACM0), you may need to manually enter download mode: Hold BOOT, press RESET, release BOOT, then immediately run the flash command.

Examples

Comprehensive Test Suite (pcal95555_comprehensive_test)

A thorough test of every public API method, organized into 17 test sections:

  • Initialization (I2C bus, driver, chip variant detection)
  • GPIO direction (single pin, multi-pin mask, initializer list)
  • GPIO read/write (ReadPin, WritePin, TogglePin, WritePins, ReadPins)
  • Pull resistor configuration (single + multi-pin, PCAL9555A)
  • Drive strength (single + multi-pin, PCAL9555A)
  • Output mode (push-pull / open-drain, PCAL9555A)
  • Polarity inversion (single, mask, initializer list)
  • Input latch (single, mask, initializer list, PCAL9555A)
  • Interrupt configuration (mask, status, callbacks, handler, PCAL9555A)
  • Port-level operations
  • Multi-pin API tests (WritePins, ReadPins, SetDirections, SetPolarities)
  • Address management (ChangeAddress, address-based constructor)
  • Configuration (SetRetries, EnsureInitialized)
  • Multi-pin PCAL9555A APIs (SetPullEnables, SetDriveStrengths, ConfigureInterrupts, etc.)
  • Interactive input (button press test, disabled by default)
  • Error handling (invalid pins, UnsupportedFeature, selective flag clearing)
  • Stress tests (rapid pin toggling)

PCAL9555A-only tests are automatically skipped (not failed) on a standard PCA9555.

To enable the interactive input test (requires a physical button on pin 0):

static constexpr bool ENABLE_INTERACTIVE_INPUT_TESTS = true;

LED Animation Demo (pcal95555_led_animation)

A visual demo driving 16 LEDs through 10 animation patterns:

  1. Sequential Chase – single LED scans left/right
  2. Bounce – LED bounces between endpoints
  3. Binary Counter – counts 0-65535 in binary on LEDs
  4. Breathing (PWM) – software PWM fade-in/fade-out
  5. Wave / Comet Tail – 4-LED comet sweeps back and forth
  6. Random Sparkle – random LED patterns
  7. Build-up / Teardown – LEDs light up then extinguish one by one
  8. Accelerating Scan – speed ramps from 120ms to 1ms and back
  9. Center Expand / Contract – symmetric outward/inward animation
  10. Alternating Flash – port-vs-port and even-vs-odd flashing

Configure LEDS_ACTIVE_LOW for your wiring (common-anode vs common-cathode).

Hardware Setup

Minimal wiring (ESP32-S3 + PCA9555/PCAL9555A)

ESP32-S3 PCA9555 / PCAL9555A
───────── ──────────────────
GPIO4 (SDA) ────────── SDA
GPIO5 (SCL) ────────── SCL
3.3V ───────────────── VDD
GND ────────────────── VSS (GND)
A0 ─── GND (or GPIO45)
A1 ─── GND (or GPIO48)
A2 ─── GND (or GPIO47)

Pull-up resistors: 4.7kΩ on SDA and SCL to 3.3V (or enable internal pull-ups).

Address: The I2C address is 0x20 + (A2<<2 | A1<<1 | A0). Default with all address pins LOW is 0x20.

LED animation demo wiring

Connect LEDs with current-limiting resistors (220Ω-1kΩ) to each I/O pin. For active-HIGH: LED anode to IO pin, cathode to GND. For active-LOW: LED cathode to IO pin, anode to VDD through resistor.

Interactive test wiring

Connect a momentary push-button between PCA9555 pin IO0_0 and GND. For PCAL9555A, the internal pull-up is enabled automatically. For PCA9555, add an external 10kΩ pull-up resistor to VDD.

Contributing

Pull requests and suggestions are welcome! Please:

  1. Follow the existing code style (clang-format, Doxygen comments)
  2. Add tests for new features in the comprehensive test suite
  3. Update documentation for any API changes
  4. Ensure the build passes for both Debug and Release configurations