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 Pcf8574Minimal<I2C> {
i2c: RefCell<I2C>,
addr: u8,
shadow: Cell<u8>,
}
impl<I2C: I2c> Pcf8574Minimal<I2C> {
pub fn new(i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let chip = Self {
i2c: RefCell::new(i2c),
addr,
shadow: Cell::new(0xFF),
};
chip.write_port(0xFF)?;
Ok(chip)
}
pub fn write_port(&self, mask: u8) -> Result<(), I2C::Error> {
self.shadow.set(mask);
self.i2c.borrow_mut().write(self.addr, &[mask])
}
pub fn read_port(&self) -> Result<u8, I2C::Error> {
let mut buf = [0u8; 1];
self.i2c.borrow_mut().read(self.addr, &mut buf)?;
Ok(buf[0])
}
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 mut s = self.shadow.get();
if high { s |= 1 << n; }
else { s &= !(1 << n); }
self.shadow.set(s);
self.i2c.borrow_mut().write(self.addr, &[s])
}
}
pub struct ExPin<'a, I2C> {
chip: &'a Pcf8574Minimal<I2C>,
pub 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>> {
Ok((self.chip.read_port().map_err(PinError)? >> self.n) & 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>> {
Ok((self.chip.shadow.get() >> self.n) & 1 == 1)
}
fn is_set_low(&mut self) -> Result<bool, PinError<I2C::Error>> {
Ok(!self.is_set_high()?)
}
}
pub struct Pcf8574Full<I2C> {
inner: Pcf8574Minimal<I2C>,
prev: Cell<u8>,
}
impl<I2C: I2c> Pcf8574Full<I2C> {
pub fn new(i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let inner = Pcf8574Minimal::new(i2c, addr)?;
let prev = inner.read_port()?;
Ok(Self { inner, prev: Cell::new(prev) })
}
pub fn pin(&self, n: u8) -> ExPin<'_, I2C> {
self.inner.pin(n)
}
pub fn write_port(&self, mask: u8) -> Result<(), I2C::Error> {
self.inner.write_port(mask)
}
pub fn read_port(&self) -> Result<u8, I2C::Error> {
self.inner.read_port()
}
pub fn clear_interrupt(&self) -> Result<u8, I2C::Error> {
let current = self.inner.read_port()?;
let changed = current ^ self.prev.get();
self.prev.set(current);
Ok(changed)
}
}