From 854e3e1dc271c307e3d2ff983e6c2463fe7ac2cc Mon Sep 17 00:00:00 2001 From: Bradley Harden Date: Mon, 21 Jun 2021 16:00:28 -0400 Subject: [PATCH] Remove the sercom::v2::Pad type (#451) The `sercom::v2::Pad` type was a simple wrapper around a `gpio::v2::Pin` type. Its purpose was to ensure that each `Pin` was properly configured to act as the corresponding SERCOM `Pad`. However, one of my goals in designing the `v2::spi` API was to avoid the need for users to manually convert `Pin` types to `Pad` types. As that module progressed, it became clear that the `v2::Pad` type served no real purpose. All the same constraints could be imposed using a slightly different approach to type-level programming. Remove the `v2::Pad` type and modify the `sercom::v2::pad` module accordingly. Update the `v1::Pad` type as well. Rename some of the type-level items in the `v2::pad` module and improve the documentation. Update the `sercom::v1` peripheral APIs to accept both `v1::Pad` types and the equivalent of a `v2::Pad`, which is simply a properly configured `v2::Pin`. Finally, redefine the type parameters of `v2::spi::Pads` to be `OptionalPad`s rather than `OptionalPinId`s, and provide a new `PadsFromIds` to make up for it. This change increases consistency in the definition of the `spi::Pads` type, and it allows me to remove a significant amount of trait boilerplate needed to make it all work. --- boards/feather_m0/src/lib.rs | 23 +- boards/p1am_100/src/lib.rs | 61 +- boards/wio_terminal/src/display.rs | 2 +- hal/src/gpio/v2/pin.rs | 34 +- hal/src/sercom/v1/pads.rs | 230 ++++--- hal/src/sercom/v2/pad.rs | 621 ++++-------------- hal/src/thumbv6m/sercom/v1/i2c.rs | 55 +- hal/src/thumbv6m/sercom/v1/spi.rs | 389 ++++++----- hal/src/thumbv6m/sercom/v1/uart.rs | 356 +++++----- hal/src/thumbv6m/sercom/v2.rs | 2 +- .../sercom/v2/{pad_info.rs => impl_pad.rs} | 9 +- hal/src/thumbv6m/sercom/v2/spi.rs | 536 ++++++--------- hal/src/thumbv7em/sercom/v1/i2c.rs | 46 +- hal/src/thumbv7em/sercom/v1/spi.rs | 436 ++++++------ hal/src/thumbv7em/sercom/v1/uart.rs | 388 ++++++----- hal/src/thumbv7em/sercom/v2.rs | 2 +- .../sercom/v2/{pad_info.rs => impl_pad.rs} | 8 +- hal/src/thumbv7em/sercom/v2/spi.rs | 404 ++++++------ hal/src/typelevel.rs | 18 +- 19 files changed, 1585 insertions(+), 2035 deletions(-) rename hal/src/thumbv6m/sercom/v2/{pad_info.rs => impl_pad.rs} (97%) rename hal/src/thumbv7em/sercom/v2/{pad_info.rs => impl_pad.rs} (97%) diff --git a/boards/feather_m0/src/lib.rs b/boards/feather_m0/src/lib.rs index e2a9542730e1..31d17cde64af 100644 --- a/boards/feather_m0/src/lib.rs +++ b/boards/feather_m0/src/lib.rs @@ -8,10 +8,8 @@ pub use embedded_hal as ehal; pub use hal::pac; use hal::clock::GenericClockController; -use hal::sercom::v2::pad::PinToPad; -use hal::sercom::v2::spi; +use hal::sercom::v2::{spi, Sercom4}; use hal::sercom::{I2CMaster3, UART0}; -use hal::spi_pads_from_pins; use hal::time::Hertz; #[cfg(feature = "usb")] @@ -105,14 +103,14 @@ hal::bsp_pins!( /// The I2C data line name: sda aliases: { - AlternateD: Sda + AlternateC: Sda } } PA23 { /// The I2C clock line name: scl aliases: { - AlternateD: Scl + AlternateC: Scl } } @@ -207,7 +205,10 @@ hal::bsp_pins!( }, ); -type SpiPads = spi_pads_from_pins!(Sercom4, DI = Miso, DO = Mosi, CK = Sclk); +/// SPI pads for the labelled SPI peripheral +/// +/// You can use these pads with other, user-defined [`spi::Config`]urations. +pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// @@ -238,7 +239,7 @@ pub fn spi_master( } /// I2C master for the labelled SDA & SCL pins -pub type I2C = I2CMaster3, PinToPad>; +pub type I2C = I2CMaster3; /// Convenience for setting up the labelled SDA, SCL pins to /// operate as an I2C master running at the specified frequency. @@ -253,13 +254,13 @@ pub fn i2c_master( let gclk0 = clocks.gclk0(); let clock = &clocks.sercom3_core(&gclk0).unwrap(); let baud = baud.into(); - let sda = sda.into().into(); - let scl = scl.into().into(); + let sda = sda.into(); + let scl = scl.into(); I2CMaster3::new(clock, baud, sercom3, pm, sda, scl) } /// UART device for the labelled RX & TX pins -pub type Uart = UART0, PinToPad, (), ()>; +pub type Uart = UART0; /// Convenience for setting up the labelled RX, TX pins to /// operate as a UART device running at the specified baud. @@ -274,7 +275,7 @@ pub fn uart( let gclk0 = clocks.gclk0(); let clock = &clocks.sercom0_core(&gclk0).unwrap(); let baud = baud.into(); - let pads = (uart_rx.into().into(), uart_tx.into().into()); + let pads = (uart_rx.into(), uart_tx.into()); UART0::new(clock, baud, sercom0, pm, pads) } diff --git a/boards/p1am_100/src/lib.rs b/boards/p1am_100/src/lib.rs index 45315086be2e..b70c8bcc328b 100644 --- a/boards/p1am_100/src/lib.rs +++ b/boards/p1am_100/src/lib.rs @@ -15,8 +15,7 @@ pub use hal::common::*; pub use hal::target_device as pac; use hal::clock::GenericClockController; -use hal::sercom::v2::pad::PinToPad; -use hal::sercom::v2::{spi, Sercom0}; +use hal::sercom::v2::{spi, Sercom1, Sercom2}; use hal::sercom::{I2CMaster0, UART5}; use hal::time::Hertz; @@ -202,6 +201,10 @@ bsp_pins!( const BASE_CONTROLLER_FREQ: Hertz = Hertz(1000000); const BASE_CONTROLLER_SPI_MODE: embedded_hal::spi::Mode = spi::MODE_2; +pub type Spi0Pads = spi::Pads; + +pub type Spi0 = spi::Spi>; + /// Convenience for setting up the labeled SPI0 peripheral. /// SPI0 has the P1AM base controller connected. /// This powers up SERCOM1 and configures it for talking to the @@ -213,37 +216,43 @@ pub fn base_controller_spi( sck: Spi0Sck, mosi: Spi0Mosi, miso: Spi0Miso, -) -> sercom::v2::spi::Spi { +) -> Spi0 { let gclk0 = &clocks.gclk0(); - let core_clock = &clocks.sercom1_core(&gclk0).unwrap(); + let clock = &clocks.sercom1_core(&gclk0).unwrap(); let pads = spi::Pads::default().sclk(sck).data_in(miso).data_out(mosi); - spi::Config::new(pm, sercom1, pads, core_clock.freq()) + spi::Config::new(pm, sercom1, pads, clock.freq()) .baud(BASE_CONTROLLER_FREQ) .spi_mode(BASE_CONTROLLER_SPI_MODE) .enable() } +type SdPads = spi::Pads; + +pub type SdSpi = spi::Spi>; + /// Convenience for setting up the labeled SPI2 peripheral. /// SPI2 has the microSD card slot connected. -/// This powers up SERCOM4 and configures it for talking to the +/// This powers up SERCOM2 and configures it for talking to the /// base controller. pub fn sdmmc_spi>( clocks: &mut GenericClockController, bus_speed: F, - sercom4: pac::SERCOM4, + sercom2: pac::SERCOM2, pm: &mut pac::PM, sck: SdSck, mosi: SdMosi, miso: SdMiso, -) -> sercom::v2::spi::Spi { +) -> SdSpi { let gclk0 = &clocks.gclk0(); - let core_clock = &clocks.sercom4_core(&gclk0).unwrap(); + let clock = &clocks.sercom2_core(&gclk0).unwrap(); let pads = spi::Pads::default().sclk(sck).data_in(miso).data_out(mosi); - spi::Config::new(pm, sercom4, pads, core_clock.freq()) + spi::Config::new(pm, sercom2, pads, clock.freq()) .baud(bus_speed) .enable() } +pub type I2C = I2CMaster0; + /// Convenience for setting up the labelled SDA, SCL pins to /// operate as an I2C master running at the specified frequency. pub fn i2c_master>( @@ -253,22 +262,13 @@ pub fn i2c_master>( pm: &mut pac::PM, sda: Sda, scl: Scl, -) -> I2CMaster0< - impl hal::sercom::AnyPad, - impl hal::sercom::AnyPad, -> { +) -> I2C { let gclk0 = clocks.gclk0(); - I2CMaster0::new( - &clocks.sercom0_core(&gclk0).unwrap(), - bus_speed.into(), - sercom0, - pm, - hal::sercom::v2::Pad::::from(sda), - hal::sercom::v2::Pad::::from(scl), - ) + let clock = &clocks.sercom0_core(&gclk0).unwrap(); + I2CMaster0::new(clock, bus_speed.into(), sercom0, pm, sda, scl) } -pub type Uart = UART5, PinToPad, (), ()>; +pub type Uart = UART5; /// Convenience for setting up the labelled RX, TX pins to /// operate as a UART device running at the specified baud. @@ -281,14 +281,8 @@ pub fn uart>( tx: UartTx, ) -> Uart { let gclk0 = clocks.gclk0(); - - UART5::new( - &clocks.sercom5_core(&gclk0).unwrap(), - baud.into(), - sercom5, - pm, - (rx.into(), tx.into()), - ) + let clock = &clocks.sercom5_core(&gclk0).unwrap(); + UART5::new(clock, baud.into(), sercom5, pm, (rx, tx)) } #[cfg(feature = "usb")] @@ -300,7 +294,6 @@ pub fn usb_allocator( dp: UsbDp, ) -> UsbBusAllocator { let gclk0 = clocks.gclk0(); - let usb_clock = &clocks.usb(&gclk0).unwrap(); - - UsbBusAllocator::new(UsbBus::new(usb_clock, pm, dm, dp, usb)) + let clock = &clocks.usb(&gclk0).unwrap(); + UsbBusAllocator::new(UsbBus::new(clock, pm, dm, dp, usb)) } diff --git a/boards/wio_terminal/src/display.rs b/boards/wio_terminal/src/display.rs index 5af1cc38083a..bb8a3123a9fc 100644 --- a/boards/wio_terminal/src/display.rs +++ b/boards/wio_terminal/src/display.rs @@ -36,7 +36,7 @@ pub struct Display { pub backlight: Pc5>, } -pub type SpiPads = spi::Pads; +pub type SpiPads = spi::PadsFromIds; /// Type alias for the ILI9341 LCD display. pub type LCD = Ili9341< diff --git a/hal/src/gpio/v2/pin.rs b/hal/src/gpio/v2/pin.rs index e3df9161b5a7..0c51a3e90d83 100644 --- a/hal/src/gpio/v2/pin.rs +++ b/hal/src/gpio/v2/pin.rs @@ -815,22 +815,6 @@ where /// [`AnyKind`]: crate::typelevel#anykind-trait-pattern pub type SpecificPin

= Pin<

::Id,

::Mode>; -/// Type alias to recover the [`PinId`] type from an implementation of -/// [`AnyPin`] -/// -/// See the [`AnyKind`] documentation for more details on the pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -pub type SpecificPinId

=

::Id; - -/// Type alias to recover the [`PinMode`] type from an implementation of -/// [`AnyPin`] -/// -/// See the [`AnyKind`] documentation for more details on the pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -pub type SpecificPinMode

=

::Mode; - impl AsRef

for SpecificPin

{ #[inline] fn as_ref(&self) -> &P { @@ -862,17 +846,25 @@ impl AsMut

for SpecificPin

{ /// See the [`OptionalKind`] documentation for more details on the pattern. /// /// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPin: Sealed {} -impl OptionalPin for NoneT {} -impl OptionalPin for P {} +pub trait OptionalPin: Sealed { + type Id: OptionalPinId; +} + +impl OptionalPin for NoneT { + type Id = NoneT; +} + +impl OptionalPin for P { + type Id = P::Id; +} /// Type-level equivalent of `Some(PinId)` /// /// See the [`OptionalKind`] documentation for more details on the pattern. /// /// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePin: OptionalPin + AnyPin {} -impl SomePin for P {} +pub trait SomePin: AnyPin {} +impl SomePin for P {} //============================================================================== // Embedded HAL traits diff --git a/hal/src/sercom/v1/pads.rs b/hal/src/sercom/v1/pads.rs index 37aece4840c4..57e389a5cac1 100644 --- a/hal/src/sercom/v1/pads.rs +++ b/hal/src/sercom/v1/pads.rs @@ -4,53 +4,112 @@ //! [`sercom::v2`](crate::sercom::v2) module. This API will eventually be //! deprecated and removed. //! -//! To recreate the `v1` API with `v2` types, this module defines its own [Pad] -//! type. The `v1::Pad` type is actually just a wrapper around a [`v2::Pad`] -//! with modified type parameters. Where the `v2::Pad` takes a `PinId` as its -//! third type parameter, a `v1::Pad` takes a configured [`Pin`]. The -//! `SercomXPadY` types of the original, `v1` API are recreated as type aliases -//! of the form `type SercomXPadY = Pad`. +//! To recreate the `v1` API with `v2` types, this module defines its own +//! [`Pad`] type, which is just a wrapper around a [`Pin`] configured as a +//! SERCOM pad. The `SercomXPadY` types of the original, `v1` API are recreated +//! as type aliases of the form //! -//! The [`PadPin`] trait is kept intact, but additional options are also -//! provided to create `v1::Pad`s. They can now be created directly from both -//! `v1` and `v2` `Pin`s and they can be converted [`From`]/[`Into`] `v2::Pad`s. +//! ``` +//! type SercomXPadY = Pad +//! ``` +//! +//! Use the [`PadPin`] trait to construct `Pad`s. The corresponding `Pin` can be +//! recovered using the [`free`] method. +//! +//! ``` +//! use atsamd_hal::pac::Peripherals; +//! use atsamd_hal::gpio::v1::GpioExt; +//! use atsamd_hal::sercom::v1::{PadPin, Sercom0Pad0}; +//! +//! let peripherals = Peripherals::take().unwrap(); +//! let mut parts = peripherals.PORT.split(); +//! let pad: Sercom0Pad0<_> = parts.pa8.into_pad(&mut parts.port); +//! let pin = pad.free(); +//! ``` +//! +//! [`free`]: Pad::free use core::marker::PhantomData; use paste::paste; -use crate::gpio::v1::{IntoFunction, Pin}; -use crate::gpio::v2::{AnyPin, PinMode, SpecificPinId}; -use crate::gpio::Port; +use crate::gpio::v1::{self, IntoFunction, Pin, Port}; +use crate::gpio::v2::{self, AnyPin, PinId, PinMode}; +use crate::sercom::v2::*; use crate::typelevel::Sealed; -pub use crate::sercom::v2::{self, *}; +//============================================================================== +// IsPad +//============================================================================== -/// A GPIO pin configured to act as a SERCOM pad +/// Extend implementations of [`IsPad`] from [`v2::Pin`]s to [`v1::Pin`]s +impl IsPad for v1::Pin +where + I: PinId, + M: PinMode, + v1::Pin: AnyPin, + v2::Pin: IsPad, +{ + type Sercom = as IsPad>::Sercom; + type PadNum = as IsPad>::PadNum; +} + +//============================================================================== +// Pad +//============================================================================== + +/// A GPIO [`Pin`] configured to act as a SERCOM [`Pad`] /// -/// This type is actually just a wrapper around a [`v2::Pad`] with modified type -/// parameters. Where the `v2::Pad` takes a `PinId` as its third type parameter, -/// a `v1::Pad` takes a configured [`Pin`]. The `SercomXPadY` types of the -/// original, `v1` API are recreated as type aliases of the form -/// `type SercomXPadY = Pad`. +/// This type is just a wrapper around a correctly-configured [`Pin`]. The +/// `SercomXPadY` types of the original, `v1` API are recreated as type aliases +/// of the form +/// +/// ``` +/// type SercomXPadY = Pad +/// ``` pub struct Pad where S: Sercom, N: PadNum, - P: AnyPin, - P::Id: GetPadMode, + P: IsPad, { - pad: v2::Pad, - pin: PhantomData

, + sercom: PhantomData, + padnum: PhantomData, + pin: P, } -macro_rules! define_pads { +impl Pad +where + S: Sercom, + N: PadNum, + P: IsPad, +{ + /// Consume the [`Pad`] and recover the corresponding [`Pin`] + #[inline] + pub fn free(self) -> P { + self.pin + } +} + +impl Sealed for Pad +where + S: Sercom, + N: PadNum, + P: IsPad, +{ +} + +//============================================================================== +// Pad aliases +//============================================================================== + +macro_rules! pad_alias { ( $($Sercom:ty),+ ) => { $( - define_pads!($Sercom: Pad0); - define_pads!($Sercom: Pad1); - define_pads!($Sercom: Pad2); - define_pads!($Sercom: Pad3); + pad_alias!($Sercom: Pad0); + pad_alias!($Sercom: Pad1); + pad_alias!($Sercom: Pad2); + pad_alias!($Sercom: Pad3); )+ }; ($Sercom:ty: $Pad:ty) => { @@ -63,80 +122,101 @@ macro_rules! define_pads { } } -define_pads!(Sercom0, Sercom1); +pad_alias!(Sercom0, Sercom1); #[cfg(any(feature = "samd21", feature = "min-samd51g"))] -define_pads!(Sercom2, Sercom3); +pad_alias!(Sercom2, Sercom3); #[cfg(any(feature = "min-samd21g", feature = "min-samd51g"))] -define_pads!(Sercom4, Sercom5); +pad_alias!(Sercom4, Sercom5); #[cfg(feature = "min-samd51n")] -define_pads!(Sercom6, Sercom7); +pad_alias!(Sercom6, Sercom7); -/// The PadPin trait makes it more ergonomic to convert a pin into a Sercom pad. -/// You should not implement this trait for yourself; only the implementations -/// in the sercom module make sense. -pub trait PadPin

: Sealed { - fn into_pad(self, port: &mut Port) -> P; +//============================================================================== +// CompatiblePad +//============================================================================== + +/// Type class to improve compatibility between `v1` and `v2` SERCOM pad types +/// +/// The `sercom::v1::pads` module uses a wrapper [`Pad`] type to represent +/// SERCOM pads. The `v2::pad` module, on the other hand, does not use a +/// wrapper. Instead, it labels each correctly-configured [`v2::Pin`] with the +/// [`IsPad`] trait. +/// +/// This trait forms a [type class] over both. It allows the [`v1::uart`], +/// [`v1::spi`] and [`v1::i2c`] modules to accept both `v1` and `v2` pad types. +/// +/// [`v1::uart`]: super::uart +/// [`v1::spi`]: super::spi +/// [`v1::i2c`]: super::i2c +/// [type class]: crate::typelevel#type-classes +pub trait CompatiblePad: Sealed { + type Sercom: Sercom; + type PadNum: PadNum; } -impl PadPin>> for Pin +impl CompatiblePad for Pad where S: Sercom, N: PadNum, - I: GetPadMode, - M: PinMode, - Pin: IntoFunction>, + P: IsPad, { - fn into_pad(self, port: &mut Port) -> Pad> { - let pin: Pin = self.into_function(port); - Pad { - pad: v2::Pad::new(pin.pin), - pin: PhantomData, - } - } + type Sercom = S; + type PadNum = N; } -type ConfiguredPin = Pin, PadMode>>; +impl CompatiblePad for P { + type Sercom = P::Sercom; + type PadNum = P::PadNum; +} -/// Convert any pin into a [`v1::Pad`](Pad) -impl From

for Pad> -where - S: Sercom, - N: PadNum, - P: AnyPin, - P::Id: GetPadMode, -{ - /// Convert from a [`Pin`] to its corresponding [`Pad`] - /// - /// This conversion is not necessarily unique for a given [`Pin`]. - #[inline] - fn from(pin: P) -> Self { - v2::Pad::::from(pin).into() - } +//============================================================================== +// PadPin +//============================================================================== + +/// The PadPin trait makes it more ergonomic to convert a pin into a Sercom pad. +/// You should not implement this trait for yourself; only the implementations +/// in the sercom module make sense. +pub trait PadPin

: Sealed { + fn into_pad(self, port: &mut Port) -> P; } -/// Convert from a [`v2::Pad`] to a [`v1::Pad`](Pad) -impl From> for Pad> +#[cfg(feature = "samd11")] +impl PadPin>> for Pin where S: Sercom, N: PadNum, - I: GetPadMode, + I: GetPad, + M: PinMode, + Pin: IntoFunction>, + Pin: IsPad, { - fn from(pad: v2::Pad) -> Self { + #[inline] + fn into_pad(self, port: &mut Port) -> Pad> { + let pin = self.into_function(port); Pad { - pad, - pin: PhantomData, + sercom: PhantomData, + padnum: PhantomData, + pin, } } } -/// Convert from a [`v1::Pad`](Pad) to a [`v2::Pad`] -impl From>> for v2::Pad +#[cfg(not(feature = "samd11"))] +impl PadPin>> for Pin where S: Sercom, N: PadNum, - I: GetPadMode, + I: GetPad, + M: PinMode, + Pin: IntoFunction>, + Pin: IsPad, { - fn from(pad: Pad>) -> Self { - pad.pad + #[inline] + fn into_pad(self, port: &mut Port) -> Pad> { + let pin = self.into_function(port); + Pad { + sercom: PhantomData, + padnum: PhantomData, + pin, + } } } diff --git a/hal/src/sercom/v2/pad.rs b/hal/src/sercom/v2/pad.rs index 500f258ab4eb..adc36b43b3ff 100644 --- a/hal/src/sercom/v2/pad.rs +++ b/hal/src/sercom/v2/pad.rs @@ -1,46 +1,30 @@ //! Define a SERCOM pad type //! -//! This module defines the [`Pad`] type, which represents a [`Pin`] configured -//! to act as a SERCOM pad. +//! This module helps configure [`Pin`]s as SERCOM pads. It provides type-level +//! tools to convert `Pin`s to the correct [`PinMode`] and to enforce type-level +//! constraints at compile-time. //! //! # Overview //! -//! Because each SERCOM pad can usually be mapped to several possible GPIO pins, -//! a `Pad` is parameterized by three [type-level enums]: [`Sercom`], [`PadNum`] -//! and [`PinId`]. For instance, pin PA11 configured to act as pad 3 for SERCOM -//! 0 would be specified as `Pad`. +//! A SERCOM pad is defined by two types, its corresponding [`Sercom`] instance +//! and its [`PadNum`], from [`Pad0`] to [`Pad3`]. However, a given SERCOM pad +//! can usually be mapped to several possible [`PinId`]s. //! -//! When a `Pad` is created, it takes ownership of the corresponding `Pin` and -//! correctly configures the [`PinMode`] automatically. Users usually don't need -//! to create `Pad`s directly. The downstream consumers of `Pad` types, like -//! [`v2::spi`](super::spi), often take care of that conversion. However, if -//! necessary, `Pad`s can be created in two different ways. They can be created -//! manually, using the [`Pad::new()`] method, or they can be converted [`From`] -//! `Pin`s. The conversion from `Pin` to `Pad` is generally many-valued, so the -//! types usually can't be inferred. +//! There are two primary traits defined in this module: +//! - The [`IsPad`] trait is implemented on `Pin` types that are properly +//! configured as SERCOM pads, with `PinMode` [`AlternateC`] or +//! [`AlternateD`]. It acts as both a [type class] for SERCOM pads and as a +//! [type-level function] to recover the corresponding [`Sercom`] and +//! [`PadNum`] types from the `Pin`. +//! - The [`GetPad`] trait maps each [`PinId`] to its corresponding, pad-related +//! types. The [`PadMode`] alias uses `GetPad` to recover the corresponding +//! `PinMode` for a given SERCOM pad, while the [`Pad`] alias recovers the +//! configured [`Pin`] type. //! -//! ``` -//! use atsamd_hal::pac::Peripherals; -//! use atsamd_hal::gpio::v2::Pins; -//! use atsamd_hal::sercom::v2::{Pad, Sercom0, Pad0, Pad1}; -//! -//! let peripherals = Peripherals::take(); -//! let pins = Pins::new(peripherals.PORT); -//! let pad0 = Pad::::new(pins.pa08); -//! let pad1: Pad = pins.pa09.into(); -//! ``` -//! -//! On the other hand, the conversion from `Pad` to `Pin` is always unique, -//! because the `Pad` always knows which `Pin` it contains. Conversion in this -//! direction can be accomplished with the [`free`] method or the -//! [`From`]/[`Into`] traits. -//! -//! ``` -//! use atsamd_hal::gpio::v2::Pin; -//! -//! let pa08 = pad0.free(); -//! let pa09: Pin<_, _> = pad1.into(); -//! ``` +//! [`AlternateC`]: crate::gpio::v2::AlternateC +//! [`AlternateD`]: crate::gpio::v2::AlternateD +//! [type class]: crate::typelevel#type-classes +//! [type-level function]: crate::typelevel#type-level-functions #![cfg_attr( feature = "min-samd51g", doc = " @@ -48,68 +32,21 @@ \n SAMx5x chips do not allow arbitrary combinations of `PinId` for a given SERCOM. Instead, all `PinId`s must belong to the same IOSET. This module -defines a [type-level enum], [`IoSet`], to enforce this restriction. The -[`InIoSet`] [type class] is responsible for labeling each `Pad` type with +defines a [type-level enum], [`IoSet`], to enforce this restriction, and the +[`InIoSet`] [type class] is responsible for labeling each `IsPad` type with its corresponding, valid `IoSet`\\(s).\n \n " )] -//! # Type-level enforcement -//! -//! This module also provides additional, type-level tools to enforce the -//! restrictions imposed by the corresponding datasheets. -//! -//! The [`PadInfo`] trait forms the core of type-level encoding for `Pad` -//! types. All other traits are derived from `PadInfo` in some way. It acts as -//! a [type-level function] mapping `PinId`s to other `Pad`-related types. -//! -//! For a given `Sercom` and `PadNum`, the type-level function -//! [`GetPadMode`] maps a `PinId` to its corresponding `PinMode`, either -//! [`AlternateC`] or [`AlternateD`], and [`PadMode`] acts a type alias for the -//! output of this function. This trait is used to automatically convert a -//! `Pin` to the correct `PinMode` for the given `Pad`. -//! -//! The [`GetPad`] trait is a type-level function mapping an input type to -//! a corresponding `Pad` type for the given `Sercom`. For SAMD21 and SAMx5x -//! chips, `GetPad` is implemented on `PinId`s, while for SAMD11 chips, `GetPad` -//! is implemented on (`PadNum`, `PinId`) tuples, marked by the -#![cfg_attr(feature = "samd11", doc = "[`NITuple`]")] -#![cfg_attr(not(feature = "samd11"), doc = "`NITuple`")] -//! [type class]. -//! -//! `GetPad` serves to improve the ergonomics of specifying downstream, `Pad` -//! types. With knowledge of a desired `Sercom`, `GetPad` allows users to -//! specify a unique `Pad` with the minimum amount of information for the given -//! chip. -//! -//! The [`ConvertPinToPad`] trait is a type-level function theat makes it easier -//! to convert a `Pin` type to its corresponding `Sercom` and `PadNum` types. -//! -//! The [`OptionalPadNum`] and [`OptionalPad`] traits use the [`OptionalKind`] -//! pattern to act as type-level versions of [`Option`] for `PadNum` and `Pad` -//! respectively. And the [`GetOptionalPad`] trait acts as a type-level function -//! to retreive an `OptionalPad`. -//! -//! Finally, the [`AnyPad`] trait defines an [`AnyKind`] type class for all -//! `Pad` types. -//! -//! [`AlternateC`]: crate::gpio::v2::AlternateC -//! [`AlternateD`]: crate::gpio::v2::AlternateD -//! [type-level enum]: crate::typelevel#type-level-enums -//! [type-level enums]: crate::typelevel#type-level-enums -//! [type class]: crate::typelevel#type-classes -//! [type-level function]: crate::typelevel#type-level-functions -//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern -//! [`new`]: Pad::new -//! [`free`]: Pad::free use paste::paste; use seq_macro::seq; use super::Sercom; -use crate::gpio::v2::{AnyPin, OptionalPinId, Pin, PinId, PinMode, SpecificPinId}; -use crate::typelevel::{Is, NoneT, Sealed}; +#[cfg(not(feature = "samd11"))] +use crate::gpio::v2::OptionalPinId; +use crate::gpio::v2::{AnyPin, OptionalPin, Pin, PinId, PinMode}; +use crate::typelevel::{NoneT, Sealed}; //============================================================================== // PadNum @@ -152,489 +89,163 @@ impl OptionalPadNum for NoneT {} impl OptionalPadNum for N {} -/// Type-level equivalent of `Some(PadNum)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePadNum: OptionalPadNum + PadNum {} - -impl SomePadNum for N {} - //============================================================================== -// PadInfo +// IsPad //============================================================================== -/// Type-level function mapping [`PinId`]s to other [`Pad`]-related types -/// -/// This trait forms the basis of all type-level enforcement in this module. Its -/// implementations are defined by macros in the [`pad_info`](super::pad_info) -/// module. -/// -/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough -/// information to uniquely identify a [`Pad`], so this trait returns the -/// corresponding [`PadNum`] and [`PinMode`]. +/// Type class for [`Pin`]s configured as SERCOM pads /// -/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two -/// different `PadNum`s for the *same* `Sercom`. For these chips, `PadInfo` -/// requires a second type parameter to specify the `PadNum` and only returns -/// the `PinMode`. -#[cfg(feature = "samd11")] -pub trait PadInfo -where - S: Sercom, - Self: PinId, -{ - /// Corresponding [`PinMode`] for `Pad` - type PinMode: PinMode; -} - -/// Type-level function mapping [`PinId`]s to other [`Pad`]-related types -/// -/// This trait forms the basis of all type-level enforcement in this module. Its -/// implementations are defined by macros in the [`pad_info`](super::pad_info) -/// module. -/// -/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough -/// information to uniquely identify a [`Pad`], so this trait returns the -/// corresponding [`PadNum`] and [`PinMode`]. -/// -/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two -/// different `PadNum`s for the *same* `Sercom`. For these chips, `PadInfo` -/// requires a second type parameter to specify the `PadNum` and only returns -/// the `PinMode`. -#[cfg(not(feature = "samd11"))] -pub trait PadInfo -where - S: Sercom, - Self: PinId, -{ - /// Corresponding [`PadNum`] for the combination of `S` and `Self` - type PadNum: PadNum; - /// Corresponding [`PinMode`] for `Pad` - type PinMode: PinMode; -} - -//============================================================================== -// ConvertPinToPad -//============================================================================== - -/// Type-level function mapping a configured [`Pin`] to its corresponding -/// [`Sercom`] and [`PadNum`] -/// -/// This trait is a [type-level function] theat makes it easier to convert a -/// [`Pin`] type in a [`PinMode`] of [`AlternateC`] or [`AlternateD`] to its -/// corresponding [`Sercom`] and [`PadNum`] types. -/// -/// The type aliases [`PinToSercom`], [`PinToPadNum`], -#[cfg_attr(feature = "samd11", doc = "[`PinToNITuple`]")] -#[cfg_attr(not(feature = "samd11"), doc = "`PinToNITuple`")] -/// and [`PinToPad`] use this trait to recover their respective types. +/// This trait serves as both a [type class] for `Pin`s configured as SERCOM +/// pads and as a [type-level function] mapping each `Pin` type to its +/// corresponding [`Sercom`] and [`PadNum`]. /// +/// [type class]: crate::typelevel#type-classes /// [type-level function]: crate::typelevel#type-level-functions -pub trait ConvertPinToPad: AnyPin { +pub trait IsPad: AnyPin { type Sercom: Sercom; type PadNum: PadNum; } -/// Type alias to recover the corresponding [`Sercom`] type from a [`Pin`] using -/// the [`ConvertPinToPad`] trait. -pub type PinToSercom

=

::Sercom; - -/// Type alias to recover the corresponding [`PadNum`] type from a [`Pin`] using -/// the [`ConvertPinToPad`] trait. -pub type PinToPadNum

=

::PadNum; - -/// Type alias to recover the corresponding [`Pad`] type from a [`Pin`] using -/// the [`ConvertPinToPad`] trait. -pub type PinToPad

= Pad, PinToPadNum

, SpecificPinId

>; - -/// Type alias to recover the corresponding [`NITuple`] type from a [`Pin`] -/// using the [`ConvertPinToPad`] trait. -#[cfg(feature = "samd11")] -pub type PinToNITuple

= (PinToPadNum

, SpecificPinId

); - //============================================================================== -// GetPadMode +// OptionalPad //============================================================================== -/// Type-level function mapping [`PinId`]s to [`PinMode`]s for a given -/// [`Sercom`] and [`PadNum`] +/// Type-level equivalent of `Option` /// -/// This trait acts as a [type-level function]. It is implemented on [`PinId`]s -/// and it takes two additional types as arguments, a [`Sercom`] and a -/// [`PadNum`]. It returns the correct [`PinMode`] for the corresponding [`Pin`] -/// configured as a [`Pad`]. +/// See the [`OptionalKind`] documentation for more details on the pattern. /// -/// [type-level function]: crate::typelevel#type-level-functions -pub trait GetPadMode -where - Self: PinId, - S: Sercom, - N: PadNum, -{ - /// Corresponding [`PinMode`] for `Pad` - type Mode: PinMode; +/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +pub trait OptionalPad: OptionalPin { + type PadNum: OptionalPadNum; } -/// Alias for the return type of [`GetPadMode`] -/// -/// See the documentation on [type-level functions] for an explanation of the -/// pattern. -/// -/// [type-level functions]: crate::typelevel#type-level-functions -pub type PadMode = >::Mode; - -#[cfg(feature = "samd11")] -impl GetPadMode for I -where - S: Sercom, - N: PadNum, - I: PadInfo, -{ - type Mode = I::PinMode; +impl OptionalPad for NoneT { + type PadNum = NoneT; } -#[cfg(not(feature = "samd11"))] -impl GetPadMode for I -where - S: Sercom, - I: PadInfo, -{ - type Mode = I::PinMode; +impl OptionalPad for P { + type PadNum = P::PadNum; } -//============================================================================== -// Pad -//============================================================================== - -/// A GPIO pin configured as a SERCOM pad -/// -/// Each `Pad` is parameterized by three [type-level enums], [`Sercom`], -/// [`PadNum`], and [`PinId`]. When created, a `Pad` takes ownership of the -/// corresponding [`Pin`]. `Pad`s usually don't need to be created by users -/// directly. But if required, they can be created with the [`new`](Self::new()) -/// function or converted [`From`] `Pin`s. The corresponding `Pin` can be -/// recovered with the [`free`](Self::free()) function or again with -/// [`From`]/[`Into`]. +/// Type-level equivalent of `Some(Pad)` /// -/// See the [module-level documentation](self) for examples. +/// See the [`OptionalKind`] documentation for more details on the pattern. /// -/// [type-level enums]: crate::typelevel#type-level-enum -/// [type-level function]: crate::typelevel#type-level-functions -pub struct Pad -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ - pin: Pin, -} - -impl Pad -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ - /// Create a new SERCOM [`Pad`] from a [`Pin`] - #[inline] - pub fn new(pin: impl AnyPin) -> Self { - let pin = pin.into().into_mode(); - Pad { pin } - } - - /// Consume the [`Pad`] and release the corresponding [`Pin`] - #[inline] - pub fn free(self) -> Pin { - self.pin - } -} - -//============================================================================== -// Convert between Pin and Pad -//============================================================================== - -impl From

for Pad -where - S: Sercom, - N: PadNum, - P: AnyPin, - P::Id: GetPadMode, -{ - /// Convert from a [`Pin`] to its corresponding [`Pad`] - /// - /// This conversion is not necessarily unique for a given [`Pin`]. - #[inline] - fn from(pin: P) -> Self { - Pad::new(pin) - } -} +/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +pub trait SomePad: IsPad {} -impl From> for Pin -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ - /// Convert from a [`Pad`] to its corresponding [`Pin`] - /// - /// This transformation is unique for a given [`Pad`]. - #[inline] - fn from(pad: Pad) -> Self { - pad.pin - } -} +impl SomePad for P {} //============================================================================== -// AnyPad +// GetPad //============================================================================== -/// Type class for [`Pad`] types +/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types /// -/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for -/// [`Pad`] types. See the `AnyKind` documentation for more details on the -/// pattern. +/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough +/// information to uniquely identify a pad, so this trait returns the +/// corresponding [`PadNum`] and [`PinMode`]. /// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -/// [type class]: crate::typelevel#type-classes -pub trait AnyPad: Is> { - type Sercom: Sercom; - type PadNum: PadNum; - type PinId: GetPadMode; -} - -/// Type constructor to recover the specific [`Pad`] from an implementation of -/// [`AnyPad`] +/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two +/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad` +/// requires a second type parameter to specify the `PadNum` and only returns +/// the `PinMode`. /// -/// See the [`AnyKind`] documentation for more details on the pattern. +/// See the documentation on [type-level functions] for more details. /// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -pub type SpecificPad

= Pad<

::Sercom,

::PadNum,

::PinId>; - -impl AsRef for Pad -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ - #[inline] - fn as_ref(&self) -> &Self { - self - } -} - -impl AsMut for Pad -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ - #[inline] - fn as_mut(&mut self) -> &mut Self { - self - } -} - -impl Sealed for Pad -where - S: Sercom, - N: PadNum, - I: GetPadMode, -{ -} - -impl AnyPad for Pad +/// [type-level functions]: crate::typelevel#type-level-functions +#[cfg(feature = "samd11")] +pub trait GetPad where S: Sercom, N: PadNum, - I: GetPadMode, + Self: PinId, { - type Sercom = S; - type PadNum = N; - type PinId = I; + type PinMode: PinMode; } -//============================================================================== -// OptionalPad -//============================================================================== - -/// Type-level equivalent of `Option` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. +/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types /// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPad: Sealed {} -impl OptionalPad for NoneT {} -impl OptionalPad for P {} - -/// Type-level equivalent of `Some(Pad)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePad: OptionalPad + AnyPad {} -impl SomePad for P {} - -//============================================================================== -// NITuple -//============================================================================== - -/// Type-class for (`PadNum`, `PinId`) tuples +/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough +/// information to uniquely identify a pad, so this trait returns the +/// corresponding [`PadNum`] and [`PinMode`]. /// -/// This trait forms a type-class for all (`PadNum`, `PinId`) tuples, and it -/// allows you to recover the constituate types from a generic type implementing -/// `NITuple`. +/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two +/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad` +/// requires a second type parameter to specify the `PadNum` and only returns +/// the `PinMode`. /// -/// This type is used by the [type-level function] [`GetPad`], through the -/// [`GetPadMarker`] trait. It allows the implementation of [`GetPad`] to be -/// fully generic, which helps type inference in downstream modules. +/// See the documentation on [type-level functions] for more details. /// -/// [type-level function]: crate::typelevel#type-level-functions -#[cfg(feature = "samd11")] -pub trait NITuple: Sealed { - type Num: PadNum; - type Id: PinId; -} - -#[cfg(feature = "samd11")] -impl Sealed for (N, I) {} - -#[cfg(feature = "samd11")] -impl NITuple for (N, I) { - type Num = N; - type Id = I; +/// [type-level functions]: crate::typelevel#type-level-functions +#[cfg(not(feature = "samd11"))] +pub trait GetPad +where + S: Sercom, + Self: PinId, +{ + type PadNum: PadNum; + type PinMode: PinMode; } //============================================================================== -// GetPadMarker +// GetPad aliases //============================================================================== -/// Marker trait for [`GetPad`] -/// -/// This trait has two purposes. First, it papers over differences between the -/// SAMD11 chips and all other chips. And second, it acts to prevent -/// conflicting-implementation errors. -/// -/// The latter purpose is a limitation of the Rust trait system. For some -/// reason, when a local trait takes type parameters, the compiler stops -/// understanding that a local trait can never be implemented for a local type -/// in some other crate. As a workaround, you can use a marker trait like this -/// one to make it possible. +/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM +/// pad #[cfg(feature = "samd11")] -pub trait GetPadMarker: NITuple {} +pub type PadMode = >::PinMode; -#[cfg(feature = "samd11")] -impl GetPadMarker for P {} - -/// Marker trait for [`GetPad`] -/// -/// This trait has two purposes. First, it papers over differences between the -/// SAMD11 chips and all other chips. And second, it acts to prevent -/// conflicting-implementation errors. -/// -/// The latter purpose is a limitation of the Rust trait system. For some -/// reason, when a local trait takes type parameters, the compiler stops -/// understanding that a local trait can never be implemented for a local type -/// in some other crate. As a workaround, you can use a marker trait like this -/// one to make it possible. +/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM +/// pad #[cfg(not(feature = "samd11"))] -pub trait GetPadMarker: PinId {} - -#[cfg(not(feature = "samd11"))] -impl GetPadMarker for I {} - -//============================================================================== -// GetPad -//============================================================================== - -/// Type-level function to uniquely specify a [`Pad`] type -/// -/// This trait acts as a [type-level function] to uniquely specify a [`Pad`] -/// type with as little information as possible. -/// -/// SAMD21 and SAMx5x chips only ever map a given [`PinId`] to a single -/// [`PadNum`] for a given [`Sercom`]. SAMD11 chips, on the other hand, -/// sometimes have the same `PinId` mapped to two different `PadNum`s for the -/// *same* `Sercom`. As a result, a `Sercom` and a `PinId` is enough information -/// to uniquely specify a `Pad` for SAMD21 and SAMDx5x chips, but you need to -/// know the `PadNum` for SAMD11 chips. -/// -/// For SAMD21 and SAMx5x chips, this trait is implemented on `PinId`s directly. -/// For SAMD11 chips, it is implemented on (`PadNum`, `PinId`) tupes, also known -/// as -#[cfg_attr(feature = "samd11", doc = "[`NITuple`]s")] -#[cfg_attr(not(feature = "samd11"), doc = "`NITuple`s")] -/// -/// This trait improves the ergonomics of fully specifying a `Pad` in downstream -/// modules, like the [`spi::Pads`](super::spi::Pads) type. -/// -/// [type-level function]: crate::typelevel#type-level-functions -pub trait GetPad -where - S: Sercom, - Self: GetPadMarker, -{ - type PadNum: PadNum; - type PinId: GetPadMode; - type Pad: AnyPad; -} +pub type PadMode = >::PinMode; +/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct +/// [`PadMode`] #[cfg(feature = "samd11")] -impl GetPad for T -where - S: Sercom, - T: NITuple, - T::Id: PadInfo, -{ - type PadNum = T::Num; - type PinId = T::Id; - type Pad = Pad; -} +pub type Pad = Pin>; +/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct +/// [`PadMode`] #[cfg(not(feature = "samd11"))] -impl GetPad for I -where - S: Sercom, - I: PadInfo, -{ - type PadNum = I::PadNum; - type PinId = I; - type Pad = Pad; -} +pub type Pad = Pin>; //============================================================================== // GetOptionalPad //============================================================================== -/// Type-level function to recover an [`OptionalPad`] from an optional -/// implementor of [`GetPad`] +/// Type-level function mapping [`OptionalPinId`]s to their corresponding +/// [`OptionalPad`]s /// -/// This trait acts as a [type-level function]. In pseudo-Rust, it is the -/// type-level equivalent of starting with an `Option` and calling -/// `.map(GetPad)` to recover an `Option`. +/// This trait acts as a [type-level function] mapping `OptionalPinId`s to their +/// corresponding `OptionalPad`. In pseudo-Rust, it is the type-level equivalent +/// of starting with `Option` and calling `.map(GetPad)` to recover an +/// `Option`. /// -/// [type-level function]: crate::typelevel#type-level-functions -pub trait GetOptionalPad: Sealed { +/// [type-level functions]: crate::typelevel#type-level-functions +#[cfg(not(feature = "samd11"))] +pub trait GetOptionalPad: OptionalPinId { type PadNum: OptionalPadNum; - type PinId: OptionalPinId; type Pad: OptionalPad; } +#[cfg(not(feature = "samd11"))] impl GetOptionalPad for NoneT { type PadNum = NoneT; - type PinId = NoneT; type Pad = NoneT; } -impl GetOptionalPad for T +#[cfg(not(feature = "samd11"))] +impl GetOptionalPad for I where S: Sercom, - T: GetPad + GetPadMarker, + I: PinId + GetPad, + Pad: IsPad, { - type PadNum = T::PadNum; - type PinId = T::PinId; - type Pad = T::Pad; + type PadNum = I::PadNum; + type Pad = Pad; } //============================================================================== @@ -664,18 +275,18 @@ seq!(N in 1..=6 { } }); -/// Type class for [`Pad`]s in a given [`IoSet`] +/// Type class for SERCOM pads in a given [`IoSet`] /// -/// This trait is used to label each [`Pad`] with its corresponding -/// [`IoSet`]\(s). Downstream types can use this trait as a [type class] to -/// restrict [`Pad`]s to a given [`IoSet`]. See the [type class] documentation -/// for more details on the pattern. +/// This trait is used to label each [`Pin`] implementing [`IsPad`] with its +/// corresponding [`IoSet`]\(s). Downstream types can use this trait as a +/// [type class] to restrict [`Pin`]s to a given [`IoSet`]. See the [type class] +/// documentation for more details on the pattern. /// /// [type class]: crate::typelevel#type-classes #[cfg(feature = "min-samd51g")] pub trait InIoSet where - Self: AnyPad, + Self: IsPad, I: IoSet, { } diff --git a/hal/src/thumbv6m/sercom/v1/i2c.rs b/hal/src/thumbv6m/sercom/v1/i2c.rs index 23399a9ee1e2..ab2714e33e26 100644 --- a/hal/src/thumbv6m/sercom/v1/i2c.rs +++ b/hal/src/thumbv6m/sercom/v1/i2c.rs @@ -2,6 +2,8 @@ use crate::clock; use crate::hal::blocking::i2c::{Read, Write, WriteRead}; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::pad::{Pad0, Pad1}; use crate::target_device::sercom0::I2CM; use crate::target_device::{PM, SERCOM0, SERCOM1}; #[cfg(feature = "samd21")] @@ -20,18 +22,36 @@ const MASTER_ACT_STOP: u8 = 3; /// Define an I2C master type for the given SERCOM and pad pair. macro_rules! i2c { ([ - $($Type:ident: ($pad0:ident, $pad1:ident, $SERCOM:ident, $powermask:ident, $clock:ident),)+ + $($Type:ident: + ( + $pad0:ident, // No longer used + $pad1:ident, // No longer used + $SERCOM:ident, + $powermask:ident, + $clock:ident + ), + )+ ]) => { + $( + /// Represents the Sercom instance configured to act as an I2C Master. /// The embedded_hal blocking I2C traits are implemented by this instance. -pub struct $Type<$pad0, $pad1> { - sda: $pad0, - scl: $pad1, +pub struct $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ + sda: P0, + scl: P1, sercom: $SERCOM, } -impl<$pad0, $pad1> $Type<$pad0, $pad1> { +impl $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ /// Configures the sercom instance to work as an I2C Master. /// The clock is obtained via the `GenericClockGenerator` type. /// `freq` specifies the bus frequency to use for I2C communication. @@ -58,8 +78,8 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { freq: F, sercom: $SERCOM, pm: &mut PM, - sda: $pad0, - scl: $pad1, + sda: P0, + scl: P1, ) -> Self { // Power up the peripheral bus clock. // safe because we're exclusively owning SERCOM @@ -101,7 +121,7 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { /// Breaks the sercom device up into its constituent pins and the SERCOM /// instance. Does not make any changes to power management. - pub fn free(self) -> ($pad0, $pad1, $SERCOM) { + pub fn free(self) -> (P0, P1, $SERCOM) { (self.sda, self.scl, self.sercom) } @@ -273,7 +293,12 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { self.fill_buffer(buffer) } } -impl<$pad0, $pad1> Write for $Type<$pad0, $pad1> { + +impl Write for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; /// Sends bytes to slave with address `addr` @@ -284,7 +309,11 @@ impl<$pad0, $pad1> Write for $Type<$pad0, $pad1> { } } -impl<$pad0, $pad1> Read for $Type<$pad0, $pad1> { +impl Read for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -294,7 +323,11 @@ impl<$pad0, $pad1> Read for $Type<$pad0, $pad1> { } } -impl<$pad0, $pad1> WriteRead for $Type<$pad0, $pad1> { +impl WriteRead for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { diff --git a/hal/src/thumbv6m/sercom/v1/spi.rs b/hal/src/thumbv6m/sercom/v1/spi.rs index 2b8c3023d4bd..dd353481ec2a 100644 --- a/hal/src/thumbv6m/sercom/v1/spi.rs +++ b/hal/src/thumbv6m/sercom/v1/spi.rs @@ -1,8 +1,9 @@ +use core::marker::PhantomData; + use crate::clock; -use crate::gpio::v2::pin::AnyPin; use crate::hal::spi::{FullDuplex, Mode, Phase, Polarity}; -use crate::sercom::pads::*; -use crate::sercom::v2; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::*; use crate::spi_common::CommonSpi; use crate::target_device::sercom0::SPI; use crate::target_device::{PM, SERCOM0, SERCOM1}; @@ -22,7 +23,79 @@ pub enum Error { /// this trait for yourself; only the implementations in the sercom module make /// sense. pub trait DipoDopo { - fn dipo_dopo(&self) -> (u8, u8); + const DIPO: u8; + const DOPO: u8; + fn dipo_dopo(&self) -> (u8, u8) { + (Self::DIPO, Self::DOPO) + } +} + +macro_rules! padout { + ( ($dipo:literal, $dopo:literal) => $pad0:ident, $pad1:ident, $pad2:ident) => { + impl DipoDopo for ($pad0, $pad1, $pad2) { + const DIPO: u8 = $dipo; + const DOPO: u8 = $dopo; + } + }; +} + +padout!((0, 1) => Pad0, Pad2, Pad3); +padout!((0, 2) => Pad0, Pad3, Pad1); + +padout!((1, 1) => Pad1, Pad2, Pad3); +padout!((1, 3) => Pad1, Pad0, Pad3); + +padout!((2, 0) => Pad2, Pad0, Pad1); +padout!((2, 2) => Pad2, Pad3, Pad1); +padout!((2, 3) => Pad2, Pad0, Pad3); + +padout!((3, 0) => Pad3, Pad0, Pad1); + +/// A pad mapping configuration for the SERCOM in SPI master mode. +/// +/// This type can only be constructed using the From implementations +/// in this module, which are restricted to valid configurations. +/// +/// Defines which sercom pad is mapped to which SPI function. +pub struct Padout +where + S: Sercom, +{ + sercom: PhantomData, + _miso: MISO, + _mosi: MOSI, + _sclk: SCLK, +} + +/// Convert from a tuple of (MISO, MOSI, SCK) to SPIMasterXPadout +impl From<(PAD0, PAD1, PAD2)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum): DipoDopo, +{ + fn from(pads: (PAD0, PAD1, PAD2)) -> Padout { + Padout { + sercom: PhantomData, + _miso: pads.0, + _mosi: pads.1, + _sclk: pads.2, + } + } +} + +impl DipoDopo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum): DipoDopo, +{ + const DIPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum)>::DIPO; + const DOPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum)>::DOPO; } /// Define an SPIMasterX type for the given Sercom number. @@ -32,212 +105,124 @@ pub trait DipoDopo { macro_rules! spi_master { ($Type:ident: ($Sercom:ident, $SERCOM:ident, $powermask:ident, $clock:ident)) => { $crate::paste::item! { - /// A pad mapping configuration for the SERCOM in SPI master mode. - /// - /// This type can only be constructed using the From implementations - /// in this module, which are restricted to valid configurations. - /// - /// Defines which sercom pad is mapped to which SPI function. - pub struct [<$Type Padout>] { - _miso: MISO, - _mosi: MOSI, - _sck: SCK, - } + pub type [<$Type Padout>] = Padout<$Sercom, MISO, MOSI, SCLK>; } - /// Define a From instance for a tuple of SercomXPadX instances that - /// converts them into an SPIMasterXPadout instance. + /// SPIMasterX represents the corresponding SERCOMX instance + /// configured to act in the role of an SPI Master. + /// Objects of this type implement the HAL `FullDuplex` and blocking + /// SPI traits. /// - /// Also defines a DipoDopo instance for the constructed padout instance - /// that returns the values used to configure the sercom pads for the - /// appropriate function in the sercom register file. - macro_rules! padout { - ($dipo_dopo:expr => $pad0:ident, $pad1:ident, $pad2:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (MISO, MOSI, SCK) to SPIMasterXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> { - [<$Type Padout>] { _miso: pads.0, _mosi: pads.1, _sck: pads.2 } - } - } - - impl DipoDopo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - { - fn dipo_dopo(&self) -> (u8, u8) { - $dipo_dopo - } - } - - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>)) -> Self { - [<$Type Padout>] { _miso: pads.0, _mosi: pads.1, _sck: pads.2 } - } - } - - impl DipoDopo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - { - fn dipo_dopo(&self) -> (u8, u8) { - $dipo_dopo - } - } - } - }; + /// This type is generic over any valid pad mapping where there is + /// a defined "data in pin out data out pin out" implementation. + pub struct $Type { + padout: Padout<$Sercom, MISO, MOSI, SCK>, + sercom: $SERCOM, } - padout!((0, 1) => Pad0, Pad2, Pad3); - padout!((0, 2) => Pad0, Pad3, Pad1); - - padout!((1, 1) => Pad1, Pad2, Pad3); - padout!((1, 3) => Pad1, Pad0, Pad3); - - padout!((2, 0) => Pad2, Pad0, Pad1); - padout!((2, 2) => Pad2, Pad3, Pad1); - padout!((2, 3) => Pad2, Pad0, Pad3); + impl CommonSpi for $Type { + /// Helper for accessing the spi member of the sercom instance + fn spi(&self) -> &SPI { + &self.sercom.spi() + } - padout!((3, 0) => Pad3, Pad0, Pad1); + /// Helper for accessing the spi member of the sercom instance + fn spi_mut(&mut self) -> &SPI { + &self.sercom.spi() + } + } - $crate::paste::item! { - /// SPIMasterX represents the corresponding SERCOMX instance - /// configured to act in the role of an SPI Master. - /// Objects of this type implement the HAL `FullDuplex` and blocking - /// SPI traits. + impl $Type { + /// Power on and configure SERCOMX to work as an SPI Master operating + /// with the specified frequency and SPI Mode. The padout specifies + /// which pins are bound to the MISO, MOSI, SCK functions. /// - /// This type is generic over any valid pad mapping where there is - /// a defined "data in pin out data out pin out" implementation. - pub struct $Type { - padout: [<$Type Padout>], + /// You can use a tuple of three SercomXPadY instances for which + /// there exists a From implementation for SPIMasterXPadout. + pub fn new, T: Into>>( + clock: &clock::$clock, + freq: F, + mode: Mode, sercom: $SERCOM, - } - - impl CommonSpi for $Type { - /// Helper for accessing the spi member of the sercom instance - fn spi(&self) -> &SPI { - &self.sercom.spi() + pm: &mut PM, + padout: T, + ) -> Self + where + Padout<$Sercom, MISO, MOSI, SCK>: DipoDopo, + { + let padout = padout.into(); + + // Power up the peripheral bus clock. + // safe because we're exclusively owning SERCOM + pm.apbcmask.modify(|_, w| w.$powermask().set_bit()); + + // reset the sercom instance + sercom.spi().ctrla.modify(|_, w| w.swrst().set_bit()); + // wait for reset to complete + while sercom.spi().syncbusy.read().swrst().bit_is_set() + || sercom.spi().ctrla.read().swrst().bit_is_set() + {} + + // Put the hardware into spi master mode + sercom.spi().ctrla.modify(|_, w| w.mode().spi_master()); + // wait for configuration to take effect + while sercom.spi().syncbusy.read().enable().bit_is_set() {} + + // 8 bit data size and enable the receiver + unsafe { + sercom.spi().ctrlb.modify(|_, w| { + w.chsize().bits(0); + w.rxen().set_bit() + }); } - /// Helper for accessing the spi member of the sercom instance - fn spi_mut(&mut self) -> &SPI { - &self.sercom.spi() - } - } + // set the baud rate + let baud = Self::calculate_baud(freq, clock.freq()); - impl $Type { - /// Power on and configure SERCOMX to work as an SPI Master operating - /// with the specified frequency and SPI Mode. The padout specifies - /// which pins are bound to the MISO, MOSI, SCK functions. - /// - /// You can use a tuple of three SercomXPadY instances for which - /// there exists a From implementation for SPIMasterXPadout. - pub fn new, T: Into<[<$Type Padout>]>>( - clock:&clock::$clock, - freq: F, - mode: Mode, - sercom: $SERCOM, - pm: &mut PM, - padout: T, - ) -> Self where - [<$Type Padout>]: DipoDopo { - let padout = padout.into(); - - // Power up the peripheral bus clock. - // safe because we're exclusively owning SERCOM - pm.apbcmask.modify(|_, w| w.$powermask().set_bit()); - - // reset the sercom instance - sercom.spi().ctrla.modify(|_, w| w.swrst().set_bit()); - // wait for reset to complete - while sercom.spi().syncbusy.read().swrst().bit_is_set() - || sercom.spi().ctrla.read().swrst().bit_is_set() - {} - - // Put the hardware into spi master mode - sercom.spi().ctrla.modify(|_, w| w.mode().spi_master()); - // wait for configuration to take effect - while sercom.spi().syncbusy.read().enable().bit_is_set() {} - - // 8 bit data size and enable the receiver - unsafe { - sercom.spi().ctrlb.modify(|_, w|{ - w.chsize().bits(0); - w.rxen().set_bit() - }); - } - - // set the baud rate - let baud = Self::calculate_baud(freq, clock.freq()); - - unsafe { - sercom.spi().baud.modify(|_, w| w.baud().bits(baud)); - - sercom.spi().ctrla.modify(|_, w| { - match mode.polarity { - Polarity::IdleLow => w.cpol().clear_bit(), - Polarity::IdleHigh => w.cpol().set_bit(), - }; - - match mode.phase { - Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), - Phase::CaptureOnSecondTransition => w.cpha().set_bit(), - }; - - let (dipo, dopo) = padout.dipo_dopo(); - w.dipo().bits(dipo); - w.dopo().bits(dopo); - - // MSB first - w.dord().clear_bit() - }); - } - - sercom.spi().ctrla.modify(|_, w| w.enable().set_bit()); - // wait for configuration to take effect - while sercom.spi().syncbusy.read().enable().bit_is_set() {} - - Self { - padout, - sercom, - } - } + unsafe { + sercom.spi().baud.modify(|_, w| w.baud().bits(baud)); + + sercom.spi().ctrla.modify(|_, w| { + match mode.polarity { + Polarity::IdleLow => w.cpol().clear_bit(), + Polarity::IdleHigh => w.cpol().set_bit(), + }; - /// Set the baud rate - pub fn set_baud>(&mut self, freq: F, clock: &clock::$clock) { - self.disable(); - let baud = Self::calculate_baud(freq, clock.freq()); - unsafe { - self.spi_mut().baud.modify(|_, w| w.baud().bits(baud)); - } - self.enable(); + match mode.phase { + Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), + Phase::CaptureOnSecondTransition => w.cpha().set_bit(), + }; + + let (dipo, dopo) = padout.dipo_dopo(); + w.dipo().bits(dipo); + w.dopo().bits(dopo); + + // MSB first + w.dord().clear_bit() + }); } - /// Tear down the SPI instance and yield the constituent pins and - /// SERCOM instance. No explicit de-initialization is performed. - pub fn free(self) -> ([<$Type Padout>], $SERCOM) { - (self.padout, self.sercom) + sercom.spi().ctrla.modify(|_, w| w.enable().set_bit()); + // wait for configuration to take effect + while sercom.spi().syncbusy.read().enable().bit_is_set() {} + + Self { padout, sercom } + } + + /// Set the baud rate + pub fn set_baud>(&mut self, freq: F, clock: &clock::$clock) { + self.disable(); + let baud = Self::calculate_baud(freq, clock.freq()); + unsafe { + self.spi_mut().baud.modify(|_, w| w.baud().bits(baud)); } + self.enable(); + } + + /// Tear down the SPI instance and yield the constituent pins and + /// SERCOM instance. No explicit de-initialization is performed. + pub fn free(self) -> (Padout<$Sercom, MISO, MOSI, SCK>, $SERCOM) { + (self.padout, self.sercom) } } @@ -263,7 +248,9 @@ macro_rules! spi_master { let intflag = self.spi().intflag.read(); // dre is data register empty if intflag.dre().bit_is_set() { - self.spi_mut().data.write(|w| unsafe{w.data().bits(byte as u16)}); + self.spi_mut() + .data + .write(|w| unsafe { w.data().bits(byte as u16) }); Ok(()) } else { Err(nb::Error::WouldBlock) @@ -271,13 +258,17 @@ macro_rules! spi_master { } } - impl ::hal::blocking::spi::transfer::Default for $Type {} + impl ::hal::blocking::spi::transfer::Default + for $Type + { + } impl ::hal::blocking::spi::write::Default for $Type {} #[cfg(feature = "unproven")] - impl ::hal::blocking::spi::write_iter::Default for $Type {} - + impl ::hal::blocking::spi::write_iter::Default + for $Type + { + } }; - } spi_master!(SPIMaster0: (Sercom0, SERCOM0, sercom0_, Sercom0CoreClock)); diff --git a/hal/src/thumbv6m/sercom/v1/uart.rs b/hal/src/thumbv6m/sercom/v1/uart.rs index 6f6c53e35811..c756e6a204ff 100644 --- a/hal/src/thumbv6m/sercom/v1/uart.rs +++ b/hal/src/thumbv6m/sercom/v1/uart.rs @@ -1,9 +1,8 @@ use crate::clock; -use crate::gpio::v2::pin::AnyPin; use crate::hal::blocking::serial::{write::Default, Write}; use crate::hal::serial; -use crate::sercom::pads::*; -use crate::sercom::v2; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::*; use crate::target_device::sercom0::USART; use crate::target_device::{PM, SERCOM0, SERCOM1}; #[cfg(feature = "samd21")] @@ -19,198 +18,185 @@ use core::marker::PhantomData; /// this trait for yourself; only the implementations in the sercom module make /// sense. pub trait RxpoTxpo { - fn rxpo_txpo(&self) -> (u8, u8); + const RXPO: u8; + const TXPO: u8; + fn rxpo_txpo(&self) -> (u8, u8) { + (Self::RXPO, Self::TXPO) + } } -/// Define a UARTX type for the given Sercom. -/// -/// Also defines the valid "pad to uart function" mappings for this instance so -/// that construction is restricted to valid configurations. -macro_rules! uart { - ($Type:ident: ($Sercom:ident, $SERCOM:ident, $powermask:ident, $clock:ident)) => { - $crate::paste::item! { - /// A pad mapping configuration for the SERCOM in UART mode. - /// - /// This type can only be constructed using the From implementations - /// in this module, which are restricted to valid configurations. - /// - /// Defines which sercom pad is mapped to which UART function. - pub struct [<$Type Padout>] { - rx: RX, - tx: TX, - rts: RTS, - cts: CTS, - } +macro_rules! padout { + ( ($rxpo:literal, $txpo:literal) => $pad0:ident, $pad1:ident) => { + impl RxpoTxpo for ($pad0, $pad1) { + const RXPO: u8 = $rxpo; + const TXPO: u8 = $txpo; + } + }; + ( ($rxpo:literal, $txpo:literal) => $pad0:ident, $pad1:ident, $pad2:ident, $pad3:ident) => { + impl RxpoTxpo for ($pad0, $pad1, $pad2, $pad3) { + const RXPO: u8 = $rxpo; + const TXPO: u8 = $txpo; + } + }; +} - /// A pad mapping configuration for the receiving half of the SERCOM in UART mode. - pub struct [<$Type RxPadout>] { - rx: RX, - cts: CTS, - } +padout!((0, 1) => Pad0, Pad2); - /// A pad mapping configuration for the transmitting half of the SERCOM in UART mode. - pub struct [<$Type TxPadout>] { - tx: TX, - rts: RTS, - } +padout!((1, 0) => Pad1, Pad0); +padout!((1, 2) => Pad1, Pad0, Pad2, Pad3); +padout!((1, 1) => Pad1, Pad2); - impl [<$Type Padout>] { - /// Splits the padout into transmit and receive halves - pub fn split(self) -> ([<$Type TxPadout>], [<$Type RxPadout>]) { - ( - [<$Type TxPadout>] { - tx: self.tx, - rts: self.rts, - }, - [<$Type RxPadout>] { - rx: self.rx, - cts: self.cts, - }, - ) - } +padout!((2, 0) => Pad2, Pad0); - /// Combines transmit and receive halves back into a duplex padout - pub fn join(tx: [<$Type TxPadout>], rx: [<$Type RxPadout>]) -> Self { - Self { - rx: rx.rx, - tx: tx.tx, - rts: tx.rts, - cts: rx.cts, - } - } - } - } +padout!((3, 0) => Pad3, Pad0); +padout!((3, 1) => Pad3, Pad2); - /// Define a From instance for either a tuple of two SercomXPadX - /// instances, or a tuple of four SercomXPadX instances that converts - /// them into an UARTXPadout instance. - /// - /// Also defines a RxpoTxpo instance for the constructed padout instance - /// that returns the values used to configure the sercom pads for the - /// appropriate function in the sercom register file. - macro_rules! padout { - ($rxpo_txpo:expr => $pad0:ident, $pad1:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (RX, TX) to UARTXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: (), cts: () } - } - } - - impl RxpoTxpo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } +/// A pad mapping configuration for the SERCOM in UART mode. +/// +/// This type can only be constructed using the From implementations +/// in this module, which are restricted to valid configurations. +/// +/// Defines which sercom pad is mapped to which UART function. +pub struct Padout +where + S: Sercom, +{ + sercom: PhantomData, + rx: RX, + tx: TX, + rts: RTS, + cts: CTS, +} - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, (), ()> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>)) -> Self { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: (), cts: () } - } - } +/// A pad mapping configuration for the receiving half of the SERCOM in UART +/// mode. +pub struct RxPadout +where + S: Sercom, +{ + sercom: PhantomData, + rx: RX, + cts: CTS, +} - impl RxpoTxpo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, (), ()> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - } - }; - ($rxpo_txpo:expr => $pad0:ident, $pad1:ident, $pad2:ident, $pad3:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN3: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - PIN3::Id: GetPadMode<$Sercom, $pad3>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: pads.2, cts: pads.3 } - } - } +/// A pad mapping configuration for the transmitting half of the SERCOM in UART +/// mode. +pub struct TxPadout +where + S: Sercom, +{ + sercom: PhantomData, + tx: TX, + rts: RTS, +} - impl RxpoTxpo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN3: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - PIN3::Id: GetPadMode<$Sercom, $pad3>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } +impl Padout +where + S: Sercom, +{ + /// Splits the padout into transmit and receive halves + pub fn split(self) -> (TxPadout, RxPadout) { + ( + TxPadout { + sercom: PhantomData, + tx: self.tx, + rts: self.rts, + }, + RxPadout { + sercom: PhantomData, + rx: self.rx, + cts: self.cts, + }, + ) + } - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - Id3: v2::GetPadMode<$Sercom, $pad3>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>)) -> Self { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: pads.2, cts: pads.3 } - } - } + /// Combines transmit and receive halves back into a duplex padout + pub fn join(tx: TxPadout, rx: RxPadout) -> Self { + Self { + sercom: PhantomData, + rx: rx.rx, + tx: tx.tx, + rts: tx.rts, + cts: rx.cts, + } + } +} - impl RxpoTxpo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - Id3: v2::GetPadMode<$Sercom, $pad3>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - } - }; +/// Convert from a tuple of (RX, TX) to UARTXPadout +impl From<(PAD0, PAD1)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum): RxpoTxpo, +{ + fn from(pads: (PAD0, PAD1)) -> Padout { + Padout { + sercom: PhantomData, + rx: pads.0, + tx: pads.1, + rts: (), + cts: (), } + } +} - padout!((0, 1) => Pad0, Pad2); +impl RxpoTxpo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum): RxpoTxpo, +{ + const RXPO: u8 = <(PAD0::PadNum, PAD1::PadNum)>::RXPO; + const TXPO: u8 = <(PAD0::PadNum, PAD1::PadNum)>::TXPO; +} - padout!((1, 0) => Pad1, Pad0); - padout!((1, 2) => Pad1, Pad0, Pad2, Pad3); - padout!((1, 1) => Pad1, Pad2); +/// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout +impl From<(PAD0, PAD1, PAD2, PAD3)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + PAD3: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum): RxpoTxpo, +{ + fn from(pads: (PAD0, PAD1, PAD2, PAD3)) -> Padout { + Padout { + sercom: PhantomData, + rx: pads.0, + tx: pads.1, + rts: pads.2, + cts: pads.3, + } + } +} - padout!((2, 0) => Pad2, Pad0); +impl RxpoTxpo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + PAD3: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum): RxpoTxpo, +{ + const RXPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum)>::RXPO; + const TXPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum)>::TXPO; +} - padout!((3, 0) => Pad3, Pad0); - padout!((3, 1) => Pad3, Pad2); +/// Define a UARTX type for the given Sercom. +/// +/// Also defines the valid "pad to uart function" mappings for this instance so +/// that construction is restricted to valid configurations. +macro_rules! uart { + ($Type:ident: ($Sercom:ident, $SERCOM:ident, $powermask:ident, $clock:ident)) => { + $crate::paste::item! { + pub type [<$Type Padout>] = Padout<$Sercom, RX, TX, RTS, CTS>; + pub type [<$Type TxPadout>] = TxPadout<$Sercom, TX, RTS>; + pub type [<$Type RxPadout>] = RxPadout<$Sercom, RX, CTS>; + } $crate::paste::item! { /// UARTX represents the corresponding SERCOMX instance @@ -221,7 +207,7 @@ macro_rules! uart { /// This type is generic over any valid pad mapping where there is /// a defined "receive pin out transmit pin out" implementation. pub struct $Type { - padout: [<$Type Padout>], + padout: Padout<$Sercom, RX, TX, RTS, CTS>, sercom: $SERCOM, } @@ -234,14 +220,14 @@ macro_rules! uart { /// You can use any tuple of two or four SercomXPadY instances /// for which there exists a From implementation for /// UARTXPadout. - pub fn new, T: Into<[<$Type Padout>]>>( + pub fn new, T: Into>>( clock: &clock::$clock, freq: F, sercom: $SERCOM, pm: &mut PM, padout: T ) -> $Type where - [<$Type Padout>]: RxpoTxpo { + Padout<$Sercom, RX, TX, RTS, CTS>: RxpoTxpo { let padout = padout.into(); pm.apbcmask.modify(|_, w| w.$powermask().set_bit()); @@ -313,7 +299,7 @@ macro_rules! uart { } } - pub fn free(self) -> ([<$Type Padout>], $SERCOM) { + pub fn free(self) -> (Padout<$Sercom, RX, TX, RTS, CTS>, $SERCOM) { (self.padout, self.sercom) } @@ -378,7 +364,7 @@ macro_rules! uart { /// The transmitting half of the corresponding UARTX instance (as returned by `UARTX::split`) pub struct [<$Type Tx>] { - padout: [<$Type TxPadout>], + padout: TxPadout<$Sercom, TX, RTS>, /// We store the SERCOM object here so we can retrieve it later, /// but conceptually, ownership is shared between the Rx and Tx halves. sercom: $SERCOM, @@ -442,7 +428,7 @@ macro_rules! uart { /// The receiving half of the corresponding UARTX instance (as returned by `UARTX::split`) pub struct [<$Type Rx>] { - padout: [<$Type RxPadout>], + padout: RxPadout<$Sercom, RX, CTS>, sercom: PhantomData<$SERCOM>, } @@ -499,7 +485,7 @@ macro_rules! uart { } } } - } + }; } uart!(UART0: (Sercom0, SERCOM0, sercom0_, Sercom0CoreClock)); diff --git a/hal/src/thumbv6m/sercom/v2.rs b/hal/src/thumbv6m/sercom/v2.rs index d571bab1e4ec..6c135e2e9c45 100644 --- a/hal/src/thumbv6m/sercom/v2.rs +++ b/hal/src/thumbv6m/sercom/v2.rs @@ -1,2 +1,2 @@ -pub mod pad_info; +pub mod impl_pad; pub mod spi; diff --git a/hal/src/thumbv6m/sercom/v2/pad_info.rs b/hal/src/thumbv6m/sercom/v2/impl_pad.rs similarity index 97% rename from hal/src/thumbv6m/sercom/v2/pad_info.rs rename to hal/src/thumbv6m/sercom/v2/impl_pad.rs index 90622f37f711..47ad290e2940 100644 --- a/hal/src/thumbv6m/sercom/v2/pad_info.rs +++ b/hal/src/thumbv6m/sercom/v2/impl_pad.rs @@ -1,4 +1,5 @@ -//! Implementations of the [`PadInfo`] trait +//! Implementations of the [`IsPad`] and [`GetPad`] traits + use crate::gpio::v2::*; use crate::sercom::v2::*; @@ -14,17 +15,17 @@ macro_rules! pad_info { $PadNum:ident ) => { #[cfg(feature = "samd11")] - impl PadInfo<$Sercom, $PadNum> for $PinId { + impl GetPad<$Sercom, $PadNum> for $PinId { type PinMode = Alternate<$Cfg>; } #[cfg(feature = "samd21")] - impl PadInfo<$Sercom> for $PinId { + impl GetPad<$Sercom> for $PinId { type PadNum = $PadNum; type PinMode = Alternate<$Cfg>; } - impl ConvertPinToPad for Pin<$PinId, Alternate<$Cfg>> { + impl IsPad for Pin<$PinId, Alternate<$Cfg>> { type Sercom = $Sercom; type PadNum = $PadNum; } diff --git a/hal/src/thumbv6m/sercom/v2/spi.rs b/hal/src/thumbv6m/sercom/v2/spi.rs index 649c6509d2ee..4b15f7507d5c 100644 --- a/hal/src/thumbv6m/sercom/v2/spi.rs +++ b/hal/src/thumbv6m/sercom/v2/spi.rs @@ -9,40 +9,54 @@ //! //! # [`Pads`] //! -//! A [`Sercom`] can use up to four [`Pin`]s as peripheral [`Pad`]s, but only -//! certain `Pin` combinations are acceptable. In particular, all `Pin`s must be -//! mapped to the same `Sercom` (see the datasheet). This HAL makes it -//! impossible to use invalid `Pin`/`Pad` combinations, and the [`Pads`] struct -//! is responsible for enforcing these constraints. +//! A [`Sercom`] can use up to four [`Pin`]s as configured as SERCOM pads, but +//! only certain `Pin` combinations are acceptable. In particular, all `Pin`s +//! must be mapped to the same `Sercom` (see the datasheet). This HAL makes it +//! impossible to use invalid `Pin` combinations, and the [`Pads`] struct is +//! responsible for enforcing these constraints. //! -//! A `Pads` type takes up to five type parameters. The first specifies the -//! `Sercom`. The remaining four, `DI`, `DO`, `CK` and `SS`, represent the Data -//! In, Data Out, Sclk and SS `Pad`s respectively, and they default to -//! [`NoneT`]. These type parameters take two different forms, depending on the -//! chip. For SAMD21 chips, they are effectively [`OptionalPinId`]s. While for -//! SAMD11 chips, they are optional ([`PadNum`], [`PinId`]) tuples. See the -//! [`GetPad`] trait for an explanation of the reasoning here. +//! A `Pads` type takes five type parameters. The first specifies the `Sercom`, +//! while the remaining four, `DI`, `DO`, `CK` and `SS`, represent the Data In, +//! Data Out, Sclk and SS pads respectively. Each of the remaining type +//! parameters is an [`OptionalPad`] and defaults to [`NoneT`]. Aliases defining +//! the pad types can be provided by the [`bsp_pins!`](crate::bsp_pins) macro. //! //! ``` -//! use atsamd_hal::gpio::v2::{PA04, PA05, PA08, PA09}; +//! use atsamd_hal::gpio::v2::{PA08, PA09, AlternateC, AlternateD}; //! use atsamd_hal::sercom::v2::{Sercom0, spi}; -//! use atsamd_hal::sercom::v2::pad::{Pad0, Pad1}; //! use atsamd_hal::typelevel::NoneT; //! -//! // For SAMD21 chips -//! type Pads = spi::Pads; -//! -//! // For SAMD11 chips -//! type Pads = spi::Pads; +//! type Miso = Pin; +//! type Sclk = Pin; +//! type Pads = spi::Pads; //! ``` +#![cfg_attr( + feature = "samd21", + doc = " +Alternatively, you can use the [`PadsFromIds`] alias to define a set of +`Pads` in terms of [`PinId`]s instead of `Pin`s. This is useful when you +don't have `Pin` aliases pre-defined. + +``` +use atsamd_hal::gpio::v2::{PA08, PA09}; +use atsamd_hal::sercom::v2::{Sercom0, spi}; +use atsamd_hal::typelevel::NoneT; + +type Pads = spi::PadsFromIds; +``` +" +)] +//! Instaces of `Pads` are created using the builder pattern. Start by creating +//! an empty set of `Pads` using [`Default`]. Then pass each respective `Pin` +//! using the corresponding methods. Both `v1::Pin` and `v2::Pin` types are +//! accepted here. //! -//! `Pads` are created using the builder pattern. Start by creating an empty set -//! of `Pads` using [`Default`]. Then pass each respective `Pin` using the -//! corresponding methods. Both `v1::Pin` and `v2::Pin` types are accepted here. -//! -//! To be accepted as part of a [`ValidConfig`], a set of `Pads` must do two -//! things: specify a type for `CK` and at least one of `DI` or `DO`; and -//! satisfy the [`DipoDopo`] trait. +//! On SAMD21 chips, the builder methods automatically convert each +//! pin to the correct [`PinMode`]. But for SAMD11 chips, users must manually +//! convert each pin before calling the builder methods. This is a consequence +//! of inherent ambiguities in the SAMD11 SERCOM pad definitions. Specifically, +//! the same `PinId` can correspond to two different `PadNum`s for the *same* +//! `Sercom`. //! //! ``` //! use atsamd_hal::target_device::Peripherals; @@ -57,6 +71,10 @@ //! .data_out(pins.pa11); //! ``` //! +//! To be accepted as [`ValidPads`], a set of `Pads` must do two things: +//! - Specify a type for `CK` and at least one of `DI` or `DO` +//! - Satisfy the [`DipoDopo`] trait +//! //! # [`Config`] //! //! Next, create a [`Config`] struct, which represents the SPI peripheral in its @@ -70,14 +88,13 @@ //! use atsamd_hal::sercom::v2::spi::{Master, NineBit}; //! use atsamd_hal::typelevel::NoneT; //! -//! // Assuming SAMD21 -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; //! type Config = spi::Config; //! ``` //! -//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] and the -//! PAC [`Sercom`] struct. It takes a reference to the PM, so that it can -//! enable the APB clock, and it takes a frequency to indicate the GCLK +//! Upon creation, the [`Config`] takes ownership of both the [`Pads`] struct +//! and the PAC [`Sercom`] struct. It takes a reference to the PM, so that it +//! can enable the APB clock, and it takes a frequency to indicate the GCLK //! configuration. Users are responsible for correctly configuring the GCLK. //! //! ``` @@ -106,10 +123,13 @@ //! .enable(); //! ``` //! +//! To be accepted as a [`ValidConfig`], the `Config` must have all the +//! necessary pads for its [`OpMode`]. +//! //! # [`Spi`] //! -//! An [`Spi`] struct can only be created from a [`Config`], and it has only one -//! type parameter, the corresponding config. +//! An [`Spi`] struct can only be created from a [`ValidConfig`], and it has +//! only one type parameter, the corresponding `Config`uration. //! //! ``` //! use atsamd_hal::gpio::v2::{PA08, PA09}; @@ -118,7 +138,7 @@ //! use atsamd_hal::typelevel::NoneT; //! //! // Assuming SAMD21 -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; //! type Config = spi::Config; //! type Spi = spi::Spi; //! ``` @@ -139,34 +159,47 @@ //! let rcvd: u16 = block!(spi.read()); //! ``` //! -//! # Using SPI with DMA -//! -//! This HAL includes support for DMA-enabled SPI transfers. An enabled `Spi` -//! struct implements the DMAC [`Buffer`](crate::dmac::transfer::Buffer) -//! trait. The provided [`send_with_dma`](Spi::send_with_dma) and -//! [`receive_with_dma`](Spi::receive_with_dma) build and begin a -//! [`dmac::Transfer`](crate::dmac::Transfer), thus starting the SPI in a -//! non-blocking way. Optionally, interrupts can be enabled on the provided -//! [`Channel`](crate::dmac::channel::Channel). Note that the `dma` feature must -//! be enabled. Please refer to the [`dmac`](crate::dmac) module-level -//! documentation for more information. ``` -//! // Assume channel is a configured `dmac::Channel`, and spi a -//! fully-configured `Spi` -//! -//! // Create data to send -//! let buffer: [u8; 50] = [0xff; 50] -//! -//! // Launch transfer -//! let dma_transfer = spi.send_with_dma(&mut buffer, channel, ()); -//! -//! // Wait for transfer to complete and reclaim resources -//! let (chan0, _, spi, _) = dma_transfer.wait(); -//! ``` -//! //! [`enable`]: Config::enable +//! [`bsp_pins`]: crate::bsp_pins //! [`Pin`]: crate::gpio::v2::pin::Pin //! [`PinId`]: crate::gpio::v2::pin::PinId -//! [`OptionalPinId`]: crate::gpio::v2::pin::OptionalPinId +//! [`PinMode`]: crate::gpio::v2::pin::PinMode +#![cfg_attr( + feature = "dma", + doc = " +# Using SPI with DMA + +This HAL includes support for DMA-enabled SPI transfers. An enabled [`Spi`] +struct implements the DMAC [`Buffer`] trait. The provided [`send_with_dma`] +and [`receive_with_dma`] methods will build and begin a [`dmac::Transfer`] +to create a non-blocking SPI transfer. + +Optionally, interrupts can be enabled on the provided [`Channel`]. Note that +the `dma` feature must be enabled. Refer to the [`dmac`] module-level +documentation for more information. + +``` +// Assume channel is a configured `dmac::Channel`, and spi a +// fully-configured `Spi` + +// Create data to send +let buffer: [u8; 50] = [0xff; 50] + +// Launch the transfer +let dma_transfer = spi.send_with_dma(&mut buffer, channel, ()); + +// Wait for the transfer to complete and reclaim resources +let (chan0, _, spi, _) = dma_transfer.wait(); +``` + +[`Buffer`]: crate::dmac::transfer::Buffer +[`send_with_dma`]: Spi::send_with_dma +[`receive_with_dma`]: Spi::receive_with_dma +[`dmac::Transfer`]: crate::dmac::Transfer +[`Channel`]: crate::dmac::channel::Channel +[`dmac`]: crate::dmac +" +)] use core::convert::{TryFrom, TryInto}; use core::marker::PhantomData; @@ -184,6 +217,7 @@ use pac::sercom0::spi::ctrla::MODE_A; use pac::sercom0::RegisterBlock; use pac::PM; +#[cfg(not(feature = "samd11"))] use crate::gpio::v2::AnyPin; use crate::sercom::v2::*; use crate::time::Hertz; @@ -204,9 +238,9 @@ use crate::typelevel::{Is, NoneT, Sealed}; /// [`OptionalPadNum`]s. Those implementations are then lifted to the /// corresponding [`Pads`] types. /// -/// To satisfy this trait, the combination of `OptionalPadNum`s must specify -/// [`SomePadNum`] for `CK` and at least one of `DI` and `DO`. Furthermore, no -/// two [`PadNum`]s can conflict. +/// To satisfy this trait, the combination of `OptionalPadNum`s must specify a +/// [`PadNum`] for `CK` and at least one of `DI` and `DO`. Furthermore, no two +/// `PadNum`s can conflict. pub trait DipoDopo { /// `DIPO` field value const DIPO: u8; @@ -229,10 +263,10 @@ pub trait DipoDopo { impl DipoDopo for Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, (DI::PadNum, DO::PadNum, CK::PadNum, SS::PadNum): DipoDopo, { const DIPO: u8 = <(DI::PadNum, DO::PadNum, CK::PadNum, SS::PadNum)>::DIPO; @@ -345,76 +379,29 @@ padnum_permutations!( () [NoneT Pad0 Pad1 Pad2 Pad3] ); // Pads //============================================================================= -/// Container for a set of SERCOM [`Pad`]s -/// -/// A [`Sercom`] can use up to four [`Pin`]s as peripheral [`Pad`]s, but only -/// certain `Pin` combinations are acceptable. In particular, all `Pin`s must be -/// mapped to the same `Sercom` (see the datasheet). This HAL makes it -/// impossible to use invalid `Pin`/`Pad` combinations, and the [`Pads`] struct -/// is responsible for enforcing these constraints. -/// -/// A `Pads` type takes up to five type parameters. The first specifies the -/// `Sercom`. The remaining four, `DI`, `DO`, `CK` and `SS`, represent the Data -/// In, Data Out, Sclk and SS `Pad`s respectively, and they default to -/// [`NoneT`]. These type parameters take two different forms, depending on the -/// chip. For SAMD21 chips, they are effectively [`OptionalPinId`]s. While for -/// SAMD11 chips, they are optional ([`PadNum`], [`PinId`]) tuples. See the -/// [`GetPad`] trait for an explanation of the reasoning here. -/// -/// ``` -/// use atsamd_hal::gpio::v2::{PA04, PA05, PA08, PA09}; -/// use atsamd_hal::sercom::v2::{Sercom0, spi}; -/// use atsamd_hal::sercom::v2::pad::{Pad0, Pad1}; -/// use atsamd_hal::typelevel::NoneT; -/// -/// // For SAMD21 chips -/// type Pads = spi::Pads; -/// -/// // For SAMD11 chips -/// type Pads = spi::Pads; -/// ``` -/// -/// `Pads` are created using the builder pattern. Start by creating an empty set -/// of `Pads` using [`Default`]. Then pass each respective `Pin` using the -/// corresponding methods. Both `v1::Pin` and `v2::Pin` types are accepted here. +/// Container for a set of SERCOM pads /// -/// To be accepted as part of a [`ValidConfig`], a set of `Pads` must do two -/// things: specify a type for `CK` and at least one of `DI` or `DO`; and -/// satisfy the [`DipoDopo`] trait. -/// -/// ``` -/// use atsamd_hal::target_device::Peripherals; -/// use atsamd_hal::gpio::v2::Pins; -/// use atsamd_hal::sercom::v2::{Sercom0, spi}; -/// -/// let mut peripherals = Peripherals::take().unwrap(); -/// let pins = Pins::new(peripherals.PORT); -/// let pads = spi::Pads::::default() -/// .sclk(pins.pa09) -/// .data_in(pins.pa08) -/// .data_out(pins.pa11); -/// ``` -/// -/// [`Pin`]: crate::gpio::v2::pin::Pin -/// [`PinId`]: crate::gpio::v2::pin::PinId -/// [`OptionalPinId`]: crate::gpio::v2::pin::OptionalPinId +/// See the [module-level](self) documentation for more details on specifying +/// a `Pads` type and creating instances. pub struct Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { - data_in: DI::Pad, - data_out: DO::Pad, - sclk: CK::Pad, - ss: SS::Pad, + sercom: PhantomData, + data_in: DI, + data_out: DO, + sclk: CK, + ss: SS, } impl Default for Pads { fn default() -> Self { Self { + sercom: PhantomData, data_in: NoneT, data_out: NoneT, sclk: NoneT, @@ -426,14 +413,15 @@ impl Default for Pads { impl Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { - /// Consume the [`Pads`] and return each individual [`Pad`] + /// Consume the [`Pads`] and return each individual + /// [`Pin`](crate::gpio::v2::Pin) #[inline] - pub fn free(self) -> (DI::Pad, DO::Pad, CK::Pad, SS::Pad) { + pub fn free(self) -> (DI, DO, CK, SS) { (self.data_in, self.data_out, self.sclk, self.ss) } } @@ -442,74 +430,62 @@ where impl Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { - /// Set the `DI` [`Pad`] + /// Set the `DI` pad /// /// In a [`MasterMode`], this is MISO. In [`Slave`] [`OpMode`], this is /// MOSI. #[inline] - pub fn data_in(self, pin: impl AnyPin) -> Pads - where - N: PadNum, - I: PadInfo, - { + pub fn data_in(self, pin: P) -> Pads { Pads { - data_in: pin.into().into(), + sercom: self.sercom, + data_in: pin, data_out: self.data_out, sclk: self.sclk, ss: self.ss, } } - /// Set the `DO` [`Pad`] + /// Set the `DO` pad /// /// In a [`MasterMode`], this is MOSI. In [`Slave`] [`OpMode`], this is /// MISO. #[inline] - pub fn data_out(self, pin: impl AnyPin) -> Pads - where - N: PadNum, - I: PadInfo, - { + pub fn data_out(self, pin: P) -> Pads { Pads { + sercom: self.sercom, data_in: self.data_in, - data_out: pin.into().into(), + data_out: pin, sclk: self.sclk, ss: self.ss, } } - /// Set the `SCK` [`Pad`] + /// Set the `SCK` pad #[inline] - pub fn sclk(self, pin: impl AnyPin) -> Pads - where - N: PadNum, - I: PadInfo, - { + pub fn sclk(self, pin: P) -> Pads { Pads { + sercom: self.sercom, data_in: self.data_in, data_out: self.data_out, - sclk: pin.into().into(), + sclk: pin, ss: self.ss, } } - /// Set the `SS` [`Pad`] + /// Set the `SS` pad #[inline] - pub fn ss(self, pin: impl AnyPin) -> Pads - where - N: PadNum, - I: PadInfo, - { + pub fn ss(self, pin: P) -> Pads { Pads { + sercom: self.sercom, data_in: self.data_in, data_out: self.data_out, sclk: self.sclk, - ss: pin.into().into(), + ss: pin, } } } @@ -518,186 +494,118 @@ where impl Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { - /// Set the `DI` [`Pad`] + /// Set the `DI` pad /// /// In a [`MasterMode`], this is MISO. In [`Slave`] [`OpMode`], this is /// MOSI. #[inline] - pub fn data_in(self, pin: impl AnyPin) -> Pads + pub fn data_in(self, pin: impl AnyPin) -> Pads, DO, CK, SS> where - I: PadInfo, + I: GetPad, + Pad: IsPad, { Pads { - data_in: pin.into().into(), + sercom: self.sercom, + data_in: pin.into().into_mode(), data_out: self.data_out, sclk: self.sclk, ss: self.ss, } } - /// Set the `DO` [`Pad`] + /// Set the `DO` pad /// /// In a [`MasterMode`], this is MOSI. In [`Slave`] [`OpMode`], this is /// MISO. #[inline] - pub fn data_out(self, pin: impl AnyPin) -> Pads + pub fn data_out(self, pin: impl AnyPin) -> Pads, CK, SS> where - I: PadInfo, + I: GetPad, + Pad: IsPad, { Pads { + sercom: self.sercom, data_in: self.data_in, - data_out: pin.into().into(), + data_out: pin.into().into_mode(), sclk: self.sclk, ss: self.ss, } } - /// Set the `SCK` [`Pad`] + /// Set the `SCK` pad #[inline] - pub fn sclk(self, pin: impl AnyPin) -> Pads + pub fn sclk(self, pin: impl AnyPin) -> Pads, SS> where - I: PadInfo, + I: GetPad, + Pad: IsPad, { Pads { + sercom: self.sercom, data_in: self.data_in, data_out: self.data_out, - sclk: pin.into().into(), + sclk: pin.into().into_mode(), ss: self.ss, } } - /// Set the `SS` [`Pad`] + /// Set the `SS` pad #[inline] - pub fn ss(self, pin: impl AnyPin) -> Pads + pub fn ss(self, pin: impl AnyPin) -> Pads> where - I: PadInfo, + I: GetPad, + Pad: IsPad, { Pads { + sercom: self.sercom, data_in: self.data_in, data_out: self.data_out, sclk: self.sclk, - ss: pin.into().into(), + ss: pin.into().into_mode(), } } } -//============================================================================= -// spi_pads_from_pins -//============================================================================= - -/// Define a set of [`spi::Pads`] using [`Pin`]s instead of -/// ([`PadNum`], [`PinId`]) tuples +/// Define a set of [`Pads`] using [`PinId`]s instead of [`Pin`]s /// -/// In some cases, it is more convenient to specify a set of `spi::Pads` using -/// `Pin`s or `Pin` aliases than it is to use the corresponding -/// ([`PadNum`], [`PinId`]) tuples. This macro makes it easier to do so. +/// In some cases, it is more convenient to specify a set of `Pads` using +/// `PinId`s rather than `Pin`s. This alias makes it easier to do so. /// -/// The first argument to the macro is required and represents the [`Sercom`]. -/// The remaining four arguments are all optional. Each represents a -/// corresponding type parameter of the `spi::Pads` type. Some of the types may -/// be omitted, but any types that are specified, must be done in the order -/// `DI`, `DO`, `CK` & `SS`. +/// The first type parameter is the [`Sercom`], while the remaining four are +/// effectively [`OptionalPinId`]s representing the corresponding type +/// parameters of [`Pads`], i.e. `DI`, `DO`, `CK` & `SS`. Each of the +/// remaining type parameters defaults to [`NoneT`]. /// /// ``` /// use atsamd_hal::pac::Peripherals; -/// use atsamd_hal::spi_pads_from_pins; -/// use atsamd_hal::gpio::v2::{Pin, PA04, PA05, AlternateD, Pins}; -/// use atsamd_hal::sercom::v2::{Sercom0, spi}; -/// -/// type Miso = Pin; -/// type Sclk = Pin; -/// pub type Pads = spi_pads_from_pins!(Sercom0, DI = Miso, CK = Sclk); -/// -/// pub fn test() -> Pads { -/// let peripherals = Peripherals::take().unwrap(); -/// let pins = Pins::new(peripherals.PORT); -/// spi::Pads::::default() -/// .sclk(pins.pa05) -/// .data_in(pins.pa04) -/// } -/// ``` -/// -/// [`spi::Pads`]: Pads -/// [`Pin`]: crate::gpio::v2::Pin -/// [`PinId`]: crate::gpio::v2::PinId -#[cfg(feature = "samd11")] -#[macro_export] -macro_rules! spi_pads_from_pins { - ( - $Sercom:ident - $( , DI = $DI:ty )? - $( , DO = $DO:ty )? - $( , CK = $CK:ty )? - $( , SS = $SS:ty )? - ) => { - $crate::sercom::v2::spi::Pads< - $crate::sercom::v2::$Sercom, - $crate::__opt_type!( $crate::sercom::v2::pad::PinToNITuple<$DI> ), - $crate::__opt_type!( $crate::sercom::v2::pad::PinToNITuple<$DO> ), - $crate::__opt_type!( $crate::sercom::v2::pad::PinToNITuple<$CK> ), - $crate::__opt_type!( $crate::sercom::v2::pad::PinToNITuple<$SS> ), - > - }; -} - -/// Define a set of [`spi::Pads`] using [`Pin`]s instead of [`PinId`]s -/// -/// In some cases, it is more convenient to specify a set of `spi::Pads` using -/// `Pin`s or `Pin` aliases than it is to use the corresponding [`PinId`]s. This -/// macro makes it easier to do so. -/// -/// The first argument to the macro is required and represents the [`Sercom`]. -/// The remaining four arguments are all optional. Each represents a -/// corresponding type parameter of the `spi::Pads` type. Some of the types may -/// be omitted, but any types that are specified, must be done in the order -/// `DI`, `DO`, `CK` & `SS`. -/// -/// ``` -/// use atsamd_hal::pac::Peripherals; -/// use atsamd_hal::spi_pads_from_pins; -/// use atsamd_hal::gpio::v2::{Pin, PA08, PA09, AlternateC, Pins}; +/// use atsamd_hal::gpio::v2::{PA08, PA09, Pins}; /// use atsamd_hal::sercom::v2::{Sercom0, spi}; +/// use atsamd_hal::typelevel::NoneT; /// -/// type Miso = Pin; -/// type Sclk = Pin; -/// pub type Pads = spi_pads_from_pins!(Sercom0, DI = Miso, CK = Sclk); +/// pub type Pads = spi::PadsFromIds; /// -/// pub fn test() -> Pads { +/// pub fn create_pads() -> Pads { /// let peripherals = Peripherals::take().unwrap(); /// let pins = Pins::new(peripherals.PORT); -/// spi::Pads::::default() -/// .sclk(pins.pa09) -/// .data_in(pins.pa08) +/// spi::Pads::default().sclk(pins.pa09).data_in(pins.pa08) /// } /// ``` /// -/// [`spi::Pads`]: Pads /// [`Pin`]: crate::gpio::v2::Pin /// [`PinId`]: crate::gpio::v2::PinId +/// [`OptionalPinId`]: crate::gpio::v2::OptionalPinId #[cfg(feature = "samd21")] -#[macro_export] -macro_rules! spi_pads_from_pins { - ( - $Sercom:ident - $( , DI = $DI:ty )? - $( , DO = $DO:ty )? - $( , CK = $CK:ty )? - $( , SS = $SS:ty )? - ) => { - $crate::sercom::v2::spi::Pads< - $crate::sercom::v2::$Sercom, - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$DI> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$DO> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$CK> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$SS> )? ), - > - }; -} +pub type PadsFromIds = Pads< + S, + >::Pad, + >::Pad, + >::Pad, + >::Pad, +>; //============================================================================= // PadSet @@ -710,12 +618,13 @@ macro_rules! spi_pads_from_pins { /// types in this module. It acts as a [type-level function], returning the /// corresponding [`Sercom`] and [`OptionalPad`] types. It serves to cut down on /// the total number of type parameters needed in the [`Config`] struct. The -/// `Config` struct doesn't need access to the [`Pad`]s directly. Rather, it -/// only needs to apply the [`SomePad`] trait bound when a `Pad` is required. +/// `Config` struct doesn't need access to the [`Pin`]s directly. Rather, it +/// only needs to apply the [`SomePad`] trait bound when a `Pin` is required. /// The `PadSet` trait allows each `Config` struct to store an instance of /// `Pads` without itself being generic over all six type parameters of the /// `Pads` type. /// +/// [`Pin`]: crate::gpio::v2::Pin /// [type-level function]: crate::typelevel#type-level-functions pub trait PadSet: Sealed { type Sercom: Sercom; @@ -728,26 +637,26 @@ pub trait PadSet: Sealed { impl Sealed for Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { } impl PadSet for Pads where S: Sercom, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { type Sercom = S; - type DataIn = DI::Pad; - type DataOut = DO::Pad; - type Sclk = CK::Pad; - type SS = SS::Pad; + type DataIn = DI; + type DataOut = DO; + type Sclk = CK; + type SS = SS; } //============================================================================= @@ -795,7 +704,7 @@ where /// Marker trait for a set of [`Pads`] that cannot transmit /// -/// A set of [`Pads`] cannot be used to transmit when the Data Out [`Pad`] is +/// A set of [`Pads`] cannot be used to transmit when the Data Out pad is /// [`NoneT`]. pub trait NotTx: ValidPads {} @@ -803,7 +712,7 @@ impl

NotTx for P where P: ValidPads {} /// Marker trait for a set of [`Pads`] that cannot receive /// -/// A set of [`Pads`] cannot be used to receive when the Data In [`Pad`] is +/// A set of [`Pads`] cannot be used to receive when the Data In pad is /// [`NoneT`]. pub trait NotRx: ValidPads {} @@ -818,9 +727,9 @@ pub trait TxOrRx: ValidPads {} impl TxOrRx for Pads where S: Sercom, - DI: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, + DI: SomePad, + CK: SomePad, + SS: OptionalPad, Self: DipoDopo, { } @@ -828,25 +737,14 @@ where impl TxOrRx for Pads where S: Sercom, - DO: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, + DO: SomePad, + CK: SomePad, + SS: OptionalPad, Self: DipoDopo, { } -//impl TxOrRx for P {} - -impl TxOrRx for Pads -where - S: Sercom, - DI: GetPad + GetPadMarker, - DO: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, - Self: DipoDopo, -{ -} +impl TxOrRx for P {} //============================================================================= // Operating mode @@ -859,7 +757,7 @@ where /// The available operating modes are [`Master`], [`MasterHWSS`] and [`Slave`]. /// In [`Master`] mode, the `SS` signal must be handled by the user, so `SS` /// must be [`NoneT`]. In [`MasterHWSS`] mode, the hardware drives the `SS` -/// line, so [`SomePad`] is required. In [`Slave`] mode, the `SS` [`Pad`] is +/// line, so [`SomePad`] is required. In [`Slave`] mode, the `SS` pad is /// required as well, to indicate when data is valid. /// /// [type-level enums]: crate::typelevel#type-level-enums @@ -1380,9 +1278,9 @@ where /// Marker trait for valid SPI [`Config`]urations /// -/// A functional SPI peripheral must have, at a minimum, an SCK [`Pad`] and -/// either a Data In or a Data Out [`Pad`]. Dependeing on the -/// [`OpMode`], an SS [`Pad`] may also be required. +/// A functional SPI peripheral must have, at a minimum, an SCK 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 @@ -1594,7 +1492,7 @@ where /// [type class]: crate::typelevel#type-classes pub trait AnySpi: Is> { type Sercom: Sercom; - type Pads: PadSet; + type Pads: ValidPads; type OpMode: OpMode; type CharSize: CharSize; type Word: 'static; diff --git a/hal/src/thumbv7em/sercom/v1/i2c.rs b/hal/src/thumbv7em/sercom/v1/i2c.rs index c988e24d9048..e4341e1ee350 100644 --- a/hal/src/thumbv7em/sercom/v1/i2c.rs +++ b/hal/src/thumbv7em/sercom/v1/i2c.rs @@ -2,6 +2,8 @@ use crate::clock; use crate::hal::blocking::i2c::{Read, Write, WriteRead}; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::{Pad0, Pad1}; use crate::target_device::sercom0::I2CM; use crate::target_device::{MCLK, SERCOM0, SERCOM1, SERCOM2, SERCOM3, SERCOM4, SERCOM5}; #[cfg(feature = "min-samd51n")] @@ -19,8 +21,8 @@ macro_rules! i2c { ([ $($Type:ident: ( - $pad0:ident, - $pad1:ident, + $pad0:ident, // No longer used + $pad1:ident, // No longer used $SERCOM:ident, $powermask:ident, $clock:ident, @@ -33,13 +35,21 @@ macro_rules! i2c { /// Represents the Sercom instance configured to act as an I2C Master. /// The embedded_hal blocking I2C traits are implemented by this instance. -pub struct $Type<$pad0, $pad1> { - sda: $pad0, - scl: $pad1, +pub struct $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ + sda: P0, + scl: P1, sercom: $SERCOM, } -impl<$pad0, $pad1> $Type<$pad0, $pad1> { +impl $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ /// Configures the sercom instance to work as an I2C Master. /// The clock is obtained via the `GenericClockGenerator` type. /// `freq` specifies the bus frequency to use for I2C communication. @@ -66,8 +76,8 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { freq: F, sercom: $SERCOM, mclk: &mut MCLK, - sda: $pad0, - scl: $pad1, + sda: P0, + scl: P1, ) -> Self { // Power up the peripheral bus clock. // safe because we're exclusively owning SERCOM @@ -109,7 +119,7 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { /// Breaks the sercom device up into its constituent pins and the SERCOM /// instance. Does not make any changes to power management. - pub fn free(self) -> ($pad0, $pad1, $SERCOM) { + pub fn free(self) -> (P0, P1, $SERCOM) { (self.sda, self.scl, self.sercom) } @@ -280,7 +290,11 @@ impl<$pad0, $pad1> $Type<$pad0, $pad1> { } } -impl<$pad0, $pad1> Write for $Type<$pad0, $pad1> { +impl Write for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; /// Sends bytes to slave with address `addr` @@ -291,7 +305,11 @@ impl<$pad0, $pad1> Write for $Type<$pad0, $pad1> { } } -impl<$pad0, $pad1> Read for $Type<$pad0, $pad1> { +impl Read for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -301,7 +319,11 @@ impl<$pad0, $pad1> Read for $Type<$pad0, $pad1> { } } -impl<$pad0, $pad1> WriteRead for $Type<$pad0, $pad1> { +impl WriteRead for $Type +where + P0: CompatiblePad, + P1: CompatiblePad, +{ type Error = I2CError; fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { diff --git a/hal/src/thumbv7em/sercom/v1/spi.rs b/hal/src/thumbv7em/sercom/v1/spi.rs index fea86d85996f..c9352a602223 100644 --- a/hal/src/thumbv7em/sercom/v1/spi.rs +++ b/hal/src/thumbv7em/sercom/v1/spi.rs @@ -1,8 +1,9 @@ +use core::marker::PhantomData; + use crate::clock; -use crate::gpio::v2::pin::AnyPin; use crate::hal::spi::{FullDuplex, Mode, Phase, Polarity}; -use crate::sercom::pads::*; -use crate::sercom::v2; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::*; use crate::spi_common::CommonSpi; use crate::target_device::sercom0::SPIM; use crate::target_device::{MCLK, SERCOM0, SERCOM1, SERCOM2, SERCOM3, SERCOM4, SERCOM5}; @@ -20,7 +21,80 @@ pub enum Error { /// this trait for yourself; only the implementations in the sercom module make /// sense. pub trait DipoDopo { - fn dipo_dopo(&self) -> (u8, u8); + const DIPO: u8; + const DOPO: u8; + fn dipo_dopo(&self) -> (u8, u8) { + (Self::DIPO, Self::DOPO) + } +} + +/// Defines a DipoDopo instance for the constructed padout instance +/// that returns the values used to configure the sercom pads for the +/// appropriate function in the sercom register file. +macro_rules! padout { + ( ($dipo:literal, $dopo:literal) => $pad0:ident, $pad1:ident, $pad2:ident) => { + impl DipoDopo for ($pad0, $pad1, $pad2) { + const DIPO: u8 = $dipo; + const DOPO: u8 = $dopo; + } + }; +} + +// dipo In master operation, DI is MISO Pad number 0-3 +// dopo 0 MOSI PAD 0 +// dopo 2 MOSI PAD 3 +// SCK can only be on PAD 1 +// (dipo,dopo) => (MISO, MOSI, SCK) +padout!((0, 2) => Pad0, Pad3, Pad1); +padout!((2, 0) => Pad2, Pad0, Pad1); +padout!((2, 2) => Pad2, Pad3, Pad1); +padout!((3, 0) => Pad3, Pad0, Pad1); + +/// A pad mapping configuration for the SERCOM in SPI master mode. +/// +/// This type can only be constructed using the From implementations +/// in this module, which are restricted to valid configurations. +/// +/// Defines which sercom pad is mapped to which SPI function. +pub struct Padout +where + S: Sercom, +{ + sercom: PhantomData, + _miso: MISO, + _mosi: MOSI, + _sclk: SCLK, +} + +/// Convert from a tuple of (MISO, MOSI, SCK) to SPIMasterXPadout +impl From<(PAD0, PAD1, PAD2)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum): DipoDopo, +{ + fn from(pads: (PAD0, PAD1, PAD2)) -> Padout { + Padout { + sercom: PhantomData, + _miso: pads.0, + _mosi: pads.1, + _sclk: pads.2, + } + } +} + +impl DipoDopo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum): DipoDopo, +{ + const DIPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum)>::DIPO; + const DOPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum)>::DOPO; } /// Define an SPIMasterX type for the given Sercom number. @@ -31,249 +105,165 @@ macro_rules! spi_master { ( $Type:ident: ($Sercom:ident, $SERCOM:ident, $powermask:ident, $clock:ident, $apmask:ident) ) => { - $crate::paste::item! { - /// A pad mapping configuration for the SERCOM in SPI master mode. - /// - /// This type can only be constructed using the From implementations - /// in this module, which are restricted to valid configurations. - /// - /// Defines which sercom pad is mapped to which SPI function. - pub struct [<$Type Padout>] { - _miso: MISO, - _mosi: MOSI, - _sck: SCK, - } + pub type [<$Type Padout>] = Padout<$Sercom, MISO, MOSI, SCLK>; } - /// Define a From instance for a tuple of SercomXPadX instances that - /// converts them into an SPIMasterXPadout instance. + /// SPIMasterX represents the corresponding SERCOMX instance + /// configured to act in the role of an SPI Master. + /// Objects of this type implement the HAL `FullDuplex` and blocking + /// SPI traits. /// - /// Also defines a DipoDopo instance for the constructed padout instance - /// that returns the values used to configure the sercom pads for the - /// appropriate function in the sercom register file. - macro_rules! padout { - ($dipo_dopo:expr => $pad0:ident, $pad1:ident, $pad2:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (MISO, MOSI, SCK) to SPIMasterXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> { - [<$Type Padout>] { _miso: pads.0, _mosi: pads.1, _sck: pads.2 } - } - } - - impl DipoDopo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - { - fn dipo_dopo(&self) -> (u8, u8) { - $dipo_dopo - } - } - - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>)) -> Self { - [<$Type Padout>] { _miso: pads.0, _mosi: pads.1, _sck: pads.2 } - } - } - - impl DipoDopo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - { - fn dipo_dopo(&self) -> (u8, u8) { - $dipo_dopo - } - } - } - }; + /// This type is generic over any valid pad mapping where there is + /// a defined "data in pin out data out pin out" implementation. + pub struct $Type { + padout: Padout<$Sercom, MISO, MOSI, SCK>, + sercom: $SERCOM, } - // dipo In master operation, DI is MISO Pad number 0-3 - // dopo 0 MOSI PAD 0 - // dopo 2 MOSI PAD 3 - // SCK can only be on PAD 1 - // (dipo,dopo) => (MISO, MOSI, SCK) - padout!((0, 2) => Pad0, Pad3, Pad1); - padout!((2, 0) => Pad2, Pad0, Pad1); - padout!((2, 2) => Pad2, Pad3, Pad1); - padout!((3, 0) => Pad3, Pad0, Pad1); + impl CommonSpi for $Type { + /// Helper for accessing the spi member of the sercom instance + fn spi(&self) -> &SPIM { + &self.sercom.spim() + } - $crate::paste::item! { - /// SPIMasterX represents the corresponding SERCOMX instance - /// configured to act in the role of an SPI Master. - /// Objects of this type implement the HAL `FullDuplex` and blocking - /// SPI traits. - /// - /// This type is generic over any valid pad mapping where there is - /// a defined "data in pin out data out pin out" implementation. - pub struct $Type { - padout: [<$Type Padout>], - sercom: $SERCOM, + /// Helper for accessing the spi member of the sercom instance + fn spi_mut(&mut self) -> &SPIM { + &self.sercom.spim() } + } - impl CommonSpi for $Type { - /// Helper for accessing the spi member of the sercom instance - fn spi(&self) -> &SPIM { - &self.sercom.spim() + impl $Type { + /// Power on and configure SERCOMX to work as an SPI Master operating + /// with the specified frequency and SPI Mode. The pinout specifies + /// which pins are bound to the MISO, MOSI, SCK functions. + pub fn new, T: Into>>( + clock: &clock::$clock, + freq: F, + mode: Mode, + sercom: $SERCOM, + mclk: &mut MCLK, + padout: T, + ) -> Self + where + Padout<$Sercom, MISO, MOSI, SCK>: DipoDopo, + { + let padout = padout.into(); + + // Power up the peripheral bus clock. + // safe because we're exclusively owning SERCOM + mclk.$apmask.modify(|_, w| w.$powermask().set_bit()); + + // reset the sercom instance + sercom.spim().ctrla.modify(|_, w| w.swrst().set_bit()); + // wait for reset to complete + while sercom.spim().syncbusy.read().swrst().bit_is_set() + || sercom.spim().ctrla.read().swrst().bit_is_set() + {} + + // Put the hardware into spi master mode + sercom.spim().ctrla.modify(|_, w| w.mode().spi_master()); + // wait for configuration to take effect + while sercom.spim().syncbusy.read().enable().bit_is_set() {} + + // 8 bit data size and enable the receiver + unsafe { + sercom.spim().ctrlb.modify(|_, w| { + w.chsize().bits(0); + w.rxen().set_bit() + }); } - /// Helper for accessing the spi member of the sercom instance - fn spi_mut(&mut self) -> &SPIM { - &self.sercom.spim() + // set the baud rate + let baud = Self::calculate_baud(freq, clock.freq()); + unsafe { + sercom.spim().baud.modify(|_, w| w.baud().bits(baud)); + + sercom.spim().ctrla.modify(|_, w| { + match mode.polarity { + Polarity::IdleLow => w.cpol().clear_bit(), + Polarity::IdleHigh => w.cpol().set_bit(), + }; + + match mode.phase { + Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), + Phase::CaptureOnSecondTransition => w.cpha().set_bit(), + }; + + let (dipo, dopo) = padout.dipo_dopo(); + w.dipo().bits(dipo); + w.dopo().bits(dopo); + + // MSB first + w.dord().clear_bit() + }); } + + sercom.spim().ctrla.modify(|_, w| w.enable().set_bit()); + // wait for configuration to take effect + while sercom.spim().syncbusy.read().enable().bit_is_set() {} + + Self { padout, sercom } } - impl $Type { - /// Power on and configure SERCOMX to work as an SPI Master operating - /// with the specified frequency and SPI Mode. The pinout specifies - /// which pins are bound to the MISO, MOSI, SCK functions. - pub fn new, T: Into<[<$Type Padout>]>>( - clock:&clock::$clock, - freq: F, - mode: Mode, - sercom: $SERCOM, - mclk: &mut MCLK, - padout: T, - ) -> Self where - [<$Type Padout>]: DipoDopo { - let padout = padout.into(); - - // Power up the peripheral bus clock. - // safe because we're exclusively owning SERCOM - mclk.$apmask.modify(|_, w| w.$powermask().set_bit()); - - // reset the sercom instance - sercom.spim().ctrla.modify(|_, w| w.swrst().set_bit()); - // wait for reset to complete - while sercom.spim().syncbusy.read().swrst().bit_is_set() - || sercom.spim().ctrla.read().swrst().bit_is_set() - {} - - // Put the hardware into spi master mode - sercom.spim().ctrla.modify(|_, w| w.mode().spi_master()); - // wait for configuration to take effect - while sercom.spim().syncbusy.read().enable().bit_is_set() {} - - // 8 bit data size and enable the receiver - unsafe { - sercom.spim().ctrlb.modify(|_, w|{ - w.chsize().bits(0); - w.rxen().set_bit() - }); - } - - // set the baud rate - let baud = Self::calculate_baud(freq, clock.freq()); - unsafe { - sercom.spim().baud.modify(|_, w| w.baud().bits(baud)); - - sercom.spim().ctrla.modify(|_, w| { - match mode.polarity { - Polarity::IdleLow => w.cpol().clear_bit(), - Polarity::IdleHigh => w.cpol().set_bit(), - }; - - match mode.phase { - Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), - Phase::CaptureOnSecondTransition => w.cpha().set_bit(), - }; - - let (dipo, dopo) = padout.dipo_dopo(); - w.dipo().bits(dipo); - w.dopo().bits(dopo); - - // MSB first - w.dord().clear_bit() - }); - } - - - sercom.spim().ctrla.modify(|_, w| w.enable().set_bit()); - // wait for configuration to take effect - while sercom.spim().syncbusy.read().enable().bit_is_set() {} - - Self { - padout, - sercom, - } + /// Set the baud rate + pub fn set_baud>(&mut self, freq: F, clock: &clock::$clock) { + self.disable(); + let baud = Self::calculate_baud(freq, clock.freq()); + unsafe { + self.spi_mut().baud.modify(|_, w| w.baud().bits(baud)); } + self.enable(); + } - /// Set the baud rate - pub fn set_baud>( - &mut self, - freq: F, - clock:&clock::$clock - ) { - self.disable(); - let baud = Self::calculate_baud(freq, clock.freq()); - unsafe { - self.spi_mut().baud.modify(|_, w| w.baud().bits(baud)); - } - self.enable(); - } + /// Tear down the SPI instance and yield the constituent pins and + /// SERCOM instance. No explicit de-initialization is performed. + pub fn free(self) -> (Padout<$Sercom, MISO, MOSI, SCK>, $SERCOM) { + (self.padout, self.sercom) + } + } + + impl FullDuplex for $Type { + type Error = Error; - /// Tear down the SPI instance and yield the constituent pins and - /// SERCOM instance. No explicit de-initialization is performed. - pub fn free(self) -> ([<$Type Padout>], $SERCOM) { - (self.padout, self.sercom) + fn read(&mut self) -> nb::Result { + let status = self.spi().status.read(); + if status.bufovf().bit_is_set() { + return Err(nb::Error::Other(Error::Overrun)); } - } - impl FullDuplex for $Type { - type Error = Error; - - fn read(&mut self) -> nb::Result { - let status = self.spi().status.read(); - if status.bufovf().bit_is_set() { - return Err(nb::Error::Other(Error::Overrun)); - } - - let intflag = self.spi().intflag.read(); - // rxc is receive complete - if intflag.rxc().bit_is_set() { - Ok(self.spi().data.read().data().bits() as u8) - } else { - Err(nb::Error::WouldBlock) - } + let intflag = self.spi().intflag.read(); + // rxc is receive complete + if intflag.rxc().bit_is_set() { + Ok(self.spi().data.read().data().bits() as u8) + } else { + Err(nb::Error::WouldBlock) } + } - fn send(&mut self, byte: u8) -> nb::Result<(), Error> { - let intflag = self.spi().intflag.read(); - // dre is data register empty - if intflag.dre().bit_is_set() { - self.spi_mut().data.write(|w| unsafe{w.data().bits(byte as u32)}); - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } + fn send(&mut self, byte: u8) -> nb::Result<(), Error> { + let intflag = self.spi().intflag.read(); + // dre is data register empty + if intflag.dre().bit_is_set() { + self.spi_mut() + .data + .write(|w| unsafe { w.data().bits(byte as u32) }); + Ok(()) + } else { + Err(nb::Error::WouldBlock) } } + } - impl ::hal::blocking::spi::transfer::Default for $Type {} - impl ::hal::blocking::spi::write::Default for $Type {} - #[cfg(feature = "unproven")] - impl ::hal::blocking::spi::write_iter::Default for $Type {} + impl ::hal::blocking::spi::transfer::Default + for $Type + { + } + impl ::hal::blocking::spi::write::Default for $Type {} + #[cfg(feature = "unproven")] + impl ::hal::blocking::spi::write_iter::Default + for $Type + { } }; } diff --git a/hal/src/thumbv7em/sercom/v1/uart.rs b/hal/src/thumbv7em/sercom/v1/uart.rs index 43f3c09ab273..8bf8ac8749d8 100644 --- a/hal/src/thumbv7em/sercom/v1/uart.rs +++ b/hal/src/thumbv7em/sercom/v1/uart.rs @@ -1,9 +1,8 @@ use crate::clock; -use crate::gpio::v2::pin::AnyPin; use crate::hal::blocking::serial::{write::Default, Write}; use crate::hal::serial; -use crate::sercom::pads::*; -use crate::sercom::v2; +use crate::sercom::v1::pads::CompatiblePad; +use crate::sercom::v2::*; use crate::target_device::sercom0::USART_INT; use crate::target_device::{MCLK, SERCOM0, SERCOM1, SERCOM2, SERCOM3, SERCOM4, SERCOM5}; #[cfg(feature = "min-samd51n")] @@ -17,7 +16,180 @@ use core::marker::PhantomData; /// this trait for yourself; only the implementations in the sercom module make /// sense. pub trait RxpoTxpo { - fn rxpo_txpo(&self) -> (u8, u8); + const RXPO: u8; + const TXPO: u8; + fn rxpo_txpo(&self) -> (u8, u8) { + (Self::RXPO, Self::TXPO) + } +} + +macro_rules! padout { + ( ($rxpo:literal, $txpo:literal) => $pad0:ident, $pad1:ident) => { + impl RxpoTxpo for ($pad0, $pad1) { + const RXPO: u8 = $rxpo; + const TXPO: u8 = $txpo; + } + }; + ( ($rxpo:literal, $txpo:literal) => $pad0:ident, $pad1:ident, $pad2:ident, $pad3:ident) => { + impl RxpoTxpo for ($pad0, $pad1, $pad2, $pad3) { + const RXPO: u8 = $rxpo; + const TXPO: u8 = $txpo; + } + }; +} + +// rxpo 0-3 RX on PAD 0-3 +// TX always PAD 0 +// txpo 0 no RTS/CTS +// txpo 1 reserved and can't be used +// txpo 2 RTS PAD 2, CTS PAD 3 +// txpo 3 RTS PAD 2, no CTS +// (rxpo_txpo) => (RX, TX, RTS, CTS) +padout!((1, 0) => Pad1, Pad0); +padout!((1, 2) => Pad1, Pad0, Pad2, Pad3); + +// todo we could support an RTS without a CTS +// padout!((1, 3) => Pad1, Pad0, Pad2); + +padout!((2, 0) => Pad2, Pad0); +padout!((3, 0) => Pad3, Pad0); + +// todo we could support an RTS without a CTS +// padout!((3, 3) => Pad3, Pad0, Pad2); + +/// A pad mapping configuration for the SERCOM in UART mode. +/// +/// This type can only be constructed using the From implementations +/// in this module, which are restricted to valid configurations. +/// +/// Defines which sercom pad is mapped to which UART function. +pub struct Padout +where + S: Sercom, +{ + sercom: PhantomData, + rx: RX, + tx: TX, + rts: RTS, + cts: CTS, +} + +/// A pad mapping configuration for the receiving half of the SERCOM in UART +/// mode. +pub struct RxPadout +where + S: Sercom, +{ + sercom: PhantomData, + rx: RX, + cts: CTS, +} + +/// A pad mapping configuration for the transmitting half of the SERCOM in UART +/// mode. +pub struct TxPadout +where + S: Sercom, +{ + sercom: PhantomData, + tx: TX, + rts: RTS, +} + +impl Padout +where + S: Sercom, +{ + /// Splits the padout into transmit and receive halves + pub fn split(self) -> (TxPadout, RxPadout) { + ( + TxPadout { + sercom: PhantomData, + tx: self.tx, + rts: self.rts, + }, + RxPadout { + sercom: PhantomData, + rx: self.rx, + cts: self.cts, + }, + ) + } + + /// Combines transmit and receive halves back into a duplex padout + pub fn join(tx: TxPadout, rx: RxPadout) -> Self { + Self { + sercom: PhantomData, + rx: rx.rx, + tx: tx.tx, + rts: tx.rts, + cts: rx.cts, + } + } +} + +/// Convert from a tuple of (RX, TX) to UARTXPadout +impl From<(PAD0, PAD1)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum): RxpoTxpo, +{ + fn from(pads: (PAD0, PAD1)) -> Padout { + Padout { + sercom: PhantomData, + rx: pads.0, + tx: pads.1, + rts: (), + cts: (), + } + } +} + +impl RxpoTxpo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum): RxpoTxpo, +{ + const RXPO: u8 = <(PAD0::PadNum, PAD1::PadNum)>::RXPO; + const TXPO: u8 = <(PAD0::PadNum, PAD1::PadNum)>::TXPO; +} + +/// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout +impl From<(PAD0, PAD1, PAD2, PAD3)> for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + PAD3: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum): RxpoTxpo, +{ + fn from(pads: (PAD0, PAD1, PAD2, PAD3)) -> Padout { + Padout { + sercom: PhantomData, + rx: pads.0, + tx: pads.1, + rts: pads.2, + cts: pads.3, + } + } +} + +impl RxpoTxpo for Padout +where + S: Sercom, + PAD0: CompatiblePad, + PAD1: CompatiblePad, + PAD2: CompatiblePad, + PAD3: CompatiblePad, + (PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum): RxpoTxpo, +{ + const RXPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum)>::RXPO; + const TXPO: u8 = <(PAD0::PadNum, PAD1::PadNum, PAD2::PadNum, PAD3::PadNum)>::TXPO; } /// Define a UARTX type for the given Sercom. @@ -36,197 +208,11 @@ macro_rules! uart { $int2: ident) ) => { $crate::paste::item! { - /// A pad mapping configuration for the SERCOM in UART mode. - /// - /// This type can only be constructed using the From implementations - /// in this module, which are restricted to valid configurations. - /// - /// Defines which sercom pad is mapped to which UART function. - pub struct [<$Type Padout>] { - rx: RX, - tx: TX, - rts: RTS, - cts: CTS, - } - - /// A pad mapping configuration for the receiving half of the SERCOM in UART mode. - pub struct [<$Type RxPadout>] { - rx: RX, - cts: CTS, - } - - /// A pad mapping configuration for the transmitting half of the SERCOM in UART mode. - pub struct [<$Type TxPadout>] { - tx: TX, - rts: RTS, - } - - impl [<$Type Padout>] { - /// Splits the padout into transmit and receive halves - pub fn split(self) -> ([<$Type TxPadout>], [<$Type RxPadout>]) { - ( - [<$Type TxPadout>] { - tx: self.tx, - rts: self.rts, - }, - [<$Type RxPadout>] { - rx: self.rx, - cts: self.cts, - }, - ) - } - - /// Combines transmit and receive halves back into a duplex padout - pub fn join(tx: [<$Type TxPadout>], rx: [<$Type RxPadout>]) -> Self { - Self { - rx: rx.rx, - tx: tx.tx, - rts: tx.rts, - cts: rx.cts, - } - } - } + pub type [<$Type Padout>] = Padout<$Sercom, RX, TX, RTS, CTS>; + pub type [<$Type TxPadout>] = TxPadout<$Sercom, TX, RTS>; + pub type [<$Type RxPadout>] = RxPadout<$Sercom, RX, CTS>; } - /// Define a From instance for either a tuple of two SercomXPadX - /// instances, or a tuple of four SercomXPadX instances that converts - /// them into an UARTXPadout instance. - /// - /// Also defines a RxpoTxpo instance for the constructed padout instance - /// that returns the values used to configure the sercom pads for the - /// appropriate function in the sercom register file. - macro_rules! padout { - ($rxpo_txpo:expr => $pad0:ident, $pad1:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (RX, TX) to UARTXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: (), cts: () } - } - } - - impl RxpoTxpo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], (), ()> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, (), ()> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>)) -> Self { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: (), cts: () } - } - } - - impl RxpoTxpo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, (), ()> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - } - }; - ($rxpo_txpo:expr => $pad0:ident, $pad1:ident, $pad2:ident, $pad3:ident) => { - $crate::paste::item! { - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>])> for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN3: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - PIN3::Id: GetPadMode<$Sercom, $pad3>, - { - fn from(pads: ([<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>])) -> [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: pads.2, cts: pads.3 } - } - } - - impl RxpoTxpo for [<$Type Padout>]<[<$Sercom $pad0>], [<$Sercom $pad1>], [<$Sercom $pad2>], [<$Sercom $pad3>]> - where - PIN0: AnyPin, - PIN1: AnyPin, - PIN2: AnyPin, - PIN3: AnyPin, - PIN0::Id: GetPadMode<$Sercom, $pad0>, - PIN1::Id: GetPadMode<$Sercom, $pad1>, - PIN2::Id: GetPadMode<$Sercom, $pad2>, - PIN3::Id: GetPadMode<$Sercom, $pad3>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - - /// Convert from a tuple of (RX, TX, RTS, CTS) to UARTXPadout - impl From<(v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>)> for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - Id3: v2::GetPadMode<$Sercom, $pad3>, - { - fn from(pads: (v2::Pad<$Sercom, $pad0, Id0>, v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>)) -> Self { - [<$Type Padout>] { rx: pads.0, tx: pads.1, rts: pads.2, cts: pads.3 } - } - } - - impl RxpoTxpo for [<$Type Padout>], v2::Pad<$Sercom, $pad1, Id1>, v2::Pad<$Sercom, $pad2, Id2>, v2::Pad<$Sercom, $pad3, Id3>> - where - Id0: v2::GetPadMode<$Sercom, $pad0>, - Id1: v2::GetPadMode<$Sercom, $pad1>, - Id2: v2::GetPadMode<$Sercom, $pad2>, - Id3: v2::GetPadMode<$Sercom, $pad3>, - { - fn rxpo_txpo(&self) -> (u8, u8) { - $rxpo_txpo - } - } - } - }; - } - - // rxpo 0-3 RX on PAD 0-3 - // TX always PAD 0 - // txpo 0 no RTS/CTS - // txpo 1 reserved and can't be used - // txpo 2 RTS PAD 2, CTS PAD 3 - // txpo 3 RTS PAD 2, no CTS - // (rxpo_txpo) => (RX, TX, RTS, CTS) - padout!((1, 0) => Pad1, Pad0); - padout!((1, 2) => Pad1, Pad0, Pad2, Pad3); - - // todo we could support an RTS without a CTS - // padout!((1, 3) => Pad1, Pad0, Pad2); - - padout!((2, 0) => Pad2, Pad0); - padout!((3, 0) => Pad3, Pad0); - - // todo we could support an RTS without a CTS - // padout!((3, 3) => Pad3, Pad0, Pad2); - $crate::paste::item! { /// UARTX represents the corresponding SERCOMX instance /// configured to act in the role of a UART Master. @@ -236,19 +222,19 @@ macro_rules! uart { /// This type is generic over any valid pad mapping where there is /// a defined "receive pin out transmit pin out" implementation. pub struct $Type { - padout: [<$Type Padout>], + padout: Padout<$Sercom, RX, TX, RTS, CTS>, sercom: $SERCOM, } impl $Type { - pub fn new, T: Into<[<$Type Padout>]>>( + pub fn new, T: Into>>( clock: &clock::$clock, freq: F, sercom: $SERCOM, mclk: &mut MCLK, padout: T, ) -> Self where - [<$Type Padout>]: RxpoTxpo { + Padout<$Sercom, RX, TX, RTS, CTS>: RxpoTxpo { let padout = padout.into(); mclk.$apmask.modify(|_, w| w.$powermask().set_bit()); @@ -326,7 +312,7 @@ macro_rules! uart { } } - pub fn free(self) -> ([<$Type Padout>], $SERCOM) { + pub fn free(self) -> (Padout<$Sercom, RX, TX, RTS, CTS>, $SERCOM) { (self.padout, self.sercom) } @@ -348,7 +334,7 @@ macro_rules! uart { /// Combines transmit and receive halves back into a duplex UART pub fn join(tx: [<$Type Tx>], rx: [<$Type Rx>]) -> Self { Self { - padout: [<$Type Padout>]::join(tx.padout, rx.padout), + padout: Padout::join(tx.padout, rx.padout), sercom: tx.sercom, } } @@ -382,7 +368,7 @@ macro_rules! uart { /// The transmitting half of the corresponding UARTX instance (as returned by `UARTX::split`) pub struct [<$Type Tx>] { - padout: [<$Type TxPadout>], + padout: TxPadout<$Sercom, TX, RTS>, /// We store the SERCOM object here so we can retrieve it later, /// but conceptually, ownership is shared between the Rx and Tx halves. sercom: $SERCOM, @@ -446,7 +432,7 @@ macro_rules! uart { /// The receiving half of the corresponding UARTX instance (as returned by `UARTX::split`) pub struct [<$Type Rx>] { - padout: [<$Type RxPadout>], + padout: RxPadout<$Sercom, RX, CTS>, sercom: PhantomData<$SERCOM>, } @@ -507,7 +493,7 @@ macro_rules! uart { } } } - } + }; } uart!( diff --git a/hal/src/thumbv7em/sercom/v2.rs b/hal/src/thumbv7em/sercom/v2.rs index d571bab1e4ec..6c135e2e9c45 100644 --- a/hal/src/thumbv7em/sercom/v2.rs +++ b/hal/src/thumbv7em/sercom/v2.rs @@ -1,2 +1,2 @@ -pub mod pad_info; +pub mod impl_pad; pub mod spi; diff --git a/hal/src/thumbv7em/sercom/v2/pad_info.rs b/hal/src/thumbv7em/sercom/v2/impl_pad.rs similarity index 97% rename from hal/src/thumbv7em/sercom/v2/pad_info.rs rename to hal/src/thumbv7em/sercom/v2/impl_pad.rs index 52bd92342783..9d98ebe17308 100644 --- a/hal/src/thumbv7em/sercom/v2/pad_info.rs +++ b/hal/src/thumbv7em/sercom/v2/impl_pad.rs @@ -1,4 +1,4 @@ -//! Implementations of the [`PadInfo`] and [`InIoSet`] traits +//! Implementations of the [`IsPad`], [`GetPad`] and [`InIoSet`] traits use crate::gpio::v2::*; use crate::sercom::v2::*; @@ -15,14 +15,14 @@ macro_rules! pad_info { $PadNum:ident, $( $IoSet:ident ),+ ) => { - impl PadInfo<$Sercom> for $PinId { + impl GetPad<$Sercom> for $PinId { type PadNum = $PadNum; type PinMode = Alternate<$Cfg>; } $( - impl InIoSet<$IoSet> for Pad<$Sercom, $PadNum, $PinId> {} + impl InIoSet<$IoSet> for Pin<$PinId, Alternate<$Cfg>> {} )+ - impl ConvertPinToPad for Pin<$PinId, Alternate<$Cfg>> { + impl IsPad for Pin<$PinId, Alternate<$Cfg>> { type Sercom = $Sercom; type PadNum = $PadNum; } diff --git a/hal/src/thumbv7em/sercom/v2/spi.rs b/hal/src/thumbv7em/sercom/v2/spi.rs index 5a782e439d67..a2e080cc2287 100644 --- a/hal/src/thumbv7em/sercom/v2/spi.rs +++ b/hal/src/thumbv7em/sercom/v2/spi.rs @@ -9,19 +9,33 @@ //! //! # [`Pads`] //! -//! A [`Sercom`] can use up to four [`Pin`]s as peripheral [`Pad`]s, but only +//! A [`Sercom`] can use up to four [`Pin`]s as peripheral pads, but only //! certain `Pin` combinations are acceptable. In particular, all `Pin`s must be //! mapped to the same `Sercom` and [`IoSet`] (see section 6.2.8.1 of the -//! datasheet). This HAL makes it impossible to use invalid `Pin`/`Pad` -//! combinations, and the [`Pads`] struct is responsible for enforcing these -//! constraints. +//! datasheet). This HAL makes it impossible to use invalid `Pin` combinations, +//! and the [`Pads`] struct is responsible for enforcing these constraints. //! //! A `Pads` type takes up to six type parameters. The first two specify the -//! `Sercom` and `IoSet`. The remaining four, `DI`, `DO`, `CK` and `SS`, are -//! effectively [`OptionalPinId`]s for the Data In, Data Out, Sclk and SS `Pad`s -//! respectively, and each defaults to [`NoneT`]. To be accepted as part of a -//! [`ValidConfig`], you must specify a `PinId` for `CK` and at least one of -//! `DI` or `DO`. +//! `Sercom` and `IoSet`, while the remaining four, `DI`, `DO`, `CK` and `SS`, +//! represent the Data In, Data Out, Sclk and SS pads respectively. Each of the +//! remaining type parameters is an [`OptionalPad`] and defaults to [`NoneT`]. +//! Aliases defining the pad types can be provided by the +//! [`bsp_pins!`](crate::bsp_pins) macro. +//! +//! ``` +//! use atsamd_hal::gpio::v2::{PA08, PA09, AlternateC}; +//! use atsamd_hal::sercom::v2::{Sercom0, spi}; +//! use atsamd_hal::sercom::v2::pad::IoSet1; +//! use atsamd_hal::typelevel::NoneT; +//! +//! type Miso = Pin; +//! type Sclk = Pin; +//! type Pads = spi::Pads; +//! ``` +//! +//! Alternatively, you can use the [`PadsFromIds`] alias to define a set of +//! `Pads` in terms of [`PinId`]s instead of `Pin`s. This is useful when you +//! don't have `Pin` aliases pre-defined. //! //! ``` //! use atsamd_hal::gpio::v2::{PA08, PA09}; @@ -29,12 +43,15 @@ //! use atsamd_hal::sercom::v2::pad::IoSet1; //! use atsamd_hal::typelevel::NoneT; //! -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; //! ``` //! -//! `Pads` are created using the builder pattern. Start by creating an empty set -//! of `Pads` using [`Default`]. Then pass each respective `Pin` using the -//! corresponding methods. Both `v1::Pin` and `v2::Pin` types are accepted here. +//! Instances of `Pads` are created using the builder pattern. Start by creating +//! an empty set of `Pads` using [`Default`]. Then pass each respective `Pin` +//! using the corresponding methods. Both `v1::Pin` and `v2::Pin` types are +//! accepted here. The builder methods automatically convert each +//! pin to the correct [`PinMode`]. +//! //! Note that the `CK` `Pin` must map to [`Pad1`], and if specified, the `SS` //! `Pin` must map to [`Pad2`]. The `DI` and `DO` `Pin`s can vary in [`PadNum`] //! based on the [`Dipo`] and [`Dopo`] values. @@ -53,6 +70,10 @@ //! .data_out(pins.pa11); //! ``` //! +//! To be accepted as [`ValidPads`], a set of `Pads` must do two things: +//! - Specify a type for `CK` and at least one of `DI` or `DO` +//! - Satisfy the [`Dipo`] and [`Dopo`] traits +//! //! # [`Config`] //! //! Next, create a [`Config`] struct, which represents the SPI peripheral in its @@ -69,7 +90,7 @@ //! use atsamd_hal::sercom::v2::pad::IoSet1; //! use atsamd_hal::typelevel::NoneT; //! -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; //! type Config = spi::Config; //! ``` //! @@ -110,6 +131,9 @@ //! .enable(); //! ``` //! +//! To be accepted as a [`ValidConfig`], the `Config` must have all the +//! necessary pads for its [`OpMode`]. +//! //! # [`Spi`] //! //! An [`Spi`] struct can only be created from a [`Config`], and it has only one @@ -122,7 +146,7 @@ //! use atsamd_hal::sercom::v2::pad::IoSet1; //! use atsamd_hal::typelevel::NoneT; //! -//! type Pads = spi::Pads; +//! type Pads = spi::PadsFromIds; //! type Config = spi::Config; //! type Spi = spi::Spi; //! ``` @@ -143,33 +167,47 @@ //! let rcvd: u16 = block!(spi.read()); //! ``` //! -//! # Using SPI with DMA -//! -//! This HAL includes support for DMA-enabled SPI transfers. An enabled `Spi` -//! struct implements the DMAC [`Buffer`](crate::dmac::transfer::Buffer) -//! trait. The provided [`send_with_dma`](Spi::send_with_dma) and -//! [`receive_with_dma`](Spi::receive_with_dma) build and begin a -//! [`dmac::Transfer`](crate::dmac::Transfer), thus starting the SPI in a -//! non-blocking way. Optionally, interrupts can be enabled on the provided -//! [`Channel`](crate::dmac::channel::Channel). Note that the `dma` feature must -//! be enabled. Please refer to the [`dmac`](crate::dmac) module-level -//! documentation for more information. ``` -//! // Assume channel is a configured `dmac::Channel`, and spi a -//! fully-configured `Spi` -//! -//! // Create data to send -//! let buffer: [u8; 50] = [0xff; 50] -//! -//! // Launch transfer -//! let dma_transfer = spi.send_with_dma(&mut buffer, channel, ()); -//! -//! // Wait for transfer to complete and reclaim resources -//! let (chan0, _, spi, _) = dma_transfer.wait(); -//! ``` -//! //! [`enable`]: Config::enable +//! [`bsp_pins`]: crate::bsp_pins //! [`Pin`]: crate::gpio::v2::pin::Pin -//! [`OptionalPinId`]: crate::gpio::v2::pin::OptionalPinId +//! [`PinId`]: crate::gpio::v2::pin::PinId +//! [`PinMode`]: crate::gpio::v2::pin::PinMode +#![cfg_attr( + feature = "dma", + doc = " +# Using SPI with DMA + +This HAL includes support for DMA-enabled SPI transfers. An enabled [`Spi`] +struct implements the DMAC [`Buffer`] trait. The provided [`send_with_dma`] +and [`receive_with_dma`] methods will build and begin a [`dmac::Transfer`] +to create a non-blocking SPI transfer. + +Optionally, interrupts can be enabled on the provided [`Channel`]. Note that +the `dma` feature must be enabled. Refer to the [`dmac`] module-level +documentation for more information. + +``` +// Assume channel is a configured `dmac::Channel`, and spi a +// fully-configured `Spi` + +// Create data to send +let buffer: [u8; 50] = [0xff; 50] + +// Launch the transfer +let dma_transfer = spi.send_with_dma(&mut buffer, channel, ()); + +// Wait for the transfer to complete and reclaim resources +let (chan0, _, spi, _) = dma_transfer.wait(); +``` + +[`Buffer`]: crate::dmac::transfer::Buffer +[`send_with_dma`]: Spi::send_with_dma +[`receive_with_dma`]: Spi::receive_with_dma +[`dmac::Transfer`]: crate::dmac::Transfer +[`Channel`]: crate::dmac::channel::Channel +[`dmac`]: crate::dmac +" +)] use core::convert::{TryFrom, TryInto}; use core::marker::PhantomData; @@ -251,10 +289,10 @@ impl Dipo for Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, DI::PadNum: Dipo, { const DIPO: DIPO_A = DI::PadNum::DIPO; @@ -300,10 +338,10 @@ impl Dopo for Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, DO::PadNum: Dopo, { const DOPO: DOPO_A = DO::PadNum::DOPO; @@ -313,73 +351,31 @@ where // Pads //============================================================================= -/// Container for a set of SERCOM [`Pad`]s -/// -/// A [`Sercom`] can use up to four [`Pin`]s as peripheral [`Pad`]s, but only -/// certain `Pin` combinations are acceptable. In particular, all `Pin`s must be -/// mapped to the same `Sercom` and [`IoSet`] (see section 6.2.8.1 of the -/// datasheet). This HAL makes it impossible to use invalid `Pin`/`Pad` -/// combinations, and the `Pads` struct is responsible for enforcing these -/// constraints. -/// -/// A `Pads` type takes up to six type parameters. The first two specify the -/// `Sercom` and `IoSet`. The remaining four, `DI`, `DO`, `CK` and `SS`, are -/// effectively [`OptionalPinId`]s for the Data In, Data Out, Sclk and SS `Pad`s -/// respectively, and each defaults to [`NoneT`]. To be accepted as part of a -/// [`ValidConfig`], you must specify a `PinId` for `CK` and at least one of -/// `DI` or `DO`. -/// -/// ``` -/// use atsamd_hal::gpio::v2::{PA08, PA09}; -/// use atsamd_hal::sercom::v2::{Sercom0, spi}; -/// use atsamd_hal::sercom::v2::pad::IoSet1; -/// use atsamd_hal::typelevel::NoneT; -/// -/// type Pads = spi::Pads; -/// ``` -/// -/// `Pads` are created using the builder pattern. Start by creating an empty set -/// of `Pads` using [`Default`]. Then pass each respective `Pin` using the -/// corresponding methods. Both `v1::Pin` and `v2::Pin` types are accepted here. -/// Note that the `CK` `Pin` must map to [`Pad1`], and if specified, the `SS` -/// `Pin` must map to [`Pad2`]. The `DI` and `DO` `Pin`s can vary in [`PadNum`] -/// based on the [`Dipo`] and [`Dopo`] values. +/// Container for a set of SERCOM pads /// -/// ``` -/// use atsamd_hal::target_device::Peripherals; -/// use atsamd_hal::gpio::v2::Pins; -/// use atsamd_hal::sercom::v2::{Sercom0, spi}; -/// use atsamd_hal::sercom::v2::pad::IoSet1; -/// -/// let mut peripherals = Peripherals::take().unwrap(); -/// let pins = Pins::new(peripherals.PORT); -/// let pads = spi::Pads::::default() -/// .sclk(pins.pa09) -/// .data_in(pins.pa08) -/// .data_out(pins.pa11); -/// ``` -/// -/// [`Pin`]: crate::gpio::v2::pin::Pin -/// [`OptionalPinId`]: crate::gpio::v2::pin::OptionalPinId +/// See the [module-level](self) documentation for more details on specifying +/// a `Pads` type and creating instances. pub struct Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { + sercom: PhantomData, ioset: PhantomData, - data_in: DI::Pad, - data_out: DO::Pad, - sclk: CK::Pad, - ss: SS::Pad, + data_in: DI, + data_out: DO, + sclk: CK, + ss: SS, } impl Default for Pads { fn default() -> Self { Self { + sercom: PhantomData, ioset: PhantomData, data_in: NoneT, data_out: NoneT, @@ -393,148 +389,132 @@ impl Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { - /// Set the `DI` [`Pad`] + /// Set the `DI` pad /// /// In a [`MasterMode`], this is MISO. In [`Slave`] [`OpMode`], this is /// MOSI. #[inline] - pub fn data_in(self, pin: impl AnyPin) -> Pads + pub fn data_in(self, pin: impl AnyPin) -> Pads, DO, CK, SS> where - Id: PadInfo, + Id: GetPad, Id::PadNum: Dipo, - Pad: InIoSet, + Pad: InIoSet, { Pads { + sercom: self.sercom, ioset: self.ioset, - data_in: pin.into().into(), + data_in: pin.into().into_mode(), data_out: self.data_out, sclk: self.sclk, ss: self.ss, } } - /// Set the `DO` [`Pad`] + /// Set the `DO` pad /// /// In a [`MasterMode`], this is MOSI. In [`Slave`] [`OpMode`], this is /// MISO. #[inline] - pub fn data_out(self, pin: impl AnyPin) -> Pads + pub fn data_out(self, pin: impl AnyPin) -> Pads, CK, SS> where - Id: PadInfo, + Id: GetPad, Id::PadNum: Dopo, - Pad: InIoSet, + Pad: InIoSet, { Pads { + sercom: self.sercom, ioset: self.ioset, data_in: self.data_in, - data_out: pin.into().into(), + data_out: pin.into().into_mode(), sclk: self.sclk, ss: self.ss, } } - /// Set the `SCK` [`Pad`], which is always [`Pad1`] + /// Set the `SCK` pad, which is always [`Pad1`] #[inline] - pub fn sclk(self, pin: impl AnyPin) -> Pads + pub fn sclk(self, pin: impl AnyPin) -> Pads, SS> where - Id: PadInfo, - Pad: InIoSet, + Id: GetPad, + Pad: InIoSet, { Pads { + sercom: self.sercom, ioset: self.ioset, data_in: self.data_in, data_out: self.data_out, - sclk: pin.into().into(), + sclk: pin.into().into_mode(), ss: self.ss, } } - /// Set the `SS` [`Pad`], which is always [`Pad2`] + /// Set the `SS` pad, which is always [`Pad2`] #[inline] - pub fn ss(self, pin: impl AnyPin) -> Pads + pub fn ss(self, pin: impl AnyPin) -> Pads> where - Id: PadInfo, - Pad: InIoSet, + Id: GetPad, + Pad: InIoSet, { Pads { + sercom: self.sercom, ioset: self.ioset, data_in: self.data_in, data_out: self.data_out, sclk: self.sclk, - ss: pin.into().into(), + ss: pin.into().into_mode(), } } - /// Consume the [`Pads`] and return each individual [`Pad`] + /// Consume the [`Pads`] and return each individual + /// [`Pin`](crate::gpio::v2::Pin) #[inline] - pub fn free(self) -> (DI::Pad, DO::Pad, CK::Pad, SS::Pad) { + pub fn free(self) -> (DI, DO, CK, SS) { (self.data_in, self.data_out, self.sclk, self.ss) } } -//============================================================================= -// spi_pads_from_pins -//============================================================================= - -/// Define a set of [`spi::Pads`] using [`Pin`]s instead of [`PinId`]s +/// Define a set of [`Pads`] using [`PinId`]s instead of [`Pin`]s /// -/// In some cases, it is more convenient to specify a set of `spi::Pads` using -/// `Pin`s or `Pin` aliases than it is to use the corresponding [`PinId`]s. This -/// macro makes it easier to do so. +/// In some cases, it is more convenient to specify a set of `Pads` using +/// `PinId`s rather than `Pin`s. This alias makes it easier to do so. /// -/// The first two arguments to the macro are required and represent the -/// [`Sercom`] and [`IoSet`] respectively. The remaining four arguments are all -/// optional. Each represents a corresponding type parameter of the `spi::Pads` -/// type. Some of the types may be omitted, but any types that are specified, -/// must be done in the order `DI`, `DO`, `CK` & `SS`. +/// The first two type parameters are the [`Sercom`] and [`IoSet`], while the +/// remaining four are effectively [`OptionalPinId`]s representing the +/// corresponding type parameters of [`Pads`], i.e. `DI`, `DO`, `CK` & `SS`. +/// Each of the remaining type parameters defaults to [`NoneT`]. /// /// ``` /// use atsamd_hal::pac::Peripherals; -/// use atsamd_hal::spi_pads_from_pins; -/// use atsamd_hal::gpio::v2::{Pin, PA08, PA09, AlternateC, Pins}; -/// use atsamd_hal::sercom::v2::{Sercom0, pad::IoSet1, spi}; +/// use atsamd_hal::gpio::v2::{PA08, PA09, Pins}; +/// use atsamd_hal::sercom::v2::{Sercom0, spi}; +/// use atsamd_hal::sercom::v2::pad::IoSet1; +/// use atsamd_hal::typelevel::NoneT; /// -/// type Miso = Pin; -/// type Sclk = Pin; -/// pub type Pads = spi_pads_from_pins!(Sercom0, IoSet1, DI = Miso, CK = Sclk); +/// pub type Pads = spi::PadsFromIds; /// -/// pub fn test() -> Pads { +/// pub fn create_pads() -> Pads { /// let peripherals = Peripherals::take().unwrap(); /// let pins = Pins::new(peripherals.PORT); -/// spi::Pads::::default() -/// .sclk(pins.pa09) -/// .data_in(pins.pa08) +/// spi::Pads::default().sclk(pins.pa09).data_in(pins.pa08) /// } /// ``` /// -/// [`spi::Pads`]: Pads /// [`Pin`]: crate::gpio::v2::Pin /// [`PinId`]: crate::gpio::v2::PinId -#[macro_export] -macro_rules! spi_pads_from_pins { - ( - $Sercom:ident, - $IoSet:ident - $( , DI = $DI:ty )? - $( , DO = $DO:ty )? - $( , CK = $CK:ty )? - $( , SS = $SS:ty )? - ) => { - $crate::sercom::v2::spi::Pads< - $crate::sercom::v2::$Sercom, - $crate::sercom::v2::pad::$IoSet, - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$DI> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$DO> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$CK> )? ), - $crate::__opt_type!( $( $crate::gpio::v2::SpecificPinId<$SS> )? ), - > - }; -} +/// [`OptionalPinId`]: crate::gpio::v2::OptionalPinId +pub type PadsFromIds = Pads< + S, + I, + >::Pad, + >::Pad, + >::Pad, + >::Pad, +>; //============================================================================= // PadSet @@ -545,14 +525,15 @@ macro_rules! spi_pads_from_pins { /// /// This trait is used as an interface between the [`Pads`] type and other /// types in this module. It acts as a [type-level function], returning the -/// corresponding [`Sercom`], [`IoSet`] and [`OptionalPad`] types. It serves to -/// cut down on the total number of type parameters needed in the [`Config`] -/// struct. The `Config` struct doesn't need access to the [`Pad`]s directly. -/// Rather, it only needs to apply the [`SomePad`] trait bound when a `Pad` is -/// required. The `PadSet` trait allows each `Config` struct to store an -/// instance of `Pads` without itself being generic over all six type parameters -/// of the `Pads` type. +/// corresponding [`Sercom`] and [`OptionalPad`] types. It serves to cut down on +/// the total number of type parameters needed in the [`Config`] struct. The +/// `Config` struct doesn't need access to the [`Pin`]s directly. Rather, it +/// only needs to apply the [`SomePad`] trait bound when a `Pin` is required. +/// The `PadSet` trait allows each `Config` struct to store an instance of +/// `Pads` without itself being generic over all six type parameters of the +/// `Pads` type. /// +/// [`Pin`]: crate::gpio::v2::Pin /// [type-level function]: crate::typelevel#type-level-functions pub trait PadSet: Sealed { type Sercom: Sercom; @@ -567,10 +548,10 @@ impl Sealed for Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { } @@ -578,17 +559,17 @@ impl PadSet for Pads where S: Sercom, I: IoSet, - DI: GetOptionalPad, - DO: GetOptionalPad, - CK: GetOptionalPad, - SS: GetOptionalPad, + DI: OptionalPad, + DO: OptionalPad, + CK: OptionalPad, + SS: OptionalPad, { type Sercom = S; type IoSet = I; - type DataIn = DI::Pad; - type DataOut = DO::Pad; - type Sclk = CK::Pad; - type SS = SS::Pad; + type DataIn = DI; + type DataOut = DO; + type Sclk = CK; + type SS = SS; } //============================================================================= @@ -636,7 +617,7 @@ where /// Marker trait for a set of [`Pads`] that cannot transmit /// -/// A set of [`Pads`] cannot be used to transmit when the Data Out [`Pad`] is +/// A set of [`Pads`] cannot be used to transmit when the Data Out pad is /// [`NoneT`]. pub trait NotTx: ValidPads {} @@ -644,7 +625,7 @@ impl

NotTx for P where P: ValidPads {} /// Marker trait for a set of [`Pads`] that cannot receive /// -/// A set of [`Pads`] cannot be used to receive when the Data In [`Pad`] is +/// A set of [`Pads`] cannot be used to receive when the Data In pad is /// [`NoneT`]. pub trait NotRx: ValidPads {} @@ -660,9 +641,9 @@ impl TxOrRx for Pads where S: Sercom, I: IoSet, - DI: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, + DI: SomePad, + CK: SomePad, + SS: OptionalPad, DI::PadNum: Dipo, { } @@ -671,25 +652,14 @@ impl TxOrRx for Pads where S: Sercom, I: IoSet, - DO: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, + DO: SomePad, + CK: SomePad, + SS: OptionalPad, DO::PadNum: Dopo, { } -impl TxOrRx for Pads -where - S: Sercom, - I: IoSet, - DI: GetPad + GetPadMarker, - DO: GetPad + GetPadMarker, - CK: GetPad, - SS: GetOptionalPad, - DI::PadNum: Dipo, - DO::PadNum: Dopo, -{ -} +impl TxOrRx for P {} //============================================================================= // Operating mode @@ -702,7 +672,7 @@ where /// The available operating modes are [`Master`], [`MasterHWSS`] and [`Slave`]. /// In [`Master`] mode, the `SS` signal must be handled by the user, so `SS` /// must be [`NoneT`]. In [`MasterHWSS`] mode, the hardware drives the `SS` -/// line, so [`SomePad`] is required. In [`Slave`] mode, the `SS` [`Pad`] is +/// line, so [`SomePad`] is required. In [`Slave`] mode, the `SS` pad is /// required as well, to indicate when data is valid. /// /// [type-level enums]: crate::typelevel#type-level-enums @@ -1339,14 +1309,14 @@ where /// Marker trait for valid SPI [`Config`]urations /// -/// A functional SPI peripheral must have, at a minimum, an SCK [`Pad`] and -/// either a Data In or a Data Out `Pad`. Dependeing on the [`OpMode`], an SS -/// `Pad` may also be required. +/// A functional SPI peripheral must have, at a minimum, an SCK 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 +/// `OpMode`, the `SS` pad must be `NoneT`, while in [`MasterHWSS`] or /// [`Slave`] [`OpMode`], the SS pad must be [`SomePad`]. pub trait ValidConfig: AnyConfig {} diff --git a/hal/src/typelevel.rs b/hal/src/typelevel.rs index 598a45579612..2160ea01bb0a 100644 --- a/hal/src/typelevel.rs +++ b/hal/src/typelevel.rs @@ -332,6 +332,12 @@ //! readers understand that a particular type parameter is restricted to an //! instances of `Class` when an `OptionalClass` could be accepted. //! +//! Note that when `Class` and `OptionalClass` contain associated types, name +//! clashes may occur when using `SomeClass` as a trait bound. This can be +//! avoided by removing the `OptionalClass` super trait from `SomeClass`. +//! Ultimately, it is redundant anyway, because any implementer of `Class` also +//! implements `OptionalClass`. +//! //! # `AnyKind` trait pattern //! //! The `AnyKind` trait pattern allows you to encapsulate types with multiple @@ -634,6 +640,7 @@ mod private { pub(crate) use private::Sealed; /// Type-level version of the [None] variant +#[derive(Default)] pub struct NoneT; impl Sealed for NoneT {} @@ -685,14 +692,3 @@ where { type Type = T; } - -#[macro_export] -#[doc(hidden)] -macro_rules! __opt_type { - () => { - $crate::typelevel::NoneT - }; - ($Type:ty) => { - $Type - }; -}