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    ///
28    /// This sets the `cs` pin high, and returns an error if that fails. It is recommended
29    /// to set the pin high the moment it's configured as an output, to avoid glitches.
30    #[inline]
31    pub fn new(bus: BUS, mut cs: CS, delay: D) -> Result<Self, CS::Error>
32    where
33        CS: OutputPin,
34    {
35        cs.set_high()?;
36        Ok(Self { bus, cs, delay })
37    }
38
39    /// Returns a reference to the underlying bus object.
40    #[inline]
41    pub fn bus(&self) -> &BUS {
42        &self.bus
43    }
44
45    /// Returns a mutable reference to the underlying bus object.
46    #[inline]
47    pub fn bus_mut(&mut self) -> &mut BUS {
48        &mut self.bus
49    }
50}
51
52impl<BUS, CS> ExclusiveDevice<BUS, CS, super::NoDelay> {
53    /// Create a new [`ExclusiveDevice`] without support for in-transaction delays.
54    ///
55    /// This sets the `cs` pin high, and returns an error if that fails. It is recommended
56    /// to set the pin high the moment it's configured as an output, to avoid glitches.
57    ///
58    /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
59    /// contract, which mandates delay support. It is relatively rare for drivers to use
60    /// in-transaction delays, so you might still want to use this method because it's more practical.
61    ///
62    /// Note that a future version of the driver might start using delays, causing your
63    /// code to panic. This wouldn't be considered a breaking change from the driver side, because
64    /// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
65    /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
66    /// the driver crate, you might want to pin the driver's version.
67    ///
68    /// # Panics
69    ///
70    /// The returned device will panic if you try to execute a transaction
71    /// that contains any operations of type [`Operation::DelayNs`].
72    #[inline]
73    pub fn new_no_delay(bus: BUS, mut cs: CS) -> Result<Self, CS::Error>
74    where
75        CS: OutputPin,
76    {
77        cs.set_high()?;
78        Ok(Self {
79            bus,
80            cs,
81            delay: super::NoDelay,
82        })
83    }
84}
85
86impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
87where
88    BUS: ErrorType,
89    CS: OutputPin,
90{
91    type Error = DeviceError<BUS::Error, CS::Error>;
92}
93
94impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
95where
96    BUS: SpiBus<Word>,
97    CS: OutputPin,
98    D: DelayNs,
99{
100    #[inline]
101    fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
102        transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs)
103    }
104}
105
106#[cfg(feature = "async")]
107#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
108impl<Word: Copy + 'static, BUS, CS, D> AsyncSpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
109where
110    BUS: AsyncSpiBus<Word>,
111    CS: OutputPin,
112    D: AsyncDelayNs,
113{
114    #[inline]
115    async fn transaction(
116        &mut self,
117        operations: &mut [Operation<'_, Word>],
118    ) -> Result<(), Self::Error> {
119        self.cs.set_low().map_err(DeviceError::Cs)?;
120
121        let op_res = 'ops: {
122            for op in operations {
123                let res = match op {
124                    Operation::Read(buf) => self.bus.read(buf).await,
125                    Operation::Write(buf) => self.bus.write(buf).await,
126                    Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
127                    Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
128                    Operation::DelayNs(ns) => match self.bus.flush().await {
129                        Err(e) => Err(e),
130                        Ok(()) => {
131                            self.delay.delay_ns(*ns).await;
132                            Ok(())
133                        }
134                    },
135                };
136                if let Err(e) = res {
137                    break 'ops Err(e);
138                }
139            }
140            Ok(())
141        };
142
143        // On failure, it's important to still flush and deassert CS.
144        let flush_res = self.bus.flush().await;
145        let cs_res = self.cs.set_high();
146
147        op_res.map_err(DeviceError::Spi)?;
148        flush_res.map_err(DeviceError::Spi)?;
149        cs_res.map_err(DeviceError::Cs)?;
150
151        Ok(())
152    }
153}