Skip to content

Commit 7288d56

Browse files
committed
async: add SPI.
1 parent a0c1ed2 commit 7288d56

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
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

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

0 commit comments

Comments
 (0)