Skip to content

Commit 0f632e4

Browse files
committed
bus/spi: add RefCell, CriticalSection and Mutex shared bus implementations.
1 parent d3836ad commit 0f632e4

File tree

7 files changed

+536
-163
lines changed

7 files changed

+536
-163
lines changed

embedded-hal-bus/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Added
1111
- i2c: add bus sharing implementations.
12+
- spi: add bus sharing implementations.
1213

1314
## [v0.1.0-alpha.1] - 2022-09-28
1415

embedded-hal-bus/src/spi.rs

-163
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use core::cell::RefCell;
2+
use critical_section::Mutex;
3+
use embedded_hal::digital::OutputPin;
4+
use embedded_hal::spi::{
5+
ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite,
6+
};
7+
8+
use super::DeviceError;
9+
10+
/// `critical-section`-based shared bus [`SpiDevice`] implementation.
11+
///
12+
/// This allows for sharing an [`SpiBus`](embedded_hal::spi::blocking::SpiBus), obtaining multiple [`SpiDevice`] instances,
13+
/// each with its own `CS` pin.
14+
///
15+
/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for
16+
/// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels).
17+
/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely
18+
/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using
19+
/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections.
20+
pub struct CriticalSectionDevice<'a, BUS, CS> {
21+
bus: &'a Mutex<RefCell<BUS>>,
22+
cs: CS,
23+
}
24+
25+
impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> {
26+
/// Create a new ExclusiveDevice
27+
pub fn new(bus: &'a Mutex<RefCell<BUS>>, cs: CS) -> Self {
28+
Self { bus, cs }
29+
}
30+
}
31+
32+
impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS>
33+
where
34+
BUS: ErrorType,
35+
CS: OutputPin,
36+
{
37+
type Error = DeviceError<BUS::Error, CS::Error>;
38+
}
39+
40+
impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead<Word> for CriticalSectionDevice<'a, BUS, CS>
41+
where
42+
BUS: SpiBusRead<Word>,
43+
CS: OutputPin,
44+
{
45+
fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> {
46+
critical_section::with(|cs| {
47+
let bus = &mut *self.bus.borrow_ref_mut(cs);
48+
49+
self.cs.set_low().map_err(DeviceError::Cs)?;
50+
51+
let mut op_res = Ok(());
52+
for buf in operations {
53+
if let Err(e) = bus.read(buf) {
54+
op_res = Err(e);
55+
break;
56+
}
57+
}
58+
59+
// On failure, it's important to still flush and deassert CS.
60+
let flush_res = bus.flush();
61+
let cs_res = self.cs.set_high();
62+
63+
op_res.map_err(DeviceError::Spi)?;
64+
flush_res.map_err(DeviceError::Spi)?;
65+
cs_res.map_err(DeviceError::Cs)?;
66+
67+
Ok(())
68+
})
69+
}
70+
}
71+
72+
impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite<Word> for CriticalSectionDevice<'a, BUS, CS>
73+
where
74+
BUS: SpiBusWrite<Word>,
75+
CS: OutputPin,
76+
{
77+
fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> {
78+
critical_section::with(|cs| {
79+
let bus = &mut *self.bus.borrow_ref_mut(cs);
80+
81+
self.cs.set_low().map_err(DeviceError::Cs)?;
82+
83+
let mut op_res = Ok(());
84+
for buf in operations {
85+
if let Err(e) = bus.write(buf) {
86+
op_res = Err(e);
87+
break;
88+
}
89+
}
90+
91+
// On failure, it's important to still flush and deassert CS.
92+
let flush_res = bus.flush();
93+
let cs_res = self.cs.set_high();
94+
95+
op_res.map_err(DeviceError::Spi)?;
96+
flush_res.map_err(DeviceError::Spi)?;
97+
cs_res.map_err(DeviceError::Cs)?;
98+
99+
Ok(())
100+
})
101+
}
102+
}
103+
104+
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for CriticalSectionDevice<'a, BUS, CS>
105+
where
106+
BUS: SpiBus<Word>,
107+
CS: OutputPin,
108+
{
109+
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
110+
critical_section::with(|cs| {
111+
let bus = &mut *self.bus.borrow_ref_mut(cs);
112+
113+
self.cs.set_low().map_err(DeviceError::Cs)?;
114+
115+
let op_res = operations.iter_mut().try_for_each(|op| match op {
116+
Operation::Read(buf) => bus.read(buf),
117+
Operation::Write(buf) => bus.write(buf),
118+
Operation::Transfer(read, write) => bus.transfer(read, write),
119+
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
120+
});
121+
122+
// On failure, it's important to still flush and deassert CS.
123+
let flush_res = bus.flush();
124+
let cs_res = self.cs.set_high();
125+
126+
op_res.map_err(DeviceError::Spi)?;
127+
flush_res.map_err(DeviceError::Spi)?;
128+
cs_res.map_err(DeviceError::Cs)?;
129+
130+
Ok(())
131+
})
132+
}
133+
}

0 commit comments

Comments
 (0)