pub struct AtomicDevice<'a, BUS, CS, D> { /* private fields */ }portable-atomic or target_has_atomic=8 only.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>
impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D>
Sourcepub fn new(
bus: &'a AtomicCell<BUS>,
cs: CS,
delay: D,
) -> Result<Self, CS::Error>where
CS: OutputPin,
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>
impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, NoDelay>
Sourcepub fn new_no_delay(bus: &'a AtomicCell<BUS>, cs: CS) -> Result<Self, CS::Error>where
CS: OutputPin,
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.