port_expander/dev/
pcal6416a.rs

1//! Support for the `PCAL6416A` "16-bit I2C-bus and SMBus I/O port with interrupt"
2use crate::I2cExt;
3
4/// `PCAL6416A` "16-bit I2C-bus and SMBus I/O port with interrupt"
5pub struct Pcal6416a<M>(M);
6
7impl<I2C> Pcal6416a<core::cell::RefCell<Driver<I2C>>>
8where
9    I2C: crate::I2cBus,
10{
11    pub fn new(i2c: I2C, addr: bool) -> Self {
12        Self::with_mutex(i2c, addr)
13    }
14}
15
16impl<I2C, M> Pcal6416a<M>
17where
18    I2C: crate::I2cBus,
19    M: crate::PortMutex<Port = Driver<I2C>>,
20{
21    pub fn with_mutex(i2c: I2C, addr: bool) -> Self {
22        Self(crate::PortMutex::create(Driver::new(i2c, addr)))
23    }
24
25    pub fn split(&mut self) -> Parts<'_, I2C, M> {
26        Parts {
27            io0_0: crate::Pin::new(0, &self.0),
28            io0_1: crate::Pin::new(1, &self.0),
29            io0_2: crate::Pin::new(2, &self.0),
30            io0_3: crate::Pin::new(3, &self.0),
31            io0_4: crate::Pin::new(4, &self.0),
32            io0_5: crate::Pin::new(5, &self.0),
33            io0_6: crate::Pin::new(6, &self.0),
34            io0_7: crate::Pin::new(7, &self.0),
35            io1_0: crate::Pin::new(8, &self.0),
36            io1_1: crate::Pin::new(9, &self.0),
37            io1_2: crate::Pin::new(10, &self.0),
38            io1_3: crate::Pin::new(11, &self.0),
39            io1_4: crate::Pin::new(12, &self.0),
40            io1_5: crate::Pin::new(13, &self.0),
41            io1_6: crate::Pin::new(14, &self.0),
42            io1_7: crate::Pin::new(15, &self.0),
43        }
44    }
45}
46
47pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
48where
49    I2C: crate::I2cBus,
50    M: crate::PortMutex<Port = Driver<I2C>>,
51{
52    pub io0_0: crate::Pin<'a, crate::mode::Input, M>,
53    pub io0_1: crate::Pin<'a, crate::mode::Input, M>,
54    pub io0_2: crate::Pin<'a, crate::mode::Input, M>,
55    pub io0_3: crate::Pin<'a, crate::mode::Input, M>,
56    pub io0_4: crate::Pin<'a, crate::mode::Input, M>,
57    pub io0_5: crate::Pin<'a, crate::mode::Input, M>,
58    pub io0_6: crate::Pin<'a, crate::mode::Input, M>,
59    pub io0_7: crate::Pin<'a, crate::mode::Input, M>,
60    pub io1_0: crate::Pin<'a, crate::mode::Input, M>,
61    pub io1_1: crate::Pin<'a, crate::mode::Input, M>,
62    pub io1_2: crate::Pin<'a, crate::mode::Input, M>,
63    pub io1_3: crate::Pin<'a, crate::mode::Input, M>,
64    pub io1_4: crate::Pin<'a, crate::mode::Input, M>,
65    pub io1_5: crate::Pin<'a, crate::mode::Input, M>,
66    pub io1_6: crate::Pin<'a, crate::mode::Input, M>,
67    pub io1_7: crate::Pin<'a, crate::mode::Input, M>,
68}
69
70#[allow(dead_code)]
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72enum Regs {
73    InputPort0 = 0x00,
74    InputPort1 = 0x01,
75    OutputPort0 = 0x02,
76    OutputPort1 = 0x03,
77    PolarityInversion0 = 0x04,
78    PolarityInversion1 = 0x05,
79    Configuration0 = 0x06,
80    Configuration1 = 0x07,
81    OutputDriveStrength0Port0 = 0x40,
82    OutputDriveStrength1Port0 = 0x41,
83    OutputDriveStrength0Port1 = 0x42,
84    OutputDriveStrength1Port1 = 0x43,
85    InputLatch0 = 0x44,
86    InputLatch1 = 0x45,
87    PullEnable0 = 0x46,
88    PullEnable1 = 0x47,
89    PullSelection0 = 0x48,
90    PullSelection1 = 0x49,
91    InterruptMask0 = 0x4A,
92    InterruptMask1 = 0x4B,
93    InterruptStatus0 = 0x4C,
94    InterruptStatus1 = 0x4D,
95    OutputPortConfiguration = 0x4F, // Bit 0: Push-Pull (0) or Open-Drain (1) for all Outputs
96}
97
98impl From<Regs> for u8 {
99    fn from(r: Regs) -> u8 {
100        r as u8
101    }
102}
103
104pub struct Driver<I2C> {
105    i2c: I2C,
106    out: Option<u16>,
107    addr: u8,
108}
109
110impl<I2C> Driver<I2C> {
111    pub fn new(i2c: I2C, addr: bool) -> Self {
112        let addr = 0x20 | (addr as u8);
113        Self {
114            i2c,
115            out: None,
116            addr,
117        }
118    }
119}
120
121impl<I2C: crate::I2cBus> Driver<I2C> {
122    fn get_out(&mut self) -> Result<u16, I2C::BusError> {
123        // Make sure the state of the OutputPort register is actually known instead of assumed to avoid glitches on reboot.
124        // This is necessary because the OutputPort register is written instead of updated.
125        match self.out {
126            Some(out) => Ok(out),
127            None => {
128                let out_low = self.i2c.read_reg(self.addr, Regs::OutputPort0)? as u16;
129                let out_high = self.i2c.read_reg(self.addr, Regs::OutputPort1)? as u16;
130                let out = out_low | (out_high << 8);
131                self.out = Some(out);
132                Ok(out)
133            }
134        }
135    }
136}
137
138impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
139    type Error = I2C::BusError;
140
141    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
142        let mut out = self.get_out()?;
143        out |= mask_high as u16;
144        out &= !mask_low as u16;
145        self.out = Some(out);
146        if (mask_high | mask_low) & 0x00FF != 0 {
147            self.i2c
148                .write_reg(self.addr, Regs::OutputPort0, (out & 0xFF) as u8)?;
149        }
150        if (mask_high | mask_low) & 0xFF00 != 0 {
151            self.i2c
152                .write_reg(self.addr, Regs::OutputPort1, (out >> 8) as u8)?;
153        }
154        Ok(())
155    }
156
157    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
158        let out = self.get_out()?;
159        Ok(((out as u32) & mask_high) | (!(out as u32) & mask_low))
160    }
161
162    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
163        let io0 = if (mask_high | mask_low) & 0x00FF != 0 {
164            self.i2c.read_reg(self.addr, Regs::InputPort0)?
165        } else {
166            0
167        };
168        let io1 = if (mask_high | mask_low) & 0xFF00 != 0 {
169            self.i2c.read_reg(self.addr, Regs::InputPort1)?
170        } else {
171            0
172        };
173        let in_ = ((io1 as u32) << 8) | io0 as u32;
174        Ok((in_ & mask_high) | (!in_ & mask_low))
175    }
176}
177
178impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
179    fn set_direction(
180        &mut self,
181        mask: u32,
182        dir: crate::Direction,
183        state: bool,
184    ) -> Result<(), Self::Error> {
185        // set state before switching direction to prevent glitch
186        if dir == crate::Direction::Output {
187            use crate::PortDriver;
188            if state {
189                self.set(mask, 0)?;
190            } else {
191                self.set(0, mask)?;
192            }
193        }
194
195        let (mask_set, mask_clear) = match dir {
196            crate::Direction::Input => (mask as u16, 0),
197            crate::Direction::Output => (0, mask as u16),
198        };
199        if mask & 0x00FF != 0 {
200            self.i2c.update_reg(
201                self.addr,
202                Regs::Configuration0,
203                (mask_set & 0xFF) as u8,
204                (mask_clear & 0xFF) as u8,
205            )?;
206        }
207        if mask & 0xFF00 != 0 {
208            self.i2c.update_reg(
209                self.addr,
210                Regs::Configuration1,
211                (mask_set >> 8) as u8,
212                (mask_clear >> 8) as u8,
213            )?;
214        }
215        Ok(())
216    }
217}
218
219impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
220    fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
221        let (mask_set, mask_clear) = match inverted {
222            false => (0, mask as u16),
223            true => (mask as u16, 0),
224        };
225
226        if mask & 0x00FF != 0 {
227            self.i2c.update_reg(
228                self.addr,
229                Regs::PolarityInversion0,
230                (mask_set & 0xFF) as u8,
231                (mask_clear & 0xFF) as u8,
232            )?;
233        }
234        if mask & 0xFF00 != 0 {
235            self.i2c.update_reg(
236                self.addr,
237                Regs::PolarityInversion1,
238                (mask_set >> 8) as u8,
239                (mask_clear >> 8) as u8,
240            )?;
241        }
242        Ok(())
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use embedded_hal_mock::eh1::i2c as mock_i2c;
249
250    #[test]
251    fn pca6416a() {
252        let expectations = [
253            // pin setup io0_0
254            mock_i2c::Transaction::write_read(0x21, vec![0x02], vec![0xff]),
255            mock_i2c::Transaction::write_read(0x21, vec![0x03], vec![0xff]),
256            mock_i2c::Transaction::write(0x21, vec![0x02, 0xfe]),
257            mock_i2c::Transaction::write_read(0x21, vec![0x06], vec![0xff]),
258            mock_i2c::Transaction::write(0x21, vec![0x06, 0xfe]),
259            // pin setup io0_7
260            mock_i2c::Transaction::write(0x21, vec![0x02, 0x7e]),
261            mock_i2c::Transaction::write_read(0x21, vec![0x06], vec![0xfe]),
262            mock_i2c::Transaction::write(0x21, vec![0x06, 0x7e]),
263            mock_i2c::Transaction::write_read(0x21, vec![0x06], vec![0x7e]),
264            mock_i2c::Transaction::write(0x21, vec![0x06, 0xfe]),
265            // pin setup io1_0
266            mock_i2c::Transaction::write(0x21, vec![0x03, 0xfe]),
267            mock_i2c::Transaction::write_read(0x21, vec![0x07], vec![0xff]),
268            mock_i2c::Transaction::write(0x21, vec![0x07, 0xfe]),
269            // pin setup io1_7
270            mock_i2c::Transaction::write(0x21, vec![0x03, 0x7e]),
271            mock_i2c::Transaction::write_read(0x21, vec![0x07], vec![0xfe]),
272            mock_i2c::Transaction::write(0x21, vec![0x07, 0x7e]),
273            mock_i2c::Transaction::write_read(0x21, vec![0x07], vec![0x7e]),
274            mock_i2c::Transaction::write(0x21, vec![0x07, 0xfe]),
275            // output io0_0, io1_0
276            mock_i2c::Transaction::write(0x21, vec![0x02, 0x7f]),
277            mock_i2c::Transaction::write(0x21, vec![0x02, 0x7e]),
278            mock_i2c::Transaction::write(0x21, vec![0x03, 0x7f]),
279            mock_i2c::Transaction::write(0x21, vec![0x03, 0x7e]),
280            // input io0_7, io1_7
281            mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0x80]),
282            mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0x7f]),
283            mock_i2c::Transaction::write_read(0x21, vec![0x01], vec![0x80]),
284            mock_i2c::Transaction::write_read(0x21, vec![0x01], vec![0x7f]),
285            // polarity io0_7, io1_7
286            mock_i2c::Transaction::write_read(0x21, vec![0x04], vec![0x00]),
287            mock_i2c::Transaction::write(0x21, vec![0x04, 0x80]),
288            mock_i2c::Transaction::write_read(0x21, vec![0x04], vec![0xff]),
289            mock_i2c::Transaction::write(0x21, vec![0x04, 0x7f]),
290            mock_i2c::Transaction::write_read(0x21, vec![0x05], vec![0x00]),
291            mock_i2c::Transaction::write(0x21, vec![0x05, 0x80]),
292            mock_i2c::Transaction::write_read(0x21, vec![0x05], vec![0xff]),
293            mock_i2c::Transaction::write(0x21, vec![0x05, 0x7f]),
294        ];
295        let mut bus = mock_i2c::Mock::new(&expectations);
296
297        let mut pcal = super::Pcal6416a::new(bus.clone(), true);
298        let pcal_pins = pcal.split();
299
300        let mut io0_0 = pcal_pins.io0_0.into_output().unwrap();
301        let io0_7 = pcal_pins.io0_7.into_output().unwrap();
302        let io0_7 = io0_7.into_input().unwrap();
303
304        let mut io1_0 = pcal_pins.io1_0.into_output().unwrap();
305        let io1_7 = pcal_pins.io1_7.into_output().unwrap();
306        let io1_7 = io1_7.into_input().unwrap();
307
308        // output high and low
309        io0_0.set_high().unwrap();
310        io0_0.set_low().unwrap();
311        io1_0.set_high().unwrap();
312        io1_0.set_low().unwrap();
313
314        // input high and low
315        assert!(io0_7.is_high().unwrap());
316        assert!(io0_7.is_low().unwrap());
317        assert!(io1_7.is_high().unwrap());
318        assert!(io1_7.is_low().unwrap());
319
320        let mut io0_7 = io0_7.into_inverted().unwrap();
321        io0_7.set_inverted(false).unwrap();
322        let mut io1_7 = io1_7.into_inverted().unwrap();
323        io1_7.set_inverted(false).unwrap();
324
325        bus.done();
326    }
327}