1use crate::I2cExt;
3
4pub struct Pi4ioe5v6408<M>(M);
6
7impl<I2C> Pi4ioe5v6408<core::cell::RefCell<Driver<I2C>>>
8where
9 I2C: crate::I2cBus,
10{
11 pub fn new(i2c: I2C, addr: bool) -> Result<Self, I2C::BusError> {
18 Self::with_mutex(i2c, addr)
19 }
20}
21
22impl<I2C, M> Pi4ioe5v6408<M>
23where
24 I2C: crate::I2cBus,
25 M: crate::PortMutex<Port = Driver<I2C>>,
26{
27 pub fn with_mutex(i2c: I2C, addr: bool) -> Result<Self, I2C::BusError> {
35 Ok(Self(crate::PortMutex::create(Driver::new(
36 i2c, addr, false,
37 )?)))
38 }
39
40 pub fn with_retained_pin_config(i2c: I2C, addr: bool) -> Result<Self, I2C::BusError> {
49 Ok(Self(crate::PortMutex::create(Driver::new(
50 i2c, addr, true,
51 )?)))
52 }
53
54 pub fn split<'a>(&'a mut self) -> Parts<'a, I2C, M> {
55 Parts {
56 io0: crate::Pin::new(0, &self.0),
57 io1: crate::Pin::new(1, &self.0),
58 io2: crate::Pin::new(2, &self.0),
59 io3: crate::Pin::new(3, &self.0),
60 io4: crate::Pin::new(4, &self.0),
61 io5: crate::Pin::new(5, &self.0),
62 io6: crate::Pin::new(6, &self.0),
63 io7: crate::Pin::new(7, &self.0),
64 }
65 }
66}
67
68pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
69where
70 I2C: crate::I2cBus,
71 M: crate::PortMutex<Port = Driver<I2C>>,
72{
73 pub io0: crate::Pin<'a, crate::mode::Input, M>,
74 pub io1: crate::Pin<'a, crate::mode::Input, M>,
75 pub io2: crate::Pin<'a, crate::mode::Input, M>,
76 pub io3: crate::Pin<'a, crate::mode::Input, M>,
77 pub io4: crate::Pin<'a, crate::mode::Input, M>,
78 pub io5: crate::Pin<'a, crate::mode::Input, M>,
79 pub io6: crate::Pin<'a, crate::mode::Input, M>,
80 pub io7: crate::Pin<'a, crate::mode::Input, M>,
81}
82
83#[allow(dead_code)]
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85enum Regs {
86 DeviceIdControl = 0x01,
87 IODirection = 0x03,
88 OutputPort = 0x05,
89 OutputHighImpedance = 0x07,
90 InputDefaultState = 0x09,
91 PullUpPullDownEnable = 0x0b,
92 PullUpPullDownSelection = 0x0d,
93 InputStatusRegister = 0x0f,
94 InterruptMaskRegister = 0x11,
95 InterruptStatusRegister = 0x13,
96}
97
98impl From<Regs> for u8 {
99 fn from(r: Regs) -> u8 {
100 r as u8
101 }
102}
103
104pub struct Driver<I2C> {
105 i2c: I2C,
106 addr: u8,
107 out: u8,
108}
109
110impl<I2C: crate::I2cBus> Driver<I2C> {
111 pub fn new(mut i2c: I2C, addr: bool, retain_config: bool) -> Result<Self, I2C::BusError> {
112 let addr = if addr { 0x44 } else { 0x43 };
113
114 let device_id = i2c.read_reg(addr, Regs::DeviceIdControl)?; assert_eq!(
116 device_id & 0xFC, 0xA0,
118 "Unexpected Device ID for the PI4IOE5V6408: 0x{:02x}",
119 device_id
120 );
121
122 let mut out = 0;
133
134 if retain_config {
135 out = i2c.read_reg(addr, Regs::OutputPort)?; } else {
137 i2c.write_reg(addr, Regs::OutputHighImpedance, 0)?; i2c.write_reg(addr, Regs::InterruptMaskRegister, 0xff)?; i2c.write_reg(addr, Regs::PullUpPullDownEnable, 0)?; }
142
143 Ok(Self { i2c, addr, out })
144 }
145}
146
147impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
148 type Error = I2C::BusError;
149
150 fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
151 let previous = self.out;
152 self.out |= mask_high as u8;
153 self.out &= !mask_low as u8;
154 if self.out != previous {
155 self.i2c.write_reg(self.addr, Regs::OutputPort, self.out)
156 } else {
157 Ok(())
159 }
160 }
161
162 fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
163 Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
164 }
165
166 fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
167 let in_ = self.i2c.read_reg(self.addr, Regs::InputStatusRegister)? as u32;
168 Ok((in_ & mask_high) | (!in_ & mask_low))
169 }
170}
171
172impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
173 fn set_direction(
174 &mut self,
175 mask: u32,
176 dir: crate::Direction,
177 state: bool,
178 ) -> Result<(), Self::Error> {
179 if dir == crate::Direction::Output {
181 use crate::PortDriver;
182 if state {
183 self.set(mask, 0)?;
184 } else {
185 self.set(0, mask)?;
186 }
187 }
188
189 let (mask_set, mask_clear) = match dir {
190 crate::Direction::Output => (mask as u8, 0), crate::Direction::Input => (0, mask as u8), };
193 self.i2c
194 .update_reg(self.addr, Regs::IODirection, mask_set, mask_clear)
195 }
196}
197
198impl<I2C: crate::I2cBus> crate::PortDriverPullDown for Driver<I2C> {
199 fn set_pull_down(&mut self, mask: u32, enable: bool) -> Result<(), Self::Error> {
200 if enable {
201 self.i2c
202 .update_reg(self.addr, Regs::PullUpPullDownSelection, 0, mask as u8)?;
203 self.i2c
204 .update_reg(self.addr, Regs::PullUpPullDownEnable, mask as u8, 0)?;
205 } else {
206 self.i2c
207 .update_reg(self.addr, Regs::PullUpPullDownEnable, 0, mask as u8)?;
208 }
209 Ok(())
210 }
211}
212
213impl<I2C: crate::I2cBus> crate::PortDriverPullUp for Driver<I2C> {
214 fn set_pull_up(&mut self, mask: u32, enable: bool) -> Result<(), Self::Error> {
215 if enable {
216 self.i2c
217 .update_reg(self.addr, Regs::PullUpPullDownSelection, mask as u8, 0)?;
218 self.i2c
219 .update_reg(self.addr, Regs::PullUpPullDownEnable, mask as u8, 0)?;
220 } else {
221 self.i2c
222 .update_reg(self.addr, Regs::PullUpPullDownEnable, 0, mask as u8)?;
223 }
224 Ok(())
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use core::cell::RefCell;
231 use embedded_hal_mock::eh1::i2c as mock_i2c;
232
233 #[test]
234 fn pi4ioe5v6408() {
235 let expectations = [
236 mock_i2c::Transaction::write_read(0x43, vec![0x01], vec![0xa2]),
238 mock_i2c::Transaction::write(0x43, vec![0x07, 0b00000000]),
239 mock_i2c::Transaction::write(0x43, vec![0x11, 0b11111111]),
240 mock_i2c::Transaction::write(0x43, vec![0x0b, 0b00000000]),
241 mock_i2c::Transaction::write_read(0x43, vec![0x03], vec![0]),
243 mock_i2c::Transaction::write(0x43, vec![0x03, 0b00000001]),
244 mock_i2c::Transaction::write(0x43, vec![0x05, 0b00000010]),
246 mock_i2c::Transaction::write_read(0x43, vec![0x03], vec![0b00000001]),
247 mock_i2c::Transaction::write(0x43, vec![0x03, 0b00000011]),
248 mock_i2c::Transaction::write_read(0x43, vec![0x03], vec![0b00000011]),
250 mock_i2c::Transaction::write(0x43, vec![0x03, 0b00000010]),
251 mock_i2c::Transaction::write(0x43, vec![0x05, 0b00000000]),
253 mock_i2c::Transaction::write(0x43, vec![0x05, 0b00000010]),
254 mock_i2c::Transaction::write(0x43, vec![0x05, 0b00000000]),
255 mock_i2c::Transaction::write_read(0x43, vec![0x0f], vec![0b00000001]),
257 mock_i2c::Transaction::write_read(0x43, vec![0x0f], vec![0b00000000]),
258 mock_i2c::Transaction::write_read(0x43, vec![0x0d], vec![0b10101010]),
260 mock_i2c::Transaction::write(0x43, vec![0x0d, 0b10101011]),
261 mock_i2c::Transaction::write_read(0x43, vec![0x0b], vec![0b00001010]),
262 mock_i2c::Transaction::write(0x43, vec![0x0b, 0b00001011]),
263 mock_i2c::Transaction::write_read(0x43, vec![0x0b], vec![0b00001011]),
265 mock_i2c::Transaction::write(0x43, vec![0x0b, 0b00001010]),
266 mock_i2c::Transaction::write_read(0x43, vec![0x0d], vec![0b10101011]),
268 mock_i2c::Transaction::write(0x43, vec![0x0d, 0b10101010]),
269 mock_i2c::Transaction::write_read(0x43, vec![0x0b], vec![0b00001010]),
270 mock_i2c::Transaction::write(0x43, vec![0x0b, 0b00001011]),
271 mock_i2c::Transaction::write_read(0x43, vec![0x0b], vec![0b00001011]),
273 mock_i2c::Transaction::write(0x43, vec![0x0b, 0b00001010]),
274 ];
275 let mut bus = mock_i2c::Mock::new(&expectations);
276
277 let mut pca = super::Pi4ioe5v6408::new(bus.clone(), false).unwrap();
278 let pca_pins = pca.split();
279
280 let io0 = pca_pins.io0.into_output().unwrap();
281 let mut io1 = pca_pins.io1.into_output_high().unwrap();
282
283 let mut io0 = io0.into_input().unwrap();
284
285 io1.set_low().unwrap();
286 io1.set_high().unwrap();
287 io1.toggle().unwrap();
288
289 assert!(io0.is_high().unwrap());
290 assert!(io0.is_low().unwrap());
291
292 io0.enable_pull_up(true).unwrap();
293 io0.enable_pull_up(false).unwrap();
294 io0.enable_pull_down(true).unwrap();
295 io0.enable_pull_down(false).unwrap();
296
297 bus.done();
298 }
299
300 #[test]
301 fn pi4ioe5v6408_retained() {
302 let expectations = [
303 mock_i2c::Transaction::write_read(0x44, vec![0x01], vec![0xa2]),
305 mock_i2c::Transaction::write_read(0x44, vec![0x05], vec![0b10101111]),
306 mock_i2c::Transaction::write(0x44, vec![0x05, 0b10101110]),
308 mock_i2c::Transaction::write_read(0x44, vec![0x03], vec![0]),
309 mock_i2c::Transaction::write(0x44, vec![0x03, 0b00000001]),
310 mock_i2c::Transaction::write_read(0x44, vec![0x03], vec![0b00000001]),
312 mock_i2c::Transaction::write(0x44, vec![0x03, 0b00000011]),
313 mock_i2c::Transaction::write(0x44, vec![0x05, 0b10101100]),
315 mock_i2c::Transaction::write(0x44, vec![0x05, 0b10101110]),
316 mock_i2c::Transaction::write(0x44, vec![0x05, 0b10101100]),
317 ];
318 let mut bus = mock_i2c::Mock::new(&expectations);
319
320 let mut pca: super::Pi4ioe5v6408<RefCell<_>> =
321 super::Pi4ioe5v6408::with_retained_pin_config(bus.clone(), true).unwrap();
322 let pca_pins = pca.split();
323
324 let _io0 = pca_pins.io0.into_output().unwrap();
325 let mut io1 = pca_pins.io1.into_output_high().unwrap();
326
327 io1.set_low().unwrap();
328 io1.set_high().unwrap();
329 io1.toggle().unwrap();
330
331 bus.done();
332 }
333}