use core::cell::{Cell, RefCell};
use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin};
use embedded_hal::i2c::I2c;
#[derive(Debug)]
pub struct PinError<E>(pub E);
impl<E: embedded_hal::i2c::Error> embedded_hal::digital::Error for PinError<E> {
fn kind(&self) -> ErrorKind { ErrorKind::Other }
}
pub struct Pcf8575Minimal<I2C> {
i2c: RefCell<I2C>,
addr: u8,
shadow: [Cell<u8>; 2],
}
impl<I2C: I2c> Pcf8575Minimal<I2C> {
pub fn new(i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let chip = Self {
i2c: RefCell::new(i2c),
addr,
shadow: [Cell::new(0xFF), Cell::new(0xFF)],
};
chip.write_both()?;
Ok(chip)
}
fn write_both(&self) -> Result<(), I2C::Error> {
let buf = [self.shadow[0].get(), self.shadow[1].get()];
self.i2c.borrow_mut().write(self.addr, &buf)
}
fn read_both(&self) -> Result<[u8; 2], I2C::Error> {
let mut buf = [0u8; 2];
self.i2c.borrow_mut().read(self.addr, &mut buf)?;
Ok(buf)
}
pub fn write_port(&self, port: usize, mask: u8) -> Result<(), I2C::Error> {
self.shadow[port].set(mask);
self.write_both()
}
pub fn read_port(&self, port: usize) -> Result<u8, I2C::Error> {
let buf = self.read_both()?;
Ok(buf[port])
}
pub fn pin(&self, n: u8) -> ExPin<'_, I2C> {
ExPin { chip: self, n }
}
pub(crate) fn set_pin(&self, n: u8, high: bool) -> Result<(), I2C::Error> {
let port_idx = (n / 8) as usize;
let bit = n % 8;
let mut s = self.shadow[port_idx].get();
if high { s |= 1 << bit; }
else { s &= !(1 << bit); }
self.shadow[port_idx].set(s);
self.write_both()
}
pub(crate) fn shadow_byte(&self, port: usize) -> u8 {
self.shadow[port].get()
}
}
pub struct ExPin<'a, I2C> {
chip: &'a Pcf8575Minimal<I2C>,
n: u8,
}
impl<I2C: I2c> ErrorType for ExPin<'_, I2C> {
type Error = PinError<I2C::Error>;
}
impl<I2C: I2c> OutputPin for ExPin<'_, I2C> {
fn set_high(&mut self) -> Result<(), PinError<I2C::Error>> {
self.chip.set_pin(self.n, true).map_err(PinError)
}
fn set_low(&mut self) -> Result<(), PinError<I2C::Error>> {
self.chip.set_pin(self.n, false).map_err(PinError)
}
}
impl<I2C: I2c> InputPin for ExPin<'_, I2C> {
fn is_high(&mut self) -> Result<bool, PinError<I2C::Error>> {
let port = (self.n / 8) as usize;
let bit = self.n % 8;
let buf = self.chip.read_both().map_err(PinError)?;
Ok((buf[port] >> bit) & 1 == 1)
}
fn is_low(&mut self) -> Result<bool, PinError<I2C::Error>> {
Ok(!self.is_high()?)
}
}
impl<I2C: I2c> StatefulOutputPin for ExPin<'_, I2C> {
fn is_set_high(&mut self) -> Result<bool, PinError<I2C::Error>> {
let port = (self.n / 8) as usize;
let bit = self.n % 8;
Ok((self.chip.shadow_byte(port) >> bit) & 1 == 1)
}
fn is_set_low(&mut self) -> Result<bool, PinError<I2C::Error>> {
Ok(!self.is_set_high()?)
}
}
pub struct Pcf8575Full<I2C> {
inner: Pcf8575Minimal<I2C>,
prev: [Cell<u8>; 2],
}
impl<I2C: I2c> Pcf8575Full<I2C> {
pub fn new(i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let inner = Pcf8575Minimal::new(i2c, addr)?;
let prev = inner.read_both()?;
Ok(Self {
inner,
prev: [Cell::new(prev[0]), Cell::new(prev[1])],
})
}
pub fn pin(&self, n: u8) -> ExPin<'_, I2C> {
self.inner.pin(n)
}
pub fn write_port(&self, port: usize, mask: u8) -> Result<(), I2C::Error> {
self.inner.write_port(port, mask)
}
pub fn read_port(&self, port: usize) -> Result<u8, I2C::Error> {
self.inner.read_port(port)
}
pub fn clear_interrupt(&self) -> Result<u16, I2C::Error> {
let current = self.inner.read_both()?;
let changed0 = current[0] ^ self.prev[0].get();
let changed1 = current[1] ^ self.prev[1].get();
self.prev[0].set(current[0]);
self.prev[1].set(current[1]);
Ok((changed0 as u16) | ((changed1 as u16) << 8))
}
}
impl<I2C: I2c> embedded_hal::digital::ErrorType for Pcf8575Full<I2C> {
type Error = PinError<I2C::Error>;
}