embedded_hal_bus/spi/
rc.rs

1extern crate alloc;
2use alloc::rc::Rc;
3
4use core::cell::RefCell;
5use embedded_hal::delay::DelayNs;
6use embedded_hal::digital::OutputPin;
7use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
8
9use super::DeviceError;
10use crate::spi::shared::transaction;
11
12/// Implementation of [`SpiDevice`] around a bus shared with `Rc<RefCell<T>>`.
13/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice), requiring allocation.
14///
15/// A single [`SpiBus`] is shared via [`RefCell`], and its ownership is handled by [`Rc`].
16/// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level).
17/// For this reason, this does not implement [`Send`].
18///
19/// When this structure is dropped, the reference count of the `Bus` instance will be decremented,
20/// and it will be cleaned up once the reference count reaches zero.
21#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
22pub struct RcDevice<Bus, Cs, Delay> {
23    bus: Rc<RefCell<Bus>>,
24    cs: Cs,
25    delay: Delay,
26}
27
28impl<Bus, Cs, Delay> RcDevice<Bus, Cs, Delay> {
29    /// Creates a new [`RcDevice`].
30    ///
31    /// This sets the `cs` pin high, and returns an error if that fails.
32    /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches.
33    ///
34    /// This function does not increment the reference count:
35    /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc<RefCell<Bus>>`.
36    #[inline]
37    pub fn new(bus: Rc<RefCell<Bus>>, mut cs: Cs, delay: Delay) -> Result<Self, Cs::Error>
38    where
39        Cs: OutputPin,
40    {
41        cs.set_high()?;
42
43        Ok(Self { bus, cs, delay })
44    }
45}
46
47impl<Bus, Cs> RcDevice<Bus, Cs, super::NoDelay> {
48    /// Creates a new [`RcDevice`] without support for in-transaction delays.
49    ///
50    /// **Warning**: It's advised to prefer [`RcDevice::new`],
51    /// as the contract of [`SpiDevice`] requests support for in-transaction delays.
52    ///
53    /// Refer to [`RefCellDevice::new_no_delay`](super::RefCellDevice::new_no_delay) for more information.
54    #[inline]
55    pub fn new_no_delay(bus: Rc<RefCell<Bus>>, mut cs: Cs) -> Result<Self, Cs::Error>
56    where
57        Cs: OutputPin,
58    {
59        cs.set_high()?;
60
61        Ok(Self {
62            bus,
63            cs,
64            delay: super::NoDelay,
65        })
66    }
67}
68
69impl<Bus, Cs, Delay> ErrorType for RcDevice<Bus, Cs, Delay>
70where
71    Bus: ErrorType,
72    Cs: OutputPin,
73{
74    type Error = DeviceError<Bus::Error, Cs::Error>;
75}
76
77impl<Word, Bus, Cs, Delay> SpiDevice<Word> for RcDevice<Bus, Cs, Delay>
78where
79    Word: Copy + 'static,
80    Bus: SpiBus<Word>,
81    Cs: OutputPin,
82    Delay: DelayNs,
83{
84    #[inline]
85    fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
86        let bus = &mut *self.bus.borrow_mut();
87
88        transaction(operations, bus, &mut self.delay, &mut self.cs)
89    }
90}