use core::cell;
use embedded_hal::i2c::I2c;
use crate::split_pins::pcf8575;
use crate::{Error, PinFlag, SlaveAddr};
#[derive(Debug, Default)]
pub struct Pcf8575<I2C> {
dev: cell::RefCell<Pcf8575Data<I2C>>,
}
#[derive(Debug, Default)]
pub(crate) struct Pcf8575Data<I2C> {
pub(crate) i2c: I2C,
pub(crate) address: u8,
pub(crate) last_set_mask: u16,
}
impl<I2C, E> Pcf8575<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C, address: SlaveAddr) -> Self {
let dev = Pcf8575Data {
i2c,
address: address.addr(0b010_0000),
last_set_mask: 0,
};
Pcf8575 {
dev: cell::RefCell::new(dev),
}
}
pub fn destroy(self) -> I2C {
self.dev.into_inner().i2c
}
pub fn set(&mut self, bits: u16) -> Result<(), Error<E>> {
self.do_on_acquired(|dev| Self::_set(dev, bits))
}
pub(crate) fn _set(mut dev: cell::RefMut<Pcf8575Data<I2C>>, bits: u16) -> Result<(), Error<E>> {
let address = dev.address;
dev.i2c
.write(address, &u16_to_u8_array(bits)[..])
.map_err(Error::I2C)?;
dev.last_set_mask = bits;
Ok(())
}
pub fn write_array(&mut self, data: &[u8]) -> Result<(), Error<E>> {
if !data.is_empty() {
if data.len() % 2 != 0 {
return Err(Error::InvalidInputData);
}
self.do_on_acquired(|mut dev| {
let address = dev.address;
dev.i2c.write(address, data).map_err(Error::I2C)?;
dev.last_set_mask =
(u16::from(data[data.len() - 1]) << 8) | u16::from(data[data.len() - 2]);
Ok(())
})?;
}
Ok(())
}
pub fn split(&self) -> pcf8575::Parts<'_, Pcf8575<I2C>, E> {
pcf8575::Parts::new(self)
}
pub(crate) fn do_on_acquired<R>(
&self,
f: impl FnOnce(cell::RefMut<Pcf8575Data<I2C>>) -> Result<R, Error<E>>,
) -> Result<R, Error<E>> {
let dev = self
.dev
.try_borrow_mut()
.map_err(|_| Error::CouldNotAcquireDevice)?;
f(dev)
}
}
impl<I2C, E> Pcf8575<I2C>
where
I2C: I2c<Error = E>,
{
pub fn get(&mut self, mask: PinFlag) -> Result<u16, Error<E>> {
self.do_on_acquired(|dev| Self::_get(dev, mask))
}
pub(crate) fn _get(
mut dev: cell::RefMut<Pcf8575Data<I2C>>,
mask: PinFlag,
) -> Result<u16, Error<E>> {
let address = dev.address;
let mask = mask.mask | dev.last_set_mask;
dev.i2c
.write(address, &u16_to_u8_array(mask)[..])
.map_err(Error::I2C)?;
let mut bits = [0; 2];
dev.i2c
.read(address, &mut bits)
.map_err(Error::I2C)
.and(Ok(u8_array_to_u16(bits)))
}
pub fn read_array(&mut self, mask: PinFlag, data: &mut [u8]) -> Result<(), Error<E>> {
if !data.is_empty() {
if data.len() % 2 != 0 {
return Err(Error::InvalidInputData);
}
self.do_on_acquired(|mut dev| {
let address = dev.address;
let mask = mask.mask | dev.last_set_mask;
dev.i2c
.write(address, &u16_to_u8_array(mask))
.map_err(Error::I2C)?;
dev.i2c.read(address, data).map_err(Error::I2C)
})?;
}
Ok(())
}
}
fn u16_to_u8_array(input: u16) -> [u8; 2] {
[input as u8, (input >> 8) as u8]
}
fn u8_array_to_u16(input: [u8; 2]) -> u16 {
u16::from(input[0]) | (u16::from(input[1]) << 8)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_convert_u16_to_u8_array() {
assert_eq!([0xCD, 0xAB], u16_to_u8_array(0xABCD));
}
#[test]
fn can_convert_u8_array_to_u16() {
assert_eq!(0xABCD, u8_array_to_u16([0xCD, 0xAB]));
}
}