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