diff --git a/boards/feather_m0/src/lib.rs b/boards/feather_m0/src/lib.rs index 76b8f6235567..35a71cfd3a40 100644 --- a/boards/feather_m0/src/lib.rs +++ b/boards/feather_m0/src/lib.rs @@ -234,13 +234,13 @@ pub use pins::*; /// SPI pads for the labelled SPI peripheral /// -/// You can use these pads with other, user-defined [`spi::Config`]urations. +/// You can use these pads with other, user-defined configurations. pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// /// This type implements [`FullDuplex`](ehal::spi::FullDuplex). -pub type Spi = spi::Spi, spi::Duplex>; +pub type Spi = spi::Spi; /// Convenience for setting up the labelled SPI peripheral. /// This powers up the SPI SERCOM and configures it for use as an @@ -259,7 +259,7 @@ pub fn spi_master( let freq = clock.freq(); let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); - spi::Config::new(pm, sercom, pads, freq) + spi::Spi::config(pm, sercom, pads, freq) .baud(baud) .spi_mode(spi::MODE_0) .enable() diff --git a/boards/feather_m4/src/lib.rs b/boards/feather_m4/src/lib.rs index e8331b35c06c..27a0e3438555 100644 --- a/boards/feather_m4/src/lib.rs +++ b/boards/feather_m4/src/lib.rs @@ -169,13 +169,13 @@ hal::bsp_pins!( /// SPI pads for the labelled SPI peripheral /// -/// You can use these pads with other, user-defined [`spi::Config`]urations. +/// You can use these pads with other, user-defined configurations. pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// /// This type implements [`FullDuplex`](ehal::spi::FullDuplex). -pub type Spi = spi::Spi, spi::Duplex>; +pub type Spi = spi::Spi; /// Convenience for setting up the labelled SPI peripheral. /// This powers up SERCOM1 and configures it for use as an @@ -194,7 +194,7 @@ pub fn spi_master( let freq = clock.freq(); let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); - spi::Config::new(mclk, sercom, pads, freq) + spi::Spi::config(mclk, sercom, pads, freq) .baud(baud) .spi_mode(spi::MODE_0) .enable() diff --git a/boards/metro_m0/src/lib.rs b/boards/metro_m0/src/lib.rs index 8d0549576028..047a795c4603 100644 --- a/boards/metro_m0/src/lib.rs +++ b/boards/metro_m0/src/lib.rs @@ -200,13 +200,13 @@ pub use pins::*; /// SPI pads for the labelled SPI peripheral /// -/// You can use these pads with other, user-defined [`spi::Config`]urations. +/// You can use these pads with other, user-defined configurations. pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// /// This type implements [`FullDuplex`](ehal::spi::FullDuplex). -pub type Spi = spi::Spi, spi::Duplex>; +pub type Spi = spi::Spi; /// Convenience for setting up the 2x3 header block for SPI. /// This powers up SERCOM4 and configures it for use as an @@ -229,7 +229,7 @@ pub fn spi_master( let freq = clock.freq(); let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); - spi::Config::new(pm, sercom4, pads, freq) + spi::Spi::config(pm, sercom4, pads, freq) .baud(baud) .spi_mode(spi::MODE_0) .enable() @@ -241,7 +241,7 @@ pub type FlashPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// /// This type implements [`FullDuplex`](ehal::spi::FullDuplex). -pub type FlashSpi = (spi::Spi, spi::Duplex>, FlashCs); +pub type FlashSpi = (spi::Spi, FlashCs); /// Convenience for accessing the on-board SPI Flash device. /// This powers up SERCOM5 and configures it for use as an @@ -260,7 +260,7 @@ pub fn flash_spi_master( let freq = clock.freq(); let (miso, mosi, sclk, mut cs) = (miso.into(), mosi.into(), sclk.into(), cs.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); - let spi = spi::Config::new(pm, sercom5, pads, freq) + let spi = spi::Spi::config(pm, sercom5, pads, freq) .baud(MegaHertz(48)) .spi_mode(spi::MODE_0) .enable(); diff --git a/boards/metro_m4/src/lib.rs b/boards/metro_m4/src/lib.rs index 10776d2f02e7..520f38fa1bd3 100644 --- a/boards/metro_m4/src/lib.rs +++ b/boards/metro_m4/src/lib.rs @@ -235,13 +235,13 @@ hal::bsp_pins!( /// SPI pads for the labelled SPI peripheral /// -/// You can use these pads with other, user-defined [`spi::Config`]urations. +/// You can use these pads with other, user-defined configurations. pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// /// This type implements [`FullDuplex`](ehal::spi::FullDuplex). -pub type Spi = spi::Spi, spi::Duplex>; +pub type Spi = spi::Spi; /// Convenience for setting up the 2x3 header block for SPI. /// This powers up SERCOM2 and configures it for use as an @@ -260,7 +260,7 @@ pub fn spi_master( let freq = clock.freq(); let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); - spi::Config::new(mclk, sercom, pads, freq) + spi::Spi::config(mclk, sercom, pads, freq) .baud(baud) .spi_mode(spi::MODE_0) .enable() diff --git a/boards/wio_terminal/src/display.rs b/boards/wio_terminal/src/display.rs index 0a8f879284ba..64a97de2499f 100644 --- a/boards/wio_terminal/src/display.rs +++ b/boards/wio_terminal/src/display.rs @@ -37,7 +37,7 @@ pub struct Display { } pub type LcdPads = spi::Pads; -pub type LcdSpi = spi::Spi, spi::Tx>; +pub type LcdSpi = spi::Spi; /// Type alias for the ILI9341 LCD display. pub type LCD = Ili9341, LcdReset>; @@ -60,7 +60,7 @@ impl Display { let gclk0 = clocks.gclk0(); let clock = &clocks.sercom7_core(&gclk0).ok_or(())?; let pads = spi::Pads::default().data_out(self.mosi).sclk(self.sck); - let spi = spi::Config::new(mclk, sercom7, pads, clock.freq()) + let spi = spi::Spi::config(mclk, sercom7, pads, clock.freq()) .spi_mode(spi::MODE_0) .baud(baud) .enable(); diff --git a/boards/wio_terminal/src/storage.rs b/boards/wio_terminal/src/storage.rs index f5cc9270723b..3369799af07d 100644 --- a/boards/wio_terminal/src/storage.rs +++ b/boards/wio_terminal/src/storage.rs @@ -58,7 +58,7 @@ pub struct SDCard { } pub type SdPads = spi::Pads; -pub type SdSpi = spi::Spi, spi::Duplex>; +pub type SdSpi = spi::Spi; type Controller = embedded_sdmmc::Controller, TS>; /// An initialized SPI SDMMC controller. @@ -71,7 +71,10 @@ impl SDCardController { /// Initializes the MMC card. An error is returned if there is no card /// or a communications error occurs. pub fn set_baud>(&mut self, baud: B) { - self.cont.device().spi().reconfigure(|c| c.set_baud(baud)); + self.cont + .device() + .spi() + .reconfigure(|c| c.set_baud(baud)); } } @@ -104,7 +107,7 @@ impl SDCard { .data_out(self.mosi) .data_in(self.miso) .sclk(self.sck); - let spi = spi::Config::new(mclk, sercom6, pads, sercom6_clk.freq()) + let spi = spi::Spi::config(mclk, sercom6, pads, sercom6_clk.freq()) .spi_mode(spi::MODE_0) .baud(400.khz()) .enable(); diff --git a/hal/src/sercom/dma.rs b/hal/src/sercom/dma.rs index f742fdf7fd7e..6031022218d1 100644 --- a/hal/src/sercom/dma.rs +++ b/hal/src/sercom/dma.rs @@ -277,15 +277,14 @@ where // SPI DMA transfers //============================================================================= -unsafe impl Buffer for Spi +unsafe impl Buffer for Spi where - C: spi::ValidConfig, - C::OpMode: spi::MasterMode, - C::Size: spi::AtomicSize, - C::Word: Beat, - A: spi::Capability, + P: spi::ValidPads, + M: spi::MasterMode, + Z: spi::AtomicSize, + Z::Word: Beat, { - type Beat = C::Word; + type Beat = Z::Word; #[inline] fn dma_ptr(&mut self) -> *mut Self::Beat { @@ -303,11 +302,13 @@ where } } -impl Spi +impl Spi where - C: spi::ValidConfig, - A: spi::Transmit, - Self: Buffer, + P: spi::ValidPads, + P::Capability: spi::Transmit, + M: spi::OpMode, + Z: spi::Size, + Self: Buffer, { /// Transform an [`Spi`] into a DMA [`Transfer`]) and /// start a send transaction. @@ -320,7 +321,7 @@ where ) -> Transfer, BufferPair, W> where Ch: AnyChannel, - B: Buffer + 'static, + B: Buffer + 'static, W: FnOnce(CallbackStatus) + 'static, { channel @@ -337,15 +338,17 @@ where // for `B`, and the fact that the buffer length of an `Spi` is always 1. let xfer = unsafe { Transfer::new_unchecked(channel, buf, self, false) }; xfer.with_waker(waker) - .begin(C::Sercom::DMA_TX_TRIGGER, trigger_action) + .begin(P::Sercom::DMA_TX_TRIGGER, trigger_action) } } -impl Spi +impl Spi where - C: spi::ValidConfig, - A: spi::Receive, - Self: Buffer, + P: spi::ValidPads, + P::Capability: spi::Receive, + M: spi::OpMode, + Z: spi::Size, + Self: Buffer, { /// Transform an [`Spi`] into a DMA [`Transfer`]) and /// start a receive transaction. @@ -358,7 +361,7 @@ where ) -> Transfer, BufferPair, W> where Ch: AnyChannel, - B: Buffer + 'static, + B: Buffer + 'static, W: FnOnce(CallbackStatus) + 'static, { channel @@ -375,6 +378,6 @@ where // for `B`, and the fact that the buffer length of an `Spi` is always 1. let xfer = unsafe { Transfer::new_unchecked(channel, self, buf, false) }; xfer.with_waker(waker) - .begin(C::Sercom::DMA_RX_TRIGGER, trigger_action) + .begin(P::Sercom::DMA_RX_TRIGGER, trigger_action) } } diff --git a/hal/src/sercom/spi.rs b/hal/src/sercom/spi.rs index bda4b47a5b89..c05852fe4192 100644 --- a/hal/src/sercom/spi.rs +++ b/hal/src/sercom/spi.rs @@ -312,18 +312,23 @@ let (chan0, _, spi, _) = dma_transfer.wait(); " )] +//============================================================================= +// Imports +//============================================================================= + use core::marker::PhantomData; -use bitflags::bitflags; -use embedded_hal::spi; -pub use embedded_hal::spi::{Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +use num_traits::{AsPrimitive, PrimInt}; -use crate::sercom::*; +use crate::sercom::{Sercom, APB_CLK_CTRL}; use crate::time::Hertz; -use crate::typelevel::{Is, NoneT, Sealed}; +use crate::typelevel::{Is, Sealed}; mod reg; -use reg::Registers; +pub use reg::*; + +mod config; +pub use config::*; //============================================================================= // Chip-specific imports @@ -362,6 +367,8 @@ pub mod lengths { }); } +mod impl_ehal_common; + #[cfg(any(feature = "samd11", feature = "samd21"))] #[path = "spi/impl_ehal_thumbv6m.rs"] pub mod impl_ehal; @@ -370,79 +377,99 @@ pub mod impl_ehal; #[path = "spi/impl_ehal_thumbv7em.rs"] pub mod impl_ehal; -//============================================================================= -// BitOrder -//============================================================================= +//============================================================================== +// DynCapability +//============================================================================== -/// Define the bit order of transactions -#[repr(u8)] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum BitOrder { - LsbFirst, - MsbFirst, +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum DynCapability { + Rx, + Tx, + Duplex, } -//============================================================================= -// Flags -//============================================================================= +impl Sealed for DynCapability {} -bitflags! { - /// Interrupt bit flags for SPI transactions - /// - /// The available interrupt flags are `DRE`, `RXC`, `TXC`, `SSL` and - /// `ERROR`. The binary format of the underlying bits exactly matches the - /// `INTFLAG` register. - pub struct Flags: u8 { - const DRE = 0x01; - const TXC = 0x02; - const RXC = 0x04; - const SSL = 0x08; - const ERROR = 0x80; - } +//============================================================================== +// Capability +//============================================================================== + +/// Type-level enum representing the simplex or duplex transaction capability +/// +/// The available, type-level variants are [`Rx`], [`Tx`] and [`Duplex`]. See +/// the [type-level enum] documentation for more details. +/// +/// [type-level enum]: crate::typelevel#type-level-enums +pub trait Capability: Sealed + Default { + const DYN: DynCapability; } +/// Type-level variant of the [`Capability`] enum for simplex, [`Receive`]-only +/// transactions +/// +/// [`Spi`] structs are `Rx` when the `DO` (Data Out) type is [`NoneT`] in the +/// corresponding [`Pads`] struct. +/// +/// While the [`Tx`] and [`Duplex`] structs are zero-sized, this struct is not. +/// Because an SPI master must initiate all transactions, using it in a simplex, +/// [`Receive`]-only context is slightly complicated. In that case, the [`Spi`] +/// struct must track whether a transaction needs to be started or is already in +/// progress. This struct contains a `bool` to track that progress. +#[derive(Default)] +pub struct Rx { + pub(super) in_progress: bool, +} + +/// Type-level variant of the [`Capability`] enum for simplex, [`Transmit`]-only +/// transactions +/// +/// [`Spi`] structs are `Tx` when the `DI` (Data In) type is [`NoneT`] in the +/// corresponding [`Pads`] struct. +#[derive(Default)] +pub struct Tx; + +/// Type-level variant of the [`Capability`] enum for duplex transactions +/// +/// [`Spi`] structs are `Duplex` when both the `DI` and `DO` [`Pads`] are +/// [`SomePad`]. +/// corresponding [`Pads`] struct. +#[derive(Default)] +pub struct Duplex; + +macro_rules! impl_capability { + ( $( $Cap: ident ),+ ) => { + $( + impl Sealed for $Cap {} + impl Capability for $Cap { + const DYN: DynCapability = DynCapability::$Cap; + } + )+ + }; +} + +impl_capability!(Rx, Tx, Duplex); + //============================================================================= -// Status +// Receive //============================================================================= -bitflags! { - /// Status bit flags for SPI transactions - /// - /// The available status flags are `BUFOVF` and `LENERR`. The binary format - /// of the underlying bits exactly matches the `STATUS` register. - pub struct Status: u16 { - const BUFOVF = 0x0004; - const LENERR = 0x0800; - } -} +/// Sub-set of [`Capability`] variants that can receive data, i.e. [`Rx`] and +/// [`Duplex`] +pub trait Receive: Capability {} -impl Status { - /// Check the [`Status`] flags for [`Error`] conditions - pub fn check_errors(&self) -> Result<(), Error> { - // Buffer overflow has priority - if self.contains(Status::BUFOVF) { - Err(Error::Overflow) - } else if self.contains(Status::LENERR) { - Err(Error::LengthError) - } else { - Ok(()) - } - } -} +impl Receive for Rx {} +impl Receive for Duplex {} //============================================================================= -// Error +// Transmit //============================================================================= -/// Error `enum` for SPI transactions -/// -/// The SPI peripheral only has two error types, buffer overflow and transaction -/// length error. -#[derive(Debug)] -pub enum Error { - Overflow, - LengthError, -} +/// Sub-set of [`Capability`] variants that can transmit dat, i.e. [`Tx`] and +/// [`Duplex`] +pub trait Transmit: Capability {} + +impl Transmit for Tx {} +impl Transmit for Duplex {} //============================================================================= // Operating mode @@ -507,14 +534,6 @@ impl MasterMode for MasterHWSS {} // Size //============================================================================= -/// Type alias for the width of the `DATA` register -#[cfg(any(feature = "samd11", feature = "samd21"))] -pub type DataWidth = u16; - -/// Type alias for the width of the `DATA` register -#[cfg(feature = "min-samd51g")] -pub type DataWidth = u32; - /// Trait alias whose definition varies by chip /// /// On SAMD11 and SAMD21 chips, this represents the [`CharSize`]. @@ -553,101 +572,32 @@ pub trait AtomicSize: Size {} impl AtomicSize for C {} #[cfg(feature = "min-samd51g")] -seq!(N in 1..=4 { +seq_macro::seq!(N in 1..=4 { impl AtomicSize for lengths::U~N {} }); //============================================================================== -// Capability +// NonAtomicSize //============================================================================== -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum DynCapability { - Rx, - Tx, - Duplex, -} - -impl Sealed for DynCapability {} - -/// Type-level enum representing the simplex or duplex transaction capability -/// -/// The available, type-level variants are [`Rx`], [`Tx`] and [`Duplex`]. See -/// the [type-level enum] documentation for more details. -/// -/// [type-level enum]: crate::typelevel#type-level-enums -pub trait Capability: Sealed + Default { - const DYN: DynCapability; -} - -/// Sub-set of [`Capability`] variants that can receive data, i.e. [`Rx`] and -/// [`Duplex`] -pub trait Receive: Capability {} - -/// Sub-set of [`Capability`] variants that can transmit dat, i.e. [`Tx`] and -/// [`Duplex`] -pub trait Transmit: Capability {} - -/// Type-level variant of the [`Capability`] enum for simplex, [`Receive`]-only -/// transactions -/// -/// [`Spi`] structs are `Rx` when the `DO` (Data Out) type is [`NoneT`] in the -/// corresponding [`Pads`] struct. -/// -/// While the [`Tx`] and [`Duplex`] structs are zero-sized, this struct is not. -/// Because an SPI master must initiate all transactions, using it in a simplex, -/// [`Receive`]-only context is slightly complicated. In that case, the [`Spi`] -/// struct must track whether a transaction needs to be started or is already in -/// progress. This struct contains a `bool` to track that progress. -#[derive(Default)] -pub struct Rx { - pub(super) in_progress: bool, -} - -impl Sealed for Rx {} -impl Capability for Rx { - const DYN: DynCapability = DynCapability::Rx; -} -impl Receive for Rx {} - -/// Type-level variant of the [`Capability`] enum for simplex, [`Transmit`]-only -/// transactions -/// -/// [`Spi`] structs are `Tx` when the `DI` (Data In) type is [`NoneT`] in the -/// corresponding [`Pads`] struct. -#[derive(Default)] -pub struct Tx; - -impl Sealed for Tx {} -impl Capability for Tx { - const DYN: DynCapability = DynCapability::Tx; -} -impl Transmit for Tx {} - -/// Type-level variant of the [`Capability`] enum for duplex transactions -/// -/// [`Spi`] structs are `Duplex` when both the `DI` and `DO` [`Pads`] are -/// [`SomePad`]. -/// corresponding [`Pads`] struct. -#[derive(Default)] -pub struct Duplex; +/// Marker trait for [`Size`]s that can't be completed in one transaction +#[cfg(feature = "min-samd51g")] +pub trait NonAtomicSize: Size {} -impl Sealed for Duplex {} -impl Capability for Duplex { - const DYN: DynCapability = DynCapability::Duplex; -} -impl Receive for Duplex {} -impl Transmit for Duplex {} +#[cfg(feature = "min-samd51g")] +seq_macro::seq!(N in 5..=255 { + impl NonAtomicSize for typenum::U~N {} +}); //============================================================================= -// Config +// Spi //============================================================================= -/// A configurable SPI peripheral in its disabled state +/// An enabled SPI peripheral that can perform transactions /// -/// See the [module-level](super) documentation for more details on declaring -/// and instantiating `Pads` types. -pub struct Config +/// See the [`impl_ehal`] documentation for details on the implementations of +/// the embedded HAL traits, which vary based on [`Size`] and [`Capability`]. +pub struct Spi where P: ValidPads, M: OpMode, @@ -655,85 +605,30 @@ where { regs: Registers, pads: P, + capability: P::Capability, mode: PhantomData, size: PhantomData, freq: Hertz, } -impl Config

