port_expander/dev/
pca9702.rs1use crate::{PortDriver, SpiBus};
15use embedded_hal::spi::Operation;
16
17pub struct Pca9702<M>(M);
19
20impl<SPI> Pca9702<core::cell::RefCell<Driver<Pca9702Bus<SPI>>>>
21where
22 SPI: crate::SpiBus,
23{
24 pub fn new(bus: SPI) -> Self {
25 Self::with_mutex(Pca9702Bus(bus))
26 }
27}
28
29impl<B, M> Pca9702<M>
30where
31 B: Pca9702BusTrait,
32 M: crate::PortMutex<Port = Driver<B>>,
33{
34 pub fn with_mutex(bus: B) -> Self {
36 Self(crate::PortMutex::create(Driver::new(bus)))
37 }
38
39 pub fn split(&mut self) -> Parts<'_, B, M> {
43 Parts {
44 in0: crate::Pin::new(0, &self.0),
45 in1: crate::Pin::new(1, &self.0),
46 in2: crate::Pin::new(2, &self.0),
47 in3: crate::Pin::new(3, &self.0),
48 in4: crate::Pin::new(4, &self.0),
49 in5: crate::Pin::new(5, &self.0),
50 in6: crate::Pin::new(6, &self.0),
51 in7: crate::Pin::new(7, &self.0),
52 }
53 }
54}
55
56pub struct Parts<'a, B, M = core::cell::RefCell<Driver<B>>>
58where
59 B: Pca9702BusTrait,
60 M: crate::PortMutex<Port = Driver<B>>,
61{
62 pub in0: crate::Pin<'a, crate::mode::Input, M>,
63 pub in1: crate::Pin<'a, crate::mode::Input, M>,
64 pub in2: crate::Pin<'a, crate::mode::Input, M>,
65 pub in3: crate::Pin<'a, crate::mode::Input, M>,
66 pub in4: crate::Pin<'a, crate::mode::Input, M>,
67 pub in5: crate::Pin<'a, crate::mode::Input, M>,
68 pub in6: crate::Pin<'a, crate::mode::Input, M>,
69 pub in7: crate::Pin<'a, crate::mode::Input, M>,
70}
71
72pub struct Driver<B> {
74 bus: B,
75}
76
77impl<B> Driver<B> {
78 fn new(bus: B) -> Self {
79 Self { bus }
80 }
81}
82
83pub trait Pca9702BusTrait {
86 type BusError;
87
88 fn read_inputs(&mut self) -> Result<u8, Self::BusError>;
90}
91
92impl<B: Pca9702BusTrait> PortDriver for Driver<B> {
93 type Error = B::BusError;
95
96 fn set(&mut self, _mask_high: u32, _mask_low: u32) -> Result<(), Self::Error> {
98 panic!("PCA9702 is input-only, cannot set output states");
99 }
100
101 fn is_set(&mut self, _mask_high: u32, _mask_low: u32) -> Result<u32, Self::Error> {
103 panic!("PCA9702 is input-only, cannot read back output states");
104 }
105
106 fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
108 let val = self.bus.read_inputs()? as u32;
109 Ok((val & mask_high) | (!val & mask_low))
110 }
111}
112
113pub struct Pca9702Bus<SPI>(pub SPI);
115
116impl<SPI> Pca9702BusTrait for Pca9702Bus<SPI>
117where
118 SPI: SpiBus,
119{
120 type BusError = SPI::BusError;
121
122 fn read_inputs(&mut self) -> Result<u8, Self::BusError> {
123 let mut buffer = [0u8];
127 let mut ops = [Operation::TransferInPlace(&mut buffer)];
128 self.0.transaction(&mut ops)?;
129
130 Ok(buffer[0])
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};
139
140 #[test]
141 fn pca9702_read_inputs() {
142 let expectations = [
143 SpiTransaction::transaction_start(),
145 SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
146 SpiTransaction::transaction_end(),
147 SpiTransaction::transaction_start(),
149 SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
150 SpiTransaction::transaction_end(),
151 SpiTransaction::transaction_start(),
153 SpiTransaction::transfer_in_place(vec![0], vec![0xA5]),
154 SpiTransaction::transaction_end(),
155 ];
156 let mut spi_mock = SpiMock::new(&expectations);
157 let mut pca = Pca9702::new(spi_mock.clone());
158 let pins = pca.split();
159
160 assert_eq!(pins.in0.is_high().unwrap(), true); assert_eq!(pins.in1.is_high().unwrap(), false);
164 assert_eq!(pins.in2.is_high().unwrap(), true);
165
166 spi_mock.done();
167 }
168
169 #[test]
170 #[should_panic]
171 fn pca9702_output_fails() {
172 let spi_mock = SpiMock::new(&[]);
173 let mut pca = Pca9702::new(spi_mock);
174 let pins = pca.split();
175
176 pins.in0.access_port_driver(|drv| {
177 drv.set(0x01, 0x00).unwrap_err();
178 });
179 }
180}