diff --git a/hal/CHANGELOG.md b/hal/CHANGELOG.md index dabea19cb0c9..938868708dbb 100644 --- a/hal/CHANGELOG.md +++ b/hal/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased Changes +- Improve the architecture of `SpiFuture` (#577) - Update `seq_macro` and remove `replace_with` dependencies (#568) - Add a `bsp_peripherals!` macro and fix a bug in `bsp_pins!` (#515) - Updated to 2021 edition, updated dependencies, removed unused dependencies (#562) diff --git a/hal/src/sercom/v2/spi.rs b/hal/src/sercom/v2/spi.rs index 77758a9d88b2..c8b9486aaae7 100644 --- a/hal/src/sercom/v2/spi.rs +++ b/hal/src/sercom/v2/spi.rs @@ -779,6 +779,31 @@ where self.change() } + /// Return the transaction length, in bytes + /// + /// This function is valid for all chips and SPI configurations. It returns + /// the number of bytes in a single SPI transaction. + #[cfg(any(feature = "samd11", feature = "samd21"))] + #[inline] + pub fn transaction_length(&self) -> u8 { + Z::BYTES + } + + /// Return the transaction length, in bytes + /// + /// This function is valid for all chips and SPI configurations. It returns + /// the number of bytes in a single SPI transaction. + #[cfg(feature = "min-samd51g")] + #[inline] + pub fn transaction_length(&self) -> u8 { + use typenum::Unsigned; + if Z::U8 == DynLength::U8 { + self.regs.get_length() + } else { + Z::U8 + } + } + /// Get the clock polarity #[inline] pub fn get_cpol(&self) -> Polarity { @@ -1178,6 +1203,15 @@ where } } + /// Return the transaction length, in bytes + /// + /// This function is valid for all chips and SPI configurations. It returns + /// the number of bytes in a single SPI transaction. + #[inline] + pub fn transaction_length(&self) -> u8 { + self.config.as_ref().transaction_length() + } + /// Update the SPI configuration. /// /// Calling this method will temporarily disable the SERCOM peripheral, as diff --git a/hal/src/sercom/v2/spi/char_size.rs b/hal/src/sercom/v2/spi/char_size.rs index 19945a6d4465..4a605b85f29a 100644 --- a/hal/src/sercom/v2/spi/char_size.rs +++ b/hal/src/sercom/v2/spi/char_size.rs @@ -28,6 +28,9 @@ pub trait CharSize: Sealed { /// Register bit pattern for the corresponding `CharSize` const BITS: u8; + + /// Number of bytes in an SPI transaction + const BYTES: u8; } /// Type alias to recover the [`Word`](CharSize::Word) type from an @@ -44,10 +47,12 @@ impl Sealed for EightBit {} impl CharSize for EightBit { type Word = u8; const BITS: u8 = 0; + const BYTES: u8 = 1; } impl Sealed for NineBit {} impl CharSize for NineBit { type Word = u16; const BITS: u8 = 1; + const BYTES: u8 = 2; } diff --git a/hal/src/sercom/v2/spi_future.rs b/hal/src/sercom/v2/spi_future.rs index 425d7e8bb690..034c3073fec9 100644 --- a/hal/src/sercom/v2/spi_future.rs +++ b/hal/src/sercom/v2/spi_future.rs @@ -1,27 +1,53 @@ -#![allow(rustdoc::broken_intra_doc_links)] //! A [`Future`]-like interface for SPI transactions //! -//! An [`SpiFuture`] takes ownership of an [`Spi`] `struct` and a `[u8]`-like -//! buffer. It then executes a full-duplex SPI transaction using iterrupts. On -//! each `RXC` or `DRE` interrupt, the [`SpiFuture`] reads or sends `STEP` bytes -//! of the buffer, where `STEP` is a value that depends on [`CharSize`], for -//! SAMD11 & SAMD21 chips, or [`Length`], for SAMD51 & SAME5x chips. +//! An [`SpiFuture`] takes ownership of a `[u8]`-like buffer and a user-defined +//! resource that can act as an [`Spi`] `struct`. It then executes a full-duplex +//! SPI transaction using iterrupts. On each `RXC` or `DRE` interrupt, the +//! `SpiFuture` reads or sends `STEP` bytes of the buffer, where `STEP` is a +//! value that depends on +#![cfg_attr(any(feature = "samd11", feature = "samd21"), doc = "[`CharSize`]")] +#![cfg_attr(feature = "min-samd51g", doc = "`CharSize`")] +//! , for SAMD11 & SAMD21 chips, or +#![cfg_attr(any(feature = "samd11", feature = "samd21"), doc = "`Length`")] +#![cfg_attr(feature = "min-samd51g", doc = "[`Length`]")] +//! , for SAMD51 & SAME5x chips. //! //! The provided buffer must implement [`AsRef`] and [`AsMut`] for `[u8]`, it //! must have an appropriate length (see below), and it must have a `'static` //! lifetime, i.e. it must be owned or a `&'static mut` reference. //! -//! [`SpiFuture`] has extra, optional capabilities as well. It can accept a -//! function or closure that will be called on completion of the transaction, -//! acting like a [`Waker`]. And it can take a GPIO [`Pin`] to use as the SS -//! line. If provided, the [`Pin`] will be set low at the beginnging of the -//! transfer and brought high at completion. +//! The user-defined resource must implement the [`AsSpi`] trait, which provides +//! access to an underlying [`Spi`] struct. Optionally, the resource can also +//! provide access to a GPIO [`Pin`] acting as the SPI `SS` line. If the +//! resource implements the `AsSpi` [`assert_ss`] and [`deassert_ss`] methods, +//! the corresponding `Pin` will be set low at the beginnging of the transfer +//! and brought high at completion. All bare `Spi` structs implement `AsSpi`, so +//! they can be used directly. //! -//! Calling [`start`] will enable the `DRE` and `RXC` interrupts and begin the +//! Finally, an `SpiFuture` can also accept a function or closure that will be +//! called on completion of the transaction, acting like a [`Waker`]. +//! +//! Create an `SpiFuture` like so +//! +//! ``` +//! use atsamd_hal::sercom::v2::spi::AnySpi; +//! use atsamd_hal::sercom::v2::spi_future::SpiFuture; +//! +//! fn wake_up() { +//! //... +//! } +//! +//! fn create_future(spi: impl AnySpi) { +//! let buf = [0_u8; 12]; +//! let future = SpiFuture::new(spi, buf).with_waker(wake_up); +//! } +//! ``` +//! +//! Like real [`Future`]s, `SpiFuture`s are lazy. Nothing happens until calling +//! [`start`]. Doing so will enable the `DRE` and `RXC` interrupts and begin the //! transaction. //! //! ``` -//! use atsamd_hal::gpio::v2::{Pin, PA10, PushPullOutput}; //! use atsamd_hal::sercom::v2::spi::AnySpi; //! use atsamd_hal::sercom::v2::spi_future::SpiFuture; //! @@ -29,132 +55,155 @@ //! //... //! } //! -//! fn use_future(spi: impl AnySpi, ss: Pin) { +//! fn use_future(spi: impl AnySpi) { //! let buf = [0_u8; 12]; -//! let future = SpiFuture::new(spi, buf) -//! .with_waker(wake_up) -//! .with_ss(ss); +//! let future = SpiFuture::new(spi, buf).with_waker(wake_up); //! future.start(); //! } //! ``` //! -//! When sending and receiving finish, the [`SpiFuture`] will automatically -//! disable the `DRE` and `RXC` interrupts. To test whether an [`SpiFuture`] is +//! When the transaction is complete, the `SpiFuture` will automatically +//! disable the `DRE` and `RXC` interrupts. To test whether an `SpiFuture` is //! complete, use the [`poll`] method. While the transaction is in progress, it //! will return [`Poll::Pending`]. When the transaction is complete, it will -//! return [`Poll::Ready`]. Once complete, you can consume the [`SpiFuture`] and +//! return [`Poll::Ready`]. Once complete, you can consume the `SpiFuture` and //! [`free`] the constituent pieces. Doing so before the transfer has completed //! is `unsafe`. //! //! The actual transfer is performed by the [`send`] and [`recv`] methods, which //! should be called from the `DRE` and `RXC` interrupt handlers, respectively. +//! See the [RTIC example](self#rtic-example) below for a complete example. //! //! ## `STEP` size and buffer length //! //! For SAMD11 & SAMD21 chips, `STEP` is equal to the number of bytes in the -//! corresponding the [`CharSize::Word`] type, i.e. 1 for [`EightBit`] and 2 for -//! [`NineBit`]. For SAMD51 & SAME5x chips, `STEP` is equal to the [`Length`] or -//! 4, whichever is less. +//! corresponding +#![cfg_attr( + any(feature = "samd11", feature = "samd21"), + doc = "[`CharSize::Word`] type, i.e. 1 for [`EightBit`] and 2 for [`NineBit`]." +)] +#![cfg_attr( + feature = "min-samd51g", + doc = "`CharSize::Word` type, i.e. 1 for `EightBit` and 2 for `NineBit`." +)] +//! For SAMD51 & SAME5x chips, `STEP` is equal to the `Length` or 4, whichever +//! is less. //! //! The provided buffer must have an appropriate length. For SAMD11 & SAMD21 -//! chips, as well as SAMDx5x chips with [`Length`]` <= 4`, a single write of -//! `STEP` bytes represents an entire SPI transaction. In these cases, the -//! provided buffer must represent an integer number of transactions. For -//! example, a SAMD51 [`Spi`] struct with a [`Length`] of 3 could use buffers of -//! length 3, 6, 9, etc. For longer [`Length`] values, the provided buffer must -//! represent exactly one SPI transaction, so the buffer length must be equal to -//! [`Length`]. For example, a SAMD51 [`Spi`] struct with a [`Length`] of 17 -//! could only use a buffer with exactly 17 bytes. -//! -//! Keep in mind that [`SpiFuture`] works only with `[u8]`-like things, which +//! chips, as well as SAMDx5x chips with `Length <= 4`, a single write of +//! `STEP` bytes represents an entire SPI transaction, i.e. they have an +//! [`AtomicSize`]. In these cases, the provided buffer must represent an +//! integer number of transactions. For example, a SAMD51 `Spi` struct with a +//! `Length` of 3 could use buffers of length 3, 6, 9, etc. For longer `Length` +//! values, the provided buffer must represent exactly one SPI transaction, so +//! the buffer length must be equal to `Length`. For example, a SAMD51 `Spi` +//! struct with a `Length` of 7 could only use a buffer with exactly 7 bytes. +//! +//! Keep in mind that `SpiFuture` works only with `[u8]`-like things, which //! can introduce some limitations. //! -//! Suppose you plan to execute [`NineBit`] transactions with a SAMD21 chip. +//! Suppose you plan to execute `NineBit` transactions with a SAMD21 chip. //! Your data may come in the form of a `[u16]` slice. However, to use it with -//! [`SpiFuture`], you will need reformulate it as a `[u8]` slice. The easiest -//! way to do so is probably to transmute the slice to `[u8]` or cast the -//! reference to `&'static mut [u8]`. Both of these operations are sound, -//! because [`u8`] has no alignment requirements. +//! `SpiFuture`, you will need reformulate it as a `[u8]` slice, perhaps using a +//! crate like [`bytemuck`] to safely transmute the data. //! //! In another scenario, suppose you wanted to use a SAMx5x chip with a -//! transaction [`Length`] of 3 bytes. Your data might come in the form of a +//! transaction `Length` of 3 bytes. Your data might come in the form of a //! `[u32]` slice. In this situation, it would **not** be appropriate to -//! transmute or cast the data to a `[u8]` slice. [`SpiFuture`] expects the data -//! to be a *byte-packed* `[u8]` slice, so the extra byte in each `u32` makes it +//! transmute the data to a `[u8]` slice. `SpiFuture` expects the data to be a +//! *byte-packed* `[u8]` slice, so the extra byte in each `u32` makes it //! incompatible. //! -//! ## [RTIC] example +//! ## RTIC example //! //! The [RTIC] framework provides a convenient way to store a `static`ally -//! allocated [`SpiFuture`], so that it can be accessed by both the interrupt -//! handlers and user code. The following example shows how [`SpiFuture`]s might -//! be used for a series of transactions. It was written for a SAMx5x chip, and -//! it uses features from the latest release of [RTIC], `v0.6-alpha.0`. +//! allocated `SpiFuture`, so that it can be accessed by both the interrupt +//! handlers and user code. The following example shows how an `SpiFuture` might +//! be used for a series of transactions. //! //! ``` //! use core::task::Poll; //! use atsamd_hal::gpio::v2::{PA08, PA09, PA10, PA11, Pin, PushPullOutput}; //! use atsamd_hal::sercom::v2::Sercom0; -//! use atsamd_hal::sercom::v2::pad::{IoSet1, Pad0, Pad1, Pad3}; +//! use atsamd_hal::sercom::v2::pad::IoSet1; //! use atsamd_hal::sercom::v2::spi::{self, Master, lengths::U12}; -//! use atsamd_hal::sercom::v2::spi_future::SpiFuture; +//! use atsamd_hal::sercom::v2::spi_future::{AsSpi, SpiFuture}; //! -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; +//! type Spi = spi::Spi, spi::Duplex>; //! type SS = Pin; -//! type Spi = spi::Spi>; -//! type Future = SpiFuture; +//! +//! type Resource = (Spi, SS); +//! +//! impl AsSpi for Resource { +//! type Spi = Spi; +//! fn spi(&self) -> &Spi { +//! &self.0 +//! } +//! fn spi_mut(&mut self) -> &mut Spi { +//! &mut self.0 +//! } +//! fn assert_ss(&mut self) { +//! let _ = self.1.set_low(); +//! } +//! fn deassert_ss(&mut self) { +//! let _ = self.1.set_high(); +//! } +//! } +//! +//! type Future = SpiFuture; //! //! //... //! -//! #[resources] -//! struct Resources { -//! #[task_local] -//! #[init(None)] -//! opt_spi_ss: Option<(Spi, SS)>, +//! #[local] +//! struct Local { +//! opt_resource: Option, +//! } //! +//! #[shared] +//! struct Shared { //! #[lock_free] -//! #[init(None)] //! opt_future: Option, //! } //! -//! #[task(resources = [opt_spi_ss, opt_future])] +//! #[task(local = [opt_resource], shared = [opt_future])] //! fn task(ctx: task::Context) { -//! let task::Context { opt_spi_ss, opt_future } = ctx; +//! let opt_resource = ctx.local.opt_resource; +//! let opt_future = ctx.shared.opt_future; //! match opt_future { //! Some(future) => { //! if let Poll::Ready(_) = future.poll() { -//! let (spi, buf, ss) = opt_future.take().unwrap().free(); -//! *opt_spi_ss = Some((spi, ss)); +//! let (resource, buf) = opt_future.take().unwrap().free(); +//! *opt_resource = Some(resource); //! consume_data(buf); //! } //! } //! None => { -//! if let Some((spi, ss)) = opt_spi_ss.take() { -//! let buf: [u8; 12] = produce_data(); -//! let future = opt_future.get_or_insert( -//! SpiFuture::new(spi, buf) -//! .with_waker(|| { task::spawn().ok(); }) -//! .with_ss(ss) -//! ); -//! future.start(); -//! } +//! let resource = opt_resource.take().unwrap(); +//! let buf: [u8; 12] = produce_data(); +//! let future = opt_future.insert( +//! SpiFuture::new(resource, buf).with_waker(|| { task::spawn().ok(); }) +//! ); +//! future.start(); //! } //! } //! } //! -//! #[task(binds = SERCOM0_0, resources = [opt_future])] +//! #[task(binds = SERCOM0_0, shared = [opt_future])] //! fn dre(ctx: dre::Context) { -//! ctx.resources.opt_future.as_mut().unwrap().send(); +//! ctx.shared.opt_future.as_mut().unwrap().send(); //! } //! -//! #[task(binds = SERCOM0_2, resources = [opt_future])] +//! #[task(binds = SERCOM0_2, shared = [opt_future])] //! fn rxc(ctx: rxc::Context) { -//! ctx.resources.opt_future.as_mut().unwrap().recv(); +//! ctx.shared.opt_future.as_mut().unwrap().recv(); //! } //! //! //... //! ``` //! +//! [`assert_ss`]: AsSpi::assert_ss +//! [`deassert_ss`]: AsSpi::assert_ss //! [`start`]: SpiFuture::start //! [`poll`]: SpiFuture::poll //! [`free`]: SpiFuture::free @@ -166,32 +215,17 @@ //! [`EightBit`]: super::spi::EightBit //! [`NineBit`]: super::spi::NineBit //! [`Length`]: super::spi::Length +//! [`AtomicSize`]: super::spi::AtomicSize //! [`Pin`]: crate::gpio::v2::pin::Pin //! [`Future`]: core::future::Future //! [`Waker`]: core::task::Waker //! [`Poll`]: core::task::Poll //! [RTIC]: https://rtic.rs/ +//! [`bytemuck`]: https://docs.rs/bytemuck -use core::convert::Infallible; use core::task::Poll; -use embedded_hal::digital::v2::OutputPin; - -use crate::gpio::v2::pin::{OptionalPin, SomePin}; -use crate::typelevel::NoneT; - -use super::spi::{AnySpi, Error, Flags}; - -#[cfg(feature = "min-samd51g")] -use { - super::spi::{ - Capability, Config, DynLength, OpMode, Spi, StaticLength, ValidConfig, ValidPads, - }, - typenum::Unsigned, -}; - -#[cfg(any(feature = "samd11", feature = "samd21"))] -use core::mem::size_of; +use super::spi::{AnySpi, Error, Flags, SpecificSpi}; #[cfg(any(feature = "samd11", feature = "samd21"))] type Data = u16; @@ -200,143 +234,122 @@ type Data = u16; type Data = u32; //============================================================================= -// CheckBufLen +// AsSpi //============================================================================= -/// Trait used to verify the [`SpiFuture`] buffer length -pub trait CheckBufLen: AnySpi { - #[cfg(feature = "min-samd51g")] - /// [`Spi`] transaction length - /// - /// This value is zero for an [`Spi`] with [`DynLength`] - const LEN: usize = ::USIZE; - - #[cfg(any(feature = "samd11", feature = "samd21"))] - /// [`Spi`] transaction length +/// Allow user types to provide access to an internal [`Spi`] struct and +/// optional GPIO [`Pin`] acting as `SS` +/// +/// This trait is used by [`SpiFuture`]s to access the stored `Spi` struct +/// within a user-defined type. It also provides an optional interface for a +/// GPIO `Pin` acting as the `SS` line. +/// +/// If the [`assert_ss`] and [`deassert_ss`] methods are implemented, the `SS` +/// line will be set low at the start of a transaction and set high at +/// completion. If no implementations are provided, the two functions default to +/// a NOP. +/// +/// [`Spi`]: super::spi::Spi +/// [`Pin`]: crate::gpio::v2::pin::Pin +/// [`assert_ss`]: AsSpi::assert_ss +/// [`deassert_ss`]: AsSpi::assert_ss +pub trait AsSpi { + /// Stored [`Spi`] struct /// /// [`Spi`]: super::spi::Spi - const LEN: usize = size_of::(); + type Spi: AnySpi; - /// Return the [`Spi`] transaction length + /// Return a shared reference to `Self::Spi` /// - /// In most cases, this returns the corresponding constant. For SAMx5x chips - /// with [`DynLength`], this returns the result of [`Spi::get_dyn_length`]. + /// Remember that the [`AnySpi`] trait requires that + /// `Self::Spi == SpecificSpi`, so the implementation of this + /// function is usually trivial, i.e. /// - /// [`Spi`]: super::spi::Spi - #[inline] - fn len(&self) -> usize { - Self::LEN - } - - /// Step size through the [`SpiFuture`] buffer + /// ``` + /// type Resource = (Spi, SS); /// - /// This is equal to the number of bytes sent in each write to the SPI DATA - /// register. It is zero for an [`Spi`] with [`DynLength`]. + /// impl AsSpi for Resource { + /// type Spi = Spi; + /// fn spi(&self) -> &Spi { + /// &self.0 + /// } + /// // ... + /// } + /// ``` + fn spi(&self) -> &SpecificSpi; + + /// Return an exclusive reference to `Self::Spi` /// - /// [`Spi`]: super::spi::Spi - const STEP: usize = if Self::LEN > 4 { 4 } else { Self::LEN }; - - /// Return the step size through the [`SpiFuture`] buffer + /// Remember that the [`AnySpi`] trait requires that + /// `Self::Spi == SpecificSpi`, so the implementation of this + /// function is usually trivial, i.e. /// - /// In most cases, this returns the corresponding constant. For SAMx5x chips - /// with [`DynLength`], this returns a result calculated from [`Self::len`]. - #[inline] - fn step(&self) -> usize { - Self::STEP - } - - /// Check that the buffer has a valid length + /// ``` + /// type Resource = (Spi, SS); /// - /// If the transaction length is greater than four, then the size of the - /// buffer must equal the transaction length. If the transaction length is - /// less than or equal to four, then the size of the buffer must be an - /// integer multiple of the transaction length. + /// impl AsSpi for Resource { + /// type Spi = Spi; + /// fn spi_mut(&mut self) -> &mut Spi { + /// &mut self.0 + /// } + /// // ... + /// } + /// ``` + fn spi_mut(&mut self) -> &mut SpecificSpi; + + /// Assert the `SS` line by bringing it low /// - /// Both of these statements apply regardless of whether the [`Spi`] has a - /// [`DynLength`]. + /// If the user-defined resource also controls a GPIO [`Pin`] representing + /// the `SS` line, this function allows an [`SpiFuture`] to assert it. /// - /// [`Spi`]: super::spi::Spi - #[inline] - fn check_buf_len(&self, buf: &impl AsRef<[u8]>) { - let buf_len = buf.as_ref().len(); - let self_len = self.len(); - if (self_len > 4 && buf_len != self_len) || (self_len <= 4 && buf_len % self_len != 0) { - panic!("Invalid SpiFuture buffer length"); - } - } -} - -#[cfg(any(feature = "samd11", feature = "samd21"))] -impl CheckBufLen for S {} - -#[cfg(feature = "min-samd51g")] -impl CheckBufLen for Spi, A> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - L: StaticLength, - A: Capability, -{ -} - -#[cfg(feature = "min-samd51g")] -impl CheckBufLen for Spi, A> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - A: Capability, -{ + /// ``` + /// type Resource = (Spi, SS); + /// + /// impl AsSpi for Resource { + /// // ... + /// fn assert_ss(&mut self) { + /// let _ = self.1.set_low(); + /// } + /// } + /// ``` + /// + /// [`Pin`]: crate::gpio::v2::pin::Pin #[inline] - fn len(&self) -> usize { - self.get_dyn_length() as usize - } + fn assert_ss(&mut self) {} + /// Deassert the `SS` line by bringing it high + /// + /// If the user-defined resource also controls a GPIO [`Pin`] representing + /// the `SS` line, this function allows an [`SpiFuture`] to deassert it. + /// + /// ``` + /// type Resource = (Spi, SS); + /// + /// impl AsSpi for Resource { + /// // ... + /// fn deassert_ss(&mut self) { + /// let _ = self.1.set_high(); + /// } + /// } + /// ``` + /// + /// [`Pin`]: crate::gpio::v2::pin::Pin #[inline] - fn step(&self) -> usize { - let len = self.len(); - if len > 4 { - 4 - } else { - len - } - } -} - -//============================================================================= -// ControlSS -//============================================================================= - -/// Trait used to control the SS line during an [`SpiFuture`] transaction -pub trait ControlSS: OptionalPin { - /// If an SS pin is present, assert it by bringing it low - fn assert(&mut self); - - /// If an SS pin is present, deassert it by bringing it high - fn deassert(&mut self); + fn deassert_ss(&mut self) {} } -impl

ControlSS for P -where - P: SomePin + OutputPin, -{ +impl AsSpi for S { + type Spi = S; #[inline] - fn assert(&mut self) { - self.set_low().unwrap(); + fn spi(&self) -> &SpecificSpi { + self.as_ref() } - #[inline] - fn deassert(&mut self) { - self.set_high().unwrap(); + fn spi_mut(&mut self) -> &mut SpecificSpi { + self.as_mut() } } -impl ControlSS for NoneT { - fn assert(&mut self) {} - fn deassert(&mut self) {} -} - //============================================================================= // SpiFuture //============================================================================= @@ -346,106 +359,91 @@ impl ControlSS for NoneT { /// See the [module-level](self) documentation for more details. /// /// [`Future`]: core::future::Future -pub struct SpiFuture +pub struct SpiFuture where - S: CheckBufLen, + R: AsSpi, B: AsRef<[u8]> + AsMut<[u8]> + 'static, - SS: ControlSS, W: FnOnce() + 'static, { - spi: S, + resource: R, buf: B, sent: usize, rcvd: usize, - ss: SS, waker: Option, } -impl SpiFuture +impl SpiFuture where - S: CheckBufLen, + R: AsSpi, B: AsRef<[u8]> + AsMut<[u8]> + 'static, { - /// Create a new [`SpiFuture`] with no SS pin or waker + /// Create a new [`SpiFuture`] with no waker #[inline] - pub fn new(spi: S, buf: B) -> Self { - spi.check_buf_len(&buf); + pub fn new(resource: R, buf: B) -> Self { + let buf_len = buf.as_ref().len(); + let spi_len = resource.spi().transaction_length() as usize; + if (spi_len > 4 && buf_len != spi_len) || (spi_len <= 4 && buf_len % spi_len != 0) { + panic!("Invalid SpiFuture buffer length"); + } SpiFuture { - spi, + resource, buf, sent: 0, rcvd: 0, - ss: NoneT, waker: None, } } } -impl SpiFuture -where - S: CheckBufLen, - B: AsRef<[u8]> + AsMut<[u8]> + 'static, - W: FnOnce() + 'static, -{ - /// Add an SS pin to the [`SpiFuture`] - /// - /// This function changes the `SS` type, so it must take an owned `self`. - #[inline] - pub fn with_ss(self, pin: SS) -> SpiFuture - where - SS: SomePin + OutputPin, - { - SpiFuture { - spi: self.spi, - buf: self.buf, - sent: self.sent, - rcvd: self.rcvd, - ss: pin, - waker: self.waker, - } - } -} - -impl SpiFuture +impl SpiFuture where - S: CheckBufLen, + S: AsSpi, B: AsRef<[u8]> + AsMut<[u8]> + 'static, - SS: ControlSS, { /// Add a waker to the [`SpiFuture`] /// /// This function changes the waker type, so it must take an owned `self`. #[inline] - pub fn with_waker(self, waker: W) -> SpiFuture + pub fn with_waker(self, waker: W) -> SpiFuture where W: FnOnce() + 'static, { SpiFuture { - spi: self.spi, + resource: self.resource, buf: self.buf, sent: self.sent, rcvd: self.rcvd, - ss: self.ss, waker: Some(waker), } } } -impl SpiFuture +impl SpiFuture where - S: CheckBufLen, + R: AsSpi, B: AsRef<[u8]> + AsMut<[u8]> + 'static, - SS: ControlSS, W: FnOnce() + 'static, { + #[inline] + fn step(&self) -> usize { + let len = self.resource.spi().transaction_length() as usize; + if len > 4 { + 4 + } else { + len + } + } + /// Start the [`SpiFuture`] transaction /// /// This will assert the SS pin, if present, and enable the `DRE` and `RXC` /// interrupts. #[inline] pub fn start(&mut self) { - self.ss.assert(); - self.spi.as_mut().enable_interrupts(Flags::DRE | Flags::RXC); + self.resource.assert_ss(); + self.resource + .spi_mut() + .enable_interrupts(Flags::DRE | Flags::RXC); } /// Send the next set of bytes from the buffer @@ -453,25 +451,28 @@ where /// This method should be called from the `DRE` interrupt handler. Once all /// bytes of the transaction have been sent, this function will /// automatically disable the `DRE` interrupt. + #[inline] pub fn send(&mut self) -> Result<(), Error> { - let _ = self.spi.as_ref().read_flags_errors()?; + let step = self.step(); + let spi = self.resource.spi_mut(); let buf = self.buf.as_ref(); + let _ = spi.read_flags_errors()?; if let Some(buf) = buf.get(self.sent..) { let mut data = buf.into_iter(); let mut bytes = [0; 4]; let mut iter = bytes.iter_mut(); - for _ in 0..self.spi.step() { + for _ in 0..step { match (iter.next(), data.next()) { (Some(b), Some(d)) => *b = *d, _ => break, } } let word = u32::from_le_bytes(bytes); - unsafe { self.spi.as_mut().write_data(word as Data) }; - self.sent += self.spi.step(); + unsafe { spi.write_data(word as Data) }; + self.sent += step; } if self.sent >= buf.len() { - self.spi.as_mut().disable_interrupts(Flags::DRE); + spi.disable_interrupts(Flags::DRE); } Ok(()) } @@ -482,26 +483,29 @@ where /// bytes of the transaction have been received, this function will /// automatically disable the `RXC` interrupt, deassert the SS pin (if /// present), and call the waker (if present). + #[inline] pub fn recv(&mut self) -> Result<(), Error> { - let _ = self.spi.as_ref().read_flags_errors()?; + let step = self.step(); + let spi = self.resource.spi_mut(); let buf = self.buf.as_mut(); + let _ = spi.read_flags_errors()?; if self.rcvd < self.sent { let buf = unsafe { buf.get_unchecked_mut(self.rcvd..) }; let mut data = buf.into_iter(); - let word = unsafe { self.spi.as_mut().read_data() as u32 }; + let word = unsafe { spi.read_data() as u32 }; let bytes = word.to_le_bytes(); let mut iter = bytes.iter(); - for _ in 0..self.spi.step() { + for _ in 0..step { match (data.next(), iter.next()) { (Some(d), Some(b)) => *d = *b, _ => break, } } - self.rcvd += self.spi.step(); + self.rcvd += step; } if self.rcvd >= buf.len() { - self.spi.as_mut().disable_interrupts(Flags::RXC); - self.ss.deassert(); + spi.disable_interrupts(Flags::RXC); + self.resource.deassert_ss(); if let Some(waker) = self.waker.take() { waker(); } @@ -527,14 +531,13 @@ where /// Consume the [`SpiFuture`] and free its components /// /// If the transaction is complete, this function will consume the - /// [`SpiFuture`] and return the [`Spi`](super::spi::Spi) object, the - /// buffer, and the SS pin, if present. + /// [`SpiFuture`] and return the resource and buffer. /// /// If the transaction is not complete, it will return `Err(self)`. #[inline] - pub fn free(self) -> Result<(S, B, SS), Self> { + pub fn free(self) -> Result<(R, B), Self> { if self.rcvd >= self.buf.as_ref().len() { - Ok((self.spi, self.buf, self.ss)) + Ok((self.resource, self.buf)) } else { Err(self) } @@ -549,7 +552,7 @@ where /// /// [`Spi`]: super::spi::Spi #[inline] - pub unsafe fn free_unchecked(self) -> (S, B, SS) { - (self.spi, self.buf, self.ss) + pub unsafe fn free_unchecked(self) -> (R, B) { + (self.resource, self.buf) } }