Multiple devices on an I2C bus

I'm new to embedded Rust, and to Rust in general, so please forgive me for having so many questions.

I have two devices on an I2C bus, and I want to use both of them in my main loop. It appears that I have to create and destroy the devices each time through the loop, so that I can get my I2C back to give to the other device. Consider the following pseudocode:

fn main() -> ! {
    (set up peripherals);

    let i2c = hal::i2c_master(arguments);

    loop {
        let mut ht16k33 = HT16K33::new(i2c, HT16K33_ADDR);
        (do stuff with ht16k33);
        let i2c = ht16k33.destroy();

        let mut something_else = AnotherDevice::new(i2c, OTHER_ADDR);
        (do stuff with something_else);
        let i2c = something_else.destroy();

        // How do I get the i2c back up to the top of the loop?
    }
}

The i2c at the bottom of the loop is just shadowing the original i2c, and will go out of scope at the bottom of the loop. How do I get the i2c back up to the top of the loop, so I can use it again?

I could do this with a recursive function, but I'm not sure if Rust properly supports tail recursion.

It looks like you are logically taking the i2c bus and giving it to a device temporarily, then when the device is finished it'll give it back to your main loop.

You could store i2c in an Option and call i2c.take().unwrap() when handing the bus to a device.

fn main() -> ! {
    (set up peripherals);

    let mut i2c = Some(hal::i2c_master(arguments));

    loop {
        let mut ht16k33 = HT16K33::new(i2c.take().unwrap(), HT16K33_ADDR);
        (do stuff with ht16k33);
        i2c = Some(ht16k33.destroy());

        let mut something_else = AnotherDevice::new(i2c.take().unwrap(), OTHER_ADDR);
        (do stuff with something_else);
        i2c = Some(something_else.destroy());

        // How do I get the i2c back up to the top of the loop?
    }
}
1 Like

Exactly.

I've been experimenting, and it appears I can do this:

fn main() -> ! {
    (set up peripherals);

    let mut i2c = hal::i2c_master(arguments);

    loop {
        let mut ht16k33 = HT16K33::new(i2c, HT16K33_ADDR);
        (do stuff with ht16k33);
        i2c = ht16k33.destroy();

        let mut something_else = AnotherDevice::new(i2c, OTHER_ADDR);
        (do stuff with something_else);
        i2c = something_else.destroy();
    }
}

It's the same as your suggestion, except the validity of i2c is determined at compile time rather than runtime.

2 Likes

It's nice to see the simple and obvious solution is the best one :slightly_smiling_face:

3 Likes

Seems like you solved your problem, but for future reference, there's also shared-bus. It solves the same problem, but doesn't require you to destroy drivers all the time. I haven't had a chance to use it yet, but I have every reason to believe that it's good.

1 Like