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 ///
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]
31pub fn new(bus: BUS, mut cs: CS, delay: D) -> Result<Self, CS::Error>
32where
33CS: OutputPin,
34 {
35 cs.set_high()?;
36Ok(Self { bus, cs, delay })
37 }
3839/// Returns a reference to the underlying bus object.
40#[inline]
41pub fn bus(&self) -> &BUS {
42&self.bus
43 }
4445/// Returns a mutable reference to the underlying bus object.
46#[inline]
47pub fn bus_mut(&mut self) -> &mut BUS {
48&mut self.bus
49 }
50}
5152impl<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]
73pub fn new_no_delay(bus: BUS, mut cs: CS) -> Result<Self, CS::Error>
74where
75CS: OutputPin,
76 {
77 cs.set_high()?;
78Ok(Self {
79 bus,
80 cs,
81 delay: super::NoDelay,
82 })
83 }
84}
8586impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
87where
88BUS: ErrorType,
89 CS: OutputPin,
90{
91type Error = DeviceError<BUS::Error, CS::Error>;
92}
9394impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
95where
96BUS: SpiBus<Word>,
97 CS: OutputPin,
98 D: DelayNs,
99{
100#[inline]
101fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
102 transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs)
103 }
104}
105106#[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
110BUS: AsyncSpiBus<Word>,
111 CS: OutputPin,
112 D: AsyncDelayNs,
113{
114#[inline]
115async fn transaction(
116&mut self,
117 operations: &mut [Operation<'_, Word>],
118 ) -> Result<(), Self::Error> {
119self.cs.set_low().map_err(DeviceError::Cs)?;
120121let op_res = 'ops: {
122for op in operations {
123let 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 {
129Err(e) => Err(e),
130Ok(()) => {
131self.delay.delay_ns(*ns).await;
132Ok(())
133 }
134 },
135 };
136if let Err(e) = res {
137break 'ops Err(e);
138 }
139 }
140Ok(())
141 };
142143// On failure, it's important to still flush and deassert CS.
144let flush_res = self.bus.flush().await;
145let cs_res = self.cs.set_high();
146147 op_res.map_err(DeviceError::Spi)?;
148 flush_res.map_err(DeviceError::Spi)?;
149 cs_res.map_err(DeviceError::Cs)?;
150151Ok(())
152 }
153}