Skip to content

Add mutex-traits-based implementations of SpiDevice and I2c #682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions embedded-hal-bus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", option
critical-section = { version = "1.0" }
defmt-03 = { package = "defmt", version = "0.3", optional = true }
portable-atomic = {version = "1.3", default-features = false, optional = true, features = ["require-cas"]}
mutex = "1.0"

[package.metadata.docs.rs]
features = ["std", "async"]
Expand Down
2 changes: 2 additions & 0 deletions embedded-hal-bus/src/i2c/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub use refcell::*;
mod mutex;
#[cfg(feature = "std")]
pub use mutex::*;
mod mutex_trait;
pub use mutex_trait::*;
mod critical_section;
pub use self::critical_section::*;
#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))]
Expand Down
65 changes: 65 additions & 0 deletions embedded-hal-bus/src/i2c/mutex_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use embedded_hal::i2c::{ErrorType, I2c};
use mutex::{BlockingMutex, RawMutex};

type Mutex<R, T> = BlockingMutex<R, T>;

/// `mutex-trait`-based shared bus [`I2c`] implementation.
///
/// Whether a single bus can be used across multiple threads depends on which
/// implementations of `RawMutex` are used.
pub struct MutexTraitDevice<'a, R, T> {
bus: &'a Mutex<R, T>,
}

impl<'a, R: RawMutex, T> MutexTraitDevice<'a, R, T> {
/// Create a new `MutexTraitDevice`.
#[inline]
pub fn new(bus: &'a Mutex<R, T>) -> Self {
Self { bus }
}
}

impl<R, T> ErrorType for MutexTraitDevice<'_, R, T>
where
T: I2c,
{
type Error = T::Error;
}

impl<R: RawMutex, T> I2c for MutexTraitDevice<'_, R, T>
where
T: I2c,
{
#[inline]
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
let bus = &mut *self.bus.lock();
bus.read(address, read)
}

#[inline]
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
let bus = &mut *self.bus.lock();
bus.write(address, write)
}

#[inline]
fn write_read(
&mut self,
address: u8,
write: &[u8],
read: &mut [u8],
) -> Result<(), Self::Error> {
let bus = &mut *self.bus.lock();
bus.write_read(address, write, read)
}

#[inline]
fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let bus = &mut *self.bus.lock();
bus.transaction(address, operations)
}
}
2 changes: 2 additions & 0 deletions embedded-hal-bus/src/spi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub use refcell::*;
mod mutex;
#[cfg(feature = "std")]
pub use mutex::*;
mod mutex_trait;
pub use mutex_trait::*;
#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))]
mod atomic;
mod critical_section;
Expand Down
95 changes: 95 additions & 0 deletions embedded-hal-bus/src/spi/mutex_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
use mutex::{BlockingMutex, RawMutex};

use super::DeviceError;
use crate::spi::shared::transaction;

type Mutex<R, T> = BlockingMutex<R, T>;

/// `mutex-trait`-based shared bus [`SpiDevice`] implementation.
///
/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances,
/// each with its own `CS` pin.
///
/// Whether a single bus can be used across multiple threads depends on which
/// implementations of `RawMutex` are used.
pub struct MutexTraitDevice<'a, R, BUS, CS, D> {
bus: &'a Mutex<R, BUS>,
cs: CS,
delay: D,
}

impl<'a, R: RawMutex, BUS, CS, D> MutexTraitDevice<'a, R, BUS, CS, D> {
/// Create a new [`MutexTraitDevice`].
///
/// This sets the `cs` pin high, and returns an error if that fails. It is recommended
/// to set the pin high the moment it's configured as an output, to avoid glitches.
#[inline]
pub fn new(bus: &'a Mutex<R, BUS>, mut cs: CS, delay: D) -> Result<Self, CS::Error>
where
CS: OutputPin,
{
cs.set_high()?;
Ok(Self { bus, cs, delay })
}
}

impl<'a, R: RawMutex, BUS, CS> MutexTraitDevice<'a, R, BUS, CS, super::NoDelay> {
/// Create a new [`MutexTraitDevice`] without support for in-transaction delays.
///
/// This sets the `cs` pin high, and returns an error if that fails. It is recommended
/// to set the pin high the moment it's configured as an output, to avoid glitches.
///
/// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
/// contract, which mandates delay support. It is relatively rare for drivers to use
/// in-transaction delays, so you might still want to use this method because it's more practical.
///
/// Note that a future version of the driver might start using delays, causing your
/// code to panic. This wouldn't be considered a breaking change from the driver side, because
/// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
/// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
/// the driver crate, you might want to pin the driver's version.
///
/// # Panics
///
/// The returned device will panic if you try to execute a transaction
/// that contains any operations of type [`Operation::DelayNs`].
#[inline]
pub fn new_no_delay(bus: &'a Mutex<R, BUS>, mut cs: CS) -> Result<Self, CS::Error>
where
CS: OutputPin,
{
cs.set_high()?;
Ok(Self {
bus,
cs,
delay: super::NoDelay,
})
}
}

impl<R, BUS, CS, D> ErrorType for MutexTraitDevice<'_, R, BUS, CS, D>
where
R: RawMutex,
BUS: ErrorType,
CS: OutputPin,
{
type Error = DeviceError<BUS::Error, CS::Error>;
}

impl<Word: Copy + 'static, R, BUS, CS, D> SpiDevice<Word> for MutexTraitDevice<'_, R, BUS, CS, D>
where
R: RawMutex,
BUS: SpiBus<Word>,
CS: OutputPin,
D: DelayNs,
{
#[inline]
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
let bus = &mut *self.bus.lock();

transaction(operations, bus, &mut self.delay, &mut self.cs)
}
}
Loading