#![deny(unsafe_code)]
#![deny(missing_docs)]
#![no_std]
extern crate embedded_hal as hal;
use core::cell;
use hal::blocking::i2c;
#[derive(Debug)]
pub enum Error<E> {
I2C(E),
CouldNotAcquireDevice,
}
#[derive(Debug, Clone)]
pub enum SlaveAddr {
Default,
Alternative(bool, bool, bool),
}
impl Default for SlaveAddr {
fn default() -> Self {
SlaveAddr::Default
}
}
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, Default)]
pub struct Xca9548aData<I2C> {
pub(crate) i2c: I2C,
pub(crate) address: u8,
pub(crate) selected_channel_mask: u8,
}
impl<I2C, E> SelectChannels for Xca9548aData<I2C>
where
I2C: i2c::Write<Error = E>,
{
type Error = Error<E>;
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<I2C>: private::Sealed {
fn do_on_acquired<R, E>(
&self,
f: impl FnOnce(cell::RefMut<Xca9548aData<I2C>>) -> Result<R, Error<E>>,
) -> Result<R, Error<E>>;
}
#[doc(hidden)]
pub trait SelectChannels: private::Sealed {
type Error;
fn select_channels(&mut self, mask: u8) -> Result<(), Self::Error>;
}
#[derive(Debug, Default)]
pub struct Xca9548a<I2C> {
pub(crate) data: cell::RefCell<Xca9548aData<I2C>>,
}
impl<I2C> Xca9548a<I2C> {
pub fn new(i2c: I2C, address: SlaveAddr) -> Self {
let data = Xca9548aData {
i2c,
address: address.addr(DEVICE_BASE_ADDRESS),
selected_channel_mask: 0,
};
Xca9548a {
data: cell::RefCell::new(data),
}
}
pub fn destroy(self) -> I2C {
self.data.into_inner().i2c
}
pub fn split<'a>(&'a self) -> Parts<'a, Xca9548a<I2C>, I2C> {
Parts::new(&self)
}
}
impl<I2C> DoOnAcquired<I2C> for Xca9548a<I2C> {
fn do_on_acquired<R, E>(
&self,
f: impl FnOnce(cell::RefMut<Xca9548aData<I2C>>) -> Result<R, Error<E>>,
) -> Result<R, Error<E>> {
let dev = self
.data
.try_borrow_mut()
.map_err(|_| Error::CouldNotAcquireDevice)?;
f(dev)
}
}
impl<I2C, E> Xca9548a<I2C>
where
I2C: i2c::Write<Error = E>,
{
pub fn select_channels(&mut self, channels: u8) -> Result<(), Error<E>> {
self.do_on_acquired(|mut dev| dev.select_channels(channels))
}
}
impl<I2C, E> Xca9548a<I2C>
where
I2C: i2c::Read<Error = E>,
{
pub fn get_channel_status(&mut self) -> Result<u8, Error<E>> {
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<I2C, E> i2c::Write for Xca9548a<I2C>
where
I2C: i2c::Write<Error = E>,
{
type Error = Error<E>;
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
self.do_on_acquired(|mut dev| dev.i2c.write(address, bytes).map_err(Error::I2C))
}
}
impl<I2C, E> i2c::Read for Xca9548a<I2C>
where
I2C: i2c::Read<Error = E>,
{
type Error = Error<E>;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.do_on_acquired(|mut dev| dev.i2c.read(address, buffer).map_err(Error::I2C))
}
}
impl<I2C, E> i2c::WriteRead for Xca9548a<I2C>
where
I2C: i2c::WriteRead<Error = E>,
{
type Error = Error<E>;
fn write_read(
&mut self,
address: u8,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Self::Error> {
self.do_on_acquired(|mut dev| {
dev.i2c
.write_read(address, bytes, buffer)
.map_err(Error::I2C)
})
}
}
mod parts;
pub use parts::{I2cSlave, Parts};
mod private {
use super::*;
pub trait Sealed {}
impl<I2C> Sealed for Xca9548aData<I2C> {}
impl<I2C> Sealed for Xca9548a<I2C> {}
impl<'a, DEV, I2C> Sealed for Parts<'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)
);
}
}