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}