{ - /// Create a new [`Config`] in the default configuration. - #[inline] - fn default(sercom: P::Sercom, pads: P, freq: impl Into) -> Self { - let mut regs = Registers { sercom }; - regs.reset(); - regs.set_op_mode(Master::MODE, Master::MSSEN); - regs.set_dipo_dopo(P::DIPO_DOPO); - #[cfg(any(feature = "samd11", feature = "samd21"))] - regs.set_char_size(EightBit::BITS); - #[cfg(feature = "min-samd51g")] - regs.set_length(1); - Self { - regs, - pads, - mode: PhantomData, - size: PhantomData, - freq: freq.into(), - } - } - - /// Create a new [`Config`] in the default configuration - /// - /// This function will enable the corresponding APB clock, reset the - /// [`Sercom`] peripheral, and return a [`Config`] in the default - /// configuration. The default [`OpMode`] is [`Master`], while the default - /// [`Size`] is an - #[cfg_attr( - any(feature = "samd11", feature = "samd21"), - doc = "[`EightBit`] [`CharSize`]" - )] - #[cfg_attr(feature = "min-samd51g", doc = "`EightBit` `CharSize`")] - /// for SAMD11 and SAMD21 chips or a - #[cfg_attr(any(feature = "samd11", feature = "samd21"), doc = "`Length` of `U1`")] - #[cfg_attr(feature = "min-samd51g", doc = "[`Length`] of `U1`")] - /// for SAMx5x chips. Note that [`Config`] takes ownership of both the - /// PAC [`Sercom`] struct as well as the [`Pads`]. - /// - /// Users must configure GCLK manually. The `freq` parameter represents the - /// GCLK frequency for this [`Sercom`] instance. +impl Spi

{ #[inline] - pub fn new( + pub fn config( apb_clk_ctrl: &APB_CLK_CTRL, - mut sercom: P::Sercom, + sercom: P::Sercom, pads: P, freq: impl Into, - ) -> Self { - sercom.enable_apb_clock(apb_clk_ctrl); - Self::default(sercom, pads, freq) + ) -> Config

{ + Config::new(apb_clk_ctrl, sercom, pads, freq) } } -impl Config +impl Spi where P: ValidPads, M: OpMode, Z: Size, { - /// Change the [`OpMode`] or [`Size`] - #[inline] - fn change(self) -> Config - where - M2: OpMode, - Z2: Size, - { - Config { - regs: self.regs, - pads: self.pads, - mode: PhantomData, - size: PhantomData, - freq: self.freq, - } - } - /// Obtain a reference to the PAC `SERCOM` struct /// /// Directly accessing the `SERCOM` could break the invariants of the @@ -743,460 +638,53 @@ where &self.regs.sercom } - /// Trigger the [`Sercom`]'s SWRST and return a [`Config`] in the - /// default configuration. - #[inline] - pub fn reset(self) -> Config

{ - Config::default(self.regs.sercom, self.pads, self.freq) - } - - /// Consume the [`Config`], reset the peripheral, and return the [`Sercom`] - /// and [`Pads`] - #[inline] - pub fn free(mut self) -> (P::Sercom, P) { - self.regs.reset(); - (self.regs.sercom, self.pads) - } - /// Obtain a pointer to the `DATA` register. Necessary for DMA transfers. #[inline] #[cfg(feature = "dma")] - pub(super) fn data_ptr(&self) -> *mut Z::Word { + pub(super) fn data_ptr(&self) -> *mut Word { self.regs.data_ptr::() } - /// Change the [`OpMode`] - #[inline] - pub fn op_mode(mut self) -> Config { - self.regs.set_op_mode(M2::MODE, M2::MSSEN); - self.change() - } - - /// Change the [`CharSize`] using the builder pattern - #[cfg(any(feature = "samd11", feature = "samd21"))] - #[inline] - pub fn char_size(mut self) -> Config { - self.regs.set_char_size(C2::BITS); - self.change() - } - - /// Change the transaction [`Length`] using the builder pattern - /// - /// To use a run-time dynamic length, set the [`Length`] type to - /// [`DynLength`] and then use the [`dyn_length`] method. - /// - /// [`dyn_length`]: Config::dyn_length - #[cfg(feature = "min-samd51g")] - #[inline] - pub fn length(mut self) -> Config { - self.regs.set_length(L2::U8); - 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 + pub fn get_length(&self) -> u8 { + #[cfg(any(feature = "samd11", feature = "samd21"))] + let result = Z::BYTES; + #[cfg(feature = "min-samd51g")] + let result = match Z::static_length() { + Some(length) => length.get(), + None => self.regs.get_length(), + }; + result } - /// 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 { - self.regs.get_cpol() - } - - /// Set the clock polarity - #[inline] - pub fn set_cpol(&mut self, cpol: Polarity) { - self.regs.set_cpol(cpol); - } - - /// Set the clock polarity using the builder pattern - #[inline] - pub fn cpol(mut self, cpol: Polarity) -> Self { - self.set_cpol(cpol); - self - } - - /// Get the clock phase - #[inline] - pub fn get_cpha(&self) -> Phase { - self.regs.get_cpha() - } - - /// Set the clock phase - #[inline] - pub fn set_cpha(&mut self, cpha: Phase) { - self.regs.set_cpha(cpha) - } - - /// Set the clock phase using the builder pattern - #[inline] - pub fn cpha(mut self, cpha: Phase) -> Self { - self.set_cpha(cpha); - self - } - - /// Get the SPI mode (clock polarity & phase) - #[inline] - pub fn get_spi_mode(&self) -> spi::Mode { - self.regs.get_spi_mode() - } - - /// Set the SPI mode (clock polarity & phase) - #[inline] - pub fn set_spi_mode(&mut self, mode: spi::Mode) { - self.regs.set_spi_mode(mode); - } - - /// Set the SPI mode (clock polarity & phase) using the builder pattern - #[inline] - pub fn spi_mode(mut self, mode: spi::Mode) -> Self { - self.set_spi_mode(mode); - self - } - - /// Get the bit order of transmission (MSB/LSB first) - /// - /// This only affects the order of bits within each byte. Bytes are always - /// transferred in little endian order from the 32-bit DATA register. - #[inline] - pub fn get_bit_order(&self) -> BitOrder { - self.regs.get_bit_order() - } - - /// Set the bit order of transmission (MSB/LSB first) using the builder - /// pattern - /// - /// This only affects the order of bits within each byte. Bytes are always - /// transferred in little endian order from the 32-bit DATA register. - #[inline] - pub fn set_bit_order(&mut self, order: BitOrder) { - self.regs.set_bit_order(order); - } - - /// Set the bit order of transmission (MSB/LSB first) using the builder - /// pattern - /// - /// This only affects the order of bits within each byte. Bytes are always - /// transferred in little endian order from the 32-bit DATA register. - #[inline] - pub fn bit_order(mut self, order: BitOrder) -> Self { - self.set_bit_order(order); - self - } - - /// Get the baud rate - /// - /// The returned baud rate may not exactly match what was set. - #[inline] - pub fn get_baud(&mut self) -> Hertz { - self.regs.get_baud(self.freq) - } - - /// Set the baud rate - /// - /// This function will calculate the best BAUD register setting based on the - /// stored GCLK frequency and desired baud rate. The maximum baud rate is - /// half the GCLK frequency. The minimum baud rate is the GCLK frequency / - /// 512. Values outside this range will saturate at the extremes. - #[inline] - pub fn set_baud(&mut self, baud: impl Into) { - self.regs.set_baud(self.freq, baud); - } - - /// Set the baud rate using the builder API - /// - /// This function will calculate the best BAUD register setting based on the - /// stored GCLK frequency and desired baud rate. The maximum baud rate is - /// half the GCLK frequency. The minimum baud rate is the GCLK frequency / - /// 512. Values outside this range will saturate at the extremes. - #[inline] - pub fn baud(mut self, baud: impl Into) -> Self { - self.set_baud(baud); - self - } - - /// Read the enabled state of the immediate buffer overflow notification - /// - /// If set to true, an [`Error::Overflow`] will be issued as soon as an - /// overflow occurs. Otherwise, it will not be issued until its place within - /// the data stream. - #[inline] - pub fn get_ibon(&self) -> bool { - self.regs.get_ibon() - } - - /// Enable or disable the immediate buffer overflow notification - /// - /// If set to true, an [`Error::Overflow`] will be issued as soon as an - /// overflow occurs. Otherwise, it will not be issued until its place within - /// the data stream. - #[inline] - pub fn set_ibon(&mut self, enabled: bool) { - self.regs.set_ibon(enabled); - } - - /// Enable or disable the immediate buffer overflow notification using the - /// builder API - /// - /// If set to true, an [`Error::Overflow`] will be issued as soon as an - /// overflow occurs. Otherwise, it will not be issued until its place within - /// the data stream. - #[inline] - pub fn ibon(mut self, enabled: bool) -> Self { - self.set_ibon(enabled); - self - } - - /// Read the enable state of run in standby mode - #[inline] - pub fn get_run_in_standby(&self) -> bool { - self.regs.get_run_in_standby() - } - - /// Enable or disable run in standby mode - #[inline] - pub fn set_run_in_standby(&mut self, enabled: bool) { - self.regs.set_run_in_standby(enabled); - } - - /// Enable or disable run in standby mode using the builder API - #[inline] - pub fn run_in_standby(mut self, enabled: bool) -> Self { - self.set_run_in_standby(enabled); - self - } - - /// Enable the SPI peripheral - /// - /// SPI transactions are not possible until the peripheral is enabled. - /// This function is limited to [`ValidConfig`]s. - #[inline] - pub fn enable(mut self) -> Spi - where - Self: ValidConfig, - { - self.regs.rx_enable(); - self.regs.enable(); + fn change_length(self) -> Spi { Spi { - config: self, - capability: P::Capability::default(), + regs: self.regs, + pads: self.pads, + capability: self.capability, + mode: self.mode, + size: PhantomData, + freq: self.freq, } } -} -#[cfg(feature = "min-samd51g")] -impl Config -where - P: ValidPads, - M: OpMode, -{ - /// Get the transaction length - #[inline] - pub fn get_dyn_length(&self) -> u8 { - self.regs.get_length() - } - - /// Set the transaction length - /// - /// Write the LENGTH register to set the transaction length. If the length - /// is zero, it will be set to 1. - #[inline] - pub fn set_dyn_length(&mut self, length: u8) { - self.regs.set_length(length); - } - - /// Set the transaction length using the builder API + /// Change the transaction [`Length`] /// - /// Write the LENGTH register to set the transaction length. If the length - /// is zero, it will be set to 1. - #[inline] - pub fn dyn_length(mut self, length: u8) -> Self { - self.set_dyn_length(length); - self - } -} - -//============================================================================= -// AnyConfig -//============================================================================= - -/// Type class for all possible [`Config`] types -/// -/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for -/// [`Config`] types. See the `AnyKind` documentation for more details on the -/// pattern. -/// -/// In addition to the normal, `AnyKind` associated types. This trait also -/// copies the [`Sercom`], [`Capability`] and [`Word`] types, to make it easier -/// to apply bounds to these types at the next level of abstraction. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -/// [type class]: crate::typelevel#type-classes -pub trait AnyConfig: Is> { - type Sercom: Sercom; - type Pads: ValidPads; - type Capability: Capability; - type OpMode: OpMode; - type Size: Size; - type Word: 'static; -} - -/// Type alias to recover the specific [`Config`] type from an implementation of -/// [`AnyConfig`] -pub type SpecificConfig = - Config<::Pads, ::OpMode, ::Size>; - -impl Sealed for Config -where - P: ValidPads, - M: OpMode, - Z: Size, -{ -} - -impl AnyConfig for Config -where - P: ValidPads, - M: OpMode, - Z: Size, -{ - type Sercom = P::Sercom; - type Pads = P; - type Capability = P::Capability; - type OpMode = M; - type Size = Z; - type Word = Z::Word; -} - -impl AsRef for Config -where - P: ValidPads, - M: OpMode, - Z: Size, -{ - #[inline] - fn as_ref(&self) -> &Self { - self - } -} - -impl AsMut for Config -where - P: ValidPads, - M: OpMode, - Z: Size, -{ - #[inline] - fn as_mut(&mut self) -> &mut Self { - self - } -} - -//============================================================================= -// ValidConfig -//============================================================================= - -/// Marker trait for valid SPI [`Config`]urations -/// -/// A functional SPI peripheral must have, at a minimum, an SCLK pad and -/// either a Data In or a Data Out pad. Dependeing on the [`OpMode`], an SS -/// pad may also be required. -/// -/// The `ValidConfig` trait is implemented only for valid combinations of -/// [`Pads`] and [`OpMode`]. No [`Config`] is valid if the SCK pad is [`NoneT`] -/// or if both the Data In and Data Out pads are `NoneT`. When in [`Master`] -/// `OpMode`, the `SS` pad must be `NoneT`, while in [`MasterHWSS`] or -/// [`Slave`] [`OpMode`], the SS pad must be [`SomePad`]. -pub trait ValidConfig: AnyConfig {} - -impl ValidConfig for Config -where - P: ValidPads, - Z: Size, -{ -} - -impl ValidConfig for Config -where - P: ValidPads, - Z: Size, - P::SS: SomePad, -{ -} - -impl ValidConfig for Config -where - P: ValidPads, - Z: Size, - P::SS: SomePad, -{ -} - -//============================================================================= -// Spi -//============================================================================= - -/// An enabled SPI peripheral that can perform transactions -/// -/// See the [`impl_ehal`] documentation for details on the implementations of -/// the embedded HAL traits, which vary based on [`Size`] and [`Capability`]. -pub struct Spi -where - C: ValidConfig, - A: Capability, -{ - config: C, - capability: A, -} - -/// Get a shared reference to the underlying [`Config`] struct -/// -/// This can be used to call the various `get_*` functions on `Config` -impl AsRef> for Spi -where - C: ValidConfig, - A: Capability, -{ - #[inline] - fn as_ref(&self) -> &SpecificConfig { - self.config.as_ref() - } -} - -impl Spi -where - C: ValidConfig, - A: Capability, -{ - /// Obtain a pointer to the `DATA` register. Necessary for DMA transfers. + /// Changing the transaction [`Length`] while is enabled is permissible but + /// dangerous. If you have sent or received *any* bytes at the current + /// [`Length`], you **must** wait for a TXC flag before changing to a new + /// [`Length`]. + #[cfg(feature = "min-samd51g")] #[inline] - #[cfg(feature = "dma")] - pub(super) fn data_ptr(&self) -> *mut C::Word - where - C::Size: Size, - { - self.config.as_ref().data_ptr() + pub fn length(mut self) -> Spi { + self.regs.set_length(L::LENGTH); + self.change_length() } /// Change the transaction [`Length`] @@ -1205,25 +693,12 @@ where /// dangerous. If you have sent or received *any* bytes at the current /// [`Length`], you **must** wait for a TXC flag before changing to a new /// [`Length`]. - #[inline] #[cfg(feature = "min-samd51g")] - pub fn length(self) -> Spi, A> - where - Config: ValidConfig, - { - Spi { - config: self.config.into().length(), - capability: self.capability, - } - } - - /// 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() + pub fn dyn_length(mut self, length: u8) -> Spi { + let length = if length > 0 { length } else { 1 }; + self.regs.set_length(length); + self.change_length() } /// Update the SPI configuration. @@ -1232,60 +707,77 @@ where /// some registers are enable-protected. This may interrupt any ongoing /// transactions. #[inline] - pub fn reconfigure(&mut self, update: impl FnOnce(&mut SpecificConfig)) { - self.config.as_mut().regs.disable(); - update(self.config.as_mut()); - self.config.as_mut().regs.enable(); + pub fn reconfigure(&mut self, update: impl FnOnce(&mut Reconfig)) { + self.regs.disable(); + let ctrla = self.regs.get_ctrla(); + let baud = self.regs.get_baud(self.freq); + let mut reconfig = Reconfig { ctrla, baud }; + update(&mut reconfig); + self.regs.set_ctrla(reconfig.ctrla); + self.regs.set_baud(self.freq, reconfig.baud); + self.regs.enable(); } /// Enable interrupts for the specified flags #[inline] pub fn enable_interrupts(&mut self, flags: Flags) { - self.config.as_mut().regs.enable_interrupts(flags) + self.regs.enable_interrupts(flags) } /// Disable interrupts for the specified flags #[inline] pub fn disable_interrupts(&mut self, flags: Flags) { - self.config.as_mut().regs.disable_interrupts(flags); + self.regs.disable_interrupts(flags); } /// Read the interrupt flags #[inline] pub fn read_flags(&self) -> Flags { - self.config.as_ref().regs.read_flags() + self.regs.read_flags() } /// Clear the corresponding interrupt flags /// - /// Only the ERROR, SSL and TXC flags can be cleared. + /// Only the `ERROR`, `SSL` and `TXC` flags can be cleared. /// - /// **Note:** The implementation of of [`serial::Write::flush`] waits on and - /// clears the `TXC` flag. Manually clearing this flag could cause it to - /// hang indefinitely. + /// **⚠️Warning⚠️:** The implementation of of [`serial::Write::flush`] waits + /// on and clears the `TXC` flag. Manually clearing this flag could + /// cause it to hang indefinitely. /// /// [`serial::Write::flush`]: embedded_hal::serial::Write::flush #[inline] pub fn clear_flags(&mut self, flags: Flags) { - self.config.as_mut().regs.clear_flags(flags); + self.regs.clear_flags(flags); } /// Read the error status flags #[inline] pub fn read_status(&self) -> Status { - self.config.as_ref().regs.read_status() + self.regs.read_status() } /// Clear the corresponding error status flags #[inline] pub fn clear_status(&mut self, status: Status) { - self.config.as_mut().regs.clear_status(status); + self.regs.clear_status(status); } /// Try to read the interrupt flags, but first check the error status flags. #[inline] pub fn read_flags_errors(&self) -> Result { - self.config.as_ref().regs.read_flags_errors() + self.regs.read_flags_errors() + } + + /// Private interface to read from the DATA register + #[inline] + pub(super) fn _read_data(&mut self) -> DataWidth { + self.regs.read_data() + } + + /// Private interface to write to the DATA register + #[inline] + pub(super) fn _write_data(&mut self, data: DataWidth) { + self.regs.write_data(data); } /// Read from the DATA register @@ -1295,7 +787,7 @@ where /// this module. #[inline] pub unsafe fn read_data(&mut self) -> DataWidth { - self.config.as_mut().regs.read_data() + self._read_data() } /// Write to the DATA register @@ -1305,44 +797,34 @@ where /// module. #[inline] pub unsafe fn write_data(&mut self, data: DataWidth) { - self.config.as_mut().regs.write_data(data); + self._write_data(data); } - /// Disable the SPI peripheral and return the [`Config`] struct + /// Reset the SPI peripheral and return the [`Config`] struct #[inline] - pub fn disable(mut self) -> C { - self.config.as_mut().regs.rx_disable(); - self.config.as_mut().regs.disable(); - self.config + pub fn disable(mut self) -> (P::Sercom, P) { + self.regs.reset(); + (self.regs.sercom, self.pads) } } #[cfg(feature = "min-samd51g")] -impl Spi +impl Spi where - C: ValidConfig, - A: Capability, + P: ValidPads, + M: OpMode, { - /// Return the current transaction length - /// - /// Read the LENGTH register to determine the current transaction length - #[inline] - pub fn get_dyn_length(&self) -> u8 { - self.config.as_ref().get_dyn_length() - } - - /// Set the transaction length - /// - /// Write the LENGTH register to set the transaction length. Panics if the - /// length is zero. + /// Change the transaction [`Length`] /// - /// Changing the transaction `LENGTH` while is enabled is permissible but + /// Changing the transaction [`Length`] while is enabled is permissible but /// dangerous. If you have sent or received *any* bytes at the current - /// `LENGTH`, you **must** wait for a TXC flag before changing to a new - /// `LENGTH`. + /// [`Length`], you **must** wait for a TXC flag before changing to a new + /// [`Length`]. + #[cfg(feature = "min-samd51g")] #[inline] pub fn set_dyn_length(&mut self, length: u8) { - self.config.as_mut().set_dyn_length(length); + let length = if length > 0 { length } else { 1 }; + self.regs.set_length(length); } } @@ -1365,29 +847,22 @@ where /// [type class]: crate::typelevel#type-classes pub trait AnySpi: Is> { type Sercom: Sercom; - type Pads: ValidPads; + type Pads: ValidPads; type Capability: Capability; type OpMode: OpMode; - type Size: Size; - type Word: 'static; - type Config: ValidConfig< - Sercom = Self::Sercom, - Pads = Self::Pads, - Capability = Self::Capability, - OpMode = Self::OpMode, - Size = Self::Size, - Word = Self::Word, - >; + type Size: Size; + type Word: PrimInt + AsPrimitive; } /// Type alias to recover the specific [`Spi`] type from an implementation of /// [`AnySpi`] -pub type SpecificSpi = Spi<::Config, ::Capability>; +pub type SpecificSpi = Spi<::Pads, ::OpMode, ::Size>; -impl AsRef for Spi +impl AsRef for Spi where - C: ValidConfig, - A: Capability, + P: ValidPads, + M: OpMode, + Z: Size, { #[inline] fn as_ref(&self) -> &Self { @@ -1395,10 +870,11 @@ where } } -impl AsMut for Spi +impl AsMut for Spi where - C: ValidConfig, - A: Capability, + P: ValidPads, + M: OpMode, + Z: Size, { #[inline] fn as_mut(&mut self) -> &mut Self { @@ -1406,19 +882,24 @@ where } } -impl Sealed for Spi +impl Sealed for Spi where - C: ValidConfig, - A: Capability, + P: ValidPads, + M: OpMode, + Z: Size, { } -impl AnySpi for Spi { - type Sercom = C::Sercom; - type Pads = C::Pads; - type Capability = C::Capability; - type OpMode = C::OpMode; - type Size = C::Size; - type Word = C::Word; - type Config = C; +impl AnySpi for Spi +where + P: ValidPads, + M: OpMode, + Z: Size, +{ + type Sercom = P::Sercom; + type Pads = P; + type Capability = P::Capability; + type OpMode = M; + type Size = Z; + type Word = Z::Word; } diff --git a/hal/src/sercom/spi/char_size.rs b/hal/src/sercom/spi/char_size.rs index 4a605b85f29a..cd2e7cb380f9 100644 --- a/hal/src/sercom/spi/char_size.rs +++ b/hal/src/sercom/spi/char_size.rs @@ -4,6 +4,8 @@ //! [`Config`]: super::Config //! [`Size`]: super::Size +use num_traits::{AsPrimitive, PrimInt}; + use crate::typelevel::Sealed; //============================================================================= @@ -22,9 +24,9 @@ use crate::typelevel::Sealed; /// /// [type-level enum]: crate::typelevel#type-level-enums /// [type-level function]: crate::typelevel#type-level-functions -pub trait CharSize: Sealed { +pub trait CharSize: Sealed + Default { /// Word size for the character size - type Word: 'static; + type Word: PrimInt + AsPrimitive; /// Register bit pattern for the corresponding `CharSize` const BITS: u8; @@ -38,10 +40,12 @@ pub trait CharSize: Sealed { pub type Word = ::Word; /// [`CharSize`] variant for 8-bit transactions -pub enum EightBit {} +#[derive(Default)] +pub struct EightBit; /// [`CharSize`] variant for 9-bit transactions -pub enum NineBit {} +#[derive(Default)] +pub struct NineBit; impl Sealed for EightBit {} impl CharSize for EightBit { diff --git a/hal/src/sercom/spi/config.rs b/hal/src/sercom/spi/config.rs new file mode 100644 index 000000000000..3cb04862acc3 --- /dev/null +++ b/hal/src/sercom/spi/config.rs @@ -0,0 +1,424 @@ +use core::marker::PhantomData; + +use embedded_hal::spi; +pub use embedded_hal::spi::{Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; + +use crate::sercom::{Sercom, SomePad, APB_CLK_CTRL}; +use crate::time::Hertz; +use crate::typelevel::{NoneT, Sealed}; + +use super::{ + BitOrder, Capability, CtrlA, DefaultSize, DynCapability, Master, MasterHWSS, OpMode, Registers, + Size, Slave, Spi, ValidPads, +}; + +#[cfg(any(feature = "samd11", feature = "samd21"))] +use super::CharSize; +#[cfg(feature = "min-samd51g")] +use super::{DynLength, StaticLength}; + +//============================================================================= +// Config +//============================================================================= + +/// A configurable SPI peripheral in its disabled state +/// +/// See the [module-level](super) documentation for more details on declaring +/// and instantiating `Pads` types. +pub struct Config +where + P: ValidPads, + M: OpMode, + Z: Size, +{ + regs: Registers, + pads: P, + mode: PhantomData, + size: Z, + freq: Hertz, + baud: Hertz, + ctrla: CtrlA, +} + +impl Config

{ + /// Create a new [`Config`] in the default configuration + /// + /// This function will enable the corresponding APB clock, reset the + /// [`Sercom`] peripheral, and return a [`Config`] in the default + /// configuration. The default [`OpMode`] is [`Master`], while the default + /// [`Size`] is an + #[cfg_attr( + any(feature = "samd11", feature = "samd21"), + doc = "[`EightBit`] [`CharSize`]" + )] + #[cfg_attr(feature = "min-samd51g", doc = "`EightBit` `CharSize`")] + /// for SAMD11 and SAMD21 chips or a + #[cfg_attr(any(feature = "samd11", feature = "samd21"), doc = "`Length` of `U1`")] + #[cfg_attr(feature = "min-samd51g", doc = "[`Length`] of `U1`")] + /// for SAMx5x chips. Note that [`Config`] takes ownership of both the + /// PAC [`Sercom`] struct as well as the [`Pads`]. + /// + /// Users must configure GCLK manually. The `freq` parameter represents the + /// GCLK frequency for this [`Sercom`] instance. + #[inline] + pub fn new( + apb_clk_ctrl: &APB_CLK_CTRL, + mut sercom: P::Sercom, + pads: P, + freq: impl Into, + ) -> Self { + sercom.enable_apb_clock(apb_clk_ctrl); + let freq = freq.into(); + let (dipo, dopo) = P::DIPO_DOPO; + Self { + regs: Registers { sercom }, + pads, + mode: PhantomData, + size: DefaultSize::default(), + freq, + baud: Hertz(freq.0 / 4), + ctrla: CtrlA::default(dipo, dopo), + } + } +} + +impl Config +where + P: ValidPads, + M: OpMode, + Z: Size, +{ + /// Obtain a reference to the PAC `SERCOM` struct + /// + /// Directly accessing the `SERCOM` could break the invariants of the + /// type-level tracking in this module, so it is unsafe. + #[inline] + pub unsafe fn sercom(&self) -> &P::Sercom { + &self.regs.sercom + } + + /// Consume the [`Config`], reset the peripheral, and return the [`Sercom`] + /// and [`Pads`] + #[inline] + pub fn free(mut self) -> (P::Sercom, P) { + self.regs.reset(); + (self.regs.sercom, self.pads) + } + + /// Change the [`OpMode`] + #[inline] + pub fn op_mode(self) -> Config { + Config { + regs: self.regs, + pads: self.pads, + mode: PhantomData, + size: self.size, + ctrla: self.ctrla, + freq: self.freq, + baud: self.baud, + } + } + + /// Change the [`Size`] + #[inline] + fn size(self, size: Z2) -> Config { + Config { + regs: self.regs, + pads: self.pads, + mode: self.mode, + size, + ctrla: self.ctrla, + freq: self.freq, + baud: self.baud, + } + } + + /// Change the [`CharSize`] + #[cfg(any(feature = "samd11", feature = "samd21"))] + #[inline] + pub fn char_size(self) -> Config { + self.size(C::default()) + } + + /// Change the transaction [`Length`] + #[cfg(feature = "min-samd51g")] + #[inline] + pub fn length(self) -> Config { + self.size(L::default()) + } + + /// Allow the transaction [`Length`] to be set dynamically + /// + /// The `LENGTH` register will be set to `length`. If `length == 0`, it will + /// be set to 1. + #[cfg(feature = "min-samd51g")] + #[inline] + pub fn dyn_length(self, length: u8) -> Config { + let size = DynLength::new(length); + self.size(size) + } + + /// Set the clock polarity using the builder pattern + #[inline] + pub fn cpol(mut self, cpol: Polarity) -> Self { + self.ctrla.cpol = cpol; + self + } + + /// Set the clock phase using the builder pattern + #[inline] + pub fn cpha(mut self, cpha: Phase) -> Self { + self.ctrla.cpha = cpha; + self + } + + /// Set the SPI mode (clock polarity & phase) using the builder pattern + #[inline] + pub fn spi_mode(mut self, mode: spi::Mode) -> Self { + self.ctrla.cpol = mode.polarity; + self.ctrla.cpha = mode.phase; + self + } + + /// Set the bit order of transmission (MSB/LSB first) using the builder + /// pattern + /// + /// This only affects the order of bits within each byte. Bytes are always + /// transferred in little endian order from the 32-bit DATA register. + #[inline] + pub fn bit_order(mut self, bit_order: BitOrder) -> Self { + self.ctrla.bit_order = bit_order; + self + } + + /// Set the baud rate using the builder API + /// + /// This function will calculate the best BAUD register setting based on the + /// stored GCLK frequency and desired baud rate. The maximum baud rate is + /// half the GCLK frequency. The minimum baud rate is the GCLK frequency / + /// 512. Values outside this range will saturate at the extremes. + #[inline] + pub fn baud(mut self, baud: impl Into) -> Self { + self.baud = baud.into(); + self + } + + /// Enable or disable the immediate buffer overflow notification using the + /// builder API + /// + /// If set to true, an [`Error::Overflow`] will be issued as soon as an + /// overflow occurs. Otherwise, it will not be issued until its place within + /// the data stream. + #[inline] + pub fn ibon(mut self, enabled: bool) -> Self { + self.ctrla.ibon = enabled; + self + } + + /// Enable or disable run in standby mode using the builder API + #[inline] + pub fn run_in_standby(mut self, enabled: bool) -> Self { + self.ctrla.run_in_standby = enabled; + self + } + + /// Enable the SPI peripheral + /// + /// SPI transactions are not possible until the peripheral is enabled. + /// This function is limited to [`ValidConfig`]s. + #[inline] + pub fn enable(mut self) -> Spi + where + Self: ValidConfig, + { + self.regs.reset(); + self.regs.set_op_mode(M::MODE, M::MSSEN); + self.regs.set_ctrla(self.ctrla); + #[cfg(any(feature = "samd11", feature = "samd21"))] + self.regs.set_char_size(Z::BITS); + #[cfg(feature = "min-samd51g")] + self.regs.set_length(self.size.length()); + self.regs.set_baud(self.freq, self.baud); + if P::Capability::DYN != DynCapability::Tx { + self.regs.rx_enable(); + } + self.regs.enable(); + Spi { + regs: self.regs, + pads: self.pads, + capability: P::Capability::default(), + mode: PhantomData, + size: PhantomData, + freq: self.freq, + } + } +} + +//============================================================================= +// ValidConfig +//============================================================================= + +/// Marker trait for valid SPI [`Config`]urations +/// +/// A functional SPI peripheral must have, at a minimum, an SCLK pad and +/// either a Data In or a Data Out pad. Dependeing on the [`OpMode`], an SS +/// pad may also be required. +/// +/// The `ValidConfig` trait is implemented only for valid combinations of +/// [`Pads`] and [`OpMode`]. No [`Config`] is valid if the SCK pad is [`NoneT`] +/// or if both the Data In and Data Out pads are `NoneT`. When in [`Master`] +/// `OpMode`, the `SS` pad must be `NoneT`, while in [`MasterHWSS`] or +/// [`Slave`] [`OpMode`], the SS pad must be [`SomePad`]. +pub trait ValidConfig: Sealed {} + +impl Sealed for Config +where + P: ValidPads, + M: OpMode, + Z: Size, +{ +} + +impl ValidConfig for Config +where + P: ValidPads, + Z: Size, +{ +} + +impl ValidConfig for Config +where + P: ValidPads, + Z: Size, + P::SS: SomePad, +{ +} + +impl ValidConfig for Config +where + P: ValidPads, + Z: Size, + P::SS: SomePad, +{ +} + +//============================================================================= +// Reconfig +//============================================================================= + +pub struct Reconfig { + pub(super) ctrla: CtrlA, + pub(super) baud: Hertz, +} + +impl Reconfig { + /// Get the clock polarity + #[inline] + pub fn get_cpol(&mut self) -> Polarity { + self.ctrla.cpol + } + + /// Set the clock polarity + #[inline] + pub fn set_cpol(&mut self, cpol: Polarity) { + self.ctrla.cpol = cpol; + } + + /// Get the clock phase + #[inline] + pub fn get_cpha(&self) -> Phase { + self.ctrla.cpha + } + + /// Set the clock phase + #[inline] + pub fn set_cpha(&mut self, cpha: Phase) { + self.ctrla.cpha = cpha + } + + /// Get the SPI mode (clock polarity & phase) + #[inline] + pub fn get_spi_mode(&self) -> spi::Mode { + spi::Mode { + polarity: self.ctrla.cpol, + phase: self.ctrla.cpha, + } + } + + /// Set the SPI mode (clock polarity & phase) + #[inline] + pub fn set_spi_mode(&mut self, mode: spi::Mode) { + self.ctrla.cpol = mode.polarity; + self.ctrla.cpha = mode.phase; + } + + /// Get the bit order of transmission (MSB/LSB first) + /// + /// This only affects the order of bits within each byte. Bytes are always + /// transferred in little endian order from the 32-bit DATA register. + #[inline] + pub fn get_bit_order(&self) -> BitOrder { + self.ctrla.bit_order + } + + /// Set the bit order of transmission (MSB/LSB first) using the builder + /// pattern + /// + /// This only affects the order of bits within each byte. Bytes are always + /// transferred in little endian order from the 32-bit DATA register. + #[inline] + pub fn set_bit_order(&mut self, order: BitOrder) { + self.ctrla.bit_order = order; + } + + /// Get the baud rate + /// + /// The returned baud rate may not exactly match what was set. + #[inline] + pub fn get_baud(&mut self) -> Hertz { + self.baud + } + + /// Set the baud rate + /// + /// This function will calculate the best BAUD register setting based on the + /// stored GCLK frequency and desired baud rate. The maximum baud rate is + /// half the GCLK frequency. The minimum baud rate is the GCLK frequency / + /// 512. Values outside this range will saturate at the extremes. + #[inline] + pub fn set_baud(&mut self, baud: impl Into) { + self.baud = baud.into(); + } + + /// Read the enabled state of the immediate buffer overflow notification + /// + /// If set to true, an [`Error::Overflow`] will be issued as soon as an + /// overflow occurs. Otherwise, it will not be issued until its place within + /// the data stream. + #[inline] + pub fn get_ibon(&self) -> bool { + self.ctrla.ibon + } + + /// Enable or disable the immediate buffer overflow notification + /// + /// If set to true, an [`Error::Overflow`] will be issued as soon as an + /// overflow occurs. Otherwise, it will not be issued until its place within + /// the data stream. + #[inline] + pub fn set_ibon(&mut self, enabled: bool) { + self.ctrla.ibon = enabled; + } + + /// Read the enable state of run in standby mode + #[inline] + pub fn get_run_in_standby(&self) -> bool { + self.ctrla.run_in_standby + } + + /// Enable or disable run in standby mode + #[inline] + pub fn set_run_in_standby(&mut self, enabled: bool) { + self.ctrla.run_in_standby = enabled; + } +} diff --git a/hal/src/sercom/spi/impl_ehal_common.rs b/hal/src/sercom/spi/impl_ehal_common.rs new file mode 100644 index 000000000000..97def23ecc17 --- /dev/null +++ b/hal/src/sercom/spi/impl_ehal_common.rs @@ -0,0 +1,416 @@ +use core::cell::Cell; + +use embedded_hal::{blocking, serial, spi}; +use nb::Error::WouldBlock; +use num_traits::AsPrimitive; + +use super::*; + +//============================================================================= +// serial::Read +//============================================================================= + +/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in a [`MasterMode`] +/// +/// `serial::Read` is only implemented for `Spi` structs with `Rx` +/// [`Capability`]. In a `MasterMode`, `Read` has to initiate transactions, so +/// it keeps track of the transaction state. If a transaction is in progress, +/// it will wait on `RXC`. If not, it will wait on `DRE`, and then send `0`. +impl serial::Read for Spi +where + P: ValidPads, + M: MasterMode, + Z: AtomicSize, + DataWidth: AsPrimitive, +{ + type Error = Error; + + #[inline] + fn read(&mut self) -> nb::Result { + let in_progress = self.capability.in_progress; + let flags = self.read_flags_errors()?; + if !in_progress && flags.contains(Flags::DRE) { + self._write_data(0); + self.capability.in_progress = true; + Err(WouldBlock) + } else if in_progress && flags.contains(Flags::RXC) { + self.capability.in_progress = false; + Ok(self._read_data().as_()) + } else { + Err(WouldBlock) + } + } +} + +/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in [`Slave`] +/// [`OpMode`] +/// +/// `serial::Read` is only implemented for `Spi` structs with `Rx` +/// [`Capability`]. In `Slave` `OpMode`, `Read` does not have to initiate +/// transactions, so it does not have to store any internal state. It only has +/// to wait on `RXC`. +impl serial::Read for Spi +where + P: ValidPads, + Z: AtomicSize, + DataWidth: AsPrimitive, +{ + type Error = Error; + + #[inline] + fn read(&mut self) -> nb::Result { + let flags = self.read_flags_errors()?; + if flags.contains(Flags::RXC) { + Ok(self._read_data().as_()) + } else { + Err(WouldBlock) + } + } +} + +//============================================================================= +// serial::Write +//============================================================================= + +/// Implement [`serial::Write`] for [`Tx`] [`Spi`] structs +/// +/// `serial::Write` is only implemented for `Spi` structs with `Tx` +/// [`Capability`]. Because the `Capability` is `Tx`, this implementation never +/// reads the DATA register and ignores all buffer overflow errors. +impl serial::Write for Spi +where + P: ValidPads, + M: OpMode, + Z: AtomicSize, +{ + type Error = Error; + + #[inline] + fn write(&mut self, word: Z::Word) -> nb::Result<(), Error> { + // Ignore buffer overflow errors + if self.read_status().contains(Status::LENERR) { + Err(Error::LengthError.into()) + } else if self.read_flags().contains(Flags::DRE) { + self._write_data(word.as_()); + Ok(()) + } else { + Err(WouldBlock) + } + } + + #[inline] + fn flush(&mut self) -> nb::Result<(), Error> { + // Ignore buffer overflow errors + if self.read_status().contains(Status::LENERR) { + Err(Error::LengthError.into()) + } else if self.read_flags().contains(Flags::TXC) { + Ok(()) + } else { + Err(WouldBlock) + } + } +} + +//============================================================================= +// blocking::serial::Write +//============================================================================= + +impl blocking::serial::write::Default for Spi +where + P: ValidPads, + M: OpMode, + Z: AtomicSize, + Spi: serial::Write, +{ +} + +//============================================================================= +// spi::FullDuplex +//============================================================================= + +/// Implement [`spi::FullDuplex`] for [`Spi`] structs with [`AtomicSize`] +/// +/// `spi::FullDuplex` is only implemented when the `Spi` struct has [`Duplex`] +/// [`Capability`] and [`AtomicSize`]. The [`Word`] size used in the +/// implementation depends on the corresponding [`Size`]. +impl spi::FullDuplex for Spi +where + P: ValidPads, + M: OpMode, + Z: AtomicSize, + DataWidth: AsPrimitive, +{ + type Error = Error; + + #[inline] + fn read(&mut self) -> nb::Result { + let flags = self.read_flags_errors()?; + if flags.contains(Flags::RXC) { + Ok(self._read_data().as_()) + } else { + Err(WouldBlock) + } + } + + #[inline] + fn send(&mut self, word: Z::Word) -> nb::Result<(), Error> { + let flags = self.read_flags_errors()?; + if flags.contains(Flags::DRE) { + self._write_data(word.as_()); + Ok(()) + } else { + Err(WouldBlock) + } + } +} + +//============================================================================= +// Note on macros +//============================================================================= + +// Macros are necessary for the following implementations of the embedded HAL +// `blocking` traits because of a limitation in the Rust trait system. The +// compiler can't seem to recongnize that the `blocking::spi::*::Default` traits +// can never be implemented for [`Spi`] in downstream crates, because that would +// violate the orphan rules. + +//============================================================================= +// blocking::spi::Transfer +//============================================================================= + +macro_rules! impl_blocking_spi_transfer { + ( $($Size:ident),+ ) => { + $( + /// Implement [`Transfer`] for [`Spi`] structs that can [`Receive`] + /// + /// The transfer accepts a slice of primitive integers that varies + /// depending on [`Size`]. + /// + /// [`Transfer`]: blocking::spi::Transfer + impl blocking::spi::Transfer> for Spi + where + P: ValidPads, + P::Capability: Receive, + M: OpMode, + { + type Error = Error; + + #[inline] + fn transfer<'w>(&mut self, words: &'w mut [Word<$Size>]) -> Result<&'w [Word<$Size>], Error> { + blocking_transfer_atomic(self, words) + } + } + )+ + } +} +pub(super) use impl_blocking_spi_transfer; + +//============================================================================= +// blocking::spi::Write +//============================================================================= + +macro_rules! impl_blocking_spi_write { + ( $($Size:ident),+ ) => { + $( + + /// Implement [`Write`] for [`Spi`] structs with [`Duplex`] + /// [`Capability`] and an [`AtomicSize`] + /// + /// The transfer accepts a slice of primitive integers that varies + /// depending on [`Size`]. + /// + /// [`Write`]: blocking::spi::Write + impl blocking::spi::Write> for Spi + where + P: ValidPads, + P::Capability: Transmit, + M: OpMode, + { + type Error = Error; + + #[inline] + fn write(&mut self, words: &[Word<$Size>]) -> Result<(), Error> { + if P::Capability::DYN == DynCapability::Duplex { + blocking_write_atomic_duplex(self, words) + } else { + blocking_write_atomic_tx(self, words) + } + } + } + )+ + } +} +pub(super) use impl_blocking_spi_write; + +//============================================================================= +// blocking::spi::WriteIter +//============================================================================= + +#[cfg(feature = "unproven")] +macro_rules! impl_blocking_spi_write_iter { + ( $($Size:ident),+ ) => { + $( + /// Implement [`WriteIter`] for [`Spi`] structs with [`Duplex`] + /// [`Capability`] and an [`AtomicSize`] + /// + /// The transfer accepts a slice of primitive integers that varies + /// depending on [`Size`]. + /// + /// [`WriteIter`]: blocking::spi::WriteIter + #[cfg(feature = "unproven")] + impl blocking::spi::WriteIter> for Spi + where + P: ValidPads, + P::Capability: Transmit, + M: OpMode, + { + type Error = Error; + + #[inline] + fn write_iter(&mut self, words: WI) -> Result<(), Error> + where + WI: IntoIterator>, + { + if P::Capability::DYN == DynCapability::Duplex { + blocking_write_iter_atomic_duplex(self, words) + } else { + blocking_write_iter_atomic_tx(self, words) + } + } + } + )+ + }; +} +#[cfg(feature = "unproven")] +pub(super) use impl_blocking_spi_write_iter; + +//============================================================================= +// Helper functions +//============================================================================= + +#[inline] +pub(super) fn blocking_transfer_atomic<'w, S>( + spi: &mut S, + words: &'w mut [S::Word], +) -> Result<&'w [S::Word], Error> +where + S: AnySpi, + DataWidth: AsPrimitive, +{ + let spi = spi.as_mut(); + let cells = Cell::from_mut(words).as_slice_of_cells(); + let mut to_send = cells.iter(); + let mut to_recv = cells.iter(); + while to_recv.len() > 0 { + let flags = spi.read_flags_errors()?; + if to_send.len() > 0 && flags.contains(Flags::DRE) { + let word = match to_send.next() { + Some(cell) => cell.get(), + None => unreachable!(), + }; + spi._write_data(word.as_()); + } + if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) { + let word = spi._read_data().as_(); + match to_recv.next() { + Some(cell) => cell.set(word), + None => unreachable!(), + } + } + } + Ok(words) +} + +#[inline] +pub(super) fn blocking_write_atomic_duplex( + spi: &mut S, + words: &[S::Word], +) -> Result<(), Error> { + let spi = spi.as_mut(); + let mut to_send = words.iter(); + let mut to_recv = to_send.len(); + while to_recv > 0 { + let flags = spi.read_flags_errors()?; + if to_send.len() > 0 && flags.contains(Flags::DRE) { + let word = match to_send.next() { + Some(word) => *word, + None => unreachable!(), + }; + spi._write_data(word.as_()); + } + if to_recv > to_send.len() && flags.contains(Flags::RXC) { + spi._read_data(); + to_recv -= 1; + } + } + Ok(()) +} + +#[inline] +pub(super) fn blocking_write_atomic_tx( + spi: &mut S, + words: &[S::Word], +) -> Result<(), Error> { + let spi = spi.as_mut(); + for word in words { + loop { + if spi.read_status().contains(Status::LENERR) { + return Err(Error::LengthError); + } + if spi.read_flags().contains(Flags::DRE) { + spi._write_data(word.as_()); + break; + } + } + } + Ok(()) +} + +#[cfg(feature = "unproven")] +#[inline] +pub(super) fn blocking_write_iter_atomic_duplex(spi: &mut S, words: WI) -> Result<(), Error> +where + S: AnySpi, + WI: IntoIterator, +{ + let spi = spi.as_mut(); + for word in words.into_iter() { + loop { + let flags = spi.read_flags_errors()?; + if flags.contains(Flags::DRE) { + spi._write_data(word.as_()); + break; + } + } + loop { + let flags = spi.read_flags_errors()?; + if flags.contains(Flags::RXC) { + spi._read_data(); + break; + } + } + } + Ok(()) +} + +#[cfg(feature = "unproven")] +#[inline] +pub(super) fn blocking_write_iter_atomic_tx(spi: &mut S, words: WI) -> Result<(), Error> +where + S: AnySpi, + WI: IntoIterator, +{ + let spi = spi.as_mut(); + for word in words.into_iter() { + loop { + if spi.read_status().contains(Status::LENERR) { + return Err(Error::LengthError); + } + if spi.read_flags().contains(Flags::DRE) { + spi._write_data(word.as_()); + break; + } + } + } + Ok(()) +} diff --git a/hal/src/sercom/spi/impl_ehal_thumbv6m.rs b/hal/src/sercom/spi/impl_ehal_thumbv6m.rs index ebf502ac5fc1..c473622d6d08 100644 --- a/hal/src/sercom/spi/impl_ehal_thumbv6m.rs +++ b/hal/src/sercom/spi/impl_ehal_thumbv6m.rs @@ -68,410 +68,14 @@ //! These traits are implemented following all of the rules outlined above for //! the different [`Size`] and [`Capability`] options. -use embedded_hal::{blocking, serial, spi}; -use nb::Error::WouldBlock; -use num_traits::{AsPrimitive, PrimInt}; +use embedded_hal::blocking; +use super::impl_ehal_common::*; use super::*; -//============================================================================= -// serial::Read -//============================================================================= - -/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in a [`MasterMode`] -/// -/// `serial::Read` is only implemented for `Spi` structs with `Rx` -/// [`Capability`]. In a `MasterMode`, `Read` has to initiate transactions, so -/// it keeps track of the transaction state. If a transaction is in progress, -/// it will wait on `RXC`. If not, it will wait on `DRE`, and then send `0`. -impl serial::Read for Spi, Rx> -where - Config: ValidConfig, - P: ValidPads, - M: MasterMode, - C: CharSize, - C::Word: PrimInt, - u16: AsPrimitive, -{ - type Error = Error; - - #[inline] - fn read(&mut self) -> nb::Result { - let in_progress = self.capability.in_progress; - let flags = self.read_flags_errors()?; - if !in_progress && flags.contains(Flags::DRE) { - unsafe { self.write_data(0) }; - self.capability.in_progress = true; - Err(WouldBlock) - } else if in_progress && flags.contains(Flags::RXC) { - self.capability.in_progress = false; - unsafe { Ok(self.read_data().as_()) } - } else { - Err(WouldBlock) - } - } -} - -/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in [`Slave`] -/// [`OpMode`] -/// -/// `serial::Read` is only implemented for `Spi` structs with `Rx` -/// [`Capability`]. In `Slave` `OpMode`, `Read` does not have to initiate -/// transactions, so it does not have to store any internal state. It only has -/// to wait on `RXC`. -impl serial::Read for Spi, Rx> -where - Config: ValidConfig, - P: ValidPads, - C: CharSize, - C::Word: PrimInt, - u16: AsPrimitive, -{ - type Error = Error; - - /// Wait for an `RXC` flag, then read the word - #[inline] - fn read(&mut self) -> nb::Result { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - unsafe { Ok(self.read_data().as_()) } - } else { - Err(WouldBlock) - } - } -} - -//============================================================================= -// serial::Write -//============================================================================= - -/// Implement [`serial::Write`] for [`Tx`] [`Spi`] structs -/// -/// `serial::Write` is only implemented for `Spi` structs with `Tx` -/// [`Capability`]. Because the `Capability` is `Tx`, this implementation never -/// reads the DATA register and ignores all buffer overflow errors. -impl serial::Write for Spi -where - C: ValidConfig, - C::Word: PrimInt + AsPrimitive, -{ - type Error = Error; - - #[inline] - fn write(&mut self, word: C::Word) -> nb::Result<(), Error> { - // Ignore buffer overflow errors - if self.read_flags().contains(Flags::DRE) { - unsafe { self.write_data(word.as_()) }; - Ok(()) - } else { - Err(WouldBlock) - } - } - - #[inline] - fn flush(&mut self) -> nb::Result<(), Error> { - // Ignore buffer overflow errors - if self.read_flags().contains(Flags::TXC) { - Ok(()) - } else { - Err(WouldBlock) - } - } -} - -//============================================================================= -// blocking::serial::Write -//============================================================================= - -impl blocking::serial::write::Default for Spi -where - C: ValidConfig, - Spi: serial::Write, -{ -} - -//============================================================================= -// spi::FullDuplex -//============================================================================= - -/// Implement [`spi::FullDuplex`] for [`Spi`] structs with [`AtomicSize`] -/// -/// `spi::FullDuplex` is only implemented when the `Spi` struct has [`Duplex`] -/// [`Capability`]. The [`Word`] size used in the implementation depends on the -/// corresponding [`CharSize`]. -impl spi::FullDuplex for Spi -where - C: ValidConfig, - C::Word: PrimInt + AsPrimitive, - u16: AsPrimitive, -{ - type Error = Error; - - #[inline] - fn read(&mut self) -> nb::Result { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - unsafe { Ok(self.read_data().as_()) } - } else { - Err(WouldBlock) - } - } - - #[inline] - fn send(&mut self, word: C::Word) -> nb::Result<(), Error> { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::DRE) { - unsafe { self.write_data(word.as_()) }; - Ok(()) - } else { - Err(WouldBlock) - } - } -} - -//============================================================================= -// Note on macros -//============================================================================= - -// Macros are necessary for the following implementations of the embedded HAL -// `blocking` traits because of a limitation in the Rust trait system. The -// compiler can't seem to recongnize that the `blocking::spi::*::Default` traits -// can never be implemented for [`Spi`] in downstream crates, because that would -// violate the orphan rules. - -//============================================================================= -// blocking::spi::Transfer -//============================================================================= - -macro_rules! impl_blocking_spi_transfer { - ( $($CharSize:ident),+ ) => { - $( - /// Implement [`Transfer`] for [`Spi`] structs that can [`Receive`] - /// - /// The transfer accepts a slice of primitive integers, depending on - /// the [`CharSize`] (`u8` or `u16`). - /// - /// [`Transfer`]: blocking::spi::Transfer - impl blocking::spi::Transfer> for Spi, A> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - A: Receive, - { - type Error = Error; - - #[inline] - fn transfer<'w>(&mut self, words: &'w mut [Word<$CharSize>]) -> Result<&'w [Word<$CharSize>], Error> { - let cells = core::cell::Cell::from_mut(words).as_slice_of_cells(); - let mut to_send = cells.iter(); - let mut to_recv = cells.iter(); - while to_recv.len() > 0 { - let flags = self.read_flags_errors()?; - if to_send.len() > 0 && flags.contains(Flags::DRE) { - let word = match to_send.next() { - Some(cell) => cell.get(), - None => unreachable!(), - }; - self.config.as_mut().regs.write_data(word as u16); - } - if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) { - let word = self.config.as_mut().regs.read_data() as Word<$CharSize>; - match to_recv.next() { - Some(cell) => cell.set(word), - None => unreachable!(), - } - } - } - Ok(words) - } - } - )+ - } -} - impl_blocking_spi_transfer!(EightBit, NineBit); -//============================================================================= -// blocking::spi::Write -//============================================================================= - -macro_rules! impl_blocking_spi_write { - ( $($CharSize:ident),+ ) => { - $( - /// Implement [`Write`] for [`Spi`] structs with [`Duplex`] - /// [`Capability`] - /// - /// The transfer accepts a slice of primitive integers, depending on - /// the [`CharSize`] (`u8` or `u16`). - /// - /// [`Write`]: blocking::spi::Write - impl blocking::spi::Write> for Spi, Duplex> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write(&mut self, words: &[Word<$CharSize>]) -> Result<(), Error> { - // We are `Duplex`, so we must receive as many words as we send, - // otherwise we could trigger an overflow - let mut to_send = words.iter(); - let mut to_recv = to_send.len(); - while to_recv > 0 { - let flags = self.read_flags_errors()?; - if to_send.len() > 0 && flags.contains(Flags::DRE) { - let word = match to_send.next() { - Some(word) => *word, - None => unreachable!(), - }; - self.config.as_mut().regs.write_data(word as u16); - } - if to_recv > to_send.len() && flags.contains(Flags::RXC) { - self.config.as_mut().regs.read_data(); - to_recv -= 1; - } - } - Ok(()) - } - } - - /// Implement [`Write`] for [`Spi`] structs with [`Tx`] - /// [`Capability`] - /// - /// The transfer accepts a slice of primitive integers, depending on - /// the [`CharSize`] (`u8` or `u16`). - /// - /// Because the `Capability` is `Tx`, this implementation never - /// reads the DATA register and ignores all buffer overflow errors. - /// - /// [`Write`]: blocking::spi::Write - impl blocking::spi::Write> for Spi, Tx> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write(&mut self, words: &[Word<$CharSize>]) -> Result<(), Error> { - // We are `Tx`, so we don't have to consider reading at all, ever. - for word in words { - loop { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - return Err(Error::LengthError) - } else if self.read_flags().contains(Flags::DRE) { - self.config.as_mut().regs.write_data(*word as u16); - break - } - } - } - Ok(()) - } - } - )+ - } -} - impl_blocking_spi_write!(EightBit, NineBit); -//============================================================================= -// blocking::spi::WriteIter -//============================================================================= - -#[cfg(feature = "unproven")] -macro_rules! impl_blocking_spi_write_iter { - ( $($CharSize:ident),+ ) => { - $( - /// Implement [`WriteIter`] for [`Spi`] structs with [`Duplex`] - /// [`Capability`] - /// - /// The transfer accepts a slice of primitive integers, depending on - /// the [`CharSize`] (`u8` or `u16`). - /// - /// [`WriteIter`]: blocking::spi::WriteIter - impl blocking::spi::WriteIter> for Spi, Duplex> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write_iter(&mut self, words: WI) -> Result<(), Error> - where - WI: IntoIterator>, - { - // We are `Duplex`, so we must receive as many words as we send, - // otherwise we could trigger an overflow. However, we don't know - // how many words there are to start with, so we have to send and - // receive them one at a time. - for word in words.into_iter() { - loop { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::DRE) { - unsafe { self.write_data(word as u16) }; - break - } - } - loop { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - self.config.as_mut().regs.read_data() as Word<$CharSize>; - break - } - } - } - Ok(()) - } - } - - /// Implement [`WriteIter`] for [`Spi`] structs with [`Tx`] - /// [`Capability`] - /// - /// The transfer accepts a slice of primitive integers, depending on - /// the [`CharSize`] (`u8` or `u16`). - /// - /// Because the `Capability` is `Tx`, this implementation never - /// reads the DATA register and ignores all buffer overflow errors. - /// - /// [`WriteIter`]: blocking::spi::WriteIter - impl blocking::spi::WriteIter> for Spi, Tx> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write_iter(&mut self, words: WI) -> Result<(), Error> - where - WI: IntoIterator>, - { - // We are `Tx`, so we don't have to consider reading at all, ever. - for word in words.into_iter() { - loop { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - return Err(Error::LengthError) - } else if self.read_flags().contains(Flags::DRE) { - unsafe { self.write_data(word as u16) }; - break - } - } - } - Ok(()) - } - } - )+ - }; -} - #[cfg(feature = "unproven")] impl_blocking_spi_write_iter!(EightBit, NineBit); diff --git a/hal/src/sercom/spi/impl_ehal_thumbv7em.rs b/hal/src/sercom/spi/impl_ehal_thumbv7em.rs index 0e67a714726c..17ad38732557 100644 --- a/hal/src/sercom/spi/impl_ehal_thumbv7em.rs +++ b/hal/src/sercom/spi/impl_ehal_thumbv7em.rs @@ -90,230 +90,19 @@ //! These traits are implemented following all of the rules outlined above for //! the different [`Size`] and [`Capability`] options. -use embedded_hal::{blocking, serial, spi}; -use nb::Error::WouldBlock; -use num_traits::{AsPrimitive, PrimInt}; -use typenum::{U1, U2, U3, U4}; +use core::cell::Cell; +use core::cmp::min; -use crate::pac::sercom0::RegisterBlock; +use embedded_hal::blocking; +use typenum::{U1, U2, U3, U4}; +use super::impl_ehal_common::*; use super::*; -//============================================================================= -// serial::Read -//============================================================================= - -/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in a [`MasterMode`] -/// -/// `serial::Read` is only implemented for `Spi` structs with `Rx` -/// [`Capability`]. In a `MasterMode`, `Read` has to initiate transactions, so -/// it keeps track of the transaction state. If a transaction is in progress, -/// it will wait on `RXC`. If not, it will wait on `DRE`, and then send `0`. -impl serial::Read for Spi, Rx> -where - Config: ValidConfig, - P: ValidPads, - M: MasterMode, - L: Length, - L::Word: PrimInt, - u32: AsPrimitive, -{ - type Error = Error; - - #[inline] - fn read(&mut self) -> nb::Result { - let in_progress = self.capability.in_progress; - let flags = self.read_flags_errors()?; - if !in_progress && flags.contains(Flags::DRE) { - unsafe { self.write_data(0) }; - self.capability.in_progress = true; - Err(WouldBlock) - } else if in_progress && flags.contains(Flags::RXC) { - self.capability.in_progress = false; - unsafe { Ok(self.read_data().as_()) } - } else { - Err(WouldBlock) - } - } -} - -/// Implement [`serial::Read`] for [`Rx`] [`Spi`] structs in [`Slave`] -/// [`OpMode`] -/// -/// `serial::Read` is only implemented for `Spi` structs with `Rx` -/// [`Capability`]. In `Slave` `OpMode`, `Read` does not have to initiate -/// transactions, so it does not have to store any internal state. It only has -/// to wait on `RXC`. -impl serial::Read for Spi, Rx> -where - Config: ValidConfig, - P: ValidPads, - L: Length, - L::Word: PrimInt, - u32: AsPrimitive, -{ - type Error = Error; - - #[inline] - fn read(&mut self) -> nb::Result { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - unsafe { Ok(self.read_data().as_()) } - } else { - Err(WouldBlock) - } - } -} - -//============================================================================= -// serial::Write -//============================================================================= - -/// Implement [`serial::Write`] for [`Tx`] [`Spi`] structs -/// -/// `serial::Write` is only implemented for `Spi` structs with `Tx` -/// [`Capability`]. Because the `Capability` is `Tx`, this implementation never -/// reads the DATA register and ignores all buffer overflow errors. -impl serial::Write for Spi -where - C: ValidConfig, - C::Size: AtomicSize, - C::Word: PrimInt + AsPrimitive, -{ - type Error = Error; - - #[inline] - fn write(&mut self, word: C::Word) -> nb::Result<(), Error> { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - Err(Error::LengthError.into()) - } else if self.read_flags().contains(Flags::DRE) { - self.config.as_mut().regs.write_data(word.as_()); - Ok(()) - } else { - Err(WouldBlock) - } - } - - #[inline] - fn flush(&mut self) -> nb::Result<(), Error> { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - Err(Error::LengthError.into()) - } else if self.read_flags().contains(Flags::TXC) { - Ok(()) - } else { - Err(WouldBlock) - } - } -} - -//============================================================================= -// blocking::serial::Write -//============================================================================= - -impl blocking::serial::write::Default for Spi -where - C: ValidConfig, - Spi: serial::Write, -{ -} - -//============================================================================= -// spi::FullDuplex -//============================================================================= - -/// Implement [`spi::FullDuplex`] for [`Spi`] structs with [`AtomicSize`] -/// -/// `spi::FullDuplex` is only implemented when the `Spi` struct has [`Duplex`] -/// [`Capability`] and the transaction [`Length`] is `<= 4` bytes. When the -/// [`Length`] is `<= 4`, the [`Word`] is a primitive integer, with a size that -/// depends on the [`Length`] (`u8`, `u16` or `u32`). -impl spi::FullDuplex for Spi -where - C: ValidConfig, - C::Size: AtomicSize, - C::Word: PrimInt + AsPrimitive, - u32: AsPrimitive, -{ - type Error = Error; - - #[inline] - fn read(&mut self) -> nb::Result { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - Ok(self.config.as_mut().regs.read_data().as_()) - } else { - Err(WouldBlock) - } - } - - #[inline] - fn send(&mut self, word: C::Word) -> nb::Result<(), Error> { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::DRE) { - self.config.as_mut().regs.write_data(word.as_()); - Ok(()) - } else { - Err(WouldBlock) - } - } -} - //============================================================================= // blocking::spi::Transfer //============================================================================= -macro_rules! impl_blocking_spi_transfer { - ( $($Length:ident),+ ) => { - $( - - /// Implement [`Transfer`] for [`Spi`] structs that can [`Receive`] - /// and have an [`AtomicSize`] - /// - /// The transaction [`Length`] must be `<= 4`. The transfer accepts - /// a slice of primitive integers, depending on the `Length` - /// (`u8`, `u16` or `u32`). - /// - /// [`Transfer`]: blocking::spi::Transfer - impl blocking::spi::Transfer> for Spi, A> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - A: Receive, - { - type Error = Error; - - #[inline] - fn transfer<'w>(&mut self, words: &'w mut [Word<$Length>]) -> Result<&'w [Word<$Length>], Error> { - let cells = core::cell::Cell::from_mut(words).as_slice_of_cells(); - let mut to_send = cells.iter(); - let mut to_recv = cells.iter(); - while to_recv.len() > 0 { - let flags = self.read_flags_errors()?; - if to_send.len() > 0 && flags.contains(Flags::DRE) { - let word = match to_send.next() { - Some(cell) => cell.get(), - None => unreachable!(), - }; - self.config.as_mut().regs.write_data(word as u32); - } - if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) { - let word = self.config.as_mut().regs.read_data() as Word<$Length>; - match to_recv.next() { - Some(cell) => cell.set(word), - None => unreachable!(), - } - } - } - Ok(words) - } - } - )+ - } -} - impl_blocking_spi_transfer!(U1, U2, U3, U4); /// Implement [`Transfer`] for [`Spi`] structs that can [`Receive`] and have @@ -324,50 +113,21 @@ impl_blocking_spi_transfer!(U1, U2, U3, U4); /// is incorrect, it will panic. /// /// [`Transfer`]: blocking::spi::Transfer -impl blocking::spi::Transfer for Spi, A> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - L: GreaterThan4, - A: Receive, -{ - type Error = Error; - - #[inline] - fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> { - if buf.len() != L::USIZE { - panic!("Slice length does not equal SPI transfer length"); - } - let sercom = unsafe { self.config.as_ref().sercom() }; - transfer_slice(sercom, buf) - } -} - -/// Implement [`Transfer`] for [`Spi`] structs that can [`Receive`] and have -/// [`DynLength`] -/// -/// The transfer accepts a slice of `u8` with a length equal to the run-time -/// dynamic transaction length. If the slice length does not match the result -/// of [`Spi::get_dyn_length`], it will panic. -/// -/// [`Transfer`]: blocking::spi::Transfer -impl blocking::spi::Transfer for Spi, A> +impl blocking::spi::Transfer for Spi where - Config: ValidConfig, P: ValidPads, + P::Capability: Receive, M: OpMode, - A: Receive, + L: NonAtomicSize, { type Error = Error; #[inline] fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> { - if buf.len() != self.get_dyn_length() as usize { + if buf.len() != self.get_length() as usize { panic!("Slice length does not equal SPI transfer length"); } - let sercom = unsafe { self.config.as_ref().sercom() }; - transfer_slice(sercom, buf) + blocking_transfer_non_atomic(self, buf) } } @@ -375,90 +135,6 @@ where // blocking::spi::Write //============================================================================= -macro_rules! impl_blocking_spi_write { - ( $($Length:ident),+ ) => { - $( - - /// Implement [`Write`] for [`Spi`] structs with [`Duplex`] - /// [`Capability`] and an [`AtomicSize`] - /// - /// The transaction `Length` must be `<= 4`. The transfer accepts - /// a slice of primitive integers, depending on the `Length` - /// (`u8`, `u16` or `u32`). - /// - /// [`Write`]: blocking::spi::Write - impl blocking::spi::Write> for Spi, Duplex> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> { - // We are `Duplex`, so we must receive as many words as we send, - // otherwise we could trigger an overflow - let mut to_send = words.iter(); - let mut to_recv = to_send.len(); - while to_recv > 0 { - let flags = self.read_flags_errors()?; - if to_send.len() > 0 && flags.contains(Flags::DRE) { - let word = match to_send.next() { - Some(word) => *word, - None => unreachable!(), - }; - self.config.as_mut().regs.write_data(word as u32); - } - if to_recv > to_send.len() && flags.contains(Flags::RXC) { - self.config.as_mut().regs.read_data() as Word<$Length>; - to_recv -= 1; - } - } - Ok(()) - } - } - - /// Implement [`Write`] for [`Spi`] structs with [`Tx`] - /// [`Capability`] and an [`AtomicSize`] - /// - /// The transaction `Length` must be `<= 4`. The transfer accepts - /// a slice of primitive integers, depending on the `Length` - /// (`u8`, `u16` or `u32`). - /// - /// Because the `Capability` is `Tx`, this implementation never - /// reads the DATA register and ignores all buffer overflow errors. - /// - /// [`Write`]: blocking::spi::Write - impl blocking::spi::Write> for Spi, Tx> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> { - // We are `Tx`, so we don't have to consider reading at all, ever. - for word in words { - loop { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - return Err(Error::LengthError) - } else if self.read_flags().contains(Flags::DRE) { - self.config.as_mut().regs.write_data(*word as u32); - break - } - } - } - Ok(()) - } - } - )+ - } -} - impl_blocking_spi_write!(U1, U2, U3, U4); /// Implement [`Write`] for [`Spi`] structs with [`Duplex`] [`Capability`] and @@ -469,107 +145,25 @@ impl_blocking_spi_write!(U1, U2, U3, U4); /// it will panic. /// /// [`Write`]: blocking::spi::Write -impl blocking::spi::Write for Spi, Duplex> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - L: GreaterThan4, -{ - type Error = Error; - - #[inline] - fn write(&mut self, buf: &[u8]) -> Result<(), Error> { - if buf.len() != L::USIZE { - panic!("Slice length does not equal SPI transfer length"); - } - let sercom = unsafe { self.config.as_ref().sercom() }; - write_slice(sercom, buf, true) - } -} - -/// Implement [`Write`] for [`Spi`] structs with [`Tx`] [`Capability`] and long -/// transaction [`Length`]s -/// -/// The transaction [`Length`] must be `> 4`. The transfer accepts a `[u8]` with -/// a length equal to the transfer [`Length`]. If the slice length is incorrect, -/// it will panic. -/// -/// Because the `Capability` is `Tx`, this implementation never reads the DATA -/// register and ignores all buffer overflow errors. -/// -/// [`Write`]: blocking::spi::Write -impl blocking::spi::Write for Spi, Tx> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - L: GreaterThan4, -{ - type Error = Error; - - #[inline] - fn write(&mut self, buf: &[u8]) -> Result<(), Error> { - if buf.len() != L::USIZE { - panic!("Slice length does not equal SPI transfer length"); - } - let sercom = unsafe { self.config.as_ref().sercom() }; - write_slice(sercom, buf, false) - } -} - -/// Implement [`Write`] for [`Spi`] structs with [`Duplex`] [`Capability`] and -/// [`DynLength`] -/// -/// The transfer accepts a `[u8]` with a length equal to the run-time dynamic -/// transaction length. If the slice length does not match the result of -/// [`Spi::get_dyn_length`], it will panic. -/// -/// [`Write`]: blocking::spi::Write -impl blocking::spi::Write for Spi, Duplex> +impl blocking::spi::Write for Spi where - Config: ValidConfig, P: ValidPads, + P::Capability: Transmit, M: OpMode, + L: NonAtomicSize, { type Error = Error; #[inline] fn write(&mut self, buf: &[u8]) -> Result<(), Error> { - if buf.len() != self.get_dyn_length() as usize { + if buf.len() != self.get_length() as usize { panic!("Slice length does not equal SPI transfer length"); } - let sercom = unsafe { self.config.as_ref().sercom() }; - write_slice(sercom, buf, true) - } -} - -/// Implement [`Write`] for [`Spi`] structs with [`Tx`] [`Capability`] and -/// [`DynLength`] -/// -/// The transfer accepts a `[u8]` with a length equal to the run-time dynamic -/// transaction length. If the slice length does not match the result of -/// `Spi::get_dyn_length`], it will panic. -/// -/// Because the `Capability` is `Tx`, this implementation never reads the DATA -/// register and ignores all buffer overflow errors. -/// -/// [`Write`]: blocking::spi::Write -impl blocking::spi::Write for Spi, Tx> -where - Config: ValidConfig, - P: ValidPads, - M: OpMode, -{ - type Error = Error; - - #[inline] - fn write(&mut self, buf: &[u8]) -> Result<(), Error> { - if buf.len() != self.get_dyn_length() as usize { - panic!("Slice length does not equal SPI transfer length"); + if P::Capability::DYN == DynCapability::Duplex { + blocking_write_non_atomic_duplex(self, buf) + } else { + blocking_write_non_atomic_tx(self, buf) } - let sercom = unsafe { self.config.as_ref().sercom() }; - write_slice(sercom, buf, false) } } @@ -577,123 +171,25 @@ where // blocking::spi::WriteIter //============================================================================= -macro_rules! impl_blocking_spi_write_iter { - ( $($Length:ident),+ ) => { - $( - - /// Implement [`WriteIter`] for [`Spi`] structs with [`Duplex`] - /// [`Capability`] and an [`AtomicSize`] - /// - /// The transaction `Length` must be `<= 4`. The transfer accepts - /// a slice of primitive integers, depending on the `Length` - /// (`u8`, `u16` or `u32`). - /// - /// [`WriteIter`]: blocking::spi::WriteIter - #[cfg(feature = "unproven")] - impl blocking::spi::WriteIter> for Spi, Duplex> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write_iter(&mut self, words: WI) -> Result<(), Error> - where - WI: IntoIterator>, - { - // We are `Duplex`, so we must receive as many words as we send, - // otherwise we could trigger an overflow. However, we don't know - // how many words there are to start with, so we have to send and - // receive them one at a time. - for word in words.into_iter() { - loop { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::DRE) { - unsafe { self.write_data(word as u32) }; - break - } - } - loop { - let flags = self.read_flags_errors()?; - if flags.contains(Flags::RXC) { - self.config.as_mut().regs.read_data() as Word<$Length>; - break - } - } - } - Ok(()) - } - } - /// Implement [`WriteIter`] for [`Spi`] structs with [`Tx`] - /// [`Capability`] and an [`AtomicSize`] - /// - /// The transaction `Length` must be `<= 4`. The transfer accepts - /// a slice of primitive integers, depending on the `Length` - /// (`u8`, `u16` or `u32`). - /// - /// Because the `Capability` is `Tx`, this implementation never - /// reads the DATA register and ignores all buffer overflow errors. - /// - /// [`WriteIter`]: blocking::spi::WriteIter - #[cfg(feature = "unproven")] - impl blocking::spi::WriteIter> for Spi, Tx> - where - Config: ValidConfig, - P: ValidPads, - M: OpMode, - { - type Error = Error; - - #[inline] - fn write_iter(&mut self, words: WI) -> Result<(), Error> - where - WI: IntoIterator>, - { - // We are `Tx`, so we don't have to consider reading at all, ever. - for word in words.into_iter() { - loop { - // Ignore buffer overflow errors - if self.read_status().contains(Status::LENERR) { - return Err(Error::LengthError) - } else if self.read_flags().contains(Flags::DRE) { - unsafe { self.write_data(word as u32) }; - break - } - } - } - Ok(()) - } - } - )+ - }; -} - +#[cfg(feature = "unproven")] impl_blocking_spi_write_iter!(U1, U2, U3, U4); //============================================================================= // Helper functions //============================================================================= -/// Transfer a `[u8]` slice four bytes at a time -/// -/// This function exists to avoid both code duplication and monomorphization -/// bloat. It will take a `[u8]` and transfer it four bytes at a time. -fn transfer_slice<'w>(sercom: &RegisterBlock, buf: &'w mut [u8]) -> Result<&'w [u8], Error> { - let cells = core::cell::Cell::from_mut(buf).as_slice_of_cells(); +#[inline] +pub(super) fn blocking_transfer_non_atomic<'w, S: AnySpi>( + spi: &mut S, + buf: &'w mut [u8], +) -> Result<&'w [u8], Error> { + let spi = spi.as_mut(); + let cells = Cell::from_mut(buf).as_slice_of_cells(); let mut to_send = cells.iter(); let mut to_recv = cells.iter(); while to_recv.len() > 0 { - let errors = sercom.spim().status.read(); - if errors.bufovf().bit_is_set() { - return Err(Error::Overflow); - } - if errors.lenerr().bit_is_set() { - return Err(Error::LengthError); - } - let flags = sercom.spim().intflag.read(); - if to_send.len() > 0 && flags.dre().bit_is_set() { + let flags = spi.read_flags_errors()?; + if to_send.len() > 0 && flags.contains(Flags::DRE) { let mut bytes = [0; 4]; for byte in &mut bytes { match to_send.next() { @@ -701,12 +197,10 @@ fn transfer_slice<'w>(sercom: &RegisterBlock, buf: &'w mut [u8]) -> Result<&'w [ None => break, } } - let word = u32::from_le_bytes(bytes); - sercom.spim().data.write(|w| unsafe { w.data().bits(word) }); + spi._write_data(u32::from_le_bytes(bytes)); } - if to_recv.len() > to_send.len() && flags.rxc().bit_is_set() { - let word = sercom.spim().data.read().data().bits(); - let bytes = word.to_le_bytes(); + if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) { + let bytes = spi._read_data().to_le_bytes(); for byte in bytes.iter() { match to_recv.next() { Some(cell) => cell.set(*byte), @@ -718,26 +212,46 @@ fn transfer_slice<'w>(sercom: &RegisterBlock, buf: &'w mut [u8]) -> Result<&'w [ Ok(buf) } -/// Write a `[u8]` four bytes at a time -/// -/// This function exists to avoid both code duplication and monomorphization -/// bloat. It will take a `[u8]` and write four bytes at a time to the SPI on -/// every DRE flag. If the `duplex` argument is true, it will read as many times -/// as it writes. Otherwise, it will skip reading the `DATA` register entirely. -/// If `duplex` is false, buffer overflow errors are ignored -fn write_slice(sercom: &RegisterBlock, buf: &[u8], duplex: bool) -> Result<(), Error> { +#[inline] +pub(super) fn blocking_write_non_atomic_duplex( + spi: &mut S, + buf: &[u8], +) -> Result<(), Error> { + let spi = spi.as_mut(); let mut to_send = buf.iter(); - let mut to_recv: usize = if duplex { to_send.len() } else { 0 }; - loop { - let errors = sercom.spim().status.read(); - if duplex && errors.bufovf().bit_is_set() { - return Err(Error::Overflow); + let mut to_recv = to_send.len(); + while to_recv > 0 { + let flags = spi.read_flags_errors()?; + if to_send.len() > 0 && flags.contains(Flags::DRE) { + let mut bytes = [0; 4]; + for byte in &mut bytes { + match to_send.next() { + Some(d) => *byte = *d, + None => break, + } + } + spi._write_data(u32::from_le_bytes(bytes)); + } + if to_recv > to_send.len() && flags.contains(Flags::RXC) { + spi._read_data(); + to_recv -= min(to_recv - to_send.len(), 4); } - if errors.lenerr().bit_is_set() { + } + Ok(()) +} + +#[inline] +pub(super) fn blocking_write_non_atomic_tx( + spi: &mut S, + buf: &[u8], +) -> Result<(), Error> { + let spi = spi.as_mut(); + let mut to_send = buf.iter(); + while to_send.len() > 0 { + if spi.read_status().contains(Status::LENERR) { return Err(Error::LengthError); } - let flags = sercom.spim().intflag.read(); - if to_send.len() > 0 && flags.dre().bit_is_set() { + if spi.read_flags().contains(Flags::DRE) { let mut bytes = [0; 4]; for byte in &mut bytes { match to_send.next() { @@ -745,16 +259,7 @@ fn write_slice(sercom: &RegisterBlock, buf: &[u8], duplex: bool) -> Result<(), E None => break, } } - let word = u32::from_le_bytes(bytes); - sercom.spim().data.write(|w| unsafe { w.data().bits(word) }); - } - if to_recv > to_send.len() && flags.rxc().bit_is_set() { - sercom.spim().data.read().data().bits(); - let diff = to_recv - to_send.len(); - to_recv -= if diff < 4 { diff } else { 4 }; - } - if to_recv == 0 && to_send.len() == 0 { - break; + spi._write_data(u32::from_le_bytes(bytes)); } } Ok(()) diff --git a/hal/src/sercom/spi/length.rs b/hal/src/sercom/spi/length.rs index 8d0c543e8817..3a9a1c12b156 100644 --- a/hal/src/sercom/spi/length.rs +++ b/hal/src/sercom/spi/length.rs @@ -9,11 +9,16 @@ //! [`Config`]: super::Config //! [`Size`]: super::Size +use core::num::NonZeroU8; + +use num_traits::{AsPrimitive, PrimInt}; use seq_macro::seq; -use typenum::{Unsigned, U0, U1, U2, U3, U4}; +use typenum::{Unsigned, U1, U2, U3, U4}; use crate::typelevel::Sealed; +use super::NonAtomicSize; + //============================================================================= // Transaction length //============================================================================= @@ -39,55 +44,87 @@ use crate::typelevel::Sealed; /// [type-level function]: crate::typelevel#type-level-functions /// [`OpMode`]: super::OpMode /// [`AtomicSize`]: super::AtomicSize -pub trait Length: Sealed + Unsigned + 'static { +pub trait Length: Sealed + 'static { + fn length(&self) -> u8; + fn static_length() -> Option; /// Word size for the transaction length /// /// For lengths 1-4, this type is `u8`, `u16` or `u32`. For longer /// transactions, this type is `[u8, Self::USIZE]`. - type Word: 'static; + type Word: PrimInt + AsPrimitive; } /// Type alias to recover the [`Word`](Length::Word) type from an /// implementation of [`Length`] pub type Word = ::Word; -/// Marker type for a run-time dynamic [`Length`] -pub type DynLength = U0; - -impl Length for DynLength { - type Word = (); -} - /// Marker trait for statically known transaction [`Length`]s -pub trait StaticLength: Length {} - -impl StaticLength for U1 {} -impl Length for U1 { - type Word = u8; +pub trait StaticLength: Length + Unsigned { + const LENGTH: u8; } -impl StaticLength for U2 {} -impl Length for U2 { - type Word = u16; +macro_rules! impl_atomic_length { + ( $( ($Unsigned: ty, $Word: ty) ),+ ) => { + $( + impl Length for $Unsigned { + #[inline] + fn length(&self) -> u8 { + Self::U8 + } + #[inline] + fn static_length() -> Option { + NonZeroU8::new(Self::U8) + } + type Word = $Word; + } + impl StaticLength for $Unsigned { + const LENGTH: u8 = Self::U8; + } + )+ + }; } -impl StaticLength for U3 {} -impl Length for U3 { - type Word = u32; -} - -impl StaticLength for U4 {} -impl Length for U4 { - type Word = u32; -} - -/// Marker trait for transaction [`Length`]s greater than four -pub trait GreaterThan4: Length {} +impl_atomic_length!((U1, u8), (U2, u16), (U3, u32), (U4, u32)); seq!(N in 5..=255 { - impl StaticLength for typenum::U~N {} - impl GreaterThan4 for typenum::U~N {} impl Length for typenum::U~N { - type Word = [u8; typenum::U~N::USIZE]; + #[inline] + fn length(&self) -> u8 { + Self::U8 + } + #[inline] + fn static_length() -> Option { + NonZeroU8::new(Self::U8) + } + type Word = u8; + } + impl StaticLength for typenum::U~N { + const LENGTH: u8 = Self::U8; } }); + +/// Marker type for a run-time dynamic [`Length`] +pub struct DynLength(u8); + +impl DynLength { + pub(super) fn new(length: u8) -> Self { + let length = if length > 0 { length } else { 1 }; + Self(length) + } +} + +impl Sealed for DynLength {} + +impl Length for DynLength { + #[inline] + fn length(&self) -> u8 { + self.0 + } + #[inline] + fn static_length() -> Option { + None + } + type Word = u8; +} + +impl NonAtomicSize for DynLength {} diff --git a/hal/src/sercom/spi/reg.rs b/hal/src/sercom/spi/reg.rs index 372d8eadcd08..6cd4d5a11e85 100644 --- a/hal/src/sercom/spi/reg.rs +++ b/hal/src/sercom/spi/reg.rs @@ -1,6 +1,7 @@ use core::convert::TryInto; -use embedded_hal::spi; +use bitflags::bitflags; +use embedded_hal::spi::{Phase, Polarity}; #[cfg(any(feature = "samd11", feature = "samd21"))] use crate::pac::sercom0::SPI; @@ -15,7 +16,119 @@ use crate::pac::sercom0::spim::ctrla::MODE_A; use crate::sercom::Sercom; use crate::time::Hertz; -use super::{BitOrder, DataWidth, Error, Flags, Phase, Polarity, Status}; +//============================================================================= +// BitOrder +//============================================================================= + +/// Define the bit order of transactions +#[repr(u8)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum BitOrder { + MsbFirst, + LsbFirst, +} + +//============================================================================= +// Flags +//============================================================================= + +bitflags! { + /// Interrupt bit flags for SPI transactions + /// + /// The available interrupt flags are `DRE`, `RXC`, `TXC`, `SSL` and + /// `ERROR`. The binary format of the underlying bits exactly matches the + /// `INTFLAG` register. + pub struct Flags: u8 { + const DRE = 0x01; + const TXC = 0x02; + const RXC = 0x04; + const SSL = 0x08; + const ERROR = 0x80; + } +} + +//============================================================================= +// Status +//============================================================================= + +bitflags! { + /// Status bit flags for SPI transactions + /// + /// The available status flags are `BUFOVF` and `LENERR`. The binary format + /// of the underlying bits exactly matches the `STATUS` register. + pub struct Status: u16 { + const BUFOVF = 0x0004; + const LENERR = 0x0800; + } +} + +impl Status { + /// Check the [`Status`] flags for [`Error`] conditions + pub fn check_errors(&self) -> Result<(), Error> { + // Buffer overflow has priority + if self.contains(Status::BUFOVF) { + Err(Error::Overflow) + } else if self.contains(Status::LENERR) { + Err(Error::LengthError) + } else { + Ok(()) + } + } +} + +//============================================================================= +// Error +//============================================================================= + +/// Error `enum` for SPI transactions +/// +/// The SPI peripheral only has two error types, buffer overflow and transaction +/// length error. +#[derive(Debug)] +pub enum Error { + Overflow, + LengthError, +} + +//============================================================================= +// DataWidth +//============================================================================= + +/// Type alias for the width of the `DATA` register +#[cfg(any(feature = "samd11", feature = "samd21"))] +pub type DataWidth = u16; + +/// Type alias for the width of the `DATA` register +#[cfg(feature = "min-samd51g")] +pub type DataWidth = u32; + +//============================================================================== +// CtrlA +//============================================================================== + +pub(super) struct CtrlA { + pub bit_order: BitOrder, + pub cpol: Polarity, + pub cpha: Phase, + pub dipo: u8, + pub dopo: u8, + pub ibon: bool, + pub run_in_standby: bool, +} + +impl CtrlA { + pub fn default(dipo: u8, dopo: u8) -> Self { + Self { + bit_order: BitOrder::MsbFirst, + cpol: Polarity::IdleLow, + cpha: Phase::CaptureOnFirstTransition, + dipo, + dopo, + ibon: false, + run_in_standby: false, + } + } +} //============================================================================== // Registers @@ -61,16 +174,6 @@ impl Registers { self.spi().data.as_ptr() as *mut _ } - /// Configure the DIPO and DOPO values - #[inline] - pub fn set_dipo_dopo(&mut self, dipo_dopo: (u8, u8)) { - let (dipo, dopo) = dipo_dopo; - self.spi().ctrla.modify(|_, w| unsafe { - w.dipo().bits(dipo); - w.dopo().bits(dopo) - }); - } - /// Configure the SPI operating mode /// /// For maximum flexibility, this module chooses to always operate in 32-bit @@ -117,100 +220,62 @@ impl Registers { .modify(|_, w| unsafe { w.chsize().bits(bits) }); } - /// Get the clock polarity - #[inline] - pub fn get_cpol(&self) -> Polarity { - let cpol = self.spi().ctrla.read().cpol().bit(); - match cpol { - false => Polarity::IdleLow, - true => Polarity::IdleHigh, - } - } - - /// Set the clock polarity - #[inline] - pub fn set_cpol(&mut self, cpol: Polarity) { - let cpol = match cpol { - Polarity::IdleLow => false, - Polarity::IdleHigh => true, + pub fn get_ctrla(&self) -> CtrlA { + let raw = self.spi().ctrla.read(); + let bit_order = if raw.dord().bit_is_clear() { + BitOrder::MsbFirst + } else { + BitOrder::LsbFirst }; - self.spi().ctrla.modify(|_, w| w.cpol().bit(cpol)); - } - - /// Get the clock phase - #[inline] - pub fn get_cpha(&self) -> Phase { - let cpha = self.spi().ctrla.read().cpha().bit(); - match cpha { - false => Phase::CaptureOnFirstTransition, - true => Phase::CaptureOnSecondTransition, - } - } - - /// Set the clock phase - #[inline] - pub fn set_cpha(&mut self, cpha: Phase) { - let cpha = match cpha { - Phase::CaptureOnFirstTransition => false, - Phase::CaptureOnSecondTransition => true, + let cpol = if raw.cpol().bit_is_clear() { + Polarity::IdleLow + } else { + Polarity::IdleHigh }; - self.spi().ctrla.modify(|_, w| w.cpha().bit(cpha)); - } - - /// Get the SPI mode (clock polarity & phase) - #[inline] - pub fn get_spi_mode(&self) -> spi::Mode { - let reg = self.spi().ctrla.read(); - let cpol = reg.cpol().bit(); - let cpha = reg.cpha().bit(); - let polarity = match cpol { - false => Polarity::IdleLow, - true => Polarity::IdleHigh, - }; - let phase = match cpha { - false => Phase::CaptureOnFirstTransition, - true => Phase::CaptureOnSecondTransition, + let cpha = if raw.cpha().bit_is_clear() { + Phase::CaptureOnFirstTransition + } else { + Phase::CaptureOnSecondTransition }; - spi::Mode { polarity, phase } + let dipo = raw.dipo().bits(); + let dopo = raw.dopo().bits(); + let ibon = raw.ibon().bit(); + let run_in_standby = raw.runstdby().bit(); + CtrlA { + bit_order, + cpol, + cpha, + dipo, + dopo, + ibon, + run_in_standby, + } } - /// Set the SPI mode (clock polarity & phase) - #[inline] - pub fn set_spi_mode(&mut self, mode: spi::Mode) { - let cpol = match mode.polarity { - Polarity::IdleLow => false, - Polarity::IdleHigh => true, - }; - let cpha = match mode.phase { - Phase::CaptureOnFirstTransition => false, - Phase::CaptureOnSecondTransition => true, - }; + pub fn set_ctrla(&mut self, ctrla: CtrlA) { self.spi().ctrla.modify(|_, w| { - w.cpol().bit(cpol); - w.cpha().bit(cpha) + match ctrla.bit_order { + BitOrder::LsbFirst => w.dord().clear_bit(), + BitOrder::MsbFirst => w.dord().set_bit(), + }; + match ctrla.cpol { + Polarity::IdleLow => w.cpol().clear_bit(), + Polarity::IdleHigh => w.cpol().set_bit(), + }; + match ctrla.cpha { + Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), + Phase::CaptureOnSecondTransition => w.cpha().set_bit(), + }; + #[allow(unused_unsafe)] + unsafe { + w.dipo().bits(ctrla.dipo) + }; + unsafe { w.dopo().bits(ctrla.dopo) }; + w.ibon().bit(ctrla.ibon); + w.runstdby().bit(ctrla.run_in_standby) }); } - /// Get the bit order of transmission (MSB/LSB first) - #[inline] - pub fn get_bit_order(&self) -> BitOrder { - let order = self.spi().ctrla.read().dord().bits(); - match order { - false => BitOrder::MsbFirst, - true => BitOrder::LsbFirst, - } - } - - /// Set the bit order of transmission (MSB/LSB first) - #[inline] - pub fn set_bit_order(&mut self, order: BitOrder) { - let order = match order { - BitOrder::MsbFirst => false, - BitOrder::LsbFirst => true, - }; - self.spi().ctrla.modify(|_, w| w.dord().bit(order)); - } - /// Get the baud rate #[inline] pub fn get_baud(&mut self, freq: Hertz) -> Hertz { @@ -230,30 +295,6 @@ impl Registers { .modify(|_, w| unsafe { w.baud().bits(bits) }); } - /// Get the enable state of the immediate buffer overflow notification - #[inline] - pub fn get_ibon(&self) -> bool { - self.spi().ctrla.read().ibon().bit() - } - - /// Set the enable state of the immediate buffer overflow notification - #[inline] - pub fn set_ibon(&mut self, enabled: bool) { - self.spi().ctrla.modify(|_, w| w.ibon().bit(enabled)); - } - - /// Get run in standby mode - #[inline] - pub fn get_run_in_standby(&self) -> bool { - self.spi().ctrla.read().runstdby().bit() - } - - /// Set run in standby mode - #[inline] - pub fn set_run_in_standby(&mut self, set: bool) { - self.spi().ctrla.modify(|_, w| w.runstdby().bit(set)); - } - /// Enable interrupts for the specified flags #[inline] pub fn enable_interrupts(&mut self, flags: Flags) { @@ -277,13 +318,6 @@ impl Registers { while self.spi().syncbusy.read().ctrlb().bit_is_set() {} } - /// Disable the receiver - #[inline] - pub fn rx_disable(&mut self) { - self.spi().ctrlb.modify(|_, w| w.rxen().clear_bit()); - while self.spi().syncbusy.read().ctrlb().bit_is_set() {} - } - /// Enable the peripheral #[inline] pub fn enable(&mut self) { diff --git a/hal/src/sercom/spi_future.rs b/hal/src/sercom/spi_future.rs index 3b713eea4872..4f87c9014690 100644 --- a/hal/src/sercom/spi_future.rs +++ b/hal/src/sercom/spi_future.rs @@ -381,7 +381,7 @@ where #[inline] pub fn new(resource: R, buf: B) -> Self { let buf_len = buf.as_ref().len(); - let spi_len = resource.spi().transaction_length() as usize; + let spi_len = resource.spi().get_length() as usize; if (spi_len > 4 && buf_len != spi_len) || (spi_len <= 4 && buf_len % spi_len != 0) { panic!("Invalid SpiFuture buffer length"); } @@ -428,7 +428,7 @@ where #[inline] fn step(&self) -> usize { - let len = self.resource.spi().transaction_length() as usize; + let len = self.resource.spi().get_length() as usize; min(len, 4) } @@ -436,22 +436,10 @@ where /// /// This will assert the SS pin, if present, and enable the `DRE` and `RXC` /// interrupts. - /// - /// When the [`Spi`] struct is [`Tx`]-only, this will also perform reads of - /// the `DATA` register to clear any accumulated overflow errors. Omitting - /// this step would lead to spurious `RXC` interrupts. #[inline] pub fn start(&mut self) { self.resource.assert_ss(); let spi = self.resource.spi_mut(); - if Self::CAP == DynCapability::Tx { - // Clear any existing RXC or BUFOVF flags - unsafe { - spi.read_data(); - spi.read_data(); - spi.read_data(); - } - } spi.enable_interrupts(Flags::DRE | Flags::RXC); } @@ -482,7 +470,7 @@ where } } let word = u32::from_le_bytes(bytes); - unsafe { spi.write_data(word as Data) }; + spi._write_data(word as Data); self.sent += step; } if self.sent >= buf.len() { @@ -509,9 +497,9 @@ where Err(err) => return Err(err), } if self.rcvd < self.sent { - let buf = unsafe { buf.get_unchecked_mut(self.rcvd..) }; + let buf = buf.get_mut(self.rcvd..).unwrap(); let mut data = buf.into_iter(); - let word = unsafe { spi.read_data() as u32 }; + let word = spi._read_data() as u32; let bytes = word.to_le_bytes(); let mut iter = bytes.iter(); for _ in 0..step {