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}