-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from FloppyDisck/initial_impl
Initial implementation
- Loading branch information
Showing
4 changed files
with
253 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "i2c-multiplexer" | ||
description = "An I2C Multiplexer library that supports the PCA9546 and TCA9546A chips" | ||
version = "0.1.0" | ||
edition = "2021" | ||
license = "MIT" | ||
repository = "https://github.com/FloppyDisck/i2c-multiplexer" | ||
readme = "README.md" | ||
keywords = ["embedded", "multiplexer", "PCA9546", "TCA9546A"] | ||
categories = ["embedded"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
embedded-svc = "0.22.0" | ||
embedded-hal = "0.2.7" | ||
|
||
crc = "3.0.0" | ||
thiserror = "1.0.38" | ||
|
||
[dev-dependencies] | ||
embedded-hal-mock = "0.9.0" | ||
rstest = "0.16.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,43 @@ | ||
# i2c-multiplexer | ||
# I2C-Multiplexer   [![Build Status]][actions] [![Latest Version]][crates.io] | ||
[Build Status]: https://img.shields.io/github/actions/workflow/status/FloppyDisck/i2c-multiplexer/rust.yml?branch=main | ||
[actions]: https://github.com/FloppyDisck/i2c-multiplexer/actions?query=branch%3Amain | ||
[Latest Version]: https://img.shields.io/crates/v/i2c-multiplexer.svg | ||
[crates.io]: https://crates.io/crates/i2c-multiplexer | ||
An I2C Multiplexer library that supports the PCA9546 and TCA9546A chips | ||
|
||
--- | ||
|
||
## Usage | ||
The sensor is initialized | ||
```rust | ||
use i2c_multiplexer::prelude::*; | ||
|
||
fn main() -> Result<()> { | ||
// Disable all ports and only enable port 0 | ||
Multiplexer::new(i2c).with_ports_disabled()?.set_port(0, true)?; | ||
} | ||
``` | ||
|
||
## Changing Address | ||
```rust | ||
use i2c_multiplexer::prelude::*; | ||
|
||
fn main() -> Result<()> { | ||
// Manually set the address | ||
Multiplexer::new(i2c).with_address(0x72); | ||
|
||
// Or set it according to the selected hardware pins | ||
// This uses A0 which means the address is 0x71 | ||
Multiplexer::new(i2c).with_address_pins(true, false, false); | ||
} | ||
``` | ||
|
||
## Setting multiple ports | ||
```rust | ||
use i2c_multiplexer::prelude::*; | ||
|
||
fn main() -> Result<()> { | ||
// Manually set the ports 0,2 to enabled and 1,3 to disabled | ||
Multiplexer::new(i2c).with_ports([true, false, true, false])?; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
use thiserror::Error; | ||
|
||
pub type Result<T> = std::result::Result<T, MultiplexerError>; | ||
|
||
#[derive(Error, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] | ||
pub enum MultiplexerError { | ||
#[error("Write Read I2C Error")] | ||
WriteReadI2CError, | ||
#[error("Write I2C Error")] | ||
WriteI2CError, | ||
#[error("Incorrect port supplies")] | ||
PortError, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
pub mod error; | ||
|
||
use embedded_hal::blocking::i2c; | ||
use error::{MultiplexerError, Result}; | ||
|
||
pub mod prelude { | ||
pub use crate::{error::MultiplexerError, Multiplexer, PortState}; | ||
} | ||
|
||
#[derive(Copy, Clone, Debug)] | ||
pub enum PortState { | ||
Enabled, | ||
Disabled, | ||
} | ||
|
||
impl From<bool> for PortState { | ||
fn from(value: bool) -> Self { | ||
match value { | ||
true => PortState::Enabled, | ||
false => PortState::Disabled, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone, Debug)] | ||
pub struct Multiplexer<I2C: 'static + Send + Sync> { | ||
i2c: I2C, | ||
address: u8, | ||
state: [bool; 4], | ||
} | ||
|
||
impl<I2C> Multiplexer<I2C> | ||
where | ||
I2C: i2c::WriteRead + i2c::Write + Send + Sync, | ||
{ | ||
pub fn new(i2c: I2C) -> Self { | ||
Self { | ||
i2c, | ||
address: 0x70, | ||
state: [false; 4], | ||
} | ||
} | ||
|
||
/// Sets the address according to the enabled hardware settings | ||
pub fn with_address_pins(mut self, a0: bool, a1: bool, a2: bool) -> Self { | ||
self.address = 0b1110_0000; | ||
if a0 { | ||
self.address |= 0b0000_0001; | ||
} | ||
if a1 { | ||
self.address |= 0b0000_0010; | ||
} | ||
if a2 { | ||
self.address |= 0b0000_0100; | ||
} | ||
self | ||
} | ||
|
||
/// Sets the address | ||
pub fn with_address(mut self, address: u8) -> Self { | ||
self.address = address; | ||
self | ||
} | ||
|
||
fn port_code(states: [bool; 4]) -> u8 { | ||
let mut code = 0; | ||
if states[0] { | ||
code |= 0b000_0001; | ||
} | ||
if states[1] { | ||
code |= 0b000_0010; | ||
} | ||
if states[2] { | ||
code |= 0b000_0100; | ||
} | ||
if states[3] { | ||
code |= 0b000_1000; | ||
} | ||
|
||
code | ||
} | ||
} | ||
|
||
impl<I2C> Multiplexer<I2C> | ||
where | ||
I2C: i2c::WriteRead + i2c::Write + Send + Sync, | ||
{ | ||
/// Disables all ports | ||
pub fn with_ports_disabled(self) -> Result<Self> { | ||
self.with_ports([false; 4]) | ||
} | ||
|
||
/// Disables all ports | ||
pub fn set_ports_disabled(mut self) -> Result<()> { | ||
self.set_ports([false; 4]) | ||
} | ||
|
||
/// Enables all ports | ||
pub fn with_ports_enabled(self) -> Result<Self> { | ||
self.with_ports([true; 4]) | ||
} | ||
|
||
/// Enables all ports | ||
pub fn set_ports_enabled(mut self) -> Result<()> { | ||
self.set_ports([true; 4]) | ||
} | ||
|
||
/// Enables / Disables the selected port | ||
pub fn set_port(&mut self, port: u8, state: impl Into<bool>) -> Result<()> { | ||
if port >= 4 { | ||
return Err(MultiplexerError::PortError); | ||
} | ||
|
||
self.state[port as usize] = state.into(); | ||
|
||
let code = Self::port_code(self.state); | ||
|
||
self.i2c_write(&[code]) | ||
} | ||
|
||
/// Sets the selected port | ||
pub fn with_port(mut self, port: u8, state: impl Into<bool>) -> Result<Self> { | ||
self.set_port(port, state.into())?; | ||
Ok(self) | ||
} | ||
|
||
/// Enables / Disables the selected ports | ||
pub fn set_ports(&mut self, ports: [bool; 4]) -> Result<()> { | ||
let code = Self::port_code(ports); | ||
self.i2c_write(&[code]) | ||
} | ||
|
||
/// Enables / Disables the selected ports | ||
pub fn with_ports(mut self, ports: [bool; 4]) -> Result<Self> { | ||
self.set_ports(ports)?; | ||
Ok(self) | ||
} | ||
|
||
fn i2c_write(&mut self, bytes: &[u8]) -> Result<()> { | ||
match self.i2c.write(self.address, bytes) { | ||
Ok(res) => Ok(res), | ||
Err(_) => Err(MultiplexerError::WriteI2CError), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use crate::prelude::*; | ||
use embedded_hal_mock::i2c::Mock; | ||
use rstest::*; | ||
|
||
#[rstest] | ||
#[case([true;4], 0b0000_1111)] | ||
#[case([false;4], 0b0000_0000)] | ||
#[case([true, false, true, false], 0b0000_0101)] | ||
fn setup_ports(#[case] ports: [bool; 4], #[case] result: u8) { | ||
assert_eq!(Multiplexer::<Mock>::port_code(ports), result) | ||
} | ||
|
||
#[rstest] | ||
#[case([true;3], 0b1110_0111)] | ||
#[case([false;3], 0b1110_0000)] | ||
#[case([true, false, false], 0b1110_0001)] | ||
#[case([false, true, false], 0b1110_0010)] | ||
#[case([true, false, true], 0b1110_0101)] | ||
fn setup_address(#[case] addr: [bool; 3], #[case] result: u8) { | ||
assert_eq!( | ||
Multiplexer::new(Mock::new([])) | ||
.with_address_pins(addr[0], addr[1], addr[2]) | ||
.address, | ||
result | ||
) | ||
} | ||
} |