embedded_hal_bus/spi/mutex.rs
1use embedded_hal::delay::DelayNs;
2use embedded_hal::digital::OutputPin;
3use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
4use std::sync::Mutex;
5
6use super::DeviceError;
7use crate::spi::shared::transaction;
8
9/// `std` `Mutex`-based shared bus [`SpiDevice`] implementation.
10///
11/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances,
12/// each with its own `CS` pin.
13///
14/// Sharing is implemented with a `std` [`Mutex`]. It allows a single bus across multiple threads,
15/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is
16/// it is only available in `std` targets.
17#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
18pub struct MutexDevice<'a, BUS, CS, D> {
19 bus: &'a Mutex<BUS>,
20 cs: CS,
21 delay: D,
22}
23
24impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> {
25 /// Create a new [`MutexDevice`].
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<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
39impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> {
40 /// Create a new [`MutexDevice`] 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<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
73impl<BUS, CS, D> ErrorType for MutexDevice<'_, BUS, CS, D>
74where
75 BUS: ErrorType,
76 CS: OutputPin,
77{
78 type Error = DeviceError<BUS::Error, CS::Error>;
79}
80
81impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for MutexDevice<'_, BUS, CS, D>
82where
83 BUS: SpiBus<Word>,
84 CS: OutputPin,
85 D: DelayNs,
86{
87 #[inline]
88 fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
89 let bus = &mut *self.bus.lock().unwrap();
90
91 transaction(operations, bus, &mut self.delay, &mut self.cs)
92 }
93}