port_expander/dev/
pca9555.rs

1//! Support for the `PCA9555` "16-bit I2C-bus and SMBus I/O port with interrupt"
2use crate::I2cExt;
3
4/// `PCA9555` "16-bit I2C-bus and SMBus I/O port with interrupt"
5pub struct Pca9555<M>(M);
6
7impl<I2C> Pca9555<core::cell::RefCell<Driver<I2C>>>
8where
9    I2C: crate::I2cBus,
10{
11    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
12        Self::with_mutex(i2c, a0, a1, a2)
13    }
14}
15
16impl<I2C, M> Pca9555<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, a2: bool) -> Self {
22        Self(crate::PortMutex::create(Driver::new(i2c, a0, a1, a2)))
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}
82
83impl From<Regs> for u8 {
84    fn from(r: Regs) -> u8 {
85        r as u8
86    }
87}
88
89pub struct Driver<I2C> {
90    i2c: I2C,
91    out: u16,
92    addr: u8,
93}
94
95impl<I2C> Driver<I2C> {
96    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
97        let addr = 0x20 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8);
98        Self {
99            i2c,
100            out: 0xffff,
101            addr,
102        }
103    }
104}
105
106impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
107    type Error = I2C::BusError;
108
109    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
110        self.out |= mask_high as u16;
111        self.out &= !mask_low as u16;
112        if (mask_high | mask_low) & 0x00FF != 0 {
113            self.i2c
114                .write_reg(self.addr, Regs::OutputPort0, (self.out & 0xFF) as u8)?;
115        }
116        if (mask_high | mask_low) & 0xFF00 != 0 {
117            self.i2c
118                .write_reg(self.addr, Regs::OutputPort1, (self.out >> 8) as u8)?;
119        }
120        Ok(())
121    }
122
123    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
124        Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
125    }
126
127    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
128        let io0 = if (mask_high | mask_low) & 0x00FF != 0 {
129            self.i2c.read_reg(self.addr, Regs::InputPort0)?
130        } else {
131            0
132        };
133        let io1 = if (mask_high | mask_low) & 0xFF00 != 0 {
134            self.i2c.read_reg(self.addr, Regs::InputPort1)?
135        } else {
136            0
137        };
138        let in_ = ((io1 as u32) << 8) | io0 as u32;
139        Ok((in_ & mask_high) | (!in_ & mask_low))
140    }
141}
142
143impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
144    fn set_direction(
145        &mut self,
146        mask: u32,
147        dir: crate::Direction,
148        state: bool,
149    ) -> Result<(), Self::Error> {
150        // set state before switching direction to prevent glitch
151        if dir == crate::Direction::Output {
152            use crate::PortDriver;
153            if state {
154                self.set(mask, 0)?;
155            } else {
156                self.set(0, mask)?;
157            }
158        }
159
160        let (mask_set, mask_clear) = match dir {
161            crate::Direction::Input => (mask as u16, 0),
162            crate::Direction::Output => (0, mask as u16),
163        };
164        if mask & 0x00FF != 0 {
165            self.i2c.update_reg(
166                self.addr,
167                Regs::Configuration0,
168                (mask_set & 0xFF) as u8,
169                (mask_clear & 0xFF) as u8,
170            )?;
171        }
172        if mask & 0xFF00 != 0 {
173            self.i2c.update_reg(
174                self.addr,
175                Regs::Configuration1,
176                (mask_set >> 8) as u8,
177                (mask_clear >> 8) as u8,
178            )?;
179        }
180        Ok(())
181    }
182}
183
184impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
185    fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
186        let (mask_set, mask_clear) = match inverted {
187            false => (0, mask as u16),
188            true => (mask as u16, 0),
189        };
190
191        if mask & 0x00FF != 0 {
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        }
199        if mask & 0xFF00 != 0 {
200            self.i2c.update_reg(
201                self.addr,
202                Regs::PolarityInversion1,
203                (mask_set >> 8) as u8,
204                (mask_clear >> 8) as u8,
205            )?;
206        }
207        Ok(())
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use embedded_hal_mock::eh1::i2c as mock_i2c;
214
215    #[test]
216    fn pca9555() {
217        let expectations = [
218            // pin setup io0_0
219            mock_i2c::Transaction::write(0x22, vec![0x02, 0xfe]),
220            mock_i2c::Transaction::write_read(0x22, vec![0x06], vec![0xff]),
221            mock_i2c::Transaction::write(0x22, vec![0x06, 0xfe]),
222            // pin setup io0_7
223            mock_i2c::Transaction::write(0x22, vec![0x02, 0x7e]),
224            mock_i2c::Transaction::write_read(0x22, vec![0x06], vec![0xfe]),
225            mock_i2c::Transaction::write(0x22, vec![0x06, 0x7e]),
226            mock_i2c::Transaction::write_read(0x22, vec![0x06], vec![0x7e]),
227            mock_i2c::Transaction::write(0x22, vec![0x06, 0xfe]),
228            // pin setup io1_0
229            mock_i2c::Transaction::write(0x22, vec![0x03, 0xfe]),
230            mock_i2c::Transaction::write_read(0x22, vec![0x07], vec![0xff]),
231            mock_i2c::Transaction::write(0x22, vec![0x07, 0xfe]),
232            // pin setup io1_7
233            mock_i2c::Transaction::write(0x22, vec![0x03, 0x7e]),
234            mock_i2c::Transaction::write_read(0x22, vec![0x07], vec![0xfe]),
235            mock_i2c::Transaction::write(0x22, vec![0x07, 0x7e]),
236            mock_i2c::Transaction::write_read(0x22, vec![0x07], vec![0x7e]),
237            mock_i2c::Transaction::write(0x22, vec![0x07, 0xfe]),
238            // output io0_0, io1_0
239            mock_i2c::Transaction::write(0x22, vec![0x02, 0x7f]),
240            mock_i2c::Transaction::write(0x22, vec![0x02, 0x7e]),
241            mock_i2c::Transaction::write(0x22, vec![0x03, 0x7f]),
242            mock_i2c::Transaction::write(0x22, vec![0x03, 0x7e]),
243            // input io0_7, io1_7
244            mock_i2c::Transaction::write_read(0x22, vec![0x00], vec![0x80]),
245            mock_i2c::Transaction::write_read(0x22, vec![0x00], vec![0x7f]),
246            mock_i2c::Transaction::write_read(0x22, vec![0x01], vec![0x80]),
247            mock_i2c::Transaction::write_read(0x22, vec![0x01], vec![0x7f]),
248            // polarity io0_7, io1_7
249            mock_i2c::Transaction::write_read(0x22, vec![0x04], vec![0x00]),
250            mock_i2c::Transaction::write(0x22, vec![0x04, 0x80]),
251            mock_i2c::Transaction::write_read(0x22, vec![0x04], vec![0xff]),
252            mock_i2c::Transaction::write(0x22, vec![0x04, 0x7f]),
253            mock_i2c::Transaction::write_read(0x22, vec![0x05], vec![0x00]),
254            mock_i2c::Transaction::write(0x22, vec![0x05, 0x80]),
255            mock_i2c::Transaction::write_read(0x22, vec![0x05], vec![0xff]),
256            mock_i2c::Transaction::write(0x22, vec![0x05, 0x7f]),
257        ];
258        let mut bus = mock_i2c::Mock::new(&expectations);
259
260        let mut pca = super::Pca9555::new(bus.clone(), false, true, false);
261        let pca_pins = pca.split();
262
263        let mut io0_0 = pca_pins.io0_0.into_output().unwrap();
264        let io0_7 = pca_pins.io0_7.into_output().unwrap();
265        let io0_7 = io0_7.into_input().unwrap();
266
267        let mut io1_0 = pca_pins.io1_0.into_output().unwrap();
268        let io1_7 = pca_pins.io1_7.into_output().unwrap();
269        let io1_7 = io1_7.into_input().unwrap();
270
271        // output high and low
272        io0_0.set_high().unwrap();
273        io0_0.set_low().unwrap();
274        io1_0.set_high().unwrap();
275        io1_0.set_low().unwrap();
276
277        // input high and low
278        assert!(io0_7.is_high().unwrap());
279        assert!(io0_7.is_low().unwrap());
280        assert!(io1_7.is_high().unwrap());
281        assert!(io1_7.is_low().unwrap());
282
283        let mut io0_7 = io0_7.into_inverted().unwrap();
284        io0_7.set_inverted(false).unwrap();
285        let mut io1_7 = io1_7.into_inverted().unwrap();
286        io1_7.set_inverted(false).unwrap();
287
288        bus.done();
289    }
290}