embedded_hal_bus/spi/
exclusive.rs

1//! SPI bus sharing mechanisms.
2
3use embedded_hal::delay::DelayNs;
4use embedded_hal::digital::OutputPin;
5use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
6#[cfg(feature = "async")]
7use embedded_hal_async::{
8    delay::DelayNs as AsyncDelayNs,
9    spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice},
10};
11
12use super::shared::transaction;
13use super::DeviceError;
14
15/// [`SpiDevice`] implementation with exclusive access to the bus (not shared).
16///
17/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
18/// ideal for when no sharing is required (only one SPI device is present on the bus).
19pub struct ExclusiveDevice<BUS, CS, D> {
20    bus: BUS,
21    cs: CS,
22    delay: D,
23}
24
25impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
26    /// Create a new [`ExclusiveDevice`].
27    #[inline]
28    pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
29        Self { bus, cs, delay }
30    }
31
32    /// Returns a reference to the underlying bus object.
33    #[inline]
34    pub fn bus(&self) -> &BUS {
35        &self.bus
36    }
37
38    /// Returns a mutable reference to the underlying bus object.
39    #[inline]
40    pub fn bus_mut(&mut self) -> &mut BUS {
41        &mut self.bus
42    }
43}
44
45impl<BUS, CS> ExclusiveDevice<BUS, CS, super::NoDelay> {
46    /// Create a new [`ExclusiveDevice`] without support for in-transaction delays.
47    ///
48    /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
49    /// contract, which mandates delay support. It is relatively rare for drivers to use
50    /// in-transaction delays, so you might still want to use this method because it's more practical.
51    ///
52    /// Note that a future version of the driver might start using delays, causing your
53    /// code to panic. This wouldn't be considered a breaking change from the driver side, because
54    /// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
55    /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
56    /// the driver crate, you might want to pin the driver's version.
57    ///
58    /// # Panics
59    ///
60    /// The returned device will panic if you try to execute a transaction
61    /// that contains any operations of type [`Operation::DelayNs`].
62    #[inline]
63    pub fn new_no_delay(bus: BUS, cs: CS) -> Self {
64        Self {
65            bus,
66            cs,
67            delay: super::NoDelay,
68        }
69    }
70}
71
72impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
73where
74    BUS: ErrorType,
75    CS: OutputPin,
76{
77    type Error = DeviceError<BUS::Error, CS::Error>;
78}
79
80impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
81where
82    BUS: SpiBus<Word>,
83    CS: OutputPin,
84    D: DelayNs,
85{
86    #[inline]
87    fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
88        transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs)
89    }
90}
91
92#[cfg(feature = "async")]
93#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
94impl<Word: Copy + 'static, BUS, CS, D> AsyncSpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
95where
96    BUS: AsyncSpiBus<Word>,
97    CS: OutputPin,
98    D: AsyncDelayNs,
99{
100    #[inline]
101    async fn transaction(
102        &mut self,
103        operations: &mut [Operation<'_, Word>],
104    ) -> Result<(), Self::Error> {
105        self.cs.set_low().map_err(DeviceError::Cs)?;
106
107        let op_res = 'ops: {
108            for op in operations {
109                let res = match op {
110                    Operation::Read(buf) => self.bus.read(buf).await,
111                    Operation::Write(buf) => self.bus.write(buf).await,
112                    Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
113                    Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
114                    Operation::DelayNs(ns) => match self.bus.flush().await {
115                        Err(e) => Err(e),
116                        Ok(()) => {
117                            self.delay.delay_ns(*ns).await;
118                            Ok(())
119                        }
120                    },
121                };
122                if let Err(e) = res {
123                    break 'ops Err(e);
124                }
125            }
126            Ok(())
127        };
128
129        // On failure, it's important to still flush and deassert CS.
130        let flush_res = self.bus.flush().await;
131        let cs_res = self.cs.set_high();
132
133        op_res.map_err(DeviceError::Spi)?;
134        flush_res.map_err(DeviceError::Spi)?;
135        cs_res.map_err(DeviceError::Cs)?;
136
137        Ok(())
138    }
139}