port_expander/dev/
pca9538.rs

1//! Support for the `PCA9538` "Remote 8-Bit I2C AND SMBus Low-power I/O Expander"
2use crate::I2cExt;
3
4/// `PCA9538` "Remote 8-Bit I2C AND SMBus Low-power I/O Expander"
5pub struct Pca9538<M>(M);
6
7impl<I2C> Pca9538<core::cell::RefCell<Driver<I2C>>>
8where
9    I2C: crate::I2cBus,
10{
11    pub fn new(i2c: I2C, a0: bool, a1: bool) -> Self {
12        Self::with_mutex(i2c, a0, a1)
13    }
14}
15
16impl<I2C, M> Pca9538<M>
17where
18    I2C: crate::I2cBus,
19    M: crate::PortMutex<Port = Driver<I2C>>,
20{
21    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool) -> Self {
22        Self(crate::PortMutex::create(Driver::new(i2c, a0, a1)))
23    }
24
25    pub fn split(&mut self) -> Parts<'_, I2C, M> {
26        Parts {
27            io0: crate::Pin::new(0, &self.0),
28            io1: crate::Pin::new(1, &self.0),
29            io2: crate::Pin::new(2, &self.0),
30            io3: crate::Pin::new(3, &self.0),
31            io4: crate::Pin::new(4, &self.0),
32            io5: crate::Pin::new(5, &self.0),
33            io6: crate::Pin::new(6, &self.0),
34            io7: crate::Pin::new(7, &self.0),
35        }
36    }
37}
38
39pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
40where
41    I2C: crate::I2cBus,
42    M: crate::PortMutex<Port = Driver<I2C>>,
43{
44    pub io0: crate::Pin<'a, crate::mode::Input, M>,
45    pub io1: crate::Pin<'a, crate::mode::Input, M>,
46    pub io2: crate::Pin<'a, crate::mode::Input, M>,
47    pub io3: crate::Pin<'a, crate::mode::Input, M>,
48    pub io4: crate::Pin<'a, crate::mode::Input, M>,
49    pub io5: crate::Pin<'a, crate::mode::Input, M>,
50    pub io6: crate::Pin<'a, crate::mode::Input, M>,
51    pub io7: crate::Pin<'a, crate::mode::Input, M>,
52}
53
54#[allow(dead_code)]
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56enum Regs {
57    InputPort = 0x00,
58    OutputPort = 0x01,
59    PolarityInversion = 0x02,
60    Configuration = 0x03,
61}
62
63impl From<Regs> for u8 {
64    fn from(r: Regs) -> u8 {
65        r as u8
66    }
67}
68
69pub struct Driver<I2C> {
70    i2c: I2C,
71    addr: u8,
72    out: u8,
73}
74
75impl<I2C> Driver<I2C> {
76    pub fn new(i2c: I2C, a0: bool, a1: bool) -> Self {
77        let addr = 0x70 | ((a1 as u8) << 1) | (a0 as u8);
78        Self {
79            i2c,
80            addr,
81            out: 0xff,
82        }
83    }
84}
85
86impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
87    type Error = I2C::BusError;
88
89    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
90        let previous = self.out;
91        self.out |= mask_high as u8;
92        self.out &= !mask_low as u8;
93        if self.out != previous {
94            self.i2c.write_reg(self.addr, Regs::OutputPort, self.out)
95        } else {
96            // don't do the transfer when nothing changed
97            Ok(())
98        }
99    }
100
101    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
102        Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
103    }
104
105    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
106        let in_ = self.i2c.read_reg(self.addr, Regs::InputPort)? as u32;
107        Ok((in_ & mask_high) | (!in_ & mask_low))
108    }
109}
110
111impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
112    fn set_direction(
113        &mut self,
114        mask: u32,
115        dir: crate::Direction,
116        state: bool,
117    ) -> Result<(), Self::Error> {
118        // set state before switching direction to prevent glitch
119        if dir == crate::Direction::Output {
120            use crate::PortDriver;
121            if state {
122                self.set(mask, 0)?;
123            } else {
124                self.set(0, mask)?;
125            }
126        }
127
128        let (mask_set, mask_clear) = match dir {
129            crate::Direction::Input => (mask as u8, 0),
130            crate::Direction::Output => (0, mask as u8),
131        };
132        self.i2c
133            .update_reg(self.addr, Regs::Configuration, mask_set, mask_clear)
134    }
135}
136
137impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
138    fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
139        let (mask_set, mask_clear) = match inverted {
140            false => (0, mask as u8),
141            true => (mask as u8, 0),
142        };
143
144        self.i2c
145            .update_reg(self.addr, Regs::PolarityInversion, mask_set, mask_clear)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use embedded_hal_mock::eh1::i2c as mock_i2c;
152
153    #[test]
154    fn pca9538() {
155        let expectations = [
156            // pin setup io0
157            mock_i2c::Transaction::write(0x71, vec![0x01, 0xfe]),
158            mock_i2c::Transaction::write_read(0x71, vec![0x03], vec![0xff]),
159            mock_i2c::Transaction::write(0x71, vec![0x03, 0xfe]),
160            // pin setup io1
161            mock_i2c::Transaction::write_read(0x71, vec![0x03], vec![0xfe]),
162            mock_i2c::Transaction::write(0x71, vec![0x03, 0xfc]),
163            // pin setup io0 as input
164            mock_i2c::Transaction::write_read(0x71, vec![0x03], vec![0xfc]),
165            mock_i2c::Transaction::write(0x71, vec![0x03, 0xfd]),
166            // io1 writes
167            mock_i2c::Transaction::write(0x71, vec![0x01, 0xfc]),
168            mock_i2c::Transaction::write(0x71, vec![0x01, 0xfe]),
169            mock_i2c::Transaction::write(0x71, vec![0x01, 0xfc]),
170            // io0 reads
171            mock_i2c::Transaction::write_read(0x71, vec![0x00], vec![0x01]),
172            mock_i2c::Transaction::write_read(0x71, vec![0x00], vec![0x00]),
173            // io4 polarity
174            mock_i2c::Transaction::write_read(0x71, vec![0x02], vec![0x00]),
175            mock_i2c::Transaction::write(0x71, vec![0x02, 0x10]),
176            // io5 polarity
177            mock_i2c::Transaction::write_read(0x71, vec![0x02], vec![0x10]),
178            mock_i2c::Transaction::write(0x71, vec![0x02, 0x30]),
179            mock_i2c::Transaction::write_read(0x71, vec![0x02], vec![0x30]),
180            mock_i2c::Transaction::write(0x71, vec![0x02, 0x10]),
181        ];
182        let mut bus = mock_i2c::Mock::new(&expectations);
183
184        let mut pca = super::Pca9538::new(bus.clone(), true, false);
185        let pca_pins = pca.split();
186
187        let io0 = pca_pins.io0.into_output().unwrap();
188        let mut io1 = pca_pins.io1.into_output_high().unwrap();
189
190        let io0 = io0.into_input().unwrap();
191
192        io1.set_low().unwrap();
193        io1.set_high().unwrap();
194        io1.toggle().unwrap();
195
196        assert!(io0.is_high().unwrap());
197        assert!(io0.is_low().unwrap());
198
199        pca_pins.io4.into_inverted().unwrap();
200        let mut io5 = pca_pins.io5;
201        io5.set_inverted(true).unwrap();
202        io5.set_inverted(false).unwrap();
203
204        bus.done();
205    }
206}