port_expander/dev/
pca9536.rs

1//! Support for the `PCA9536` "4-bit I2C-bus and SMBus I/O port"
2use crate::I2cExt;
3
4/// `PCA9536` "4-bit I2C-bus and SMBus I/O port"
5pub struct Pca9536<M>(M);
6
7impl<I2C> Pca9536<core::cell::RefCell<Driver<I2C>>>
8where
9    I2C: crate::I2cBus,
10{
11    pub fn new(i2c: I2C) -> Self {
12        Self::with_mutex(i2c)
13    }
14}
15
16impl<I2C, M> Pca9536<M>
17where
18    I2C: crate::I2cBus,
19    M: crate::PortMutex<Port = Driver<I2C>>,
20{
21    pub fn with_mutex(i2c: I2C) -> Self {
22        Self(crate::PortMutex::create(Driver::new(i2c)))
23    }
24
25    pub fn split(&mut self) -> Parts<'_, I2C, M> {
26        Parts {
27            io0: crate::Pin::new(0, &self.0),
28            io1: crate::Pin::new(1, &self.0),
29            io2: crate::Pin::new(2, &self.0),
30            io3: crate::Pin::new(3, &self.0),
31        }
32    }
33}
34
35pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
36where
37    I2C: crate::I2cBus,
38    M: crate::PortMutex<Port = Driver<I2C>>,
39{
40    pub io0: crate::Pin<'a, crate::mode::Input, M>,
41    pub io1: crate::Pin<'a, crate::mode::Input, M>,
42    pub io2: crate::Pin<'a, crate::mode::Input, M>,
43    pub io3: crate::Pin<'a, crate::mode::Input, M>,
44}
45
46#[allow(dead_code)]
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48enum Regs {
49    InputPort = 0x00,
50    OutputPort = 0x01,
51    PolarityInversion = 0x02,
52    Configuration = 0x03,
53}
54
55impl From<Regs> for u8 {
56    fn from(r: Regs) -> u8 {
57        r as u8
58    }
59}
60
61const ADDRESS: u8 = 0x41;
62
63pub struct Driver<I2C> {
64    i2c: I2C,
65    out: u8,
66}
67
68impl<I2C> Driver<I2C> {
69    pub fn new(i2c: I2C) -> Self {
70        Self { i2c, out: 0xff }
71    }
72}
73
74impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
75    type Error = I2C::BusError;
76
77    fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
78        let previous = self.out;
79        self.out |= mask_high as u8;
80        self.out &= !mask_low as u8;
81        if self.out != previous {
82            self.i2c.write_reg(ADDRESS, Regs::OutputPort, self.out)
83        } else {
84            // don't do the transfer when nothing changed
85            Ok(())
86        }
87    }
88
89    fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
90        Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
91    }
92
93    fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
94        let in_ = self.i2c.read_reg(ADDRESS, Regs::InputPort)? as u32;
95        Ok((in_ & mask_high) | (!in_ & mask_low))
96    }
97}
98
99impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
100    fn set_direction(
101        &mut self,
102        mask: u32,
103        dir: crate::Direction,
104        state: bool,
105    ) -> Result<(), Self::Error> {
106        // set state before switching direction to prevent glitch
107        if dir == crate::Direction::Output {
108            use crate::PortDriver;
109            if state {
110                self.set(mask, 0)?;
111            } else {
112                self.set(0, mask)?;
113            }
114        }
115
116        let (mask_set, mask_clear) = match dir {
117            crate::Direction::Input => (mask as u8, 0),
118            crate::Direction::Output => (0, mask as u8),
119        };
120        self.i2c
121            .update_reg(ADDRESS, Regs::Configuration, mask_set, mask_clear)
122    }
123}
124
125impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
126    fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
127        let (mask_set, mask_clear) = match inverted {
128            false => (0, mask as u8),
129            true => (mask as u8, 0),
130        };
131
132        self.i2c
133            .update_reg(ADDRESS, Regs::PolarityInversion, mask_set, mask_clear)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use embedded_hal_mock::eh1::i2c as mock_i2c;
140
141    #[test]
142    fn pca9536() {
143        let expectations = [
144            // pin setup io0
145            mock_i2c::Transaction::write(super::ADDRESS, vec![0x01, 0xfe]),
146            mock_i2c::Transaction::write_read(super::ADDRESS, vec![0x03], vec![0xff]),
147            mock_i2c::Transaction::write(super::ADDRESS, vec![0x03, 0xfe]),
148            // pin setup io1
149            mock_i2c::Transaction::write_read(super::ADDRESS, vec![0x03], vec![0xfe]),
150            mock_i2c::Transaction::write(super::ADDRESS, vec![0x03, 0xfc]),
151            // pin setup io0 as input
152            mock_i2c::Transaction::write_read(super::ADDRESS, vec![0x03], vec![0xfc]),
153            mock_i2c::Transaction::write(super::ADDRESS, vec![0x03, 0xfd]),
154            // io1 writes
155            mock_i2c::Transaction::write(super::ADDRESS, vec![0x01, 0xfc]),
156            mock_i2c::Transaction::write(super::ADDRESS, vec![0x01, 0xfe]),
157            mock_i2c::Transaction::write(super::ADDRESS, vec![0x01, 0xfc]),
158            // io0 reads
159            mock_i2c::Transaction::write_read(super::ADDRESS, vec![0x00], vec![0x01]),
160            mock_i2c::Transaction::write_read(super::ADDRESS, vec![0x00], vec![0x00]),
161        ];
162        let mut bus = mock_i2c::Mock::new(&expectations);
163
164        let mut pca = super::Pca9536::new(bus.clone());
165        let pca_pins = pca.split();
166
167        let io0 = pca_pins.io0.into_output().unwrap();
168        let mut io1 = pca_pins.io1.into_output_high().unwrap();
169
170        let io0 = io0.into_input().unwrap();
171
172        io1.set_low().unwrap();
173        io1.set_high().unwrap();
174        io1.toggle().unwrap();
175
176        assert!(io0.is_high().unwrap());
177        assert!(io0.is_low().unwrap());
178
179        bus.done();
180    }
181}