use crate::{PortDriver, SpiBus};
use embedded_hal::spi::Operation;
pub struct Pca9702<M>(M);
impl<SPI> Pca9702<core::cell::RefCell<Driver<Pca9702Bus<SPI>>>>
where
SPI: crate::SpiBus,
{
pub fn new(bus: SPI) -> Self {
Self::with_mutex(Pca9702Bus(bus))
}
}
impl<B, M> Pca9702<M>
where
B: Pca9702BusTrait,
M: crate::PortMutex<Port = Driver<B>>,
{
pub fn with_mutex(bus: B) -> Self {
Self(crate::PortMutex::create(Driver::new(bus)))
}
pub fn split(&mut self) -> Parts<'_, B, M> {
Parts {
in0: crate::Pin::new(0, &self.0),
in1: crate::Pin::new(1, &self.0),
in2: crate::Pin::new(2, &self.0),
in3: crate::Pin::new(3, &self.0),
in4: crate::Pin::new(4, &self.0),
in5: crate::Pin::new(5, &self.0),
in6: crate::Pin::new(6, &self.0),
in7: crate::Pin::new(7, &self.0),
}
}
}
pub struct Parts<'a, B, M = core::cell::RefCell<Driver<B>>>
where
B: Pca9702BusTrait,
M: crate::PortMutex<Port = Driver<B>>,
{
pub in0: crate::Pin<'a, crate::mode::Input, M>,
pub in1: crate::Pin<'a, crate::mode::Input, M>,
pub in2: crate::Pin<'a, crate::mode::Input, M>,
pub in3: crate::Pin<'a, crate::mode::Input, M>,
pub in4: crate::Pin<'a, crate::mode::Input, M>,
pub in5: crate::Pin<'a, crate::mode::Input, M>,
pub in6: crate::Pin<'a, crate::mode::Input, M>,
pub in7: crate::Pin<'a, crate::mode::Input, M>,
}
pub struct Driver<B> {
bus: B,
}
impl<B> Driver<B> {
fn new(bus: B) -> Self {
Self { bus }
}
}
pub trait Pca9702BusTrait {
type BusError;
fn read_inputs(&mut self) -> Result<u8, Self::BusError>;
}
impl<B: Pca9702BusTrait> PortDriver for Driver<B> {
type Error = B::BusError;
fn set(&mut self, _mask_high: u32, _mask_low: u32) -> Result<(), Self::Error> {
panic!("PCA9702 is input-only, cannot set output states");
}
fn is_set(&mut self, _mask_high: u32, _mask_low: u32) -> Result<u32, Self::Error> {
panic!("PCA9702 is input-only, cannot read back output states");
}
fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
let val = self.bus.read_inputs()? as u32;
Ok((val & mask_high) | (!val & mask_low))
}
}
pub struct Pca9702Bus<SPI>(pub SPI);
impl<SPI> Pca9702BusTrait for Pca9702Bus<SPI>
where
SPI: SpiBus,
{
type BusError = SPI::BusError;
fn read_inputs(&mut self) -> Result<u8, Self::BusError> {
let mut buffer = [0u8];
let mut ops = [Operation::TransferInPlace(&mut buffer)];
self.0.transaction(&mut ops)?;
Ok(buffer[0])
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};
#[test]
fn pca9702_read_inputs() {
let expectations = [
SpiTransaction::transaction_start(),
SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
SpiTransaction::transaction_end(),
SpiTransaction::transaction_start(),
SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
SpiTransaction::transaction_end(),
SpiTransaction::transaction_start(),
SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
SpiTransaction::transaction_end(),
];
let mut spi_mock = SpiMock::new(&expectations);
let mut pca = Pca9702::new(spi_mock.clone());
let pins = pca.split();
assert_eq!(pins.in0.is_high().unwrap(), true); assert_eq!(pins.in1.is_high().unwrap(), false);
assert_eq!(pins.in2.is_high().unwrap(), true);
spi_mock.done();
}
#[test]
#[should_panic]
fn pca9702_output_fails() {
let spi_mock = SpiMock::new(&[]);
let mut pca = Pca9702::new(spi_mock);
let pins = pca.split();
pins.in0.access_port_driver(|drv| {
drv.set(0x01, 0x00).unwrap_err();
});
}
}