port_expander/
multi.rs

1/// Set multiple pins at the same time.
2///
3/// The usual method of setting multiple pins
4///
5/// ```no_run
6/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
7/// # let mut pcf = port_expander::Pcf8574::new(i2c, false, false, false);
8/// # let p = pcf.split();
9/// # let mut io0 = p.p0;
10/// # let mut io1 = p.p1;
11/// io0.set_high().unwrap();
12/// io1.set_low().unwrap();
13/// ```
14///
15/// can be problematic because the time between the two operations might be significant (they are
16/// done as two separate bus transactions).  If it is desired that multiple pins change state in a
17/// single bus transaction, the `write_multiple()` function provides an interface to do this.
18///
19/// ## Example
20/// ```no_run
21/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
22/// # let mut pcf = port_expander::Pcf8574::new(i2c, false, false, false);
23/// # let p = pcf.split();
24/// # let mut io0 = p.p0;
25/// # let mut io1 = p.p1;
26/// port_expander::write_multiple(
27///     [&mut io0, &mut io1],
28///     [true, false],
29/// ).unwrap();
30/// ```
31pub fn write_multiple<PD, MUTEX, MODE: crate::mode::HasOutput, const N: usize>(
32    pins: [&mut crate::Pin<'_, MODE, MUTEX>; N],
33    states: [bool; N],
34) -> Result<(), PD::Error>
35where
36    PD: crate::PortDriver,
37    MUTEX: crate::PortMutex<Port = PD>,
38{
39    let mut mask_set_high = 0x00;
40    let mut mask_set_low = 0x00;
41
42    let port_driver = pins[0].port_driver();
43    for (pin, state) in pins.iter().zip(states.iter()) {
44        assert!(core::ptr::eq(pin.port_driver(), port_driver));
45        if *state {
46            mask_set_high |= pin.pin_mask();
47        } else {
48            mask_set_low |= pin.pin_mask();
49        }
50    }
51
52    pins[0].port_driver().lock(|drv| {
53        drv.set(mask_set_high, mask_set_low)?;
54        Ok(())
55    })
56}
57
58/// Read multiple pins at the same time.
59///
60/// When a port-expander sends an interrupt that one of its inputs changed state, it might be
61/// important to find out which input was responsible as quickly as possible _and_ by checking all
62/// inputs at once.  The naive approach of checking the pins in order
63///
64/// ```no_run
65/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
66/// # let mut pcf = port_expander::Pcf8574::new(i2c, false, false, false);
67/// # let p = pcf.split();
68/// # let io0 = p.p0;
69/// # let io1 = p.p1;
70/// if io0.is_high().unwrap() {
71///     // ...
72/// } else if io1.is_high().unwrap() {
73///     // ...
74/// }
75/// ```
76///
77/// is suboptimal because each read will happen as its own bus transaction and there is thus quite
78/// some delay.  Also the pins are checked one after the other, not all at once, which could lead
79/// to glitches.  The `read_multiple()` function provides an interface to circumvent these
80/// problems.
81///
82/// ## Example
83/// ```no_run
84/// # let i2c = embedded_hal_mock::eh1::i2c::Mock::new(&[]);
85/// # let mut pcf = port_expander::Pcf8574::new(i2c, false, false, false);
86/// # let p = pcf.split();
87/// # let io0 = p.p0;
88/// # let io1 = p.p1;
89/// let values = port_expander::read_multiple([&io0, &io1]).unwrap();
90/// if values[0] {
91///     // ...
92/// } else if values[1] {
93///     // ...
94/// }
95/// ```
96pub fn read_multiple<PD, MUTEX, MODE: crate::mode::HasInput, const N: usize>(
97    pins: [&crate::Pin<'_, MODE, MUTEX>; N],
98) -> Result<[bool; N], PD::Error>
99where
100    PD: crate::PortDriver,
101    MUTEX: crate::PortMutex<Port = PD>,
102{
103    let mask = pins.iter().map(|p| p.pin_mask()).fold(0, |m, p| m | p);
104    let port_driver = pins[0].port_driver();
105    let mask_in = port_driver.lock(|drv| drv.get(mask, 0))?;
106
107    let mut ret = [false; N];
108    for (pin, state) in pins.iter().zip(ret.iter_mut()) {
109        assert!(core::ptr::eq(pin.port_driver(), port_driver));
110        *state = mask_in & pin.pin_mask() != 0;
111    }
112
113    Ok(ret)
114}
115
116#[cfg(test)]
117mod tests {
118    use embedded_hal_mock::eh1::i2c as mock_i2c;
119    use embedded_hal_mock::eh1::spi as mock_spi;
120
121    #[test]
122    fn pcf8574_write_multiple() {
123        let expectations = [
124            // single writes for multiple pins
125            mock_i2c::Transaction::write(0x21, vec![0b10111011]),
126            mock_i2c::Transaction::write(0x21, vec![0b10101111]),
127        ];
128        let mut bus = mock_i2c::Mock::new(&expectations);
129
130        let mut pcf = crate::Pcf8574::new(bus.clone(), true, false, false);
131        let mut pcf_pins = pcf.split();
132
133        super::write_multiple(
134            [&mut pcf_pins.p2, &mut pcf_pins.p4, &mut pcf_pins.p6],
135            [false, true, false],
136        )
137        .unwrap();
138
139        super::write_multiple([&mut pcf_pins.p2, &mut pcf_pins.p4], [true, false]).unwrap();
140
141        bus.done();
142    }
143
144    #[test]
145    fn pcf8575_write_multiple() {
146        let expectations = [
147            // single writes for multiple pins
148            mock_i2c::Transaction::write(0x21, vec![0b10111011, 0b11011101]),
149            mock_i2c::Transaction::write(0x21, vec![0b10101111, 0b11010111]),
150        ];
151        let mut bus = mock_i2c::Mock::new(&expectations);
152
153        let mut pcf = crate::Pcf8575::new(bus.clone(), true, false, false);
154        let mut pcf_pins = pcf.split();
155
156        super::write_multiple(
157            [
158                &mut pcf_pins.p02,
159                &mut pcf_pins.p04,
160                &mut pcf_pins.p06,
161                &mut pcf_pins.p11,
162                &mut pcf_pins.p13,
163                &mut pcf_pins.p15,
164            ],
165            [false, true, false, false, true, false],
166        )
167        .unwrap();
168
169        super::write_multiple(
170            [
171                &mut pcf_pins.p02,
172                &mut pcf_pins.p04,
173                &mut pcf_pins.p11,
174                &mut pcf_pins.p13,
175            ],
176            [true, false, true, false],
177        )
178        .unwrap();
179
180        bus.done();
181    }
182
183    #[test]
184    fn pca9536_read_multiple() {
185        let expectations = [
186            // single reads for multiple pins
187            mock_i2c::Transaction::write_read(0x41, vec![0x00], vec![0b00000101]),
188            mock_i2c::Transaction::write_read(0x41, vec![0x00], vec![0b00001010]),
189        ];
190        let mut bus = mock_i2c::Mock::new(&expectations);
191
192        let mut pca = crate::Pca9536::new(bus.clone());
193        let pca_pins = pca.split();
194
195        let res = super::read_multiple([&pca_pins.io0, &pca_pins.io1, &pca_pins.io2]).unwrap();
196        assert_eq!(res, [true, false, true]);
197
198        let res = super::read_multiple([&pca_pins.io1, &pca_pins.io0, &pca_pins.io3]).unwrap();
199        assert_eq!(res, [true, false, true]);
200
201        bus.done();
202    }
203
204    #[test]
205    fn pca9702_read_multiple() {
206        let expectations = [
207            mock_spi::Transaction::transaction_start(),
208            mock_spi::Transaction::transfer_in_place(vec![0], vec![0b10101010]),
209            mock_spi::Transaction::transaction_end(),
210            mock_spi::Transaction::transaction_start(),
211            mock_spi::Transaction::transfer_in_place(vec![0], vec![0b10101010]),
212            mock_spi::Transaction::transaction_end(),
213        ];
214
215        let mut bus = mock_spi::Mock::new(&expectations);
216
217        let mut pca = crate::Pca9702::new(bus.clone());
218        let pca_pins = pca.split();
219
220        let res = super::read_multiple([&pca_pins.in0, &pca_pins.in1, &pca_pins.in2]).unwrap();
221        assert_eq!(res, [false, true, false]);
222
223        let res = super::read_multiple([&pca_pins.in1, &pca_pins.in0, &pca_pins.in3]).unwrap();
224        assert_eq!(res, [true, false, true]);
225
226        bus.done();
227    }
228
229    #[test]
230    #[should_panic]
231    fn pca9538_multiple_assert_same_chip() {
232        let expectations = [
233            // single reads for multiple pins
234            mock_i2c::Transaction::write_read(0x70, vec![0x00], vec![0b00000101]),
235        ];
236        let mut bus = mock_i2c::Mock::new(&expectations);
237
238        let mut pca0 = crate::Pca9538::new(bus.clone(), false, false);
239        let pca0_pins = pca0.split();
240        let mut pca1 = crate::Pca9538::new(bus.clone(), false, true);
241        let pca1_pins = pca1.split();
242
243        let _ = super::read_multiple([&pca0_pins.io0, &pca1_pins.io1]);
244
245        bus.done();
246    }
247}