Skip to content

Commit b5c29b3

Browse files
bors[bot]Dirbaio
andauthored
Merge #347
347: async: add SPI r=eldruin a=Dirbaio This is an exact mirror of the blocking SPI trait (including the proposed changes in #351 ), except the following differences: - `W: 'static` is required everywhere, otherwise complicated lifetime bounds are required in the future GATs. Co-authored-by: Dario Nieuwenhuis <[email protected]>
2 parents c2830f1 + 62b177c commit b5c29b3

File tree

3 files changed

+337
-11
lines changed

3 files changed

+337
-11
lines changed

embedded-hal-async/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#![deny(missing_docs)]
1111
#![no_std]
1212
#![feature(generic_associated_types)]
13+
#![feature(type_alias_impl_trait)]
1314

1415
pub mod delay;
1516
pub mod digital;
1617
pub mod i2c;
18+
pub mod spi;

embedded-hal-async/src/spi.rs

+324
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
//! Serial Peripheral Interface
2+
3+
use core::{fmt::Debug, future::Future};
4+
5+
pub use embedded_hal::spi::{
6+
Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
7+
};
8+
use embedded_hal::{digital::blocking::OutputPin, spi::blocking};
9+
10+
/// SPI device trait
11+
///
12+
/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
13+
/// with a CS (Chip Select) pin.
14+
///
15+
/// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for important information on SPI Bus vs Device traits.
16+
pub trait SpiDevice: ErrorType {
17+
/// SPI Bus type for this device.
18+
type Bus: ErrorType;
19+
20+
/// Future returned by the `transaction` method.
21+
type TransactionFuture<'a, R, F, Fut>: Future<Output = Result<R, Self::Error>> + 'a
22+
where
23+
Self: 'a,
24+
R: 'a,
25+
F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
26+
Fut: Future<
27+
Output = (
28+
&'a mut Self::Bus,
29+
Result<R, <Self::Bus as ErrorType>::Error>,
30+
),
31+
> + 'a;
32+
33+
/// Perform a transaction against the device.
34+
///
35+
/// - Locks the bus
36+
/// - Asserts the CS (Chip Select) pin.
37+
/// - Calls `f` with an exclusive reference to the bus, which can then be used to do transfers against the device.
38+
/// - [Flushes](SpiBusFlush::flush) the bus.
39+
/// - Deasserts the CS pin.
40+
/// - Unlocks the bus.
41+
///
42+
/// The locking mechanism is implementation-defined. The only requirement is it must prevent two
43+
/// transactions from executing concurrently against the same bus. Examples of implementations are:
44+
/// critical sections, blocking mutexes, async mutexes, returning an error or panicking if the bus is already busy.
45+
fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut>
46+
where
47+
F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
48+
Fut: Future<
49+
Output = (
50+
&'a mut Self::Bus,
51+
Result<R, <Self::Bus as ErrorType>::Error>,
52+
),
53+
> + 'a;
54+
}
55+
56+
impl<T: SpiDevice> SpiDevice for &mut T {
57+
type Bus = T::Bus;
58+
59+
type TransactionFuture<'a, R, F, Fut> = T::TransactionFuture<'a, R, F, Fut>
60+
where
61+
Self: 'a, R: 'a, F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
62+
Fut: Future<Output = (&'a mut Self::Bus, Result<R, <Self::Bus as ErrorType>::Error>)> + 'a;
63+
64+
fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut>
65+
where
66+
F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
67+
Fut: Future<
68+
Output = (
69+
&'a mut Self::Bus,
70+
Result<R, <Self::Bus as ErrorType>::Error>,
71+
),
72+
> + 'a,
73+
{
74+
T::transaction(self, f)
75+
}
76+
}
77+
78+
/// Flush support for SPI bus
79+
pub trait SpiBusFlush: ErrorType {
80+
/// Future returned by the `flush` method.
81+
type FlushFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
82+
where
83+
Self: 'a;
84+
85+
/// Wait until all operations have completed and the bus is idle.
86+
///
87+
/// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for information on flushing.
88+
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a>;
89+
}
90+
91+
impl<T: SpiBusFlush> SpiBusFlush for &mut T {
92+
type FlushFuture<'a> = T::FlushFuture<'a> where Self: 'a;
93+
94+
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
95+
T::flush(self)
96+
}
97+
}
98+
99+
/// Read-only SPI bus
100+
pub trait SpiBusRead<Word: 'static + Copy = u8>: SpiBusFlush {
101+
/// Future returned by the `read` method.
102+
type ReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
103+
where
104+
Self: 'a;
105+
106+
/// Read `words` from the slave.
107+
///
108+
/// The word value sent on MOSI during reading is implementation-defined,
109+
/// typically `0x00`, `0xFF`, or configurable.
110+
///
111+
/// Implementations are allowed to return before the operation is
112+
/// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing.
113+
fn read<'a>(&'a mut self, words: &'a mut [Word]) -> Self::ReadFuture<'a>;
114+
}
115+
116+
impl<T: SpiBusRead<Word>, Word: 'static + Copy> SpiBusRead<Word> for &mut T {
117+
type ReadFuture<'a> = T::ReadFuture<'a> where Self: 'a;
118+
119+
fn read<'a>(&'a mut self, words: &'a mut [Word]) -> Self::ReadFuture<'a> {
120+
T::read(self, words)
121+
}
122+
}
123+
124+
/// Write-only SPI
125+
pub trait SpiBusWrite<Word: 'static + Copy = u8>: SpiBusFlush {
126+
/// Future returned by the `write` method.
127+
type WriteFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
128+
where
129+
Self: 'a;
130+
131+
/// Write `words` to the slave, ignoring all the incoming words
132+
///
133+
/// Implementations are allowed to return before the operation is
134+
/// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing.
135+
fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a>;
136+
}
137+
138+
impl<T: SpiBusWrite<Word>, Word: 'static + Copy> SpiBusWrite<Word> for &mut T {
139+
type WriteFuture<'a> = T::WriteFuture<'a> where Self: 'a;
140+
141+
fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a> {
142+
T::write(self, words)
143+
}
144+
}
145+
146+
/// Read-write SPI bus
147+
///
148+
/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins.
149+
///
150+
/// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for important information on SPI Bus vs Device traits.
151+
pub trait SpiBus<Word: 'static + Copy = u8>: SpiBusRead<Word> + SpiBusWrite<Word> {
152+
/// Future returned by the `transfer` method.
153+
type TransferFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
154+
where
155+
Self: 'a;
156+
157+
/// Write and read simultaneously. `write` is written to the slave on MOSI and
158+
/// words received on MISO are stored in `read`.
159+
///
160+
/// It is allowed for `read` and `write` to have different lengths, even zero length.
161+
/// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter,
162+
/// incoming words after `read` has been filled will be discarded. If `write` is shorter,
163+
/// the value of words sent in MOSI after all `write` has been sent is implementation-defined,
164+
/// typically `0x00`, `0xFF`, or configurable.
165+
///
166+
/// Implementations are allowed to return before the operation is
167+
/// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing.
168+
fn transfer<'a>(
169+
&'a mut self,
170+
read: &'a mut [Word],
171+
write: &'a [Word],
172+
) -> Self::TransferFuture<'a>;
173+
174+
/// Future returned by the `transfer_in_place` method.
175+
type TransferInPlaceFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
176+
where
177+
Self: 'a;
178+
179+
/// Write and read simultaneously. The contents of `words` are
180+
/// written to the slave, and the received words are stored into the same
181+
/// `words` buffer, overwriting it.
182+
///
183+
/// Implementations are allowed to return before the operation is
184+
/// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing.
185+
fn transfer_in_place<'a>(
186+
&'a mut self,
187+
words: &'a mut [Word],
188+
) -> Self::TransferInPlaceFuture<'a>;
189+
}
190+
191+
impl<T: SpiBus<Word>, Word: 'static + Copy> SpiBus<Word> for &mut T {
192+
type TransferFuture<'a> = T::TransferFuture<'a> where Self: 'a;
193+
194+
fn transfer<'a>(
195+
&'a mut self,
196+
read: &'a mut [Word],
197+
write: &'a [Word],
198+
) -> Self::TransferFuture<'a> {
199+
T::transfer(self, read, write)
200+
}
201+
202+
type TransferInPlaceFuture<'a> = T::TransferInPlaceFuture<'a> where Self: 'a;
203+
204+
fn transfer_in_place<'a>(
205+
&'a mut self,
206+
words: &'a mut [Word],
207+
) -> Self::TransferInPlaceFuture<'a> {
208+
T::transfer_in_place(self, words)
209+
}
210+
}
211+
212+
/// Error type for [`ExclusiveDevice`] operations.
213+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
214+
pub enum ExclusiveDeviceError<BUS, CS> {
215+
/// An inner SPI bus operation failed
216+
Spi(BUS),
217+
/// Asserting or deasserting CS failed
218+
Cs(CS),
219+
}
220+
221+
impl<BUS, CS> Error for ExclusiveDeviceError<BUS, CS>
222+
where
223+
BUS: Error + Debug,
224+
CS: Debug,
225+
{
226+
fn kind(&self) -> ErrorKind {
227+
match self {
228+
Self::Spi(e) => e.kind(),
229+
Self::Cs(_) => ErrorKind::ChipSelectFault,
230+
}
231+
}
232+
}
233+
234+
/// [`SpiDevice`] implementation with exclusive access to the bus (not shared).
235+
///
236+
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
237+
/// ideal for when no sharing is required (only one SPI device is present on the bus).
238+
pub struct ExclusiveDevice<BUS, CS> {
239+
bus: BUS,
240+
cs: CS,
241+
}
242+
243+
impl<BUS, CS> ExclusiveDevice<BUS, CS> {
244+
/// Create a new ExclusiveDevice
245+
pub fn new(bus: BUS, cs: CS) -> Self {
246+
Self { bus, cs }
247+
}
248+
}
249+
250+
impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS>
251+
where
252+
BUS: ErrorType,
253+
CS: OutputPin,
254+
{
255+
type Error = ExclusiveDeviceError<BUS::Error, CS::Error>;
256+
}
257+
258+
impl<BUS, CS> blocking::SpiDevice for ExclusiveDevice<BUS, CS>
259+
where
260+
BUS: blocking::SpiBusFlush,
261+
CS: OutputPin,
262+
{
263+
type Bus = BUS;
264+
265+
fn transaction<R>(
266+
&mut self,
267+
f: impl FnOnce(&mut Self::Bus) -> Result<R, <Self::Bus as ErrorType>::Error>,
268+
) -> Result<R, Self::Error> {
269+
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
270+
271+
let f_res = f(&mut self.bus);
272+
273+
// On failure, it's important to still flush and deassert CS.
274+
let flush_res = self.bus.flush();
275+
let cs_res = self.cs.set_high();
276+
277+
let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?;
278+
flush_res.map_err(ExclusiveDeviceError::Spi)?;
279+
cs_res.map_err(ExclusiveDeviceError::Cs)?;
280+
281+
Ok(f_res)
282+
}
283+
}
284+
285+
impl<BUS, CS> SpiDevice for ExclusiveDevice<BUS, CS>
286+
where
287+
BUS: SpiBusFlush,
288+
CS: OutputPin,
289+
{
290+
type Bus = BUS;
291+
292+
type TransactionFuture<'a, R, F, Fut> = impl Future<Output = Result<R, Self::Error>> + 'a
293+
where
294+
Self: 'a, R: 'a, F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
295+
Fut: Future<Output = (&'a mut Self::Bus, Result<R, <Self::Bus as ErrorType>::Error>)> + 'a;
296+
297+
fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut>
298+
where
299+
R: 'a,
300+
F: FnOnce(&'a mut Self::Bus) -> Fut + 'a,
301+
Fut: Future<
302+
Output = (
303+
&'a mut Self::Bus,
304+
Result<R, <Self::Bus as ErrorType>::Error>,
305+
),
306+
> + 'a,
307+
{
308+
async move {
309+
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
310+
311+
let (bus, f_res) = f(&mut self.bus).await;
312+
313+
// On failure, it's important to still flush and deassert CS.
314+
let flush_res = bus.flush().await;
315+
let cs_res = self.cs.set_high();
316+
317+
let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?;
318+
flush_res.map_err(ExclusiveDeviceError::Spi)?;
319+
cs_res.map_err(ExclusiveDeviceError::Cs)?;
320+
321+
Ok(f_res)
322+
}
323+
}
324+
}

0 commit comments

Comments
 (0)