Skip to content

Commit

Permalink
Add more documentation to I2C Mux code
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbeechey committed Jan 9, 2025
1 parent 72dca78 commit a604b14
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 12 deletions.
10 changes: 10 additions & 0 deletions boards/stm32l476rg/src/bin/mux_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const MUX_ADDRESS: u8 = 0x70;

type I2c1Bus = Mutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>;

/// Task that reads the temperature from a specific channel of an I2C Mux.
#[embassy_executor::task(pool_size = 4)]
async fn read_temperature_from_mux(
i2c_bus: &'static I2c1Bus,
Expand All @@ -31,14 +32,19 @@ async fn read_temperature_from_mux(
mux_address
);

// First, we create a HypedI2c object that wraps the I2C bus.
let hyped_i2c = Stm32l476rgI2c::new(i2c_bus);

// Then, we create an I2C Mux object that wraps the HypedI2c object. `i2c_mux` can now be used anywhere that
// `hyped_i2c` could be used, but it will automatically switch to the correct channel before sending any I2C commands.
let mut i2c_mux = match I2cMux::new(hyped_i2c, channel, mux_address) {
Ok(i2c_mux) => i2c_mux,
Err(_) => {
panic!("Failed to create I2C Mux. Check the wiring and the I2C address of the Mux.")
}
};

// Finally, we create a Temperature object by passing the I2C Mux object and the I2C address of the temperature sensor.
let mut temperature_sensor = Temperature::new(&mut i2c_mux, temp_address).expect(
"Failed to create temperature sensor. Check the wiring and the I2C address of the sensor.",
);
Expand All @@ -61,10 +67,14 @@ async fn read_temperature_from_mux(
async fn main(spawner: Spawner) -> ! {
let p = embassy_stm32::init(Default::default());
let i2c = I2c::new_blocking(p.I2C1, p.PB8, p.PB9, Hertz(100_000), Default::default());

// Initialize the I2C bus and store it in a static cell so that it can be accessed from the tasks.
static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
let i2c_bus = I2C_BUS.init(Mutex::new(RefCell::new(i2c)));
defmt::info!("I2C initialized.");

// Spawn tasks that read the temperature from each channel of the I2C Mux.

spawner.must_spawn(read_temperature_from_mux(
i2c_bus,
TemperatureAddresses::Address3f,
Expand Down
1 change: 0 additions & 1 deletion lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ fn impl_hyped_i2c(ast: &syn::DeriveInput) -> TokenStream {
}

impl #impl_generics #name #ty_generics {
/// Create a new instance of our I2C implementation for the STM32L476RG
pub fn new(i2c: &'static Mutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>) -> Self {
Self { i2c }
}
Expand Down
31 changes: 20 additions & 11 deletions lib/io/hyped_i2c/src/i2c_mux.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
use crate::{HypedI2c, I2cError};

#[derive(Debug)]
pub enum I2cMuxError {
InvalidChannel,
}

/// A struct that represents an I2C multiplexer. (TCA9548A Low-Voltage 8-channel I2C Switch with Reset.)
///
/// The I2cMux struct is a wrapper around an I2c struct that adds the ability to select a channel on
/// a multiplexer before performing an I2C operation. Multiplexers are used to allow multiple I2C
/// devices to share the same I2C bus by selecting which device is connected to the bus.
///
/// The I2cMux struct implements the HypedI2c trait, which allows it to be used in place of an I2c
/// struct in any code that uses the HypedI2c trait.
///
/// Data sheet: https://www.ti.com/lit/ds/symlink/tca9548a.pdf
pub struct I2cMux<T: HypedI2c> {
i2c: T,
mux_address: u8,
channel: u8,
}

#[derive(Debug)]
pub enum I2cMuxError {
InvalidChannel,
}

impl<T: HypedI2c> I2cMux<T> {
pub fn new(i2c: T, channel: u8, mux_address: u8) -> Result<Self, I2cMuxError> {
// Check that the channel is valid
// Check that the channel is valid (channels start at 0)
if channel >= MAX_MUX_CHANNELS {
return Err(I2cMuxError::InvalidChannel);
}
Expand All @@ -24,15 +34,14 @@ impl<T: HypedI2c> I2cMux<T> {
})
}

/// Selects the channel on the multiplexer
/// Selects the channel on the multiplexer by writing the channel number
fn select_channel(&mut self) -> Result<(), I2cError> {
match self.i2c.write_byte(self.mux_address, 1 << self.channel) {
Ok(_) => Ok(()),
Err(e) => Err(e as I2cError),
}
self.i2c.write_byte(self.mux_address, 1 << self.channel)
}
}

/// The implementations of the HypedI2c trait for the I2cMux struct simply select the channel on the
/// multiplexer before calling the corresponding method on the inner I2c struct.
impl<T: HypedI2c> HypedI2c for I2cMux<T> {
fn read_byte(&mut self, device_address: u8, register_address: u8) -> Option<u8> {
match self.select_channel() {
Expand Down

0 comments on commit a604b14

Please sign in to comment.