atsamd_hal/sercom/spi_future.rs
1//! A [`Future`]-like interface for SPI transactions
2//!
3//! An [`SpiFuture`] takes ownership of an [`Spi`] `struct` and a `[u8]`-like
4//! buffer. It then executes a full-duplex SPI transaction using iterrupts. On
5//! each `RXC` or `DRE` interrupt, the [`SpiFuture`] reads or sends `STEP` bytes
6//! of the buffer, where `STEP` is a value that depends on [`CharSize`], for
7//! SAMD11 & SAMD21 chips, or [`Length`], for SAMD51 & SAME5x chips.
8//!
9//! The provided buffer must implement [`AsRef`] and [`AsMut`] for `[u8]`, it
10//! must have an appropriate length (see below), and it must have a `'static`
11//! lifetime, i.e. it must be owned or a `&'static mut` reference.
12//!
13//! [`SpiFuture`] has extra, optional capabilities as well. It can accept a
14//! function or closure that will be called on completion of the transaction,
15//! acting like a [`Waker`]. And it can take a GPIO [`Pin`] to use as the SS
16//! line. If provided, the [`Pin`] will be set low at the beginnging of the
17//! transfer and brought high at completion.
18//!
19//! Calling [`start`] will enable the `DRE` and `RXC` interrupts and begin the
20//! transaction.
21//!
22//! ```
23//! use atsamd_hal::gpio::{Pin, PA10, PushPullOutput};
24//! use atsamd_hal::sercom::spi::AnySpi;
25//! use atsamd_hal::sercom::spi_future::SpiFuture;
26//!
27//! fn wake_up() {
28//! //...
29//! }
30//!
31//! fn use_future(spi: impl AnySpi, ss: Pin<PA10, PushPullOutput>) {
32//! let buf = [0_u8; 12];
33//! let future = SpiFuture::new(spi, buf)
34//! .with_waker(wake_up)
35//! .with_ss(ss);
36//! future.start();
37//! }
38//! ```
39//!
40//! When sending and receiving finish, the [`SpiFuture`] will automatically
41//! disable the `DRE` and `RXC` interrupts. To test whether an [`SpiFuture`] is
42//! complete, use the [`poll`] method. While the transaction is in progress, it
43//! will return [`Poll::Pending`]. When the transaction is complete, it will
44//! return [`Poll::Ready`]. Once complete, you can consume the [`SpiFuture`] and
45//! [`free`] the constituent pieces. Doing so before the transfer has completed
46//! is `unsafe`.
47//!
48//! The actual transfer is performed by the [`send`] and [`recv`] methods, which
49//! should be called from the `DRE` and `RXC` interrupt handlers, respectively.
50//!
51//! ## `STEP` size and buffer length
52//!
53//! For SAMD11 & SAMD21 chips, `STEP` is equal to the number of bytes in the
54//! corresponding the [`CharSize::Word`] type, i.e. 1 for [`EightBit`] and 2 for
55//! [`NineBit`]. For SAMD51 & SAME5x chips, `STEP` is equal to the [`Length`] or
56//! 4, whichever is less.
57//!
58//! The provided buffer must have an appropriate length. For SAMD11 & SAMD21
59//! chips, as well as SAMDx5x chips with [`Length`]` <= 4`, a single write of
60//! `STEP` bytes represents an entire SPI transaction. In these cases, the
61//! provided buffer must represent an integer number of transactions. For
62//! example, a SAMD51 [`Spi`] struct with a [`Length`] of 3 could use buffers of
63//! length 3, 6, 9, etc. For longer [`Length`] values, the provided buffer must
64//! represent exactly one SPI transaction, so the buffer length must be equal to
65//! [`Length`]. For example, a SAMD51 [`Spi`] struct with a [`Length`] of 17
66//! could only use a buffer with exactly 17 bytes.
67//!
68//! Keep in mind that [`SpiFuture`] works only with `[u8]`-like things, which
69//! can introduce some limitations.
70//!
71//! Suppose you plan to execute [`NineBit`] transactions with a SAMD21 chip.
72//! Your data may come in the form of a `[u16]` slice. However, to use it with
73//! [`SpiFuture`], you will need reformulate it as a `[u8]` slice. The easiest
74//! way to do so is probably to transmute the slice to `[u8]` or cast the
75//! reference to `&'static mut [u8]`. Both of these operations are sound,
76//! because [`u8`] has no alignment requirements.
77//!
78//! In another scenario, suppose you wanted to use a SAMx5x chip with a
79//! transaction [`Length`] of 3 bytes. Your data might come in the form of a
80//! `[u32]` slice. In this situation, it would **not** be appropriate to
81//! transmute or cast the data to a `[u8]` slice. [`SpiFuture`] expects the data
82//! to be a *byte-packed* `[u8]` slice, so the extra byte in each `u32` makes it
83//! incompatible.
84//!
85//! ## [RTIC] example
86//!
87//! The [RTIC] framework provides a convenient way to store a `static`ally
88//! allocated [`SpiFuture`], so that it can be accessed by both the interrupt
89//! handlers and user code. The following example shows how [`SpiFuture`]s might
90//! be used for a series of transactions. It was written for a SAMx5x chip, and
91//! it uses features from the latest release of [RTIC], `v0.6-alpha.0`.
92//!
93//! ```
94//! use core::task::Poll;
95//! use atsamd_hal::gpio::{PA08, PA09, PA10, PA11, Pin, PushPullOutput};
96//! use atsamd_hal::sercom::Sercom0;
97//! use atsamd_hal::sercom::pad::{IoSet1, Pad0, Pad1, Pad3};
98//! use atsamd_hal::sercom::spi::{self, Master, lengths::U12};
99//! use atsamd_hal::sercom::spi_future::SpiFuture;
100//!
101//! type Pads = spi::Pads<Sercom0, IoSet1, (Pad0, PA08), (Pad3, PA11), (Pad1, PA09)>;
102//! type SS = Pin<PA10, PushPullOutput>;
103//! type Spi = spi::Spi<spi::Config<Pads, Master, U12>>;
104//! type Future = SpiFuture<Spi, [u8; 12], SS, fn()>;
105//!
106//! //...
107//!
108//! #[resources]
109//! struct Resources {
110//! #[task_local]
111//! #[init(None)]
112//! opt_spi_ss: Option<(Spi, SS)>,
113//!
114//! #[lock_free]
115//! #[init(None)]
116//! opt_future: Option<Future>,
117//! }
118//!
119//! #[task(resources = [opt_spi_ss, opt_future])]
120//! fn task(ctx: task::Context) {
121//! let task::Context { opt_spi_ss, opt_future } = ctx;
122//! match opt_future {
123//! Some(future) => {
124//! if let Poll::Ready(_) = future.poll() {
125//! let (spi, buf, ss) = opt_future.take().unwrap().free();
126//! *opt_spi_ss = Some((spi, ss));
127//! consume_data(buf);
128//! }
129//! }
130//! None => {
131//! if let Some((spi, ss)) = opt_spi_ss.take() {
132//! let buf: [u8; 12] = produce_data();
133//! let future = opt_future.get_or_insert(
134//! SpiFuture::new(spi, buf)
135//! .with_waker(|| { task::spawn().ok(); })
136//! .with_ss(ss)
137//! );
138//! future.start();
139//! }
140//! }
141//! }
142//! }
143//!
144//! #[task(binds = SERCOM0_0, resources = [opt_future])]
145//! fn dre(ctx: dre::Context) {
146//! ctx.resources.opt_future.as_mut().unwrap().send();
147//! }
148//!
149//! #[task(binds = SERCOM0_2, resources = [opt_future])]
150//! fn rxc(ctx: rxc::Context) {
151//! ctx.resources.opt_future.as_mut().unwrap().recv();
152//! }
153//!
154//! //...
155//! ```
156//!
157//! [`start`]: SpiFuture::start
158//! [`poll`]: SpiFuture::poll
159//! [`free`]: SpiFuture::free
160//! [`send`]: SpiFuture::send
161//! [`recv`]: SpiFuture::recv
162//! [`Spi`]: super::spi::Spi
163//! [`CharSize`]: super::spi::CharSize
164//! [`CharSize::Word`]: super::spi::CharSize::Word
165//! [`EightBit`]: super::spi::EightBit
166//! [`NineBit`]: super::spi::NineBit
167//! [`Length`]: super::spi::Length
168//! [`Pin`]: crate::gpio::pin::Pin
169//! [`Future`]: core::future::Future
170//! [`Waker`]: core::task::Waker
171//! [`Poll`]: core::task::Poll
172//! [RTIC]: https://rtic.rs/
173
174use atsamd_hal_macros::hal_cfg;
175
176use core::convert::Infallible;
177use core::task::Poll;
178
179use crate::ehal_02::digital::v2::OutputPin;
180
181use crate::gpio::pin::{OptionalPin, SomePin};
182use crate::typelevel::NoneT;
183
184use super::spi::{AnySpi, Error, Flags};
185
186#[allow(unused_imports)]
187// This isn't used in the `d11` or `d21` builds, but is for `doc` and `d5x`
188use super::spi::Spi;
189
190#[hal_cfg("sercom0-d5x")]
191use {
192 super::spi::{Capability, Config, DynLength, OpMode, StaticLength, ValidConfig, ValidPads},
193 typenum::Unsigned,
194};
195
196#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
197use core::mem::size_of;
198
199#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
200type Data = u16;
201
202#[hal_cfg("sercom0-d5x")]
203type Data = u32;
204
205#[cfg(doc)]
206#[hal_cfg(not("sercom0-d5x"))]
207/// This type is not present with the selected feature set, defined for
208/// documentation only
209pub enum DynLength {}
210
211//=============================================================================
212// CheckBufLen
213//=============================================================================
214
215/// Trait used to verify the [`SpiFuture`] buffer length
216#[allow(clippy::len_without_is_empty)]
217pub trait CheckBufLen: AnySpi {
218 #[hal_cfg("sercom0-d5x")]
219 /// [`Spi`] transaction length
220 ///
221 /// This value is zero for an [`Spi`] with [`DynLength`]
222 const LEN: usize = <Self::Size as Unsigned>::USIZE;
223
224 #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
225 /// [`Spi`] transaction length
226 ///
227 /// [`Spi`]: super::spi::Spi
228 const LEN: usize = size_of::<Self::Word>();
229
230 /// Return the [`Spi`] transaction length
231 ///
232 /// In most cases, this returns the corresponding constant. For SAMx5x chips
233 /// with [`DynLength`], this returns the result of [`Spi::get_dyn_length`].
234 ///
235 /// [`Spi`]: super::spi::Spi
236 #[inline]
237 fn len(&self) -> usize {
238 Self::LEN
239 }
240
241 /// Step size through the [`SpiFuture`] buffer
242 ///
243 /// This is equal to the number of bytes sent in each write to the SPI DATA
244 /// register. It is zero for an [`Spi`] with [`DynLength`].
245 ///
246 /// [`Spi`]: super::spi::Spi
247 const STEP: usize = if Self::LEN > 4 { 4 } else { Self::LEN };
248
249 /// Return the step size through the [`SpiFuture`] buffer
250 ///
251 /// In most cases, this returns the corresponding constant. For SAMx5x chips
252 /// with [`DynLength`], this returns a result calculated from [`Self::len`].
253 #[inline]
254 fn step(&self) -> usize {
255 Self::STEP
256 }
257
258 /// Check that the buffer has a valid length
259 ///
260 /// If the transaction length is greater than four, then the size of the
261 /// buffer must equal the transaction length. If the transaction length is
262 /// less than or equal to four, then the size of the buffer must be an
263 /// integer multiple of the transaction length.
264 ///
265 /// Both of these statements apply regardless of whether the [`Spi`] has a
266 /// [`DynLength`].
267 ///
268 /// [`Spi`]: super::spi::Spi
269 #[inline]
270 fn check_buf_len(&self, buf: &impl AsRef<[u8]>) {
271 let buf_len = buf.as_ref().len();
272 let self_len = self.len();
273 if (self_len > 4 && buf_len != self_len) || (self_len <= 4 && buf_len % self_len != 0) {
274 panic!("Invalid SpiFuture buffer length");
275 }
276 }
277}
278
279#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
280impl<S: AnySpi> CheckBufLen for S {}
281
282#[hal_cfg("sercom0-d5x")]
283impl<P, M, L, A> CheckBufLen for Spi<Config<P, M, L>, A>
284where
285 Config<P, M, L>: ValidConfig,
286 P: ValidPads,
287 M: OpMode,
288 L: StaticLength,
289 A: Capability,
290{
291}
292
293#[hal_cfg("sercom0-d5x")]
294impl<P, M, A> CheckBufLen for Spi<Config<P, M, DynLength>, A>
295where
296 Config<P, M, DynLength>: ValidConfig,
297 P: ValidPads,
298 M: OpMode,
299 A: Capability,
300{
301 #[inline]
302 fn len(&self) -> usize {
303 self.get_dyn_length() as usize
304 }
305
306 #[inline]
307 fn step(&self) -> usize {
308 let len = self.len();
309 if len > 4 {
310 4
311 } else {
312 len
313 }
314 }
315}
316
317//=============================================================================
318// ControlSS
319//=============================================================================
320
321/// Trait used to control the SS line during an [`SpiFuture`] transaction
322pub trait ControlSS: OptionalPin {
323 /// If an SS pin is present, assert it by bringing it low
324 fn assert(&mut self);
325
326 /// If an SS pin is present, deassert it by bringing it high
327 fn deassert(&mut self);
328}
329
330impl<P> ControlSS for P
331where
332 P: SomePin + OutputPin<Error = Infallible>,
333{
334 #[inline]
335 fn assert(&mut self) {
336 self.set_low().unwrap();
337 }
338
339 #[inline]
340 fn deassert(&mut self) {
341 self.set_high().unwrap();
342 }
343}
344
345impl ControlSS for NoneT {
346 fn assert(&mut self) {}
347 fn deassert(&mut self) {}
348}
349
350//=============================================================================
351// SpiFuture
352//=============================================================================
353
354/// A [`Future`]-like interface for SPI transactions
355///
356/// See the [module-level](self) documentation for more details.
357///
358/// [`Future`]: core::future::Future
359pub struct SpiFuture<S, B, SS = NoneT, W = fn()>
360where
361 S: CheckBufLen,
362 B: AsRef<[u8]> + AsMut<[u8]> + 'static,
363 SS: ControlSS,
364 W: FnOnce() + 'static,
365{
366 spi: S,
367 buf: B,
368 sent: usize,
369 rcvd: usize,
370 ss: SS,
371 waker: Option<W>,
372}
373
374impl<S, B> SpiFuture<S, B>
375where
376 S: CheckBufLen,
377 B: AsRef<[u8]> + AsMut<[u8]> + 'static,
378{
379 /// Create a new [`SpiFuture`] with no SS pin or waker
380 #[inline]
381 pub fn new(spi: S, buf: B) -> Self {
382 spi.check_buf_len(&buf);
383 SpiFuture {
384 spi,
385 buf,
386 sent: 0,
387 rcvd: 0,
388 ss: NoneT,
389 waker: None,
390 }
391 }
392}
393
394impl<S, B, W> SpiFuture<S, B, NoneT, W>
395where
396 S: CheckBufLen,
397 B: AsRef<[u8]> + AsMut<[u8]> + 'static,
398 W: FnOnce() + 'static,
399{
400 /// Add an SS pin to the [`SpiFuture`]
401 ///
402 /// This function changes the `SS` type, so it must take an owned `self`.
403 #[inline]
404 pub fn with_ss<SS>(self, pin: SS) -> SpiFuture<S, B, SS, W>
405 where
406 SS: SomePin + OutputPin<Error = Infallible>,
407 {
408 SpiFuture {
409 spi: self.spi,
410 buf: self.buf,
411 sent: self.sent,
412 rcvd: self.rcvd,
413 ss: pin,
414 waker: self.waker,
415 }
416 }
417}
418
419impl<S, B, SS> SpiFuture<S, B, SS>
420where
421 S: CheckBufLen,
422 B: AsRef<[u8]> + AsMut<[u8]> + 'static,
423 SS: ControlSS,
424{
425 /// Add a waker to the [`SpiFuture`]
426 ///
427 /// This function changes the waker type, so it must take an owned `self`.
428 #[inline]
429 pub fn with_waker<W>(self, waker: W) -> SpiFuture<S, B, SS, W>
430 where
431 W: FnOnce() + 'static,
432 {
433 SpiFuture {
434 spi: self.spi,
435 buf: self.buf,
436 sent: self.sent,
437 rcvd: self.rcvd,
438 ss: self.ss,
439 waker: Some(waker),
440 }
441 }
442}
443
444impl<S, B, SS, W> SpiFuture<S, B, SS, W>
445where
446 S: CheckBufLen,
447 B: AsRef<[u8]> + AsMut<[u8]> + 'static,
448 SS: ControlSS,
449 W: FnOnce() + 'static,
450{
451 /// Start the [`SpiFuture`] transaction
452 ///
453 /// This will assert the SS pin, if present, and enable the `DRE` and `RXC`
454 /// interrupts.
455 #[inline]
456 pub fn start(&mut self) {
457 self.ss.assert();
458 self.spi.as_mut().enable_interrupts(Flags::DRE | Flags::RXC);
459 }
460
461 /// Send the next set of bytes from the buffer
462 ///
463 /// This method should be called from the `DRE` interrupt handler. Once all
464 /// bytes of the transaction have been sent, this function will
465 /// automatically disable the `DRE` interrupt.
466 pub fn send(&mut self) -> Result<(), Error> {
467 let _ = self.spi.as_ref().read_flags_errors()?;
468 let buf = self.buf.as_ref();
469 if let Some(buf) = buf.get(self.sent..) {
470 let mut data = buf.iter();
471 let mut bytes = [0; 4];
472 let mut iter = bytes.iter_mut();
473 for _ in 0..self.spi.step() {
474 match (iter.next(), data.next()) {
475 (Some(b), Some(d)) => *b = *d,
476 _ => break,
477 }
478 }
479 let word = u32::from_le_bytes(bytes);
480 unsafe { self.spi.as_mut().write_data(word as Data) };
481 self.sent += self.spi.step();
482 }
483 if self.sent >= buf.len() {
484 self.spi.as_mut().disable_interrupts(Flags::DRE);
485 }
486 Ok(())
487 }
488
489 /// Received the next set of bytes and write them to the buffer
490 ///
491 /// This method should be called from the `RXC` interrupt handler. Once all
492 /// bytes of the transaction have been received, this function will
493 /// automatically disable the `RXC` interrupt, deassert the SS pin (if
494 /// present), and call the waker (if present).
495 pub fn recv(&mut self) -> Result<(), Error> {
496 let _ = self.spi.as_ref().read_flags_errors()?;
497 let buf = self.buf.as_mut();
498 if self.rcvd < self.sent {
499 let buf = unsafe { buf.get_unchecked_mut(self.rcvd..) };
500 let mut data = buf.iter_mut();
501 // Allow this lint as it will put out a warning on thumbv7em but not thumbv6m
502 #[allow(clippy::unnecessary_cast)]
503 let word = unsafe { self.spi.as_mut().read_data() as u32 };
504 let bytes = word.to_le_bytes();
505 let mut iter = bytes.iter();
506 for _ in 0..self.spi.step() {
507 match (data.next(), iter.next()) {
508 (Some(d), Some(b)) => *d = *b,
509 _ => break,
510 }
511 }
512 self.rcvd += self.spi.step();
513 }
514 if self.rcvd >= buf.len() {
515 self.spi.as_mut().disable_interrupts(Flags::RXC);
516 self.ss.deassert();
517 if let Some(waker) = self.waker.take() {
518 waker();
519 }
520 }
521 Ok(())
522 }
523
524 /// Poll the [`SpiFuture`]
525 ///
526 /// This function will return [`Poll::Pending`] until all bytes have been
527 /// received. When the transaction is complete, it will return
528 /// [`Poll::Ready`] with a reference to the completed buffer.
529 #[inline]
530 pub fn poll(&self) -> Poll<&[u8]> {
531 let buf = self.buf.as_ref();
532 if self.rcvd >= buf.len() {
533 Poll::Ready(buf)
534 } else {
535 Poll::Pending
536 }
537 }
538
539 /// Consume the [`SpiFuture`] and free its components
540 ///
541 /// If the transaction is complete, this function will consume the
542 /// [`SpiFuture`] and return the [`Spi`] object, the
543 /// buffer, and the SS pin, if present.
544 ///
545 /// If the transaction is not complete, it will return `Err(self)`.
546 #[inline]
547 pub fn free(self) -> Result<(S, B, SS), Self> {
548 if self.rcvd >= self.buf.as_ref().len() {
549 Ok((self.spi, self.buf, self.ss))
550 } else {
551 Err(self)
552 }
553 }
554
555 /// Consume the [`SpiFuture`] and free its components without checking for
556 /// completion
557 ///
558 /// # Safety
559 ///
560 /// Ending the transaction prematurely could leave the [`Spi`] in an
561 /// inconsistent state. It is not safe to call this function unless the
562 /// transaction is complete.
563 ///
564 /// [`Spi`]: super::spi::Spi
565 #[inline]
566 pub unsafe fn free_unchecked(self) -> (S, B, SS) {
567 (self.spi, self.buf, self.ss)
568 }
569}