port_expander/dev/
pcf8575.rs

1//! Support for the `PCF8575` "Remote 16-bit I/O expander for I2C-bus with interrupt"
2
3/// `PCF8575` "Remote 16-bit I/O expander for I2C-bus with interrupt"
4pub struct Pcf8575<M>(M);
5
6impl<I2C> Pcf8575<core::cell::RefCell<Driver<I2C>>>
7where
8    I2C: crate::I2cBus,
9{
10    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
11        Self::with_mutex(i2c, a0, a1, a2)
12    }
13}
14
15impl<I2C, M> Pcf8575<M>
16where
17    I2C: crate::I2cBus,
18    M: crate::PortMutex<Port = Driver<I2C>>,
19{
20    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
21        Self(crate::PortMutex::create(Driver::new(i2c, a0, a1, a2)))
22    }
23
24    pub fn split(&mut self) -> Parts<'_, I2C, M> {
25        Parts {
26            p00: crate::Pin::new(0, &self.0),
27            p01: crate::Pin::new(1, &self.0),
28            p02: crate::Pin::new(2, &self.0),
29            p03: crate::Pin::new(3, &self.0),
30            p04: crate::Pin::new(4, &self.0),
31            p05: crate::Pin::new(5, &self.0),
32            p06: crate::Pin::new(6, &self.0),
33            p07: crate::Pin::new(7, &self.0),
34            p10: crate::Pin::new(8, &self.0),
35            p11: crate::Pin::new(9, &self.0),
36            p12: crate::Pin::new(10, &self.0),
37            p13: crate::Pin::new(11, &self.0),
38            p14: crate::Pin::new(12, &self.0),
39            p15: crate::Pin::new(13, &self.0),
40            p16: crate::Pin::new(14, &self.0),
41            p17: crate::Pin::new(15, &self.0),
42        }
43    }
44}
45
46pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
47where
48    I2C: crate::I2cBus,
49    M: crate::PortMutex<Port = Driver<I2C>>,
50{
51    pub p00: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
52    pub p01: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
53    pub p02: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
54    pub p03: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
55    pub p04: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
56    pub p05: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
57    pub p06: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
58    pub p07: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
59    pub p10: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
60    pub p11: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
61    pub p12: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
62    pub p13: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
63    pub p14: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
64    pub p15: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
65    pub p16: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
66    pub p17: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
67}
68
69pub struct Driver<I2C> {
70    i2c: I2C,
71    out: [u8; 2],
72    addr: u8,
73}
74
75impl<I2C> Driver<I2C> {
76    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
77        Self {
78            i2c,
79            out: [0xff; 2],
80            addr: 0x20 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8),
81        }
82    }
83}
84
85impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
86    type Error = I2C::BusError;
87
88    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
89        let mut out = u16::from_le_bytes(self.out);
90        out |= mask_high as u16;
91        out &= !mask_low as u16;
92
93        self.out = out.to_le_bytes();
94
95        self.i2c.write(self.addr, &self.out)?;
96        Ok(())
97    }
98
99    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
100        let out = u16::from_le_bytes(self.out);
101
102        Ok(((out as u32) & mask_high) | (!(out as u32) & mask_low))
103    }
104
105    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
106        let mut buf = [0x00; 2];
107        self.i2c.read(self.addr, &mut buf)?;
108        let in_ = u16::from_le_bytes(buf) as u32;
109
110        Ok((in_ & mask_high) | (!in_ & mask_low))
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use embedded_hal_mock::eh1::i2c as mock_i2c;
117
118    #[test]
119    fn pcf8575() {
120        let expectations = [
121            mock_i2c::Transaction::write(0x21, vec![0b11111111, 0b11111111]),
122            mock_i2c::Transaction::write(0x21, vec![0b11111011, 0b11111111]),
123            mock_i2c::Transaction::read(0x21, vec![0b01000000, 0b00000000]),
124            mock_i2c::Transaction::read(0x21, vec![0b10111111, 0b11111111]),
125            mock_i2c::Transaction::write(0x21, vec![0b11111011, 0b11111111]),
126            mock_i2c::Transaction::write(0x21, vec![0b11111011, 0b11111011]),
127            mock_i2c::Transaction::read(0x21, vec![0b00000000, 0b01000000]),
128            mock_i2c::Transaction::read(0x21, vec![0b11111111, 0b10111111]),
129        ];
130        let mut bus = mock_i2c::Mock::new(&expectations);
131
132        let mut pcf = super::Pcf8575::new(bus.clone(), true, false, false);
133        let mut pcf_pins = pcf.split();
134
135        pcf_pins.p02.set_high().unwrap();
136        pcf_pins.p02.set_low().unwrap();
137
138        assert!(pcf_pins.p06.is_high().unwrap());
139        assert!(pcf_pins.p06.is_low().unwrap());
140
141        pcf_pins.p12.set_high().unwrap();
142        pcf_pins.p12.set_low().unwrap();
143
144        assert!(pcf_pins.p16.is_high().unwrap());
145        assert!(pcf_pins.p16.is_low().unwrap());
146
147        bus.done();
148    }
149}