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 ///
26 /// This sets the `cs` pin high, and returns an error if that fails. It is recommended
27 /// to set the pin high the moment it's configured as an output, to avoid glitches.
28 #[inline]
29 pub fn new(bus: &'a RefCell<BUS>, mut cs: CS, delay: D) -> Result<Self, CS::Error>
30 where
31 CS: OutputPin,
32 {
33 cs.set_high()?;
34 Ok(Self { bus, cs, delay })
35 }
36}
37
38impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> {
39 /// Create a new [`RefCellDevice`] without support for in-transaction delays.
40 ///
41 /// This sets the `cs` pin high, and returns an error if that fails. It is recommended
42 /// to set the pin high the moment it's configured as an output, to avoid glitches.
43 ///
44 /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice`
45 /// contract, which mandates delay support. It is relatively rare for drivers to use
46 /// in-transaction delays, so you might still want to use this method because it's more practical.
47 ///
48 /// Note that a future version of the driver might start using delays, causing your
49 /// code to panic. This wouldn't be considered a breaking change from the driver side, because
50 /// drivers are allowed to assume `SpiDevice` implementations comply with the contract.
51 /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade
52 /// the driver crate, you might want to pin the driver's version.
53 ///
54 /// # Panics
55 ///
56 /// The returned device will panic if you try to execute a transaction
57 /// that contains any operations of type [`Operation::DelayNs`].
58 #[inline]
59 pub fn new_no_delay(bus: &'a RefCell<BUS>, mut cs: CS) -> Result<Self, CS::Error>
60 where
61 CS: OutputPin,
62 {
63 cs.set_high()?;
64 Ok(Self {
65 bus,
66 cs,
67 delay: super::NoDelay,
68 })
69 }
70}
71
72impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D>
73where
74 BUS: ErrorType,
75 CS: OutputPin,
76{
77 type Error = DeviceError<BUS::Error, CS::Error>;
78}
79
80impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for RefCellDevice<'a, BUS, CS, D>
81where
82 BUS: SpiBus<Word>,
83 CS: OutputPin,
84 D: DelayNs,
85{
86 #[inline]
87 fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
88 let bus = &mut *self.bus.borrow_mut();
89
90 transaction(operations, bus, &mut self.delay, &mut self.cs)
91 }
92}