port_expander/dev/
pca9554.rs

1//! Support for the `PCA9554` and `PCA9554a` "8-bit I2C-bus and SMBus I/O port with interrupt"
2use crate::I2cExt;
3
4/// `PCA9554` "8-bit I2C-bus and SMBus I/O port with interrupt"
5pub struct Pca9554<M>(M);
6/// `PCA9554A` "8-bit I2C-bus and SMBus I/O port with interrupt"
7pub struct Pca9554A<M>(M);
8
9impl<I2C> Pca9554<core::cell::RefCell<Driver<I2C>>>
10where
11    I2C: crate::I2cBus,
12{
13    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
14        Self::with_mutex(i2c, a0, a1, a2)
15    }
16}
17
18impl<I2C> Pca9554A<core::cell::RefCell<Driver<I2C>>>
19where
20    I2C: crate::I2cBus,
21{
22    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
23        Self::with_mutex(i2c, a0, a1, a2)
24    }
25}
26
27impl<I2C, M> Pca9554<M>
28where
29    I2C: crate::I2cBus,
30    M: crate::PortMutex<Port = Driver<I2C>>,
31{
32    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
33        Self(crate::PortMutex::create(Driver::new(
34            i2c, false, a0, a1, a2,
35        )))
36    }
37
38    pub fn split<'a>(&'a mut self) -> Parts<'a, I2C, M> {
39        Parts {
40            io0: crate::Pin::new(0, &self.0),
41            io1: crate::Pin::new(1, &self.0),
42            io2: crate::Pin::new(2, &self.0),
43            io3: crate::Pin::new(3, &self.0),
44            io4: crate::Pin::new(4, &self.0),
45            io5: crate::Pin::new(5, &self.0),
46            io6: crate::Pin::new(6, &self.0),
47            io7: crate::Pin::new(7, &self.0),
48        }
49    }
50}
51
52impl<I2C, M> Pca9554A<M>
53where
54    I2C: crate::I2cBus,
55    M: crate::PortMutex<Port = Driver<I2C>>,
56{
57    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
58        Self(crate::PortMutex::create(Driver::new(i2c, true, a0, a1, a2)))
59    }
60
61    pub fn split(&mut self) -> Parts<'_, I2C, M> {
62        Parts {
63            io0: crate::Pin::new(0, &self.0),
64            io1: crate::Pin::new(1, &self.0),
65            io2: crate::Pin::new(2, &self.0),
66            io3: crate::Pin::new(3, &self.0),
67            io4: crate::Pin::new(4, &self.0),
68            io5: crate::Pin::new(5, &self.0),
69            io6: crate::Pin::new(6, &self.0),
70            io7: crate::Pin::new(7, &self.0),
71        }
72    }
73}
74
75pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
76where
77    I2C: crate::I2cBus,
78    M: crate::PortMutex<Port = Driver<I2C>>,
79{
80    pub io0: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
81    pub io1: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
82    pub io2: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
83    pub io3: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
84    pub io4: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
85    pub io5: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
86    pub io6: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
87    pub io7: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
88}
89
90#[allow(dead_code)]
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92enum Regs {
93    InputPort0 = 0x00,
94    OutputPort0 = 0x01,
95    PolarityInversion0 = 0x02,
96    Configuration0 = 0x03,
97}
98
99impl From<Regs> for u8 {
100    fn from(r: Regs) -> u8 {
101        r as u8
102    }
103}
104
105pub struct Driver<I2C> {
106    i2c: I2C,
107    out: u8,
108    addr: u8,
109}
110
111impl<I2C> Driver<I2C> {
112    pub fn new(i2c: I2C, is_a_variant: bool, a0: bool, a1: bool, a2: bool) -> Self {
113        let addr = if is_a_variant {
114            0x38 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8)
115        } else {
116            0x20 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8)
117        };
118        Self {
119            i2c,
120            out: 0xff,
121            addr,
122        }
123    }
124}
125
126impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
127    type Error = I2C::BusError;
128
129    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
130        self.out |= mask_high as u8;
131        self.out &= !mask_low as u8;
132        self.i2c
133            .write_reg(self.addr, Regs::OutputPort0, (self.out & 0xFF) as u8)?;
134        Ok(())
135    }
136
137    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
138        Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
139    }
140
141    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
142        let io0 = if (mask_high | mask_low) & 0x00FF != 0 {
143            self.i2c.read_reg(self.addr, Regs::InputPort0)?
144        } else {
145            0
146        };
147        let in_ = io0 as u32;
148        Ok((in_ & mask_high) | (!in_ & mask_low))
149    }
150}
151
152impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
153    fn set_direction(
154        &mut self,
155        mask: u32,
156        dir: crate::Direction,
157        state: bool,
158    ) -> Result<(), Self::Error> {
159        // set state before switching direction to prevent glitch
160        if dir == crate::Direction::Output {
161            use crate::PortDriver;
162            if state {
163                self.set(mask, 0)?;
164            } else {
165                self.set(0, mask)?;
166            }
167        }
168
169        let (mask_set, mask_clear) = match dir {
170            crate::Direction::Input => (mask as u16, 0),
171            crate::Direction::Output => (0, mask as u16),
172        };
173        if mask & 0x00FF != 0 {
174            self.i2c.update_reg(
175                self.addr,
176                Regs::Configuration0,
177                (mask_set & 0xFF) as u8,
178                (mask_clear & 0xFF) as u8,
179            )?;
180        }
181        Ok(())
182    }
183}
184
185impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
186    fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
187        let (mask_set, mask_clear) = match inverted {
188            false => (0, mask as u8),
189            true => (mask as u8, 0),
190        };
191
192        self.i2c.update_reg(
193            self.addr,
194            Regs::PolarityInversion0,
195            (mask_set & 0xFF) as u8,
196            (mask_clear & 0xFF) as u8,
197        )?;
198        Ok(())
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use embedded_hal_mock::eh1::i2c as mock_i2c;
205
206    #[test]
207    fn pca9554a() {
208        let expectations = [
209            // set pin0 low and then high
210            mock_i2c::Transaction::write(0x39, vec![0x01, 0xfe]),
211            mock_i2c::Transaction::write(0x39, vec![0x01, 0xff]),
212            mock_i2c::Transaction::write_read(0x39, vec![0x00], vec![0xff]),
213            // set pin1 low
214            mock_i2c::Transaction::write(0x39, vec![0x01, 0xfd]),
215            mock_i2c::Transaction::write_read(0x39, vec![0x00], vec![0xfd]),
216            // set pin2 as output
217            mock_i2c::Transaction::write(0x39, vec![0x01, 0xf9]),
218            mock_i2c::Transaction::write_read(0x39, vec![0x03], vec![0xff]),
219            mock_i2c::Transaction::write(0x39, vec![0x03, 0xfb]),
220        ];
221        let mut bus = mock_i2c::Mock::new(&expectations);
222
223        let mut pca = super::Pca9554A::new(bus.clone(), true, false, false);
224        let pca_pins = pca.split();
225
226        let mut pin0 = pca_pins.io0;
227        let mut pin1 = pca_pins.io1;
228        let pin2 = pca_pins.io2;
229
230        pin0.set_low().unwrap();
231        pin0.set_high().unwrap();
232        assert!(pin0.is_high().unwrap());
233        pin1.set_low().unwrap();
234        assert!(pin1.is_low().unwrap());
235        pin2.into_output().unwrap();
236
237        bus.done();
238    }
239
240    #[test]
241    fn pca9554() {
242        let expectations = [
243            // set pin0 low and then high
244            mock_i2c::Transaction::write(0x21, vec![0x01, 0xfe]),
245            mock_i2c::Transaction::write(0x21, vec![0x01, 0xff]),
246            mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0xff]),
247            // set pin1 low
248            mock_i2c::Transaction::write(0x21, vec![0x01, 0xfd]),
249            mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0xfd]),
250            // set pin2 as output
251            mock_i2c::Transaction::write(0x21, vec![0x01, 0xf9]),
252            mock_i2c::Transaction::write_read(0x21, vec![0x03], vec![0xff]),
253            mock_i2c::Transaction::write(0x21, vec![0x03, 0xfb]),
254        ];
255        let mut bus = mock_i2c::Mock::new(&expectations);
256
257        let mut pca = super::Pca9554::new(bus.clone(), true, false, false);
258        let pca_pins = pca.split();
259
260        let mut pin0 = pca_pins.io0;
261        let mut pin1 = pca_pins.io1;
262        let pin2 = pca_pins.io2;
263
264        pin0.set_low().unwrap();
265        pin0.set_high().unwrap();
266        assert!(pin0.is_high().unwrap());
267        pin1.set_low().unwrap();
268        assert!(pin1.is_low().unwrap());
269        pin2.into_output().unwrap();
270
271        bus.done();
272    }
273}