1//! SPI bus sharing mechanisms.
23use 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};
1112use super::shared::transaction;
13use super::DeviceError;
1415/// [`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}
2425impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
26/// Create a new [`ExclusiveDevice`].
27#[inline]
28pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
29Self { bus, cs, delay }
30 }
3132/// Returns a reference to the underlying bus object.
33#[inline]
34pub fn bus(&self) -> &BUS {
35&self.bus
36 }
3738/// Returns a mutable reference to the underlying bus object.
39#[inline]
40pub fn bus_mut(&mut self) -> &mut BUS {
41&mut self.bus
42 }
43}
4445impl<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]
63pub fn new_no_delay(bus: BUS, cs: CS) -> Self {
64Self {
65 bus,
66 cs,
67 delay: super::NoDelay,
68 }
69 }
70}
7172impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
73where
74BUS: ErrorType,
75 CS: OutputPin,
76{
77type Error = DeviceError<BUS::Error, CS::Error>;
78}
7980impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
81where
82BUS: SpiBus<Word>,
83 CS: OutputPin,
84 D: DelayNs,
85{
86#[inline]
87fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
88 transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs)
89 }
90}
9192#[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
96BUS: AsyncSpiBus<Word>,
97 CS: OutputPin,
98 D: AsyncDelayNs,
99{
100#[inline]
101async fn transaction(
102&mut self,
103 operations: &mut [Operation<'_, Word>],
104 ) -> Result<(), Self::Error> {
105self.cs.set_low().map_err(DeviceError::Cs)?;
106107let op_res = 'ops: {
108for op in operations {
109let 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 {
115Err(e) => Err(e),
116Ok(()) => {
117self.delay.delay_ns(*ns).await;
118Ok(())
119 }
120 },
121 };
122if let Err(e) = res {
123break 'ops Err(e);
124 }
125 }
126Ok(())
127 };
128129// On failure, it's important to still flush and deassert CS.
130let flush_res = self.bus.flush().await;
131let cs_res = self.cs.set_high();
132133 op_res.map_err(DeviceError::Spi)?;
134 flush_res.map_err(DeviceError::Spi)?;
135 cs_res.map_err(DeviceError::Cs)?;
136137Ok(())
138 }
139}