diff --git a/boards/stm32l476rg/src/bin/mux_test.rs b/boards/stm32l476rg/src/bin/mux_test.rs index a3d77dc..ce4490b 100644 --- a/boards/stm32l476rg/src/bin/mux_test.rs +++ b/boards/stm32l476rg/src/bin/mux_test.rs @@ -18,6 +18,7 @@ const MUX_ADDRESS: u8 = 0x70; type I2c1Bus = Mutex>>; +/// 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, @@ -31,7 +32,11 @@ 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(_) => { @@ -39,6 +44,7 @@ async fn read_temperature_from_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.", ); @@ -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 = 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, diff --git a/lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs b/lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs index 29155e2..e9025c1 100644 --- a/lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs +++ b/lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs @@ -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>>) -> Self { Self { i2c } } diff --git a/lib/io/hyped_i2c/src/i2c_mux.rs b/lib/io/hyped_i2c/src/i2c_mux.rs index 81ec035..4b45858 100644 --- a/lib/io/hyped_i2c/src/i2c_mux.rs +++ b/lib/io/hyped_i2c/src/i2c_mux.rs @@ -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 { i2c: T, mux_address: u8, channel: u8, } -#[derive(Debug)] -pub enum I2cMuxError { - InvalidChannel, -} - impl I2cMux { pub fn new(i2c: T, channel: u8, mux_address: u8) -> Result { - // 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); } @@ -24,15 +34,14 @@ impl I2cMux { }) } - /// 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 HypedI2c for I2cMux { fn read_byte(&mut self, device_address: u8, register_address: u8) -> Option { match self.select_channel() {