Crate pcf857x

Source
Expand description

This is a platform agnostic Rust driver for the PCF8574, PCF8574A and PCF8575 I/O expanders, based on the embedded-hal traits.

This driver allows you to:

  • Set all the outputs to 0 or 1 at once. See set().
  • Read selected inputs. See get().
  • Set all the outputs repeatedly looping through an array. See write_array().
  • Read selected inputs repeatedly filling up an array. See read_array().
  • Split the device into individual input/output pins. See split().

§The devices

The devices consist of 8 or 16 quasi-bidirectional ports, I²C-bus interface, three hardware address inputs and interrupt output. The quasi-bidirectional port can be independently assigned as an input to monitor interrupt status or keypads, or as an output to activate indicator devices such as LEDs.

The active LOW open-drain interrupt output (INT) can be connected to the interrupt logic of the microcontroller and is activated when any input state differs from its corresponding input port register state.

Datasheets:

§Splitting the device into individual input/output pins

By calling split() on the device it is possible to get a structure holding the individual pins as separate elements. These pins implement the OutputPin and InputPin traits (the latter only if activating the unproven feature). This way it is possible to use the pins transparently as normal I/O pins regardless of the fact that an I/O expander is connected in between. You can therefore also pass them to code expecting an OutputPin or InputPin.

However, you need to keep the device you split alive (lifetime annotations have put in place for Rust to enforce this).

For each operation done on an input/output pin, a read or write will be done through I2C for all the pins, using a cached value for the rest of pins not being operated on. This should all be transparent to the user but if operating on more than one pin at once, the set and get methods will be faster. Similarly, if several pins must be changed/read at the same time, the set and get methods would be the correct choice.

At the moment, no mutex has been implemented for the individual pin access.

§Usage examples (see also examples folder)

Please find additional examples using hardware in this repository: driver-examples

§Instantiating with the default address

Import this crate and an embedded_hal implementation, then instantiate the device:

use linux_embedded_hal::I2cdev;
use pcf857x::{ Pcf8574, SlaveAddr };

let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let mut expander = Pcf8574::new(dev, address);

§Providing an alternative address

use linux_embedded_hal::I2cdev;
use pcf857x::{ Pcf8574, SlaveAddr };

let dev = I2cdev::new("/dev/i2c-1").unwrap();
let (a2, a1, a0) = (false, false, true);
let address = SlaveAddr::Alternative(a2, a1, a0);
let mut expander = Pcf8574::new(dev, address);

§Setting the output pins and reading P0 and P7

use linux_embedded_hal::I2cdev;
use pcf857x::{ Pcf8574, SlaveAddr, PinFlag };

let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let mut expander = Pcf8574::new(dev, address);
let output_pin_status = 0b1010_1010;
expander.set(output_pin_status).unwrap();

let mask_of_pins_to_be_read = PinFlag::P0 | PinFlag::P7;
let read_input_pin_status = expander.get(mask_of_pins_to_be_read).unwrap();

println!("Input pin status: {}", read_input_pin_status);

§Splitting device into individual input/output pins and setting them.

use linux_embedded_hal::I2cdev;
use pcf857x::{ Pcf8574, SlaveAddr, PinFlag, OutputPin };

let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let expander = Pcf8574::new(dev, address);
let mut parts = expander.split();
parts.p0.set_high().unwrap();
parts.p7.set_low().unwrap();

§Splitting device into individual input/output pins and reading them.

Only available if compiling with the “unproven” feature

use linux_embedded_hal::I2cdev;
use pcf857x::{ Pcf8574, SlaveAddr, PinFlag };
#[cfg(feature="unproven")]
use pcf857x::InputPin;

let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let expander = Pcf8574::new(dev, address);
let mut parts = expander.split();
#[cfg(feature="unproven")]
{
    let is_input_p0_low = parts.p0.is_low().unwrap();
    let is_input_p2_low = parts.p2.is_low().unwrap();
}

Modules§

  • Module containing structures specific to PCF8574 and PCF8574A
  • Module containing structures specific to PCF8575

Structs§

Enums§

  • All possible errors in this crate
  • Possible slave addresses

Traits§