1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! **shared-bus** is a crate to allow sharing bus peripherals safely between multiple devices.
//!
//! In the `embedded-hal` ecosystem, it is convention for drivers to "own" the bus peripheral they
//! are operating on.  This implies that only _one_ driver can have access to a certain bus.  That,
//! of course, poses an issue when multiple devices are connected to a single bus.
//!
//! _shared-bus_ solves this by giving each driver a bus-proxy to own which internally manages
//! access to the actual bus in a safe manner.  For a more in-depth introduction of the problem
//! this crate is trying to solve, take a look at the [blog post][blog-post].
//!
//! There are different 'bus managers' for different use-cases:
//!
//! # Sharing within a single task/thread
//! As long as all users of a bus are contained in a single task/thread, bus sharing is very
//! simple.  With no concurrency possible, no special synchronization is needed.  This is where
//! a [`BusManagerSimple`] should be used:
//!
//! ```
//! # use embedded_hal::blocking::i2c;
//! # use embedded_hal::blocking::i2c::Write as _;
//! # struct MyDevice<T>(T);
//! # impl<T: i2c::Write> MyDevice<T> {
//! #     pub fn new(t: T) -> Self { MyDevice(t) }
//! #     pub fn do_something_on_the_bus(&mut self) {
//! #         self.0.write(0xab, &[0x00]);
//! #     }
//! # }
//! #
//! # fn _example(i2c: impl i2c::Write) {
//! // For example:
//! // let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 90.khz(), clocks, &mut rcc.apb1);
//!
//! let bus = shared_bus::BusManagerSimple::new(i2c);
//!
//! let mut proxy1 = bus.acquire_i2c();
//! let mut my_device = MyDevice::new(bus.acquire_i2c());
//!
//! proxy1.write(0x39, &[0xc0, 0xff, 0xee]);
//! my_device.do_something_on_the_bus();
//! # }
//! ```
//!
//! The `BusManager::acquire_*()` methods can be called as often as needed; each call will yield
//! a new bus-proxy of the requested type.
//!
//! # Sharing across multiple tasks/threads
//! For sharing across multiple tasks/threads, synchronization is needed to ensure all bus-accesses
//! are strictly serialized and can't race against each other.  The synchronization is handled by
//! a platform-specific [`BusMutex`] implementation.  _shared-bus_ already contains some
//! implementations for common targets.  For each one, there is also a macro for easily creating
//! a bus-manager with `'static` lifetime, which is almost always a requirement when sharing across
//! task/thread boundaries.  As an example:
//!
//! ```
//! # struct MyDevice<T>(T);
//! # impl<T> MyDevice<T> {
//! #     pub fn new(t: T) -> Self { MyDevice(t) }
//! #     pub fn do_something_on_the_bus(&mut self) { }
//! # }
//! #
//! # struct SomeI2cBus;
//! # let i2c = SomeI2cBus;
//! // For example:
//! // let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 90.khz(), clocks, &mut rcc.apb1);
//!
//! // The bus is a 'static reference -> it lives forever and references can be
//! // shared with other threads.
//! let bus: &'static _ = shared_bus::new_std!(SomeI2cBus = i2c).unwrap();
//!
//! let mut proxy1 = bus.acquire_i2c();
//! let mut my_device = MyDevice::new(bus.acquire_i2c());
//!
//! // We can easily move a proxy to another thread:
//! # let t =
//! std::thread::spawn(move || {
//!     my_device.do_something_on_the_bus();
//! });
//! # t.join().unwrap();
//! ```
//!
//! Those platform-specific bits are guarded by a feature that needs to be enabled.  Here is an
//! overview of what's already available:
//!
//! | Mutex | Bus Manager | `'static` Bus Macro | Feature Name |
//! | --- | --- | --- | --- |
//! | `std::sync::Mutex` | [`BusManagerStd`] | [`new_std!()`] | `std` |
//! | `cortex_m::interrupt::Mutex` | [`BusManagerCortexM`] | [`new_cortexm!()`] | `cortex-m` |
//!
//! # Supported Busses
//! Currently, the following busses can be shared with _shared-bus_:
//!
//! | Bus | Proxy Type | Acquire Method | Comments |
//! | --- | --- | --- | --- |
//! | I2C | [`I2cProxy`] | [`.acquire_i2c()`] | |
//! | SPI | [`SpiProxy`] | [`.acquire_spi()`] | SPI can only be shared within a single task (See [`SpiProxy`] for details). |
//!
//!
//! [`.acquire_i2c()`]: ./struct.BusManager.html#method.acquire_i2c
//! [`.acquire_spi()`]: ./struct.BusManager.html#method.acquire_spi
//! [`BusManagerCortexM`]: ./type.BusManagerCortexM.html
//! [`BusManagerSimple`]: ./type.BusManagerSimple.html
//! [`BusManagerStd`]: ./type.BusManagerStd.html
//! [`BusMutex`]: ./trait.BusMutex.html
//! [`I2cProxy`]: ./struct.I2cProxy.html
//! [`SpiProxy`]: ./struct.SpiProxy.html
//! [`new_cortexm!()`]: ./macro.new_cortexm.html
//! [`new_std!()`]: ./macro.new_std.html
//! [blog-post]: https://blog.rahix.de/001-shared-bus
#![doc(html_root_url = "https://docs.rs/shared-bus")]
#![cfg_attr(not(feature = "std"), no_std)]

