#i2c #spi #embedded-hal #shared-bus #rtic

no-std shared-bus-rtic

Provides utilities for sharing peripheral communication buses in an RTIC application

6 releases

0.2.2 Jul 21, 2020
0.2.1 Jul 16, 2020
0.1.2 Jul 5, 2020

#127 in Concurrency

Download history 101/week @ 2021-06-30 257/week @ 2021-07-07 104/week @ 2021-07-14 358/week @ 2021-07-21 370/week @ 2021-07-28 140/week @ 2021-08-04 154/week @ 2021-08-11 271/week @ 2021-08-18 920/week @ 2021-08-25 685/week @ 2021-09-01 339/week @ 2021-09-08 156/week @ 2021-09-15 334/week @ 2021-09-22 203/week @ 2021-09-29 162/week @ 2021-10-06 191/week @ 2021-10-13

238 downloads per month

MIT license

128 lines


Provides macros and type definitions for using a shared peripheral bus in an RTIC application


Note that all of the drivers that use the same underlying bus must be stored within a single resource (e.g. as one larger struct) within the RTIC resources. This ensures that RTIC will prevent one driver from interrupting another while they are using the same underlying bus. The crate provides a detection mechanism that will panic if the shared bus is used in multiple task priorities without proper locking.


This crate is compatible with thumbv6 architectures. To enable support for thumbv6 devices, enable the thumbv6 feature in your Cargo.toml:

features = ["thumbv6"]

Usage Example

use shared_bus_rtic::SharedBus;

struct SharedBusResources<T> {
    device: Device<SharedBus<T>>,
    other_device: OtherDevice<SharedBus<T>>,

// ...

// Replace this type with the type of your bus (e.g. hal::i2c::I2c<...>).
type BusType = ();

struct Resources {
    shared_bus_resources: SharedBusResources<BusType>,

fn init(c: init::Context) -> init::LateResources {
    let manager = shared_bus_rtic::new!(bus, BusType);
    let device = Device::new(manager.acquire());
    let other_device = OtherDevice::new(manager.acquire());

    init::LateResources {
        shared_bus_resources: SharedBusResources { device, other_device },

Valid Example

struct SharedBusResources<Bus> {
    device_on_shared_bus: Device<Bus>,
    other_device_on_shared_bus: OtherDevice<Bus>,

// ...

struct Resources {
    shared_bus_resources: SharedBusResources<Bus>,

#[task(resources=[shared_bus_resources], priority=5)
pub fn high_priority_task(c: high_priority_task::Context) {
    // Good - This task cannot interrupt the lower priority task that is using the bus because of a
    // resource lock.

#[task(resources=[shared_bus_resources], priority=0)
pub fn low_priority_task(c: low_priority_task::Context) {
    // Good - RTIC properly locks the entire shared bus from concurrent access.
    c.resources.shared_bus_resources.lock(|bus| bus.other_device_on_shared_bus.read());

In the above example, it can be seen that both devices on the bus are stored as a single resource (in a shared struct). Because of this, RTIC properly locks the resource when either the high or low priority task is using the bus.

Unsound Example

The following example is unsound and should not be repeated. When a resource is interrupted, the crate will panic.

struct Resources {
    device_on_shared_bus: Device<Bus>,
    other_device_on_shared_bus: OtherDevice<Bus>,

#[task(resources=[device_on_shared_bus], priority=5)
pub fn high_priority_task(c: high_priority_task::Context) {
    // ERROR: This task might interrupt the read on the other device!
    // If it does interrupt the low priority task, the shared bus manager will panic.

#[task(resources=[other_device_on_shared_bus], priority=0)
pub fn low_priority_task(c: low_priority_task::Context) {
    // Attempt to read data from the device.

In the above incorrect example, RTIC may interrupt the low priority task to complete the high priority task. However, the low priority task may be using the shared bus. In this case, the communication may be corrupted by multiple devices using the bus at the same time. To detect this, shared-bus-rtic will detect any bus contention and panic if the bus is already in use.