From d0debd29f4d9b478a6e747513a00674a07e4751d Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Sat, 3 Aug 2024 09:25:15 +0200 Subject: [PATCH] Reorganize code --- src/device_impl.rs | 261 ++++++++++++++++++++++++++++++++++ src/lib.rs | 347 +-------------------------------------------- src/types.rs | 87 ++++++++++++ 3 files changed, 352 insertions(+), 343 deletions(-) create mode 100644 src/device_impl.rs create mode 100644 src/types.rs diff --git a/src/device_impl.rs b/src/device_impl.rs new file mode 100644 index 0000000..57c7f25 --- /dev/null +++ b/src/device_impl.rs @@ -0,0 +1,261 @@ +use crate::{ + parts::{Parts, Parts2, Parts4}, + private, Error, SlaveAddr, Xca9543a, Xca9545a, Xca9548a, DEVICE_BASE_ADDRESS, +}; +use core::cell; +use embedded_hal::i2c as ehal; + +#[doc(hidden)] +#[derive(Debug)] +pub struct Xca954xaData { + /// The concrete I²C device implementation. + pub(crate) i2c: I2C, + /// The I²C device address. + pub(crate) address: u8, + pub(crate) selected_channel_mask: u8, +} + +impl SelectChannels for Xca954xaData +where + I2C: ehal::I2c, + E: core::fmt::Debug, +{ + type Error = Error; + fn select_channels(&mut self, channels: u8) -> Result<(), Self::Error> { + self.i2c + .write(self.address, &[channels]) + .map_err(Error::I2C)?; + self.selected_channel_mask = channels; + Ok(()) + } +} + +#[doc(hidden)] +pub trait DoOnAcquired: private::Sealed { + fn do_on_acquired( + &self, + f: impl FnOnce(cell::RefMut>) -> Result>, + ) -> Result>; +} + +#[doc(hidden)] +pub trait SelectChannels: private::Sealed { + type Error; + fn select_channels(&mut self, mask: u8) -> Result<(), Self::Error>; +} + +impl ehal::Error for Error +where + E: ehal::Error, +{ + fn kind(&self) -> ehal::ErrorKind { + match self { + Error::I2C(e) => e.kind(), + Error::CouldNotAcquireDevice => ehal::ErrorKind::Other, + } + } +} + +macro_rules! i2c_traits { + ( $name:ident ) => { + impl DoOnAcquired for $name { + fn do_on_acquired( + &self, + f: impl FnOnce(cell::RefMut>) -> Result>, + ) -> Result> { + let dev = self + .data + .try_borrow_mut() + .map_err(|_| Error::CouldNotAcquireDevice)?; + f(dev) + } + } + + impl ehal::ErrorType for $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + type Error = Error; + } + + impl ehal::I2c for $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + fn transaction( + &mut self, + address: u8, + operations: &mut [ehal::Operation<'_>], + ) -> Result<(), Error> { + self.do_on_acquired(|mut dev| { + dev.i2c.transaction(address, operations).map_err(Error::I2C) + }) + } + + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.do_on_acquired(|mut dev| dev.i2c.read(address, read).map_err(Error::I2C)) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.do_on_acquired(|mut dev| dev.i2c.write(address, write).map_err(Error::I2C)) + } + + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + self.do_on_acquired(|mut dev| { + dev.i2c.write_read(address, write, read).map_err(Error::I2C) + }) + } + } + }; +} + +macro_rules! impl_device { + ( $name:ident, $parts:ident ) => { + impl $name { + /// Create new instance of the device + pub fn new(i2c: I2C, address: SlaveAddr) -> Self { + let data = Xca954xaData { + i2c, + address: address.addr(DEVICE_BASE_ADDRESS), + selected_channel_mask: 0, + }; + $name { + data: cell::RefCell::new(data), + } + } + + /// Destroy driver instance, return I²C bus instance. + pub fn destroy(self) -> I2C { + self.data.into_inner().i2c + } + + /// Split device into individual I2C devices + /// + /// It is not possible to know the compatibilities between channels + /// so when talking to a split I2C device, only its channel + /// will be selected. + pub fn split(&self) -> $parts<$name, I2C> { + $parts::new(&self) + } + } + }; + ( $name:ident, $parts:ident, no_interrupts ) => { + impl_device!($name, $parts); + + impl $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + /// Get status of channels. + /// + /// Each bit corresponds to a channel. + /// Bit 0 corresponds to channel 0 and so on up to bit 7 which + /// corresponds to channel 7. + /// A `0` means the channel is disabled and a `1` that the channel is enabled. + pub fn get_channel_status(&mut self) -> Result> { + let mut data = [0]; + self.do_on_acquired(|mut dev| { + let address = dev.address; + dev.i2c + .read(address, &mut data) + .map_err(Error::I2C) + .and(Ok(data[0])) + }) + } + } + + impl $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + /// Select which channels are enabled. + /// + /// Each bit corresponds to a channel. + /// Bit 0 corresponds to channel 0 and so on up to bit 7 which + /// corresponds to channel 7. + /// A `0` disables the channel and a `1` enables it. + /// Several channels can be enabled at the same time + pub fn select_channels(&mut self, channels: u8) -> Result<(), Error> { + self.do_on_acquired(|mut dev| dev.select_channels(channels)) + } + } + }; + ( $name:ident, $parts:ident, $mask:expr, interrupts ) => { + impl_device!($name, $parts); + + impl $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + /// Get status of channels. + /// + /// Each bit corresponds to a channel. + /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. + /// A `0` means the channel is disabled and a `1` that the channel is enabled. + pub fn get_channel_status(&mut self) -> Result> { + let mut data = [0]; + self.do_on_acquired(|mut dev| { + let address = dev.address; + dev.i2c + .read(address, &mut data) + .map_err(Error::I2C) + .and(Ok(data[0] & $mask)) + }) + } + + /// Get status of channel interrupts. + /// + /// Each bit corresponds to a channel. + /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. + /// A `1` means the channel's interrupt is high and a `0` that the channel's interrupt is low. + /// Note: I2C interrupts are usually active LOW! + pub fn get_interrupt_status(&mut self) -> Result> { + let mut data = [0]; + self.do_on_acquired(|mut dev| { + let address = dev.address; + dev.i2c + .read(address, &mut data) + .map_err(Error::I2C) + .and(Ok((data[0] >> 4) & $mask)) + }) + } + } + + impl $name + where + I2C: ehal::I2c, + E: ehal::Error, + { + /// Select which channels are enabled. + /// + /// Each bit corresponds to a channel. + /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. + /// A `0` disables the channel and a `1` enables it. + /// Several channels can be enabled at the same time. + /// + /// Channels/bits that does not exist for the specific device are ignored. + pub fn select_channels(&mut self, channels: u8) -> Result<(), Error> { + self.do_on_acquired(|mut dev| dev.select_channels(channels & $mask)) + } + } + }; +} + +impl_device!(Xca9548a, Parts, no_interrupts); +i2c_traits!(Xca9548a); + +impl_device!(Xca9543a, Parts2, 0x03, interrupts); +i2c_traits!(Xca9543a); + +impl_device!(Xca9545a, Parts4, 0x0f, interrupts); +i2c_traits!(Xca9545a); diff --git a/src/lib.rs b/src/lib.rs index 8984f44..5bd88e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,314 +168,11 @@ #![deny(missing_docs)] #![no_std] -use core::cell; -use embedded_hal::i2c as ehal; - -/// All possible errors in this crate -#[derive(Debug)] -pub enum Error { - /// I²C bus error - I2C(E), - /// Could not acquire device. Maybe it is already acquired. - CouldNotAcquireDevice, -} - -/// Possible slave addresses -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub enum SlaveAddr { - /// Default slave address - #[default] - Default, - /// Alternative slave address providing bit values for A2, A1 and A0 - /// Note: Some devices does not have all Ax pins, these should be set to false. - Alternative(bool, bool, bool), -} - -impl SlaveAddr { - fn addr(self, default: u8) -> u8 { - match self { - SlaveAddr::Default => default, - SlaveAddr::Alternative(a2, a1, a0) => { - default | ((a2 as u8) << 2) | ((a1 as u8) << 1) | a0 as u8 - } - } - } -} const DEVICE_BASE_ADDRESS: u8 = 0b111_0000; - -#[doc(hidden)] -#[derive(Debug)] -pub struct Xca954xaData { - /// The concrete I²C device implementation. - pub(crate) i2c: I2C, - /// The I²C device address. - pub(crate) address: u8, - pub(crate) selected_channel_mask: u8, -} - -impl SelectChannels for Xca954xaData -where - I2C: ehal::I2c, - E: core::fmt::Debug, -{ - type Error = Error; - fn select_channels(&mut self, channels: u8) -> Result<(), Self::Error> { - self.i2c - .write(self.address, &[channels]) - .map_err(Error::I2C)?; - self.selected_channel_mask = channels; - Ok(()) - } -} - -#[doc(hidden)] -pub trait DoOnAcquired: private::Sealed { - fn do_on_acquired( - &self, - f: impl FnOnce(cell::RefMut>) -> Result>, - ) -> Result>; -} - -#[doc(hidden)] -pub trait SelectChannels: private::Sealed { - type Error; - fn select_channels(&mut self, mask: u8) -> Result<(), Self::Error>; -} - -/// Device driver for T/PCA9548A -#[derive(Debug)] -pub struct Xca9548a { - pub(crate) data: cell::RefCell>, -} - -/// Device driver for T/PCA9543A -#[derive(Debug)] -pub struct Xca9543a { - pub(crate) data: cell::RefCell>, -} - -/// Device driver for T/PCA9545A -#[derive(Debug)] -pub struct Xca9545a { - pub(crate) data: cell::RefCell>, -} - -impl ehal::Error for Error -where - E: ehal::Error, -{ - fn kind(&self) -> ehal::ErrorKind { - match self { - Error::I2C(e) => e.kind(), - Error::CouldNotAcquireDevice => ehal::ErrorKind::Other, - } - } -} - -macro_rules! i2c_traits { - ( $name:ident ) => { - impl DoOnAcquired for $name { - fn do_on_acquired( - &self, - f: impl FnOnce(cell::RefMut>) -> Result>, - ) -> Result> { - let dev = self - .data - .try_borrow_mut() - .map_err(|_| Error::CouldNotAcquireDevice)?; - f(dev) - } - } - - impl ehal::ErrorType for $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - type Error = Error; - } - - impl ehal::I2c for $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - fn transaction( - &mut self, - address: u8, - operations: &mut [ehal::Operation<'_>], - ) -> Result<(), Error> { - self.do_on_acquired(|mut dev| { - dev.i2c.transaction(address, operations).map_err(Error::I2C) - }) - } - - fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.do_on_acquired(|mut dev| dev.i2c.read(address, read).map_err(Error::I2C)) - } - - fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.do_on_acquired(|mut dev| dev.i2c.write(address, write).map_err(Error::I2C)) - } - - fn write_read( - &mut self, - address: u8, - write: &[u8], - read: &mut [u8], - ) -> Result<(), Self::Error> { - self.do_on_acquired(|mut dev| { - dev.i2c.write_read(address, write, read).map_err(Error::I2C) - }) - } - } - }; -} - -macro_rules! impl_device { - ( $name:ident, $parts:ident ) => { - impl $name { - /// Create new instance of the device - pub fn new(i2c: I2C, address: SlaveAddr) -> Self { - let data = Xca954xaData { - i2c, - address: address.addr(DEVICE_BASE_ADDRESS), - selected_channel_mask: 0, - }; - $name { - data: cell::RefCell::new(data), - } - } - - /// Destroy driver instance, return I²C bus instance. - pub fn destroy(self) -> I2C { - self.data.into_inner().i2c - } - - /// Split device into individual I2C devices - /// - /// It is not possible to know the compatibilities between channels - /// so when talking to a split I2C device, only its channel - /// will be selected. - pub fn split(&self) -> $parts<$name, I2C> { - $parts::new(&self) - } - } - }; - ( $name:ident, $parts:ident, no_interrupts ) => { - impl_device!($name, $parts); - - impl $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - /// Get status of channels. - /// - /// Each bit corresponds to a channel. - /// Bit 0 corresponds to channel 0 and so on up to bit 7 which - /// corresponds to channel 7. - /// A `0` means the channel is disabled and a `1` that the channel is enabled. - pub fn get_channel_status(&mut self) -> Result> { - let mut data = [0]; - self.do_on_acquired(|mut dev| { - let address = dev.address; - dev.i2c - .read(address, &mut data) - .map_err(Error::I2C) - .and(Ok(data[0])) - }) - } - } - - impl $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - /// Select which channels are enabled. - /// - /// Each bit corresponds to a channel. - /// Bit 0 corresponds to channel 0 and so on up to bit 7 which - /// corresponds to channel 7. - /// A `0` disables the channel and a `1` enables it. - /// Several channels can be enabled at the same time - pub fn select_channels(&mut self, channels: u8) -> Result<(), Error> { - self.do_on_acquired(|mut dev| dev.select_channels(channels)) - } - } - }; - ( $name:ident, $parts:ident, $mask:expr, interrupts ) => { - impl_device!($name, $parts); - - impl $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - /// Get status of channels. - /// - /// Each bit corresponds to a channel. - /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. - /// A `0` means the channel is disabled and a `1` that the channel is enabled. - pub fn get_channel_status(&mut self) -> Result> { - let mut data = [0]; - self.do_on_acquired(|mut dev| { - let address = dev.address; - dev.i2c - .read(address, &mut data) - .map_err(Error::I2C) - .and(Ok(data[0] & $mask)) - }) - } - - /// Get status of channel interrupts. - /// - /// Each bit corresponds to a channel. - /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. - /// A `1` means the channel's interrupt is high and a `0` that the channel's interrupt is low. - /// Note: I2C interrupts are usually active LOW! - pub fn get_interrupt_status(&mut self) -> Result> { - let mut data = [0]; - self.do_on_acquired(|mut dev| { - let address = dev.address; - dev.i2c - .read(address, &mut data) - .map_err(Error::I2C) - .and(Ok((data[0] >> 4) & $mask)) - }) - } - } - - impl $name - where - I2C: ehal::I2c, - E: ehal::Error, - { - /// Select which channels are enabled. - /// - /// Each bit corresponds to a channel. - /// Bit 0 corresponds to channel 0, bit 1 to channel 1 and so on. - /// A `0` disables the channel and a `1` enables it. - /// Several channels can be enabled at the same time. - /// - /// Channels/bits that does not exist for the specific device are ignored. - pub fn select_channels(&mut self, channels: u8) -> Result<(), Error> { - self.do_on_acquired(|mut dev| dev.select_channels(channels & $mask)) - } - } - }; -} - -impl_device!(Xca9548a, Parts, no_interrupts); -i2c_traits!(Xca9548a); - -impl_device!(Xca9543a, Parts2, 0x03, interrupts); -i2c_traits!(Xca9543a); - -impl_device!(Xca9545a, Parts4, 0x0f, interrupts); -i2c_traits!(Xca9545a); - +mod types; +pub use types::{Error, SlaveAddr, Xca9543a, Xca9545a, Xca9548a}; +mod device_impl; +pub use device_impl::{DoOnAcquired, SelectChannels, Xca954xaData}; mod parts; pub use crate::parts::{I2cSlave, Parts, Parts2, Parts4}; @@ -492,39 +189,3 @@ mod private { impl<'a, DEV, I2C> Sealed for Parts4<'a, DEV, I2C> {} impl<'a, DEV, I2C> Sealed for I2cSlave<'a, DEV, I2C> {} } - -#[cfg(test)] -mod tests { - use super::DEVICE_BASE_ADDRESS as BASE_ADDR; - use super::*; - - #[test] - fn can_get_default_address() { - let addr = SlaveAddr::default(); - assert_eq!(BASE_ADDR, addr.addr(BASE_ADDR)); - } - - #[test] - fn can_generate_alternative_addresses() { - assert_eq!( - 0b111_0000, - SlaveAddr::Alternative(false, false, false).addr(BASE_ADDR) - ); - assert_eq!( - 0b111_0001, - SlaveAddr::Alternative(false, false, true).addr(BASE_ADDR) - ); - assert_eq!( - 0b111_0010, - SlaveAddr::Alternative(false, true, false).addr(BASE_ADDR) - ); - assert_eq!( - 0b111_0100, - SlaveAddr::Alternative(true, false, false).addr(BASE_ADDR) - ); - assert_eq!( - 0b111_0111, - SlaveAddr::Alternative(true, true, true).addr(BASE_ADDR) - ); - } -} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..7b37a75 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,87 @@ +use core::cell; +use crate::Xca954xaData; + +/// All possible errors in this crate +#[derive(Debug)] +pub enum Error { + /// I²C bus error + I2C(E), + /// Could not acquire device. Maybe it is already acquired. + CouldNotAcquireDevice, +} + +/// Possible slave addresses +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum SlaveAddr { + /// Default slave address + #[default] + Default, + /// Alternative slave address providing bit values for A2, A1 and A0 + /// Note: Some devices does not have all Ax pins, these should be set to false. + Alternative(bool, bool, bool), +} + +impl SlaveAddr { + pub(crate) fn addr(self, default: u8) -> u8 { + match self { + SlaveAddr::Default => default, + SlaveAddr::Alternative(a2, a1, a0) => { + default | ((a2 as u8) << 2) | ((a1 as u8) << 1) | a0 as u8 + } + } + } +} + +/// Device driver for T/PCA9548A +#[derive(Debug)] +pub struct Xca9548a { + pub(crate) data: cell::RefCell>, +} + +/// Device driver for T/PCA9543A +#[derive(Debug)] +pub struct Xca9543a { + pub(crate) data: cell::RefCell>, +} + +/// Device driver for T/PCA9545A +#[derive(Debug)] +pub struct Xca9545a { + pub(crate) data: cell::RefCell>, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::DEVICE_BASE_ADDRESS as BASE_ADDR; + + #[test] + fn can_get_default_address() { + let addr = SlaveAddr::default(); + assert_eq!(BASE_ADDR, addr.addr(BASE_ADDR)); + } + + #[test] + fn can_generate_alternative_addresses() { + assert_eq!( + 0b111_0000, + SlaveAddr::Alternative(false, false, false).addr(BASE_ADDR) + ); + assert_eq!( + 0b111_0001, + SlaveAddr::Alternative(false, false, true).addr(BASE_ADDR) + ); + assert_eq!( + 0b111_0010, + SlaveAddr::Alternative(false, true, false).addr(BASE_ADDR) + ); + assert_eq!( + 0b111_0100, + SlaveAddr::Alternative(true, false, false).addr(BASE_ADDR) + ); + assert_eq!( + 0b111_0111, + SlaveAddr::Alternative(true, true, true).addr(BASE_ADDR) + ); + } +}