#![warn(missing_docs)]

mod manager;
mod mutex;
mod proxies;
mod macros;

#[doc(hidden)]
#[cfg(feature = "std")]
pub use once_cell;

#[doc(hidden)]
#[cfg(feature = "cortex-m")]
pub use cortex_m;

pub use manager::BusManager;
pub use mutex::BusMutex;
pub use mutex::NullMutex;
#[cfg(feature = "cortex-m")]
pub use mutex::CortexMMutex;
pub use proxies::I2cProxy;
pub use proxies::SpiProxy;

/// A bus manager for sharing within a single task/thread.
///
/// This is the bus manager with the least overhead; it should always be used when all bus users
/// are confined to a single task/thread as it has no side-effects (like blocking or turning off
/// interrupts).
///
/// # Example
/// ```
/// # use embedded_hal::blocking::i2c;
/// # use embedded_hal::blocking::i2c::Write as _;
/// # struct MyDevice<T>(T);
/// # impl<T: i2c::Write> MyDevice<T> {
/// #     pub fn new(t: T) -> Self { MyDevice(t) }
/// #     pub fn do_something_on_the_bus(&mut self) {
/// #         self.0.write(0xab, &[0x00]);
/// #     }
/// # }
/// #
/// # fn _example(i2c: impl i2c::Write) {
/// // For example:
/// // let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 90.khz(), clocks, &mut rcc.apb1);
///
/// let bus = shared_bus::BusManagerSimple::new(i2c);
///
/// let mut proxy1 = bus.acquire_i2c();
/// let mut my_device = MyDevice::new(bus.acquire_i2c());
///
/// proxy1.write(0x39, &[0xc0, 0xff, 0xee]);
/// my_device.do_something_on_the_bus();
/// # }
/// ```
pub type BusManagerSimple<BUS> = BusManager<NullMutex<BUS>>;

/// A bus manager for safely sharing between threads on a platform with `std` support.
///
/// This manager internally uses a `std::sync::Mutex` for synchronizing bus accesses.  As sharing
/// across threads will in most cases require a manager with `'static` lifetime, the
/// [`shared_bus::new_std!()`][new_std] macro exists to create such a bus manager.
///
/// [new_std]: ./macro.new_std.html
///
/// This type is only available with the `std` feature.
#[cfg(feature = "std")]
pub type BusManagerStd<BUS> = BusManager<::std::sync::Mutex<BUS>>;

/// A bus manager for safely sharing between tasks on Cortex-M.
///
/// This manager works by turning off interrupts for each bus transaction which prevents racy
/// accesses from different tasks/execution contexts (e.g. interrupts).  Usually, for sharing
/// between tasks, a manager with `'static` lifetime is needed which can be created using the
/// [`shared_bus::new_cortexm!()`][new_cortexm] macro.
///
/// [new_cortexm]: ./macro.new_cortexm.html
///
/// This type is only available with the `cortex-m` feature.
#[cfg(feature = "cortex-m")]
pub type BusManagerCortexM<BUS> = BusManager<CortexMMutex<BUS>>;