Skip to content

Commit ad66fa9

Browse files
committed
async: add SPI.
1 parent c80e9d9 commit ad66fa9

File tree

2 files changed

+311
-0
lines changed

2 files changed

+311
-0
lines changed

embedded-hal-async/src/lib.rs

Lines changed: 2 additions & 0 deletions
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

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

0 commit comments

Comments
 (0)