Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for MAX7321 #10

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The following list is what `port-expander` currently supports. If you needs
support for an additional device, it should be easy to add. It's best to take
a similar existing implementation as inspiration. Contributions welcome!

- [`MAX7321`](https://docs.rs/port-expander/latest/port_expander/dev/max7321/struct.Max7321.html)
- [`PCA9536`](https://docs.rs/port-expander/latest/port_expander/dev/pca9536/struct.Pca9536.html)
- [`PCA9538`](https://docs.rs/port-expander/latest/port_expander/dev/pca9538/struct.Pca9538.html)
- [`PCA9555`](https://docs.rs/port-expander/latest/port_expander/dev/pca9555/struct.Pca9555.html)
Expand Down
118 changes: 118 additions & 0 deletions src/dev/max7321.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! Support for the Maxim 7321 I2C 8-Port Open Drain port expander
pub struct Max7321<M>(M);

/// MAX7321 "I2C Port Expander with 8 Open-Drain I/Os"
impl<I2C> Max7321<shared_bus::NullMutex<Driver<I2C>>>
where
I2C: crate::I2cBus,
{
pub fn new(i2c: I2C, a3: bool, a2: bool, a1: bool, a0: bool) -> Self {
Self::with_mutex(i2c, a3, a2, a1, a0)
}
}

impl<I2C, M> Max7321<M>
where
I2C: crate::I2cBus,
M: shared_bus::BusMutex<Bus = Driver<I2C>>,
{
pub fn with_mutex(i2c: I2C, a3: bool, a2: bool, a1: bool, a0: bool) -> Self {
Self(shared_bus::BusMutex::create(Driver::new(
i2c, a3, a2, a1, a0,
)))
}

pub fn split<'a>(&'a mut self) -> Parts<'a, I2C, M> {
Parts {
p0: crate::Pin::new(0, &self.0),
p1: crate::Pin::new(1, &self.0),
p2: crate::Pin::new(2, &self.0),
p3: crate::Pin::new(3, &self.0),
p4: crate::Pin::new(4, &self.0),
p5: crate::Pin::new(5, &self.0),
p6: crate::Pin::new(6, &self.0),
p7: crate::Pin::new(7, &self.0),
}
}
}

pub struct Parts<'a, I2C, M = shared_bus::NullMutex<Driver<I2C>>>
where
I2C: crate::I2cBus,
M: shared_bus::BusMutex<Bus = Driver<I2C>>,
{
pub p0: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p1: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p2: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p3: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p4: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p5: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p6: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
pub p7: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
}

pub struct Driver<I2C> {
i2c: I2C,
out: u8,
addr: u8,
}

impl<I2C> Driver<I2C> {
pub fn new(i2c: I2C, a3: bool, a2: bool, a1: bool, a0: bool) -> Self {
let addr = 0x60 | ((a3 as u8) << 3) | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8);
Self {
i2c,
out: 0xff,
addr,
}
}
}

impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
type Error = I2C::BusError;

fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
self.out |= mask_high as u8;
self.out &= !mask_low as u8;
self.i2c.write(self.addr, &[self.out])?;
Ok(())
}

fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
}

fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
let mut buf = [0x00];
self.i2c.read(self.addr, &mut buf)?;
let in_ = buf[0] as u32;
Ok((in_ & mask_high) | (!in_ & mask_low))
}
}

#[cfg(test)]
mod tests {
use embedded_hal_mock::i2c as mock_i2c;

#[test]
fn max7321() {
let expectations = [
mock_i2c::Transaction::write(0b01101101, vec![0b11111111]),
mock_i2c::Transaction::write(0b01101101, vec![0b11111011]),
mock_i2c::Transaction::read(0b01101101, vec![0b01000000]),
mock_i2c::Transaction::read(0b01101101, vec![0b10111111]),
];
let mut bus = mock_i2c::Mock::new(&expectations);

let mut max = super::Max7321::new(bus.clone(), true, true, false, true);
let mut max_pins = max.split();

max_pins.p2.set_high().unwrap();
max_pins.p2.set_low().unwrap();

assert!(max_pins.p6.is_high().unwrap());
assert!(max_pins.p6.is_low().unwrap());

bus.done();
}
}
1 change: 1 addition & 0 deletions src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! In most cases you will not need anything from here explicitly, the exposed types at the root of
//! the crate should be enough.

pub mod max7321;
pub mod pca9536;
pub mod pca9538;
pub mod pca9555;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
//! additional device, it should be easy to add. It's best to take a similar existing
//! implementation as inspiration. Contributions welcome!
//!
//! - [`MAX7321`](Max7321)
//! - [`PCA9536`](Pca9536)
//! - [`PCA9538`](Pca9538)
//! - [`PCA9555`](Pca9555)
Expand Down Expand Up @@ -73,6 +74,7 @@ pub(crate) use common::PortDriver;
pub(crate) use common::PortDriverPolarity;
pub(crate) use common::PortDriverTotemPole;

pub use dev::max7321::Max7321;
pub use dev::pca9536::Pca9536;
pub use dev::pca9538::Pca9538;
pub use dev::pca9555::Pca9555;
Expand Down
Loading