Skip to content

Commit d0bd938

Browse files
committed
Add mutex-traits-based implementations of SpiDevice and I2c
1 parent de0d966 commit d0bd938

File tree

5 files changed

+166
-1
lines changed

5 files changed

+166
-1
lines changed

embedded-hal-bus/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ version = "0.3.0"
1616

1717
[features]
1818
# Enable shared bus implementations using `std::sync::Mutex`
19-
std = ["alloc"]
19+
std = ["alloc", "mutex/std"]
2020
# Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS
2121
#
2222
# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware
@@ -37,6 +37,7 @@ embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", option
3737
critical-section = { version = "1.0" }
3838
defmt-03 = { package = "defmt", version = "0.3", optional = true }
3939
portable-atomic = {version = "1.3", default-features = false, optional = true, features = ["require-cas"]}
40+
mutex = "1.0"
4041

4142
[package.metadata.docs.rs]
4243
features = ["std", "async"]

embedded-hal-bus/src/i2c/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub use refcell::*;
66
mod mutex;
77
#[cfg(feature = "std")]
88
pub use mutex::*;
9+
mod mutex_traits;
10+
pub use mutex_traits::*;
911
mod critical_section;
1012
pub use self::critical_section::*;
1113
#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use embedded_hal::i2c::{ErrorType, I2c};
2+
use mutex::{BlockingMutex, RawMutex};
3+
4+
type Mutex<R, T> = BlockingMutex<R, T>;
5+
6+
/// `mutex-traits`-based shared bus [`I2c`] implementation.
7+
///
8+
/// Whether a single bus can be used across multiple threads depends on which
9+
/// implementation of `RawMutex` is used.
10+
pub struct MutexTraitsDevice<'a, R, T> {
11+
bus: &'a Mutex<R, T>,
12+
}
13+
14+
impl<'a, R: RawMutex, T> MutexTraitsDevice<'a, R, T> {
15+
/// Create a new `MutexTraitsDevice`.
16+
#[inline]
17+
pub fn new(bus: &'a Mutex<R, T>) -> Self {
18+
Self { bus }
19+
}
20+
}
21+
22+
impl<R, T> ErrorType for MutexTraitsDevice<'_, R, T>
23+
where
24+
T: I2c,
25+
{
26+
type Error = T::Error;
27+
}
28+
29+
impl<R: RawMutex, T> I2c for MutexTraitsDevice<'_, R, T>
30+
where
31+
T: I2c,
32+
{
33+
#[inline]
34+
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
35+
let bus = &mut *self.bus.lock();
36+
bus.read(address, read)
37+
}
38+
39+
#[inline]
40+
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
41+
let bus = &mut *self.bus.lock();
42+
bus.write(address, write)
43+
}
44+
45+
#[inline]
46+
fn write_read(
47+
&mut self,
48+
address: u8,
49+
write: &[u8],
50+
read: &mut [u8],
51+
) -> Result<(), Self::Error> {
52+
let bus = &mut *self.bus.lock();
53+
bus.write_read(address, write, read)
54+
}
55+
56+
#[inline]
57+
fn transaction(
58+
&mut self,
59+
address: u8,
60+
operations: &mut [embedded_hal::i2c::Operation<'_>],
61+
) -> Result<(), Self::Error> {
62+
let bus = &mut *self.bus.lock();
63+
bus.transaction(address, operations)
64+
}
65+
}

embedded-hal-bus/src/spi/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub use refcell::*;
1111
mod mutex;
1212
#[cfg(feature = "std")]
1313
pub use mutex::*;
14+
mod mutex_traits;
15+
pub use mutex_traits::*;
1416
#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))]
1517
mod atomic;
1618
mod critical_section;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use embedded_hal::delay::DelayNs;
2+
use embedded_hal::digital::OutputPin;
3+
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
4+
use mutex::{BlockingMutex, RawMutex};
5+
6+
use super::DeviceError;
7+
use crate::spi::shared::transaction;
8+
9+
type Mutex<R, T> = BlockingMutex<R, T>;
10+
11+
/// `mutex-traits`-based shared bus [`SpiDevice`] implementation.
12+
///
13+
/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances,
14+
/// each with its own `CS` pin.
15+
///
16+
/// Whether a single bus can be used across multiple threads depends on which
17+
/// implementation of `RawMutex` is used.
18+
pub struct MutexTraitsDevice<'a, R, BUS, CS, D> {
19+
bus: &'a Mutex<R, BUS>,
20+
cs: CS,
21+
delay: D,
22+
}
23+
24+
impl<'a, R: RawMutex, BUS, CS, D> MutexTraitsDevice<'a, R, BUS, CS, D> {
25+
/// Create a new [`MutexTraitsDevice`].
26+
///
27+
/// This sets the `cs` pin high, and returns an error if that fails. It is recommended
28+
/// to set the pin high the moment it's configured as an output, to avoid glitches.
29+
#[inline]
30+
pub fn new(bus: &'a Mutex<R, BUS>, mut cs: CS, delay: D) -> Result<Self, CS::Error>
31+
where
32+
CS: OutputPin,
33+
{
34+
cs.set_high()?;
35+
Ok(Self { bus, cs, delay })
36+
}
37+
}
38+
39+
impl<'a, R: RawMutex, BUS, CS> MutexTraitsDevice<'a, R, BUS, CS, super::NoDelay> {
40+
/// Create a new [`MutexTraitsDevice`] without support for in-transaction delays.
41+
///
42+
/// This sets the `cs` pin high, and returns an error if that fails. It is recommended
43+
/// to set the pin high the moment it's configured as an output, to avoid glitches.
44+
///
45+
/// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
46+
/// contract, which mandates delay support. It is relatively rare for drivers to use
47+
/// in-transaction delays, so you might still want to use this method because it's more practical.
48+
///
49+
/// Note that a future version of the driver might start using delays, causing your
50+
/// code to panic. This wouldn't be considered a breaking change from the driver side, because
51+
/// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
52+
/// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
53+
/// the driver crate, you might want to pin the driver's version.
54+
///
55+
/// # Panics
56+
///
57+
/// The returned device will panic if you try to execute a transaction
58+
/// that contains any operations of type [`Operation::DelayNs`].
59+
#[inline]
60+
pub fn new_no_delay(bus: &'a Mutex<R, BUS>, mut cs: CS) -> Result<Self, CS::Error>
61+
where
62+
CS: OutputPin,
63+
{
64+
cs.set_high()?;
65+
Ok(Self {
66+
bus,
67+
cs,
68+
delay: super::NoDelay,
69+
})
70+
}
71+
}
72+
73+
impl<R, BUS, CS, D> ErrorType for MutexTraitsDevice<'_, R, BUS, CS, D>
74+
where
75+
R: RawMutex,
76+
BUS: ErrorType,
77+
CS: OutputPin,
78+
{
79+
type Error = DeviceError<BUS::Error, CS::Error>;
80+
}
81+
82+
impl<Word: Copy + 'static, R, BUS, CS, D> SpiDevice<Word> for MutexTraitsDevice<'_, R, BUS, CS, D>
83+
where
84+
R: RawMutex,
85+
BUS: SpiBus<Word>,
86+
CS: OutputPin,
87+
D: DelayNs,
88+
{
89+
#[inline]
90+
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
91+
let bus = &mut *self.bus.lock();
92+
93+
transaction(operations, bus, &mut self.delay, &mut self.cs)
94+
}
95+
}

0 commit comments

Comments
 (0)