1#![no_std]
2
3#[cfg(feature = "bus")]
4pub mod bus;
5pub mod error;
6
7use embedded_hal::i2c::I2c;
8use error::{MultiplexerError, Result};
9
10pub mod prelude {
11 #[cfg(feature = "bus")]
12 pub use crate::bus::{BusPort, MultiplexerBus};
13 pub use crate::{error::MultiplexerError, Multiplexer, PortState};
14}
15
16#[derive(Copy, Clone, Debug)]
17pub enum PortState {
18 Enabled,
19 Disabled,
20}
21
22impl From<bool> for PortState {
23 fn from(value: bool) -> Self {
24 match value {
25 true => PortState::Enabled,
26 false => PortState::Disabled,
27 }
28 }
29}
30
31#[derive(Copy, Clone, Debug)]
32pub struct Multiplexer<I2C: 'static + Send + Sync> {
33 i2c: I2C,
34 address: u8,
35 state: [bool; 4],
36}
37
38pub(crate) fn address_from_pins(a0: bool, a1: bool, a2: bool) -> u8 {
39 let mut address = 0b1110_0000;
40 if a0 {
41 address |= 0b0000_0001;
42 }
43 if a1 {
44 address |= 0b0000_0010;
45 }
46 if a2 {
47 address |= 0b0000_0100;
48 }
49 address
50}
51
52impl<I2C> Multiplexer<I2C>
53where
54 I2C: I2c + Send + Sync,
55{
56 pub fn new(i2c: I2C) -> Self {
57 Self {
58 i2c,
59 address: 0x70,
60 state: [false; 4],
61 }
62 }
63
64 pub fn with_address_pins(mut self, a0: bool, a1: bool, a2: bool) -> Self {
66 self.address = address_from_pins(a0, a1, a2);
67 self
68 }
69
70 pub fn with_address(mut self, address: u8) -> Self {
72 self.address = address;
73 self
74 }
75
76 fn port_code(states: [bool; 4]) -> u8 {
77 let mut code = 0;
78 if states[0] {
79 code |= 0b000_0001;
80 }
81 if states[1] {
82 code |= 0b000_0010;
83 }
84 if states[2] {
85 code |= 0b000_0100;
86 }
87 if states[3] {
88 code |= 0b000_1000;
89 }
90
91 code
92 }
93}
94
95impl<I2C> Multiplexer<I2C>
96where
97 I2C: I2c + Send + Sync,
98{
99 pub fn with_ports_disabled(self) -> Result<Self, I2C::Error> {
101 self.with_ports([false; 4])
102 }
103
104 pub fn set_ports_disabled(mut self) -> Result<(), I2C::Error> {
106 self.set_ports([false; 4])
107 }
108
109 pub fn with_ports_enabled(self) -> Result<Self, I2C::Error> {
111 self.with_ports([true; 4])
112 }
113
114 pub fn set_ports_enabled(mut self) -> Result<(), I2C::Error> {
116 self.set_ports([true; 4])
117 }
118
119 pub fn set_port(&mut self, port: u8, state: impl Into<bool>) -> Result<(), I2C::Error> {
121 if port >= 4 {
122 return Err(MultiplexerError::PortError);
123 }
124
125 self.state[port as usize] = state.into();
126
127 let code = Self::port_code(self.state);
128
129 self.i2c_write(&[code])
130 }
131
132 pub fn with_port(mut self, port: u8, state: impl Into<bool>) -> Result<Self, I2C::Error> {
134 self.set_port(port, state.into())?;
135 Ok(self)
136 }
137
138 pub fn set_ports(&mut self, ports: [bool; 4]) -> Result<(), I2C::Error> {
140 let code = Self::port_code(ports);
141 self.i2c_write(&[code])
142 }
143
144 pub fn with_ports(mut self, ports: [bool; 4]) -> Result<Self, I2C::Error> {
146 self.set_ports(ports)?;
147 Ok(self)
148 }
149
150 fn i2c_write(&mut self, bytes: &[u8]) -> Result<(), I2C::Error> {
151 self.i2c
152 .write(self.address, bytes)
153 .map_err(MultiplexerError::I2CError)
154 }
155}
156
157#[cfg(test)]
158mod test {
159 use crate::prelude::*;
160 use embedded_hal_mock::common::Generic;
161 use embedded_hal_mock::eh1::i2c::{Mock, Transaction};
162 use rstest::*;
163
164 impl Multiplexer<Generic<Transaction>> {
165 fn done(mut self) {
166 self.i2c.done();
167 }
168 }
169
170 #[rstest]
171 #[case([true;4], 0b0000_1111)]
172 #[case([false;4], 0b0000_0000)]
173 #[case([true, false, true, false], 0b0000_0101)]
174 fn setup_ports(#[case] ports: [bool; 4], #[case] result: u8) {
175 assert_eq!(Multiplexer::<Mock>::port_code(ports), result)
176 }
177
178 #[rstest]
179 #[case([true;3], 0b1110_0111)]
180 #[case([false;3], 0b1110_0000)]
181 #[case([true, false, false], 0b1110_0001)]
182 #[case([false, true, false], 0b1110_0010)]
183 #[case([true, false, true], 0b1110_0101)]
184 fn setup_address(#[case] addr: [bool; 3], #[case] result: u8) {
185 let i2c = Mock::new(&[]);
186 let multiplexer = Multiplexer::new(i2c).with_address_pins(addr[0], addr[1], addr[2]);
187 assert_eq!(multiplexer.address, result);
188 multiplexer.done();
189 }
190}