Skip to content

Commit f9dcafc

Browse files
authored
Merge pull request #1 from FloppyDisck/initial_impl
Initial implementation
2 parents 25e4fb3 + fe30945 commit f9dcafc

File tree

4 files changed

+253
-1
lines changed

4 files changed

+253
-1
lines changed

Cargo.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "i2c-multiplexer"
3+
description = "An I2C Multiplexer library that supports the PCA9546 and TCA9546A chips"
4+
version = "0.1.0"
5+
edition = "2021"
6+
license = "MIT"
7+
repository = "https://github.com/FloppyDisck/i2c-multiplexer"
8+
readme = "README.md"
9+
keywords = ["embedded", "multiplexer", "PCA9546", "TCA9546A"]
10+
categories = ["embedded"]
11+
12+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13+
14+
[dependencies]
15+
embedded-svc = "0.22.0"
16+
embedded-hal = "0.2.7"
17+
18+
crc = "3.0.0"
19+
thiserror = "1.0.38"
20+
21+
[dev-dependencies]
22+
embedded-hal-mock = "0.9.0"
23+
rstest = "0.16.0"

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,43 @@
1-
# i2c-multiplexer
1+
# I2C-Multiplexer   [![Build Status]][actions] [![Latest Version]][crates.io]
2+
[Build Status]: https://img.shields.io/github/actions/workflow/status/FloppyDisck/i2c-multiplexer/rust.yml?branch=main
3+
[actions]: https://github.com/FloppyDisck/i2c-multiplexer/actions?query=branch%3Amain
4+
[Latest Version]: https://img.shields.io/crates/v/i2c-multiplexer.svg
5+
[crates.io]: https://crates.io/crates/i2c-multiplexer
26
An I2C Multiplexer library that supports the PCA9546 and TCA9546A chips
7+
8+
---
9+
10+
## Usage
11+
The sensor is initialized
12+
```rust
13+
use i2c_multiplexer::prelude::*;
14+
15+
fn main() -> Result<()> {
16+
// Disable all ports and only enable port 0
17+
Multiplexer::new(i2c).with_ports_disabled()?.set_port(0, true)?;
18+
}
19+
```
20+
21+
## Changing Address
22+
```rust
23+
use i2c_multiplexer::prelude::*;
24+
25+
fn main() -> Result<()> {
26+
// Manually set the address
27+
Multiplexer::new(i2c).with_address(0x72);
28+
29+
// Or set it according to the selected hardware pins
30+
// This uses A0 which means the address is 0x71
31+
Multiplexer::new(i2c).with_address_pins(true, false, false);
32+
}
33+
```
34+
35+
## Setting multiple ports
36+
```rust
37+
use i2c_multiplexer::prelude::*;
38+
39+
fn main() -> Result<()> {
40+
// Manually set the ports 0,2 to enabled and 1,3 to disabled
41+
Multiplexer::new(i2c).with_ports([true, false, true, false])?;
42+
}
43+
```

src/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use thiserror::Error;
2+
3+
pub type Result<T> = std::result::Result<T, MultiplexerError>;
4+
5+
#[derive(Error, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
6+
pub enum MultiplexerError {
7+
#[error("Write Read I2C Error")]
8+
WriteReadI2CError,
9+
#[error("Write I2C Error")]
10+
WriteI2CError,
11+
#[error("Incorrect port supplies")]
12+
PortError,
13+
}

src/lib.rs

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

0 commit comments

Comments
 (0)