embedded_hal_bus::spi

Struct AtomicDevice

source
pub struct AtomicDevice<'a, BUS, CS, D> { /* private fields */ }
Expand description

Atomics-based shared bus SpiDevice implementation.

This allows for sharing an SpiBus, obtaining multiple SpiDevice instances, each with its own CS pin.

Sharing is implemented with a AtomicDevice, which consists of an UnsafeCell and an AtomicBool “locked” flag. This means it has low overhead, like RefCellDevice. Aditionally, it is Send, which allows sharing a single bus across multiple threads (interrupt priority level), like CriticalSectionDevice, while not using critical sections and therefore impacting real-time performance less.

The downside is using a simple AtomicBool for locking doesn’t prevent two threads from trying to lock it at once. For example, the main thread can be doing a SPI transaction, and an interrupt fires and tries to do another. In this case, a Busy error is returned that must be handled somehow, usually dropping the data or trying again later.

Note that retrying in a loop on Busy errors usually leads to deadlocks. In the above example, it’ll prevent the interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If this is an issue for your use case, you most likely should use CriticalSectionDevice instead.

This primitive is particularly well-suited for applications that have external arbitration rules that prevent Busy errors in the first place, such as the RTIC framework.

Implementations§

source§

impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D>

source

pub fn new( bus: &'a AtomicCell<BUS>, cs: CS, delay: D, ) -> Result<Self, CS::Error>
where CS: OutputPin,

Create a new AtomicDevice.

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.

source§

impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, NoDelay>
where BUS: ErrorType, CS: OutputPin,

source

pub fn new_no_delay(bus: &'a AtomicCell<BUS>, cs: CS) -> Result<Self, CS::Error>
where CS: OutputPin,

Create a new AtomicDevice 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.

Trait Implementations§

source§

impl<'a, BUS, CS, D> ErrorType for AtomicDevice<'a, BUS, CS, D>
where BUS: ErrorType, CS: OutputPin,

source§

type Error = AtomicError<DeviceError<<BUS as ErrorType>::Error, <CS as ErrorType>::Error>>

Error type.
source§

impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for AtomicDevice<'a, BUS, CS, D>
where BUS: SpiBus<Word>, CS: OutputPin, D: DelayNs,

source§

fn transaction( &mut self, operations: &mut [Operation<'_, Word>], ) -> Result<(), Self::Error>

Perform a transaction against the device. Read more
source§

fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error>

Do a read within a transaction. Read more
source§

fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error>

Do a write within a transaction. Read more
source§

fn transfer( &mut self, read: &mut [Word], write: &[Word], ) -> Result<(), Self::Error>

Do a transfer within a transaction. Read more
source§

fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error>

Do an in-place transfer within a transaction. Read more

Auto Trait Implementations§

§

impl<'a, BUS, CS, D> Freeze for AtomicDevice<'a, BUS, CS, D>
where CS: Freeze, D: Freeze,

§

impl<'a, BUS, CS, D> !RefUnwindSafe for AtomicDevice<'a, BUS, CS, D>

§

impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D>
where CS: Send, D: Send, BUS: Send,

§

impl<'a, BUS, CS, D> Sync for AtomicDevice<'a, BUS, CS, D>
where CS: Sync, D: Sync, BUS: Send,

§

impl<'a, BUS, CS, D> Unpin for AtomicDevice<'a, BUS, CS, D>
where CS: Unpin, D: Unpin,

§

impl<'a, BUS, CS, D> !UnwindSafe for AtomicDevice<'a, BUS, CS, D>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.