embedded_hal_bus/spi/
refcell.rs

1use core::cell::RefCell;
2use embedded_hal::delay::DelayNs;
3use embedded_hal::digital::OutputPin;
4use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
5
6use super::DeviceError;
7use crate::spi::shared::transaction;
8
9/// `RefCell`-based shared bus [`SpiDevice`] implementation.
10///
11/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances,
12/// each with its own `CS` pin.
13///
14/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`,
15/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several
16/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead.
17pub struct RefCellDevice<'a, BUS, CS, D> {
18    bus: &'a RefCell<BUS>,
19    cs: CS,
20    delay: D,
21}
22
23impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> {
24    /// Create a new [`RefCellDevice`].
25    #[inline]
26    pub fn new(bus: &'a RefCell<BUS>, cs: CS, delay: D) -> Self {
27        Self { bus, cs, delay }
28    }
29}
30
31impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> {
32    /// Create a new [`RefCellDevice`] without support for in-transaction delays.
33    ///
34    /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
35    /// contract, which mandates delay support. It is relatively rare for drivers to use
36    /// in-transaction delays, so you might still want to use this method because it's more practical.
37    ///
38    /// Note that a future version of the driver might start using delays, causing your
39    /// code to panic. This wouldn't be considered a breaking change from the driver side, because
40    /// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
41    /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
42    /// the driver crate, you might want to pin the driver's version.
43    ///
44    /// # Panics
45    ///
46    /// The returned device will panic if you try to execute a transaction
47    /// that contains any operations of type [`Operation::DelayNs`].
48    #[inline]
49    pub fn new_no_delay(bus: &'a RefCell<BUS>, cs: CS) -> Self {
50        Self {
51            bus,
52            cs,
53            delay: super::NoDelay,
54        }
55    }
56}
57
58impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D>
59where
60    BUS: ErrorType,
61    CS: OutputPin,
62{
63    type Error = DeviceError<BUS::Error, CS::Error>;
64}
65
66impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for RefCellDevice<'a, BUS, CS, D>
67where
68    BUS: SpiBus<Word>,
69    CS: OutputPin,
70    D: DelayNs,
71{
72    #[inline]
73    fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
74        let bus = &mut *self.bus.borrow_mut();
75
76        transaction(operations, bus, &mut self.delay, &mut self.cs)
77    }
78}