port_expander/dev/
pcf8574.rs

1//! Support for the `PCF8574` & `PCF8574A` "Remote 8-bit I/O expander for I2C-bus with interrupt"
2
3/// `PCF8574` "Remote 8-bit I/O expander for I2C-bus with interrupt"
4pub struct Pcf8574<M>(M);
5/// `PCF8574A` "Remote 8-bit I/O expander for I2C-bus with interrupt"
6pub struct Pcf8574a<M>(M);
7
8impl<I2C> Pcf8574<core::cell::RefCell<Driver<I2C>>>
9where
10    I2C: crate::I2cBus,
11{
12    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
13        Self::with_mutex(i2c, a0, a1, a2)
14    }
15}
16
17impl<I2C> Pcf8574a<core::cell::RefCell<Driver<I2C>>>
18where
19    I2C: crate::I2cBus,
20{
21    pub fn new(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
22        Self::with_mutex(i2c, a0, a1, a2)
23    }
24}
25
26impl<I2C, M> Pcf8574<M>
27where
28    I2C: crate::I2cBus,
29    M: crate::PortMutex<Port = Driver<I2C>>,
30{
31    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
32        Self(crate::PortMutex::create(Driver::new(
33            i2c, false, a0, a1, a2,
34        )))
35    }
36
37    pub fn split(&mut self) -> Parts<'_, I2C, M> {
38        Parts {
39            p0: crate::Pin::new(0, &self.0),
40            p1: crate::Pin::new(1, &self.0),
41            p2: crate::Pin::new(2, &self.0),
42            p3: crate::Pin::new(3, &self.0),
43            p4: crate::Pin::new(4, &self.0),
44            p5: crate::Pin::new(5, &self.0),
45            p6: crate::Pin::new(6, &self.0),
46            p7: crate::Pin::new(7, &self.0),
47        }
48    }
49}
50
51impl<I2C, M> Pcf8574a<M>
52where
53    I2C: crate::I2cBus,
54    M: crate::PortMutex<Port = Driver<I2C>>,
55{
56    pub fn with_mutex(i2c: I2C, a0: bool, a1: bool, a2: bool) -> Self {
57        Self(crate::PortMutex::create(Driver::new(i2c, true, a0, a1, a2)))
58    }
59
60    pub fn split(&mut self) -> Parts<'_, I2C, M> {
61        Parts {
62            p0: crate::Pin::new(0, &self.0),
63            p1: crate::Pin::new(1, &self.0),
64            p2: crate::Pin::new(2, &self.0),
65            p3: crate::Pin::new(3, &self.0),
66            p4: crate::Pin::new(4, &self.0),
67            p5: crate::Pin::new(5, &self.0),
68            p6: crate::Pin::new(6, &self.0),
69            p7: crate::Pin::new(7, &self.0),
70        }
71    }
72}
73
74pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
75where
76    I2C: crate::I2cBus,
77    M: crate::PortMutex<Port = Driver<I2C>>,
78{
79    pub p0: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
80    pub p1: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
81    pub p2: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
82    pub p3: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
83    pub p4: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
84    pub p5: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
85    pub p6: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
86    pub p7: crate::Pin<'a, crate::mode::QuasiBidirectional, M>,
87}
88
89pub struct Driver<I2C> {
90    i2c: I2C,
91    out: u8,
92    addr: u8,
93}
94
95impl<I2C> Driver<I2C> {
96    pub fn new(i2c: I2C, is_a_variant: bool, a0: bool, a1: bool, a2: bool) -> Self {
97        let addr = if is_a_variant {
98            0x38 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8)
99        } else {
100            0x20 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8)
101        };
102        Self {
103            i2c,
104            out: 0xff,
105            addr,
106        }
107    }
108}
109
110impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
111    type Error = I2C::BusError;
112
113    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
114        self.out |= mask_high as u8;
115        self.out &= !mask_low as u8;
116        self.i2c.write(self.addr, &[self.out])?;
117        Ok(())
118    }
119
120    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
121        Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
122    }
123
124    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
125        let mut buf = [0x00];
126        self.i2c.read(self.addr, &mut buf)?;
127        let in_ = buf[0] as u32;
128        Ok((in_ & mask_high) | (!in_ & mask_low))
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use embedded_hal_mock::eh1::i2c as mock_i2c;
135
136    #[test]
137    fn pcf8574() {
138        let expectations = [
139            mock_i2c::Transaction::write(0x21, vec![0b11111111]),
140            mock_i2c::Transaction::write(0x21, vec![0b11111011]),
141            mock_i2c::Transaction::read(0x21, vec![0b01000000]),
142            mock_i2c::Transaction::read(0x21, vec![0b10111111]),
143        ];
144        let mut bus = mock_i2c::Mock::new(&expectations);
145
146        let mut pcf = super::Pcf8574::new(bus.clone(), true, false, false);
147        let mut pcf_pins = pcf.split();
148
149        pcf_pins.p2.set_high().unwrap();
150        pcf_pins.p2.set_low().unwrap();
151
152        assert!(pcf_pins.p6.is_high().unwrap());
153        assert!(pcf_pins.p6.is_low().unwrap());
154
155        bus.done();
156    }
157
158    #[test]
159    fn pcf8574a() {
160        let expectations = [
161            mock_i2c::Transaction::write(0x39, vec![0b11111111]),
162            mock_i2c::Transaction::write(0x39, vec![0b11101111]),
163            mock_i2c::Transaction::read(0x39, vec![0b00000001]),
164            mock_i2c::Transaction::read(0x39, vec![0b11111110]),
165        ];
166        let mut bus = mock_i2c::Mock::new(&expectations);
167
168        let mut pcf_a = super::Pcf8574a::new(bus.clone(), true, false, false);
169        let mut pcf_a_pins = pcf_a.split();
170
171        pcf_a_pins.p4.set_high().unwrap();
172        pcf_a_pins.p4.set_low().unwrap();
173
174        assert!(pcf_a_pins.p0.is_high().unwrap());
175        assert!(pcf_a_pins.p0.is_low().unwrap());
176
177        bus.done();
178    }
179}