1use crate::I2cExt;
3
4pub struct Pcal6408a<M>(M);
6
7impl<I2C> Pcal6408a<core::cell::RefCell<Driver<I2C>>>
8where
9 I2C: crate::I2cBus,
10{
11 pub fn new(i2c: I2C, addr: bool) -> Self {
12 Self::with_mutex(i2c, addr)
13 }
14}
15
16impl<I2C, M> Pcal6408a<M>
17where
18 I2C: crate::I2cBus,
19 M: crate::PortMutex<Port = Driver<I2C>>,
20{
21 pub fn with_mutex(i2c: I2C, addr: bool) -> Self {
22 Self(crate::PortMutex::create(Driver::new(i2c, addr)))
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 io4: crate::Pin::new(4, &self.0),
32 io5: crate::Pin::new(5, &self.0),
33 io6: crate::Pin::new(6, &self.0),
34 io7: crate::Pin::new(7, &self.0),
35 }
36 }
37}
38
39pub struct Parts<'a, I2C, M = core::cell::RefCell<Driver<I2C>>>
40where
41 I2C: crate::I2cBus,
42 M: crate::PortMutex<Port = Driver<I2C>>,
43{
44 pub io0: crate::Pin<'a, crate::mode::Input, M>,
45 pub io1: crate::Pin<'a, crate::mode::Input, M>,
46 pub io2: crate::Pin<'a, crate::mode::Input, M>,
47 pub io3: crate::Pin<'a, crate::mode::Input, M>,
48 pub io4: crate::Pin<'a, crate::mode::Input, M>,
49 pub io5: crate::Pin<'a, crate::mode::Input, M>,
50 pub io6: crate::Pin<'a, crate::mode::Input, M>,
51 pub io7: crate::Pin<'a, crate::mode::Input, M>,
52}
53
54#[allow(dead_code)]
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56enum Regs {
57 InputPort = 0x00,
58 OutputPort = 0x01,
59 PolarityInversion = 0x02,
60 Configuration = 0x03,
61 OutputDriveStrength0 = 0x40,
62 OutputDriveStrength1 = 0x41,
63 InputLatch = 0x42,
64 PullEnable = 0x43,
65 PullSelection = 0x44,
66 InterruptMask = 0x45,
67 InterruptStatus = 0x46,
68 OutputPortConfiguration = 0x47, }
70
71impl From<Regs> for u8 {
72 fn from(r: Regs) -> u8 {
73 r as u8
74 }
75}
76
77pub struct Driver<I2C> {
78 i2c: I2C,
79 out: Option<u8>,
80 addr: u8,
81}
82
83impl<I2C> Driver<I2C> {
84 pub fn new(i2c: I2C, addr: bool) -> Self {
85 let addr = 0x20 | (addr as u8);
86 Self {
87 i2c,
88 out: None,
89 addr,
90 }
91 }
92}
93
94impl<I2C: crate::I2cBus> Driver<I2C> {
95 fn get_out(&mut self) -> Result<u8, I2C::BusError> {
96 match self.out {
99 Some(out) => Ok(out),
100 None => {
101 let out = self.i2c.read_reg(self.addr, Regs::OutputPort)?;
102 self.out = Some(out);
103 Ok(out)
104 }
105 }
106 }
107}
108
109impl<I2C: crate::I2cBus> crate::PortDriver for Driver<I2C> {
110 type Error = I2C::BusError;
111
112 fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
113 let mut out = self.get_out()?;
114 out |= mask_high as u8;
115 out &= !mask_low as u8;
116 self.out = Some(out);
117 if (mask_high | mask_low) & 0x00FF != 0 {
118 self.i2c.write_reg(self.addr, Regs::OutputPort, out)?;
119 }
120 Ok(())
121 }
122
123 fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
124 let out = self.get_out()?;
125 Ok(((out as u32) & mask_high) | (!(out as u32) & mask_low))
126 }
127
128 fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
129 let io0 = if (mask_high | mask_low) & 0x00FF != 0 {
130 self.i2c.read_reg(self.addr, Regs::InputPort)?
131 } else {
132 0
133 };
134 let in_ = io0 as u32;
135 Ok((in_ & mask_high) | (!in_ & mask_low))
136 }
137}
138
139impl<I2C: crate::I2cBus> crate::PortDriverTotemPole for Driver<I2C> {
140 fn set_direction(
141 &mut self,
142 mask: u32,
143 dir: crate::Direction,
144 state: bool,
145 ) -> Result<(), Self::Error> {
146 if mask & 0xFF == 0 {
147 return Ok(());
148 }
149 if dir == crate::Direction::Output {
151 use crate::PortDriver;
152 if state {
153 self.set(mask, 0)?;
154 } else {
155 self.set(0, mask)?;
156 }
157 }
158
159 let (mask_set, mask_clear) = match dir {
160 crate::Direction::Input => (mask as u8, 0),
161 crate::Direction::Output => (0, mask as u8),
162 };
163 self.i2c
164 .update_reg(self.addr, Regs::Configuration, mask_set, mask_clear)?;
165 Ok(())
166 }
167}
168
169impl<I2C: crate::I2cBus> crate::PortDriverPolarity for Driver<I2C> {
170 fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
171 if mask & 0xFF == 0 {
172 return Ok(());
173 }
174 let (mask_set, mask_clear) = match inverted {
175 false => (0, mask as u8),
176 true => (mask as u8, 0),
177 };
178
179 self.i2c
180 .update_reg(self.addr, Regs::PolarityInversion, mask_set, mask_clear)?;
181 Ok(())
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use embedded_hal_mock::eh1::i2c as mock_i2c;
188
189 #[test]
190 fn pca6408a() {
191 let expectations = [
192 mock_i2c::Transaction::write_read(0x21, vec![0x01], vec![0xff]),
194 mock_i2c::Transaction::write(0x21, vec![0x01, 0xfe]),
195 mock_i2c::Transaction::write_read(0x21, vec![0x03], vec![0xff]),
196 mock_i2c::Transaction::write(0x21, vec![0x03, 0xfe]),
197 mock_i2c::Transaction::write(0x21, vec![0x01, 0x7e]),
199 mock_i2c::Transaction::write_read(0x21, vec![0x03], vec![0xfe]),
200 mock_i2c::Transaction::write(0x21, vec![0x03, 0x7e]),
201 mock_i2c::Transaction::write_read(0x21, vec![0x03], vec![0x7e]),
202 mock_i2c::Transaction::write(0x21, vec![0x03, 0xfe]),
203 mock_i2c::Transaction::write(0x21, vec![0x01, 0x7f]),
205 mock_i2c::Transaction::write(0x21, vec![0x01, 0x7e]),
206 mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0x80]),
208 mock_i2c::Transaction::write_read(0x21, vec![0x00], vec![0x7f]),
209 mock_i2c::Transaction::write_read(0x21, vec![0x02], vec![0x00]),
211 mock_i2c::Transaction::write(0x21, vec![0x02, 0x80]),
212 mock_i2c::Transaction::write_read(0x21, vec![0x02], vec![0xff]),
213 mock_i2c::Transaction::write(0x21, vec![0x02, 0x7f]),
214 ];
215 let mut bus = mock_i2c::Mock::new(&expectations);
216
217 let mut pcal = super::Pcal6408a::new(bus.clone(), true);
218 let pcal_pins = pcal.split();
219
220 let mut io0 = pcal_pins.io0.into_output().unwrap();
221 let io7 = pcal_pins.io7.into_output().unwrap();
222 let io7 = io7.into_input().unwrap();
223
224 io0.set_high().unwrap();
226 io0.set_low().unwrap();
227
228 assert!(io7.is_high().unwrap());
230 assert!(io7.is_low().unwrap());
231
232 let mut io7 = io7.into_inverted().unwrap();
233 io7.set_inverted(false).unwrap();
234
235 bus.done();
236 }
237}