xl9555-hal 0.3.0

embedded-hal driver for the XL9555 GPIO expander
Documentation

xl9555-hal

crates.io docs.rs license

An embedded-hal 1.0 driver for the XL9555 16-bit I²C GPIO expander.

Features

  • embedded-hal 1.0 digital traits (InputPin, OutputPin, StatefulOutputPin)
  • Register cache for efficient read-modify-write GPIO operations
  • Low-level driver API (set/get direction, level, toggle)
  • High-level pin API (input(), output() on any DriverHandler)
  • Interrupt-safe shared access via critical_section::Mutex<RefCell<_>>
  • #![no_std] compatible — no alloc required
  • Extensible DriverHandler trait — easy to add new backends
  • Async support (optional): Wait trait (wait_for_high, wait_for_low, wait_for_rising_edge, wait_for_falling_edge, wait_for_any_edge) + monitor() task
  • Up to 8 XL9555 devices per I²C bus (3 address pins)

Driver Handler

Instead of coupling pins to a specific synchronization primitive, the library introduces the DriverHandler trait:

Input / Output
  │
  ▼
DriverHandler
  │
  ├── Xl9555<I>             (direct &mut access)
  ├── Xl9555Shared<I>       (critical_section::Mutex<RefCell<Xl9555<I>>>)
  └── ... (extensible)

Any type implementing DriverHandler can construct GPIO pins — the GPIO API stays independent of the sharing strategy.

Usage

Add to your Cargo.toml:

[dependencies]
xl9555-hal = "0.3"

Low-level API

use xl9555_hal::access::DriverHandler;
use xl9555_hal::impls::Xl9555Shared;
use xl9555_hal::types::PinId;

// Address pins: A3=GND, A2=GND, A1=GND → 7-bit address 0x20
let gpio = Xl9555Shared::new(i2c, false, false, false)?;

gpio.set_output(PinId::P00)?;
gpio.set_high(PinId::P00)?;

let level = gpio.get_level(PinId::P03)?;  // reads INPUT register
gpio.toggle(PinId::P00)?;

High-level Pin API

use embedded_hal::digital::PinState;
use xl9555_hal::access::DriverHandler;

let mut led = gpio.output(PinId::P00, PinState::High)?;
led.set_low()?;

let button = gpio.input(PinId::P01)?;
if button.is_high()? { /* ... */ }

Shared Access (interrupt-safe)

use xl9555_hal::impls::Xl9555Shared;

let gpio = Xl9555Shared::new(i2c, false, false, false)?;

// Use `gpio` anywhere `DriverHandler` is expected
let mut led = gpio.output(PinId::P00, PinState::High)?;

Async Edge Detection (feature = "async")

#![no_std]

use embassy_executor::Spawner;
use embassy_hal::gpio::{Input, InputConfig};
use embassy_hal::i2c;
use embassy_hal::i2c::I2c;
use embassy_time::Timer;
use static_cell::StaticCell;
use xl9555_hal::access::DriverHandler;
use xl9555_hal::impls::Xl9555Shared;
use xl9555_hal::types::PinId;

#[embassy_executor::task]
async fn xl9555_int_task(
    xl9555: &'static Xl9555Shared<I2c<'static, i2c::Blocking>>,
    int_pin: Input<'static>,
) {
    xl9555.monitor(int_pin).await
}

#[embassy_executor::task]
async fn key_task(
    mut key: xl9555_hal::input::Input<'static, Xl9555Shared<I2c<'static, i2c::Blocking>>>,
) {
    loop {
        key.wait_for_falling_edge().await.unwrap();
        // debounce
        Timer::after_millis(50).await;
    }
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_hal::init(Default::default());

    let i2c = I2c::new(p.I2C0, i2c::master::Config::default())
        .with_sda(p.GPIO41)
        .with_scl(p.GPIO42);

    static XL9555: StaticCell<Xl9555Shared<I2c<'static, i2c::Blocking>>> = StaticCell::new();
    let xl9555 = XL9555.init(Xl9555Shared::new(i2c, false, false, false).unwrap());

    let key = xl9555.input(PinId::P15).unwrap();
    let int_pin = Input::new(p.GPIO0, InputConfig::default());

    spawner.spawn(xl9555_int_task(xl9555, int_pin)).unwrap();
    spawner.spawn(key_task(key)).unwrap();
}

Cargo Features

Feature Description Default
critical-section DriverHandler impl for critical_section::Mutex<RefCell> yes
async Wait trait impl + monitor() for edge/level detection no

Examples

  • blink — synchronous LED blink and button input
  • async_embassy — async edge detection via Embassy (requires --features async)

Status

  • XL9555 driver with register cache
  • GPIO direction control (input / output)
  • GPIO output with set_high, set_low, toggle
  • GPIO input reading
  • OutputPin + StatefulOutputPin
  • InputPin
  • ErrorType
  • Xl9555Shared (critical_section::Mutex<RefCell<_>>)
  • DriverHandler trait (extensible access pattern)
  • Built-in input() / output() convenience methods
  • Wait trait (wait_for_high, wait_for_low, wait_for_rising_edge, wait_for_falling_edge, wait_for_any_edge)
  • monitor() task (INT pin → signal dispatch)
  • embassy-time debounce / retry support

MSRV

Current stable Rust (compatible with embedded-hal 1.0).

License

Licensed under either of

at your option.