From e3be4f8025f3fa9344910aa627891ad7bde86980 Mon Sep 17 00:00:00 2001 From: Andrea Nall Date: Sun, 23 May 2021 21:08:42 -0500 Subject: [PATCH] Massive GPIO refactor Bring in line with atsamd-hal GPIO v2 Copied as much as possible. Docs lifted mostly as-is. Also add sample BSP for the Feather RP2040 in boards/feather_rp2040 May include a few random fixes from currently futile attempt to get doctests working. --- Cargo.toml | 1 + boards/feather_rp2040/Cargo.toml | 19 + boards/feather_rp2040/src/lib.rs | 56 ++ rp2040-hal/Cargo.toml | 11 +- rp2040-hal/examples/blinky.rs | 17 +- rp2040-hal/examples/gpio_in_out.rs | 19 +- rp2040-hal/src/clocks/macros.rs | 2 - rp2040-hal/src/gpio.rs | 299 --------- rp2040-hal/src/gpio/dynpin.rs | 527 ++++++++++++++++ rp2040-hal/src/gpio/mod.rs | 34 + rp2040-hal/src/gpio/pin.rs | 965 +++++++++++++++++++++++++++++ rp2040-hal/src/gpio/reg.rs | 353 +++++++++++ rp2040-hal/src/lib.rs | 2 + rp2040-hal/src/prelude.rs | 2 - rp2040-hal/src/rom_data.rs | 45 +- rp2040-hal/src/sio.rs | 14 +- rp2040-hal/src/typelevel.rs | 697 +++++++++++++++++++++ 17 files changed, 2706 insertions(+), 357 deletions(-) create mode 100644 boards/feather_rp2040/Cargo.toml create mode 100644 boards/feather_rp2040/src/lib.rs delete mode 100644 rp2040-hal/src/gpio.rs create mode 100644 rp2040-hal/src/gpio/dynpin.rs create mode 100644 rp2040-hal/src/gpio/mod.rs create mode 100644 rp2040-hal/src/gpio/pin.rs create mode 100644 rp2040-hal/src/gpio/reg.rs create mode 100644 rp2040-hal/src/typelevel.rs diff --git a/Cargo.toml b/Cargo.toml index dbe359b..d3a7647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ "rp2040-hal", + "boards/feather_rp2040" ] diff --git a/boards/feather_rp2040/Cargo.toml b/boards/feather_rp2040/Cargo.toml new file mode 100644 index 0000000..d5a9114 --- /dev/null +++ b/boards/feather_rp2040/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "feather_rp2040" +version = "0.1.0" +authors = ["Andrea Nall "] +edition = "2018" +homepage = "https://github.com/rp-rs/rp-hal/boards/feather_rp2040" +description = "Board Support Package for the Adafruit Feather RP2040" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.7.2" +rp2040-hal = { path = "../../rp2040-hal", version = "0.1.0" } +cortex-m-rt = { version = "0.6.14", optional = true } + +[features] +default = ["rt"] +rt = ["cortex-m-rt"] \ No newline at end of file diff --git a/boards/feather_rp2040/src/lib.rs b/boards/feather_rp2040/src/lib.rs new file mode 100644 index 0000000..6cf3c20 --- /dev/null +++ b/boards/feather_rp2040/src/lib.rs @@ -0,0 +1,56 @@ +#![no_std] + +extern crate rp2040_hal as hal; + +#[cfg(feature = "rt")] +extern crate cortex_m_rt; +#[cfg(feature = "rt")] +pub use cortex_m_rt::entry; + +pub use hal::pac; + +hal::bsp_pins!( + Gpio0 { + name: tx, + aliases: { FunctionUart: UartTx } + }, + Gpio1 { + name: rx, + aliases: { FunctionUart: UartRx } + }, + Gpio2 { + name: sda, + aliases: { FunctionI2C: Sda } + }, + Gpio3 { + name: scl, + aliases: { FunctionI2C: Scl } + }, + Gpio6 { name: d4 }, + Gpio7 { name: d5 }, + Gpio8 { name: d6 }, + Gpio9 { name: d9 }, + Gpio10 { name: d10 }, + Gpio11 { name: d11 }, + Gpio12 { name: d12 }, + Gpio13 { name: d13 }, + Gpio16 { name: neopixel }, + Gpio18 { + name: sclk, + aliases: { FunctionSpi: Sclk } + }, + Gpio19 { + name: mosi, + aliases: { FunctionSpi: Mosi } + }, + Gpio20 { + name: miso, + aliases: { FunctionSpi: Miso } + }, + Gpio24 { name: d24 }, + Gpio25 { name: d25 }, + Gpio26 { name: a0 }, + Gpio27 { name: a1 }, + Gpio28 { name: a2 }, + Gpio29 { name: a3 }, +); diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 9fbe9cb..2d4921a 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -10,13 +10,14 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cortex-m = "0.7.1" -embedded-hal = { version = "0.2.4", features = ["unproven"] } -embedded-time = "0.10.1" -nb = "1.0.0" +cortex-m = "0.7.2" +embedded-hal = { version = "0.2.5", features=["unproven"] } +embedded-time = "0.10" +nb = "1.0" rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" } +paste = "1.0" [dev-dependencies] -cortex-m-rt = "0.6.13" +cortex-m-rt = "0.6.14" panic-halt = "0.2.0" rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" } diff --git a/rp2040-hal/examples/blinky.rs b/rp2040-hal/examples/blinky.rs index e567df8..c7a3806 100644 --- a/rp2040-hal/examples/blinky.rs +++ b/rp2040-hal/examples/blinky.rs @@ -6,8 +6,10 @@ use cortex_m_rt::entry; use embedded_hal::digital::v2::OutputPin; +use hal::pac; +use hal::sio::Sio; use panic_halt as _; -use rp2040_hal::prelude::*; +use rp2040_hal as hal; #[link_section = ".boot2"] #[used] @@ -15,13 +17,16 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; #[entry] fn main() -> ! { - let mut pac = rp2040_pac::Peripherals::take().unwrap(); + let mut pac = pac::Peripherals::take().unwrap(); let sio = Sio::new(pac.SIO); - let pins = pac - .IO_BANK0 - .split(pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); - let mut led_pin = pins.gpio25.into_output(); + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + let mut led_pin = pins.gpio25.into_push_pull_output(); loop { led_pin.set_high().unwrap(); diff --git a/rp2040-hal/examples/gpio_in_out.rs b/rp2040-hal/examples/gpio_in_out.rs index 21947bf..db0025a 100644 --- a/rp2040-hal/examples/gpio_in_out.rs +++ b/rp2040-hal/examples/gpio_in_out.rs @@ -8,8 +8,10 @@ use cortex_m_rt::entry; use embedded_hal::digital::v2::{InputPin, OutputPin}; +use hal::pac; +use hal::sio::Sio; use panic_halt as _; -use rp2040_hal::prelude::*; +use rp2040_hal as hal; #[link_section = ".boot2"] #[used] @@ -17,14 +19,17 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; #[entry] fn main() -> ! { - let mut pac = rp2040_pac::Peripherals::take().unwrap(); + let mut pac = pac::Peripherals::take().unwrap(); let sio = Sio::new(pac.SIO); - let pins = pac - .IO_BANK0 - .split(pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); - let mut led_pin = pins.gpio25.into_output(); - let button_pin = pins.gpio15.into_input().pull_up(); + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + let mut led_pin = pins.gpio25.into_push_pull_output(); + let button_pin = pins.gpio15.into_pull_up_input(); loop { if button_pin.is_high().unwrap() { diff --git a/rp2040-hal/src/clocks/macros.rs b/rp2040-hal/src/clocks/macros.rs index 1095705..5f24a34 100644 --- a/rp2040-hal/src/clocks/macros.rs +++ b/rp2040-hal/src/clocks/macros.rs @@ -1,5 +1,3 @@ -#[macro_use] - macro_rules! int_division { ($name:ident, $div:ident, $u:ty) => { impl IntegerDivision for $name { diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs deleted file mode 100644 index 70c9f62..0000000 --- a/rp2040-hal/src/gpio.rs +++ /dev/null @@ -1,299 +0,0 @@ -//! General Purpose Input and Output (GPIO) -//! -//! To access the GPIO pins you must call the `split` method on the IO bank. This will return a -//! `Parts` struct with access to the individual pins: -//! -//! ```rust -//! use rp2040_hal::prelude::*; -//! let mut pac = rp2040_pac::Peripherals::take().unwrap(); -//! let sio = Sio::new(pac.SIO); -//! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); -//! ``` -//! -//! Once you have the GPIO pins struct, you can take individual pins and configure them: -//! -//! ```rust -//! let mut led_pin = pins.gpio25.into_output(); -//! led_pin.set_high().unwrap(); -//! ``` -//! -//! Input pins support the following options: -//! - Pull high, pull low, or floating -//! - Schmitt trigger -//! -//! Output pins support the following options: -//! - Slew rate (fast or slow) -//! - Drive strength (2, 4, 8 or 12 mA) -use crate::sio; - -/// Mode marker for an input pin -pub struct Input; -/// Mode marker for an output pin -pub struct Output; -/// Mode marker for a pin in an unknown state (generally happens at startup) -pub struct Unknown; - -/// This trait adds a method to extract pins from an IO bank and convert them into HAL objects -pub trait GpioExt { - /// The type of struct that will hold the pins once they're converted to HAL objects - type Parts; - - /// Convert the IO bank into a struct of HAL pin objects - // TODO: Do we need a marker to check that clocks are up? - fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts; -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// The amount of current that a pin can drive when used as an output -pub enum OutputDriveStrength { - /// 2 mA - TwoMilliAmps, - /// 4 mA - FourMilliAmps, - /// 8 mA - EightMilliAmps, - /// 12 mA - TwelveMilliAmps, -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// The slew rate of a pin when used as an output -pub enum OutputSlewRate { - /// Slew slow - Slow, - /// Slew fast - Fast, -} - -macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $siotoken : ident, $PADSX:ident, $padsx:ident, $gpioxs:expr, [ - $($PXi:ident: ($pxi:ident, $i:expr, $is:expr),)+ - ]) => { - #[doc = "HAL objects for the "] - #[doc = $gpioxs] - #[doc = " bank of GPIO pins"] - pub mod $gpiox { - use core::convert::Infallible; - use core::marker::PhantomData; - use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; - use super::*; - - use crate::resets::SubsystemReset; - - impl GpioExt for pac::$GPIOX { - type Parts = Parts; - - fn split(self, pads: pac::$PADSX, sio: sio::$siotoken, resets: &mut pac::RESETS) -> Parts { - // FIXME: bring both of these up at the same time - self.reset_bring_up(resets); - pads.reset_bring_up(resets); - Parts { - _pads: pads, - _sio: sio, - $( - $pxi: $PXi { _mode: PhantomData }, - )+ - } - } - } - - #[doc = "Struct containing HAL objects for all the "] - #[doc = $gpioxs] - #[doc = " pins"] - pub struct Parts { - _pads: pac::$PADSX, - _sio: sio::$siotoken, - $( - #[doc = "GPIO pin "] - #[doc = $is] - pub $pxi: $PXi, - )+ - } - - type PacDriveStrength = pac::$padsx::gpio::DRIVE_A; - - $( - #[doc = "HAL object for GPIO pin "] - #[doc = $is] - pub struct $PXi { - _mode: PhantomData, - } - - impl $PXi { - // This is safe because Parts owns the pads, and each pin is responsible - // for its own pad - fn pad(&self) -> &pac::$padsx::GPIO { - unsafe { - &(*pac::$PADSX::ptr()).gpio[$i] - } - } - - // This is safe because Parts owns the SIO. But callers must only touch their - // own pin - fn sio(&self) -> &pac::sio::RegisterBlock { - unsafe { - &(*pac::SIO::ptr()) - } - } - - // This is safe because Parts owns the bank, and each pin is responsible - // for its own slice of the bank - fn gpio_ctrl(&self) -> &pac::$gpiox::gpio::GPIO_CTRL { - unsafe { - &(*pac::$GPIOX::ptr()).gpio[$i].gpio_ctrl - } - } - - #[doc = "Configure this pin as an output"] - pub fn into_output(self)-> $PXi { - self.pad().reset(); - self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() }); - // TODO: Can we update the PAC to give us a safe register field - // instead of `bits`? - self.sio().gpio_oe_set.write(|x| unsafe { x.bits(1 << $i) }); - $PXi { _mode: PhantomData } - } - - #[doc = "Configure this pin as an input"] - pub fn into_input(self) -> $PXi { - self.pad().reset(); - self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() }); - self.sio().gpio_oe_clr.write(|x| unsafe { x.bits(1 << $i) }); - - $PXi { _mode: PhantomData } - } - } - - impl OutputPin for $PXi { - type Error = Infallible; - - fn set_low(&mut self) -> Result<(), Self::Error> { - self.sio().gpio_out_clr.write(|x| unsafe { x.bits(1 << $i) }); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.sio().gpio_out_set.write(|x| unsafe { x.bits(1 << $i) }); - Ok(()) - } - } - - impl StatefulOutputPin for $PXi { - fn is_set_low(&self) -> Result { - Ok(!self.is_set_high()?) - } - - fn is_set_high(&self) -> Result { - Ok(self.sio().gpio_out.read().bits() & (1 << $i) != 0) - } - } - - macro_rules! impl_input_for { - ($MODE:ident) => { - impl InputPin for $PXi<$MODE> { - type Error = Infallible; - - fn is_low(&self) -> Result { - Ok(!self.is_high()?) - } - - fn is_high(&self) -> Result { - Ok(self.sio().gpio_in.read().bits() & (1 << $i) != 0) - } - } - }; - } - // Not allowed for Unknown since we don't know what state the pad is in - impl_input_for!(Input); - impl_input_for!(Output); - - impl $PXi { - #[doc = "Configure the drive strength for this output pin"] - pub fn drive_strength(self, strength: OutputDriveStrength) -> Self { - let converted = match strength { - OutputDriveStrength::TwoMilliAmps => PacDriveStrength::_2MA, - OutputDriveStrength::FourMilliAmps => PacDriveStrength::_4MA, - OutputDriveStrength::EightMilliAmps => PacDriveStrength::_8MA, - OutputDriveStrength::TwelveMilliAmps => PacDriveStrength::_12MA, - }; - self.pad().modify(|_, w| w.drive().variant(converted)); - self - } - - #[doc = "Configure the slew rate for this output pin"] - pub fn slew_rate(self, slew_rate: OutputSlewRate) -> Self { - self.pad().modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast)); - self - } - } - - impl $PXi { - #[doc = "Pull this input pin high using internal resistors"] - pub fn pull_up(self) -> Self { - self.pad().modify(|_, w| w.pue().set_bit().pde().clear_bit()); - self - } - - #[doc = "Pull this input pin low using internal resistors"] - pub fn pull_down(self) -> Self { - self.pad().modify(|_, w| w.pue().clear_bit().pde().set_bit()); - self - } - - #[doc = "Allow this input pin to float (i.e. don't pull it high or low)"] - pub fn float(self) -> Self { - self.pad().modify(|_, w| w.pue().clear_bit().pde().clear_bit()); - self - } - - #[doc = "Enable the schmitt trigger for this input pin"] - pub fn enable_schmitt_trigger(self) -> Self { - self.pad().modify(|_, w| w.schmitt().set_bit()); - self - } - - #[doc = "Disable the schmitt trigger for this input pin"] - pub fn disable_schmitt_trigger(self) -> Self { - self.pad().modify(|_, w| w.schmitt().clear_bit()); - self - } - } - )+ - } - }; -} - -gpio!( - IO_BANK0, io_bank0, SioGpioBank0, PADS_BANK0, pads_bank0, "IO_BANK0", [ - Gpio0: (gpio0, 0, "0"), - Gpio1: (gpio1, 1, "1"), - Gpio2: (gpio2, 2, "2"), - Gpio3: (gpio3, 3, "3"), - Gpio4: (gpio4, 4, "4"), - Gpio5: (gpio5, 5, "5"), - Gpio6: (gpio6, 6, "6"), - Gpio7: (gpio7, 7, "7"), - Gpio8: (gpio8, 8, "8"), - Gpio9: (gpio9, 9, "9"), - Gpio10: (gpio10, 10, "10"), - Gpio11: (gpio11, 11, "11"), - Gpio12: (gpio12, 12, "12"), - Gpio13: (gpio13, 13, "13"), - Gpio14: (gpio14, 14, "14"), - Gpio15: (gpio15, 15, "15"), - Gpio16: (gpio16, 16, "16"), - Gpio17: (gpio17, 17, "17"), - Gpio18: (gpio18, 18, "18"), - Gpio19: (gpio19, 19, "19"), - Gpio20: (gpio20, 20, "20"), - Gpio21: (gpio21, 21, "21"), - Gpio22: (gpio22, 22, "22"), - Gpio23: (gpio23, 23, "23"), - Gpio24: (gpio24, 24, "24"), - Gpio25: (gpio25, 25, "25"), - Gpio26: (gpio26, 26, "26"), - Gpio27: (gpio27, 27, "27"), - Gpio28: (gpio28, 28, "28"), - Gpio29: (gpio29, 29, "29"), - ] -); diff --git a/rp2040-hal/src/gpio/dynpin.rs b/rp2040-hal/src/gpio/dynpin.rs new file mode 100644 index 0000000..471bbc0 --- /dev/null +++ b/rp2040-hal/src/gpio/dynpin.rs @@ -0,0 +1,527 @@ +//! # Type-erased, value-level module for GPIO pins +//! +//! Based heavily on `atsamd-hal`. +//! +//! Although the type-level API is generally preferred, it is not suitable in +//! all cases. Because each pin is represented by a distinct type, it is not +//! possible to store multiple pins in a homogeneous data structure. The +//! value-level API solves this problem by erasing the type information and +//! tracking the pin at run-time. +//! +//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two +//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`] +//! respectively. The implementation of these types closely mirrors the +//! type-level API. +//! +//! Instances of [`DynPin`] cannot be created directly. Rather, they must be +//! created from their type-level equivalents using [`From`]/[`Into`]. +//! +//! ``` +//! // Move a pin out of the Pins struct and convert to a DynPin +//! let gpio12: DynPin = pins.gpio12.into(); +//! ``` +//! +//! Conversions between pin modes use a value-level version of the type-level +//! API. +//! +//! ``` +//! // Use one of the literal function names +//! gpio12.into_floating_input(); +//! // Use a method and a DynPinMode variant +//! gpio12.into_mode(DYN_FLOATING_INPUT); +//! ``` +//! +//! Because the pin state cannot be tracked at compile-time, many [`DynPin`] +//! operations become fallible. Run-time checks are inserted to ensure that +//! users don't try to, for example, set the output level of an input pin. +//! +//! Users may try to convert value-level pins back to their type-level +//! equivalents. However, this option is fallible, because the compiler cannot +//! guarantee the pin has the correct ID or is in the correct mode at +//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/ +//! [`TryInto`](core::convert::TryInto) for this conversion. +//! +//! ``` +//! // Convert to a `DynPin` +//! let gpio12: DynPin = pins.gpio12.into(); +//! // Change pin mode +//! pa27.into_floating_input(); +//! // Convert back to a `Pin` +//! let gpio12: Pin = gpio12.try_into().unwrap(); +//! ``` +//! +//! # Embedded HAL traits +//! +//! This module implements all of the embedded HAL GPIO traits for [`DynPin`]. +//! However, whereas the type-level API uses +//! `Error = core::convert::Infallible`, the value-level API can return a real +//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the +//! operation, the trait functions will return +//! [`InvalidPinType`](Error::InvalidPinType). +use super::pin::{Pin, PinId, PinMode, ValidPinMode}; +use super::reg::RegisterInterface; +use core::convert::TryFrom; + +use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; + +//============================================================================== +// DynPinMode configurations +//============================================================================== + +/// Value-level `enum` for disabled configurations +#[derive(PartialEq, Eq, Clone, Copy)] +#[allow(missing_docs)] +pub enum DynDisabled { + Floating, + PullDown, + PullUp, +} + +/// Value-level `enum` for input configurations +#[derive(PartialEq, Eq, Clone, Copy)] +#[allow(missing_docs)] +pub enum DynInput { + Floating, + PullDown, + PullUp, +} + +/// Value-level `enum` for output configurations +#[derive(PartialEq, Eq, Clone, Copy)] +#[allow(missing_docs)] +pub enum DynOutput { + PushPull, + Readable, +} + +/// Value-level `enum` for output configurations +#[derive(PartialEq, Eq, Clone, Copy)] +#[allow(missing_docs)] +pub enum DynFunction { + Spi, + Xip, + Uart, + I2C, + Pwm, + Pio0, + Pio1, + Clock, + UsbAux, +} + +//============================================================================== +// DynPinMode +//============================================================================== + +/// Value-level `enum` representing pin modes +#[derive(PartialEq, Eq, Clone, Copy)] +#[allow(missing_docs)] +pub enum DynPinMode { + Disabled(DynDisabled), + Input(DynInput), + Output(DynOutput), + Function(DynFunction), +} + +impl DynPinMode { + #[inline] + fn valid_for(&self, id: DynPinId) -> bool { + use DynFunction::*; + use DynGroup::*; + use DynPinMode::*; + match self { + Disabled(_) => true, + Input(_) => true, + Output(_) => true, + Function(alt) => match id.group { + Bank0 => match alt { + Spi | Uart | I2C | Pwm | Pio0 | Pio1 | UsbAux => true, + Clock if id.num >= 20 && id.num <= 25 => true, + _ => false, + }, + #[allow(clippy::match_like_matches_macro)] + Qspi => match alt { + Xip => true, + _ => false, + }, + }, + } + } +} + +/// Value-level variant of [`DynPinMode`] for floating disabled mode +pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating); +/// Value-level variant of [`DynPinMode`] for pull-down disabled mode +pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown); +/// Value-level variant of [`DynPinMode`] for pull-up disabled mode +pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp); + +/// Value-level variant of [`DynPinMode`] for floating input mode +pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); +/// Value-level variant of [`DynPinMode`] for pull-down input mode +pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); +/// Value-level variant of [`DynPinMode`] for pull-up input mode +pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); + +/// Value-level variant of [`DynPinMode`] for push-pull output mode +pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); +/// Value-level variant of [`DynPinMode`] for readable push-pull output mode +pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable); + +macro_rules! dyn_function { + ( $($Func:ident),+ ) => { + crate::paste::paste! { + $( + #[ + doc = "Value-level variant of [`DynPinMode`] for alternate " + "peripheral function " $Func + ] + pub const []: DynPinMode = + DynPinMode::Function(DynFunction::$Func); + )+ + } + }; +} + +dyn_function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux); + +//============================================================================== +// DynGroup & DynPinId +//============================================================================== + +/// Value-level `enum` for pin groups +#[derive(PartialEq, Clone, Copy)] +pub enum DynGroup { + /// . + Bank0, + /// . + Qspi, +} + +/// Value-level `struct` representing pin IDs +#[derive(PartialEq, Clone, Copy)] +pub struct DynPinId { + /// . + pub group: DynGroup, + /// . + pub num: u8, +} + +//============================================================================== +// DynRegisters +//============================================================================== + +/// Provide a safe register interface for [`DynPin`]s +/// +/// This `struct` takes ownership of a [`DynPinId`] and provides an API to +/// access the corresponding regsiters. +struct DynRegisters { + id: DynPinId, +} + +// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] +// guarantees that each pin is a singleton, so this implementation is safe. +unsafe impl RegisterInterface for DynRegisters { + #[inline] + fn id(&self) -> DynPinId { + self.id + } +} + +impl DynRegisters { + /// Create a new instance of [`DynRegisters`] + /// + /// # Safety + /// + /// Users must never create two simultaneous instances of this `struct` with + /// the same [`DynPinId`] + #[inline] + unsafe fn new(id: DynPinId) -> Self { + DynRegisters { id } + } +} + +//============================================================================== +// Error +//============================================================================== + +/// GPIO error type +/// +/// [`DynPin`]s are not tracked and verified at compile-time, so run-time +/// operations are fallible. This `enum` represents the corresponding errors. +#[derive(Debug)] +pub enum Error { + /// The pin did not have the correct ID or mode for the requested operation + InvalidPinType, + /// The pin does not support the requeted mode + InvalidPinMode, +} + +//============================================================================== +// DynPin +//============================================================================== + +/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`] +/// +/// This type acts as a type-erased version of [`Pin`]. Every pin is represented +/// by the same type, and pins are tracked and distinguished at run-time. +pub struct DynPin { + regs: DynRegisters, + mode: DynPinMode, +} + +impl DynPin { + /// Create a new [`DynPin`] + /// + /// # Safety + /// + /// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there + /// must be at most one corresponding [`DynPin`] in existence at any given + /// time. Violating this requirement is `unsafe`. + #[inline] + unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { + DynPin { + regs: DynRegisters::new(id), + mode, + } + } + + /// Return a copy of the pin ID + #[inline] + pub fn id(&self) -> DynPinId { + self.regs.id + } + + /// Return a copy of the pin mode + #[inline] + pub fn mode(&self) -> DynPinMode { + self.mode + } + + /// Convert the pin to the requested [`DynPinMode`] + #[inline] + pub fn try_into_mode(&mut self, mode: DynPinMode) -> Result<(), Error> { + // FIXME: check valid modes + // Only modify registers if we are actually changing pin mode + if mode.valid_for(self.regs.id) { + if mode != self.mode { + self.regs.do_change_mode(mode); + self.mode = mode; + } + Ok(()) + } else { + Err(Error::InvalidPinMode) + } + } + + /// Disable the pin and set it to float + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_floating_disabled(&mut self) { + self.try_into_mode(DYN_FLOATING_DISABLED).unwrap(); // always valid + } + + /// Disable the pin and set it to pull down + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_pull_down_disabled(&mut self) { + self.try_into_mode(DYN_PULL_DOWN_DISABLED).unwrap(); // always valid + } + + /// Disable the pin and set it to pull up + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_pull_up_disabled(&mut self) { + self.try_into_mode(DYN_PULL_UP_DISABLED).unwrap(); // always valid + } + + /// Configure the pin to operate as a floating input + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_floating_input(&mut self) { + self.try_into_mode(DYN_FLOATING_INPUT).unwrap(); // always valid + } + + /// Configure the pin to operate as a pulled down input + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_pull_down_input(&mut self) { + self.try_into_mode(DYN_PULL_DOWN_INPUT).unwrap(); // always valid + } + + /// Configure the pin to operate as a pulled up input + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_pull_up_input(&mut self) { + self.try_into_mode(DYN_PULL_UP_INPUT).unwrap(); // always valid + } + + /// Configure the pin to operate as a push-pull output + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_push_pull_output(&mut self) { + self.try_into_mode(DYN_PUSH_PULL_OUTPUT).unwrap(); // always valid + } + + /// Configure the pin to operate as a readable push pull output + #[inline] + #[allow(clippy::wrong_self_convention)] // matches pin api + pub fn into_readable_output(&mut self) { + self.try_into_mode(DYN_READABLE_OUTPUT).unwrap(); // always valid + } + + #[inline] + fn _read(&self) -> Result { + match self.mode { + DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()), + _ => Err(Error::InvalidPinType), + } + } + #[inline] + fn _write(&mut self, bit: bool) -> Result<(), Error> { + match self.mode { + DynPinMode::Output(_) => { + self.regs.write_pin(bit); + Ok(()) + } + _ => Err(Error::InvalidPinType), + } + } + #[inline] + fn _toggle(&mut self) -> Result<(), Error> { + match self.mode { + DynPinMode::Output(_) => { + self.regs.toggle_pin(); + Ok(()) + } + _ => Err(Error::InvalidPinType), + } + } + #[inline] + fn _read_out(&self) -> Result { + match self.mode { + DynPinMode::Output(_) => Ok(self.regs.read_out_pin()), + _ => Err(Error::InvalidPinType), + } + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _is_low(&self) -> Result { + Ok(self._read()? == false) + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _is_high(&self) -> Result { + Ok(self._read()? == true) + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _set_low(&mut self) -> Result<(), Error> { + self._write(false) + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _set_high(&mut self) -> Result<(), Error> { + self._write(true) + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _is_set_low(&self) -> Result { + Ok(self._read_out()? == false) + } + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + fn _is_set_high(&self) -> Result { + Ok(self._read_out()? == true) + } +} + +//============================================================================== +// Convert between Pin and DynPin +//============================================================================== + +impl From> for DynPin +where + I: PinId, + M: PinMode + ValidPinMode, +{ + /// Erase the type-level information in a [`Pin`] and return a value-level + /// [`DynPin`] + #[inline] + fn from(_pin: Pin) -> Self { + // The `Pin` is consumed, so it is safe to replace it with the + // corresponding `DynPin` + unsafe { DynPin::new(I::DYN, M::DYN) } + } +} + +impl TryFrom for Pin +where + I: PinId, + M: PinMode + ValidPinMode, +{ + type Error = Error; + + /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] + /// + /// There is no way for the compiler to know if the conversion will be + /// successful at compile-time. We must verify the conversion at run-time + /// or refuse to perform it. + #[inline] + fn try_from(pin: DynPin) -> Result { + if pin.regs.id == I::DYN && pin.mode == M::DYN { + // The `DynPin` is consumed, so it is safe to replace it with the + // corresponding `Pin` + Ok(unsafe { Self::new() }) + } else { + Err(Error::InvalidPinType) + } + } +} + +//============================================================================== +// Embedded HAL traits +//============================================================================== + +impl OutputPin for DynPin { + type Error = Error; + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high() + } + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low() + } +} + +impl InputPin for DynPin { + type Error = Error; + #[inline] + fn is_high(&self) -> Result { + self._is_high() + } + #[inline] + fn is_low(&self) -> Result { + self._is_low() + } +} + +impl ToggleableOutputPin for DynPin { + type Error = Error; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle() + } +} + +impl StatefulOutputPin for DynPin { + #[inline] + fn is_set_high(&self) -> Result { + self._is_set_high() + } + #[inline] + fn is_set_low(&self) -> Result { + self._is_set_low() + } +} diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs new file mode 100644 index 0000000..f8a00e7 --- /dev/null +++ b/rp2040-hal/src/gpio/mod.rs @@ -0,0 +1,34 @@ +//! General Purpose Input and Output (GPIO) +//! +//! See [`pin`](self::pin) for documentation. + +// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 +pub mod pin; +pub use pin::*; + +pub mod dynpin; +pub use dynpin::*; + +mod reg; + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +/// The amount of current that a pin can drive when used as an output +pub enum OutputDriveStrength { + /// 2 mA + TwoMilliAmps, + /// 4 mA + FourMilliAmps, + /// 8 mA + EightMilliAmps, + /// 12 mA + TwelveMilliAmps, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +/// The slew rate of a pin when used as an output +pub enum OutputSlewRate { + /// Slew slow + Slow, + /// Slew fast + Fast, +} diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs new file mode 100644 index 0000000..c6561e4 --- /dev/null +++ b/rp2040-hal/src/gpio/pin.rs @@ -0,0 +1,965 @@ +//! # Type-level module for GPIO pins +//! +//! Based heavily on `atsamd-hal`. +//! +//! This module provides a type-level API for GPIO pins. It uses the type system +//! to track the state of pins at compile-time. To do so, it uses traits to +//! represent [type classes] and types as instances of those type classes. For +//! example, the trait [`InputConfig`] acts as a [type-level enum] of the +//! available input configurations, and the types [`Floating`], [`PullDown`] and +//! [`PullUp`] are its type-level variants. +//! +//! When applied as a trait bound, a type-level enum restricts type parameters +//! to the corresponding variants. All of the traits in this module are closed, +//! using the `Sealed` trait pattern, so the type-level instances found in this +//! module are the only possible variants. +//! +//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and +//! [`PinMode`]. +//! +//! A `PinId` identifies a pin by it's group (A, B, C or D) and pin number. Each +//! `PinId` instance is named according to its datasheet identifier, e.g. +//! [`Gpio0`](`bank0::Gpio0`). +//! +//! A `PinMode` represents the various pin modes. The available `PinMode` +//! variants are [`Disabled`], [`Input`], [`Output`] and +//! [`Function`], each with its own corresponding configurations. +//! +//! It is not possible for users to create new instances of a [`Pin`]. Singleton +//! instances of each pin are made available to users through the [`Pins`] +//! struct. +//! +//! To create the [`Pins`] struct, users must supply the PAC +//! [`IO_BANK0`](crate::pac::IO_BANK0) and [`PAD_BANK0`](crate::pac::PADS_BANK0) peripherals as well as the [SIO partition](crate::sio). +//! The [`Pins`] struct takes +//! ownership of the peripherals and provides the corresponding pins. Each [`Pin`] +//! within the [`Pins`] struct can be moved out and used individually. +//! +//! +//! ``` +//! let mut peripherals = Peripherals::take().unwrap(); +//! let sio = Sio::new(peripherals.SIO); +//! let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); +//! ``` +//! +//! Pins can be converted between modes using several different methods. +//! +//! ``` +//! // Use one of the literal function names +//! let gpio12 = pins.gpio12.into_floating_input(); +//! // Use a generic method and one of the `PinMode` variant types +//! let gpio12 = pins.gpio12.into_mode::(); +//! // Specify the target type and use `From`/`Into` +//! let gpio12: Pin = pins.gpio12.into(); +//! ``` +//! +//! # Embedded HAL traits +//! +//! This module implements all of the embedded HAL GPIO traits for each [`Pin`] +//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], +//! [`ToggleableOutputPin`] and [`StatefulOutputPin`]. +//! +//! For example, you can control the logic level of an `OutputPin` like so +//! +//! ``` +//! use rp2040_hal::pac::Peripherals; +//! use rp2040_hak::gpio::v2::Pins; +//! use embedded_hal::digital::v2::OutputPin; +//! +//! let mut peripherals = Peripherals::take().unwrap(); +//! let sio = Sio::new(peripherals.SIO); +//! let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); +//! +//! pins.gpio12.set_high(); +//! ``` +//! +//! # Type-level features +//! +//! This module also provides additional, type-level tools to work with GPIO +//! pins. +//! +//! The [`OptionalPinId`] and [`OptionalPin`] traits use the [`OptionalKind`] +//! pattern to act as type-level versions of [`Option`] for `PinId` and `Pin` +//! respectively. And the [`AnyPin`] trait defines an [`AnyKind`] type class +//! for all `Pin` types. +//! +//! [type classes]: crate::typelevel#type-classes +//! [type-level enum]: crate::typelevel#type-level-enum +//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern +use super::dynpin::{DynDisabled, DynInput, DynOutput, DynPinId, DynPinMode}; +use super::{OutputDriveStrength, OutputSlewRate}; +use crate::gpio::reg::RegisterInterface; +use crate::typelevel::{Is, NoneT, Sealed}; +use core::convert::Infallible; +use core::marker::PhantomData; + +use crate::gpio::dynpin::DynFunction; +use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; + +use core::mem::transmute; + +/// Type-level marker for tracking which pin modes are valid for which pins +pub trait ValidPinMode: Sealed {} + +//============================================================================== +// Disabled configurations +//============================================================================== + +/// Type-level `enum` for disabled configurations +pub trait DisabledConfig: Sealed { + /// Corresponding [`DynDisabled`](super::DynDisabled) + const DYN: DynDisabled; +} + +/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] +pub enum Floating {} +/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] +pub enum PullDown {} +/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] +pub enum PullUp {} + +impl Sealed for Floating {} +impl Sealed for PullDown {} +impl Sealed for PullUp {} + +impl DisabledConfig for Floating { + const DYN: DynDisabled = DynDisabled::Floating; +} +impl DisabledConfig for PullDown { + const DYN: DynDisabled = DynDisabled::PullDown; +} +impl DisabledConfig for PullUp { + const DYN: DynDisabled = DynDisabled::PullUp; +} + +/// Type-level variant of [`PinMode`] for disabled modes +/// +/// Type `C` is one of three configurations: [`Floating`], [`PullDown`] or +/// [`PullUp`] +pub struct Disabled { + cfg: PhantomData, +} + +impl Sealed for Disabled {} + +/// Type-level variant of [`PinMode`] for floating disabled mode +pub type FloatingDisabled = Disabled; + +/// Type-level variant of [`PinMode`] for pull-down disabled mode +pub type PullDownDisabled = Disabled; + +/// Type-level variant of [`PinMode`] for pull-up disabled mode +pub type PullUpDisabled = Disabled; + +impl ValidPinMode for Disabled {} + +//============================================================================== +// Input configurations +//============================================================================== + +/// Type-level `enum` for input configurations +pub trait InputConfig: Sealed { + /// Corresponding [`DynInput`](super::DynInput) + const DYN: DynInput; +} + +impl InputConfig for Floating { + const DYN: DynInput = DynInput::Floating; +} +impl InputConfig for PullDown { + const DYN: DynInput = DynInput::PullDown; +} +impl InputConfig for PullUp { + const DYN: DynInput = DynInput::PullUp; +} + +/// Type-level variant of [`PinMode`] for input modes +/// +/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or +/// [`PullUp`] +pub struct Input { + cfg: PhantomData, +} + +impl Sealed for Input {} + +/// Type-level variant of [`PinMode`] for floating input mode +pub type FloatingInput = Input; + +/// Type-level variant of [`PinMode`] for pull-down input mode +pub type PullDownInput = Input; + +/// Type-level variant of [`PinMode`] for pull-up input mode +pub type PullUpInput = Input; + +impl ValidPinMode for Input {} + +//============================================================================== +// Output configurations +//============================================================================== + +/// Type-level `enum` for output configurations +pub trait OutputConfig: Sealed { + /// Corresponding [`DynOutput`](super::DynOutput) + const DYN: DynOutput; +} + +/// Type-level variant of [`OutputConfig`] for a push-pull configuration +pub enum PushPull {} +/// Type-level variant of [`OutputConfig`] for a readable push-pull +/// configuration +pub enum Readable {} + +impl Sealed for PushPull {} +impl Sealed for Readable {} + +impl OutputConfig for PushPull { + const DYN: DynOutput = DynOutput::PushPull; +} +impl OutputConfig for Readable { + const DYN: DynOutput = DynOutput::Readable; +} + +/// Type-level variant of [`PinMode`] for output modes +/// +/// Type `C` is one of two output configurations: [`PushPull`] or [`Readable`] +pub struct Output { + cfg: PhantomData, +} + +impl Sealed for Output {} + +/// Type-level variant of [`PinMode`] for push-pull output mode +pub type PushPullOutput = Output; + +/// Type-level variant of [`PinMode`] for readable push-pull output mode +type ReadableOutput = Output; + +impl ValidPinMode for Output {} + +// + +/// Type-level variant of [`PinMode`] for alternate peripheral functions +/// +/// Type `C` is an [`FunctionConfig`] +pub struct Function { + cfg: PhantomData, +} + +impl Sealed for Function {} + +/// Type-level enum for alternate peripheral function configurations +pub trait FunctionConfig: Sealed { + /// Corresponding [`DynFunction`](super::DynFunction) + const DYN: DynFunction; +} + +macro_rules! function { + ( + $( + $Func:ident + ),+ + ) => { + $crate::paste::paste! { + $( + #[ + doc = "Type-level variant of [`FunctionConfig`] for \ + alternate peripheral function " $Func + ] + pub enum $Func {} + impl Sealed for $Func {} + impl FunctionConfig for $Func { + const DYN: DynFunction = DynFunction::$Func; + } + #[ + doc = "Type-level variant of [`PinMode`] for alternate \ + peripheral function [`" $Func "`]" + ] + pub type [] = Function<$Func>; + )+ + } + }; +} + +function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux); + +//============================================================================== +// Pin modes +//============================================================================== + +/// Type-level `enum` representing pin modes +pub trait PinMode: Sealed + Sized { + /// Corresponding [`DynPinMode`](super::DynPinMode) + const DYN: DynPinMode; +} + +impl PinMode for Disabled { + const DYN: DynPinMode = DynPinMode::Disabled(C::DYN); +} + +impl PinMode for Input { + const DYN: DynPinMode = DynPinMode::Input(C::DYN); +} + +impl PinMode for Output { + const DYN: DynPinMode = DynPinMode::Output(C::DYN); +} + +impl PinMode for Function { + const DYN: DynPinMode = DynPinMode::Function(C::DYN); +} + +//============================================================================== +// Pin IDs +//============================================================================== + +/// Type-level `enum` for pin IDs +pub trait PinId: Sealed { + /// Corresponding [`DynPinId`](super::DynPinId) + const DYN: DynPinId; + /// [`PinMode`] at reset + type Reset; +} + +macro_rules! pin_id { + ($Group:ident, $Id:ident, $NUM:literal, $reset : ident) => { + #[doc = "Pin ID representing pin "] + pub enum $Id {} + impl Sealed for $Id {} + impl PinId for $Id { + type Reset = $reset; + const DYN: DynPinId = DynPinId { + group: DynGroup::$Group, + num: $NUM, + }; + } + }; +} + +//============================================================================== +// OptionalPinId +//============================================================================== + +/// Type-level equivalent of `Option` +/// +/// See the [`OptionalKind`] documentation for more details on the pattern. +/// +/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +pub trait OptionalPinId {} + +impl OptionalPinId for NoneT {} + +impl OptionalPinId for I {} + +/// Type-level equivalent of `Some(PinId)` +/// +/// See the [`OptionalKind`] documentation for more details on the pattern. +/// +/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +pub trait SomePinId: OptionalPinId + PinId {} + +impl SomePinId for I {} + +//============================================================================== +// Registers +//============================================================================== + +/// Provide a safe register interface for [`Pin`]s +/// +/// This `struct` takes ownership of a [`PinId`] and provides an API to +/// access the corresponding regsiters. +struct Registers { + id: PhantomData, +} + +// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that +// each pin is a singleton, so this implementation is safe. +unsafe impl RegisterInterface for Registers { + #[inline] + fn id(&self) -> DynPinId { + I::DYN + } +} + +impl Registers { + /// Create a new instance of [`Registers`] + /// + /// # Safety + /// + /// Users must never create two simultaneous instances of this `struct` with + /// the same [`PinId`] + #[inline] + unsafe fn new() -> Self { + Registers { id: PhantomData } + } + + /// Provide a type-level equivalent for the + /// [`RegisterInterface::change_mode`] method. + #[inline] + fn change_mode>(&mut self) { + RegisterInterface::do_change_mode(self, M::DYN); + } +} + +//============================================================================== +// Pin +//============================================================================== + +/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types +pub struct Pin +where + I: PinId, + M: PinMode + ValidPinMode, +{ + regs: Registers, + mode: PhantomData, +} + +impl Pin +where + I: PinId, + M: PinMode + ValidPinMode, +{ + /// Create a new [`Pin`] + /// + /// # Safety + /// + /// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be + /// at most one corresponding [`Pin`] in existence at any given time. + /// Violating this requirement is `unsafe`. + #[inline] + pub(crate) unsafe fn new() -> Pin { + Pin { + regs: Registers::new(), + mode: PhantomData, + } + } + + /// Convert the pin to the requested [`PinMode`] + #[inline] + pub fn into_mode>(mut self) -> Pin { + if N::DYN != M::DYN { + self.regs.change_mode::(); + } + // Safe because we drop the existing Pin + unsafe { Pin::new() } + } + + /// Disable the pin and set it to float + #[inline] + pub fn into_floating_disabled(self) -> Pin { + self.into_mode() + } + + /// Disable the pin and set it to pull down + #[inline] + pub fn into_pull_down_disabled(self) -> Pin { + self.into_mode() + } + + /// Disable the pin and set it to pull up + #[inline] + pub fn into_pull_up_disabled(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a floating input + #[inline] + pub fn into_floating_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a pulled down input + #[inline] + pub fn into_pull_down_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a pulled up input + #[inline] + pub fn into_pull_up_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_push_pull_output(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a readable push pull output + #[inline] + pub fn into_readable_output(self) -> Pin { + self.into_mode() + } + + /// Read the current drive strength of the pin. + #[inline] + pub fn get_drive_strength(&self) -> OutputDriveStrength { + self.regs.read_drive_strength() + } + + /// Set the drive strength for the pin. + #[inline] + pub fn set_drive_strength(&mut self, strength: OutputDriveStrength) { + self.regs.write_drive_strength(strength); + } + + /// Get the slew rate for the pin. + #[inline] + pub fn get_slew_rate(&self) -> OutputSlewRate { + self.regs.read_slew_rate() + } + + /// Set the slew rate for the pin + #[inline] + pub fn set_slew_rate(&mut self, rate: OutputSlewRate) { + self.regs.write_slew_rate(rate) + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_low(&self) -> bool { + self.regs.read_pin() == false + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_high(&self) -> bool { + self.regs.read_pin() == true + } + + #[inline] + pub(crate) fn _set_low(&mut self) { + self.regs.write_pin(false); + } + + #[inline] + pub(crate) fn _set_high(&mut self) { + self.regs.write_pin(true); + } + + #[inline] + pub(crate) fn _toggle(&mut self) { + self.regs.toggle_pin(); + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_set_low(&self) -> bool { + self.regs.read_out_pin() == false + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_set_high(&self) -> bool { + self.regs.read_out_pin() == true + } +} + +//============================================================================== +// AnyPin +//============================================================================== + +/// Type class for [`Pin`] types +/// +/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for +/// [`Pin`] types. See the `AnyKind` documentation for more details on the +/// pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +/// [type class]: crate::typelevel#type-classes +pub trait AnyPin +where + Self: Sealed, + Self: Is>, + ::Mode: ValidPinMode<::Id>, +{ + /// [`PinId`] of the corresponding [`Pin`] + type Id: PinId; + /// [`PinMode`] of the corresponding [`Pin`] + type Mode: PinMode; +} + +impl Sealed for Pin +where + I: PinId, + M: PinMode + ValidPinMode, +{ +} + +impl AnyPin for Pin +where + I: PinId, + M: PinMode + ValidPinMode, +{ + type Id = I; + type Mode = M; +} + +/// Type alias to recover the specific [`Pin`] type from an implementation of +/// [`AnyPin`] +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +pub type SpecificPin

= Pin<

::Id,

::Mode>; + +impl AsRef

for SpecificPin

{ + #[inline] + fn as_ref(&self) -> &P { + // SAFETY: This is guaranteed to be safe, because P == SpecificPin

+ // Transmuting between `v1` and `v2` `Pin` types is also safe, because + // both are zero-sized, and single-field, newtype structs are guaranteed + // to have the same layout as the field anyway, even for repr(Rust). + unsafe { transmute(self) } + } +} + +impl AsMut

for SpecificPin

{ + #[inline] + fn as_mut(&mut self) -> &mut P { + // SAFETY: This is guaranteed to be safe, because P == SpecificPin

+ // Transmuting between `v1` and `v2` `Pin` types is also safe, because + // both are zero-sized, and single-field, newtype structs are guaranteed + // to have the same layout as the field anyway, ValidPinMode en for repr(Rust). + unsafe { transmute(self) } + } +} + +//============================================================================== +// Optional pins +//============================================================================== + +/// Type-level equivalent of `Option` +/// +/// See the [`OptionalKind`] documentation for more details on the pattern. +/// +/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern +pub trait OptionalPin: Sealed { + #[allow(missing_docs)] + 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: AnyPin {} +impl SomePin for P {} + +//============================================================================== +// Embedded HAL traits +//============================================================================== + +impl OutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + type Error = Infallible; + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } +} + +impl InputPin for Pin +where + I: PinId, +{ + type Error = Infallible; + #[inline] + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +impl InputPin for Pin> +where + I: PinId, + C: InputConfig, +{ + type Error = Infallible; + #[inline] + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +impl ToggleableOutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } +} + +impl StatefulOutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + #[inline] + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + #[inline] + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } +} + +//============================================================================== +// Pin definitions +//============================================================================== + +macro_rules! gpio { + ($Group:ident, [ $($Func:ident),+ ], [ + $($PXi:ident: ($i:expr, $is:expr, $reset:ident $(, [ $($PinFunc:ident),+ ])? )),+ + ]) => { + $crate::paste::paste! { + #[doc = "GPIO Pins for " $Group] + pub mod [<$Group:lower>] { + use crate::sio::[]; + use pac::{[],[]}; + + /// Bank0 bank pin id + pub trait BankPinId {} + use crate::typelevel::Sealed; + use crate::gpio::dynpin::{DynGroup,DynPinId}; + + // FIXME: Somehow just import what we need + #[allow(unused_imports)] + use super::{PullDownDisabled,PullUpDisabled,FloatingDisabled}; + + use super::{Pin,PinId}; + use crate::resets::SubsystemReset; + + $( + pin_id!($Group, $PXi, $i, $reset); + impl BankPinId for $PXi {} + + $( $(impl super::ValidPinMode<$PXi> for super::Function {})+ )* + )+ + + /// Collection of all the individual [`Pin`]s + pub struct Pins { + _io: [], + _pads: [], + _sio: [], + $( + #[doc = "Pin " $PXi] + pub [<$PXi:lower>] : Pin<$PXi,<$PXi as PinId>::Reset>, + )+ + } + + impl Pins { + /// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s + pub fn new(io : [], pads: [], sio: [], reset : &mut pac::RESETS) -> Self { + io.reset_bring_up(reset); + pads.reset_bring_up(reset); + unsafe { Self { + _io: io, + _pads: pads, + _sio: sio, + $( + [<$PXi:lower>]: Pin::new(), + )+ + } + } + } + } + + $( impl super::ValidPinMode for super::Function {} )+ + } + } + } +} + +gpio!( + Bank0, [ Spi, Uart, I2C, Pwm, Pio0, Pio1, UsbAux ], [ + Gpio0: (0, "0", PullDownDisabled), + Gpio1: (1, "1", PullDownDisabled), + Gpio2: (2, "2", PullDownDisabled), + Gpio3: (3, "3", PullDownDisabled), + Gpio4: (4, "4", PullDownDisabled), + Gpio5: (5, "5", PullDownDisabled), + Gpio6: (6, "6", PullDownDisabled), + Gpio7: (7, "7", PullDownDisabled), + Gpio8: (8, "8", PullDownDisabled), + Gpio9: (9, "9", PullDownDisabled), + Gpio10: (10, "10", PullDownDisabled), + Gpio11: (11, "11", PullDownDisabled), + Gpio12: (12, "12", PullDownDisabled), + Gpio13: (13, "13", PullDownDisabled), + Gpio14: (14, "14", PullDownDisabled), + Gpio15: (15, "15", PullDownDisabled), + Gpio16: (16, "16", PullDownDisabled), + Gpio17: (17, "17", PullDownDisabled), + Gpio18: (18, "18", PullDownDisabled), + Gpio19: (19, "19", PullDownDisabled), + Gpio20: (20, "20", PullDownDisabled, [Clock]), + Gpio21: (21, "21", PullDownDisabled, [Clock]), + Gpio22: (22, "22", PullDownDisabled, [Clock]), + Gpio23: (23, "23", PullDownDisabled, [Clock]), + Gpio24: (24, "24", PullDownDisabled, [Clock]), + Gpio25: (25, "25", PullDownDisabled, [Clock]), + Gpio26: (26, "26", PullDownDisabled), + Gpio27: (27, "27", PullDownDisabled), + Gpio28: (28, "28", PullDownDisabled), + Gpio29: (29, "29", PullDownDisabled) + ] +); + +pub use bank0::Pins; // this is probably the default everyone is going to want + +gpio!( + Qspi, [ Xip ], [ + Sck: (0, "sck", PullDownDisabled), + Cs: (1, "cs", PullUpDisabled), + Sd0: (2, "sd0", FloatingDisabled), + Sd1: (3, "sd1", FloatingDisabled), + Sd2: (4, "sd2", FloatingDisabled), + Sd3: (5, "sd3", FloatingDisabled) + ] +); + +//============================================================================== +// bsp_pins +//============================================================================== + +/// Helper macro to give meaningful names to GPIO pins +/// +/// The normal [`Pins`] struct names each [`Pin`] according to its [`PinId`]. +/// However, BSP authors would prefer to name each [`Pin`] according to its +/// function. This macro defines a new `Pins` struct with custom field names +/// for each [`Pin`], and it defines type aliases and constants to make it +/// easier to work with the [`Pin`]s and [`DynPin`](super::DynPin)s. +/// +/// When specifying pin aliases, be sure to use a [`PinMode`]. See +/// [here](self#types) for a list of the available [`PinMode`] type aliases. +/// +/// # Example +/// ... +#[macro_export] +macro_rules! bsp_pins { + ( + $( + $( #[$id_cfg:meta] )* + $Id:ident { + $( #[$name_doc:meta] )* + name: $name:ident $(,)? + $( + aliases: { + $( + $( #[$alias_cfg:meta] )* + $Mode:ident: $Alias:ident + ),+ + } + )? + } $(,)? + )+ + ) => { + $crate::paste::paste! { + + /// BSP replacement for the HAL + /// [`Pins`](rp2040_hal::gpio::Pins) type + /// + /// This type is intended to provide more meaningful names for the + /// given pins. + pub struct Pins { + $( + $( #[$id_cfg] )* + $( #[$name_doc] )* + pub $name: $crate::gpio::Pin< + $crate::gpio::bank0::$Id, + <$crate::gpio::bank0::$Id as $crate::gpio::PinId>::Reset + >, + )+ + } + + impl Pins { + /// Take ownership of the PAC [`PORT`] and split it into + /// discrete [`Pin`]s. + /// + /// This struct serves as a replacement for the HAL [`Pins`] + /// struct. It is intended to provide more meaningful names for + /// each [`Pin`] in a BSP. Any [`Pin`] not defined by the BSP is + /// dropped. + /// + /// [`Pin`](rp2040_hal::gpio::Pin) + /// [`Pins`](rp2040_hal::gpio::Pins) + #[inline] + pub fn new(io : $crate::pac::IO_BANK0, pads: $crate::pac::PADS_BANK0, sio: $crate::sio::SioGpioBank0, reset : &mut $crate::pac::RESETS) -> Self { + let mut pins = $crate::gpio::Pins::new(io,pads,sio,reset); + Self { + $( + $( #[$id_cfg] )* + $name: pins.[<$Id:lower>], + )+ + } + } + } + $( + $( #[$id_cfg] )* + $crate::bsp_pins!(@aliases, $( $( $( #[$alias_cfg] )* $Id $Mode $Alias )+ )? ); + )+ + } + }; + ( @aliases, $( $( $( #[$attr:meta] )* $Id:ident $Mode:ident $Alias:ident )+ )? ) => { + $crate::paste::paste! { + $( + $( + $( #[$attr] )* + /// Alias for a configured [`Pin`](rp2040_hal::gpio::Pin) + pub type $Alias = $crate::gpio::Pin< + $crate::gpio::bank0::$Id, + $crate::gpio::$Mode + >; + + $( #[$attr] )* + #[doc = "[DynPinId](rp2040_hal::gpio::DynPinId) "] + #[doc = "for the `" $Alias "` alias."] + pub const [<$Alias:snake:upper _ID>]: $crate::gpio::DynPinId = + <$crate::gpio::bank0::$Id as $crate::gpio::PinId>::DYN; + + $( #[$attr] )* + #[doc = "[DynPinMode]rp2040_hal::gpio::DynPinMode) "] + #[doc = "for the `" $Alias "` alias."] + pub const [<$Alias:snake:upper _MODE>]: $crate::gpio::DynPinMode = + <$crate::gpio::$Mode as $crate::gpio::PinMode>::DYN; + )+ + )? + } + }; +} diff --git a/rp2040-hal/src/gpio/reg.rs b/rp2040-hal/src/gpio/reg.rs new file mode 100644 index 0000000..19aa439 --- /dev/null +++ b/rp2040-hal/src/gpio/reg.rs @@ -0,0 +1,353 @@ +// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 +use super::dynpin::{DynGroup, DynPinId}; +use super::{OutputDriveStrength, OutputSlewRate}; +use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode}; +use crate::pac; + +//============================================================================== +// ModeFields +//============================================================================== + +/// Collect all fields needed to set the [`PinMode`](super::PinMode) +#[derive(Default)] +struct ModeFields { + inen: bool, + pue: bool, + pde: bool, + + sio_outen: bool, + funcsel: u8, +} + +const SIO_FUNCSEL: u8 = 5; +const NULL_FUNCSEL: u8 = 0x1f; + +impl From for ModeFields { + #[inline] + fn from(mode: DynPinMode) -> Self { + use DynPinMode::*; + let mut fields = Self::default(); + match mode { + Disabled(config) => { + use DynDisabled::*; + fields.funcsel = NULL_FUNCSEL; + fields.sio_outen = false; + fields.inen = false; + + match config { + Floating => (), + PullDown => { + fields.pde = true; + } + PullUp => { + fields.pue = true; + } + } + } + Input(config) => { + use DynInput::*; + fields.funcsel = SIO_FUNCSEL; + fields.sio_outen = false; + fields.inen = true; + + match config { + Floating => (), + PullDown => { + fields.pde = true; + } + PullUp => { + fields.pue = true; + } + } + } + Output(config) => { + use DynOutput::*; + fields.funcsel = SIO_FUNCSEL; + fields.sio_outen = true; + match config { + PushPull => { + fields.inen = false; + } + Readable => { + fields.inen = true; + } + } + } + Function(func) => { + use DynFunction::*; + fields.funcsel = match func { + Xip => 0, + Spi => 1, + Uart => 2, + I2C => 3, + Pwm => 4, + // Pio => 5 + Pio0 => 6, + Pio1 => 7, + Clock => 8, + UsbAux => 9, + }; + fields.inen = true; + } + }; + + fields + } +} +pub(super) unsafe trait RegisterInterface { + /// Provide a [`DynPinId`] identifying the set of registers controlled by + /// this type. + fn id(&self) -> DynPinId; + + #[inline] + fn mask_32(&self) -> u32 { + 1 << self.id().num + } + + /// Read the logic level of an input put + #[inline] + fn read_pin(&self) -> bool { + let mask = self.mask_32(); + (match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_in.read().bits(), + DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_in.read().bits(), + }) & mask + != 0 + } + + /// Write the logic level of an output pin + #[inline] + fn write_pin(&mut self, bit: bool) { + let mask = self.mask_32(); + // This ordering to try and make the group match inline if bit can't be + unsafe { + match self.id().group { + DynGroup::Bank0 => { + if bit { + (*pac::SIO::ptr()).gpio_out_set.write(|w| w.bits(mask)); + } else { + (*pac::SIO::ptr()).gpio_out_clr.write(|w| w.bits(mask)); + } + } + DynGroup::Qspi => { + if bit { + (*pac::SIO::ptr()).gpio_hi_out_set.write(|w| w.bits(mask)); + } else { + (*pac::SIO::ptr()).gpio_hi_out_clr.write(|w| w.bits(mask)); + } + } + }; + } + } + + /// Toggle the logic level of an output pin + #[inline] + fn toggle_pin(&mut self) { + let mask = self.mask_32(); + match self.id().group { + DynGroup::Bank0 => unsafe { (*pac::SIO::ptr()).gpio_out_xor.write(|w| w.bits(mask)) }, + DynGroup::Qspi => unsafe { (*pac::SIO::ptr()).gpio_hi_out_xor.write(|w| w.bits(mask)) }, + } + } + + /// Read back the logic level of an output pin + #[inline] + fn read_out_pin(&self) -> bool { + let mask = self.mask_32(); + (match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_out.read().bits(), + DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_out.read().bits(), + }) & mask + != 0 + } + + #[inline] + fn read_drive_strength(&self) -> OutputDriveStrength { + use OutputDriveStrength::*; + let num = self.id().num as usize; + let strength = match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] + .read() + .drive() + .bits(), + DynGroup::Qspi => qspi_read_drive(num), + }; + match strength { + 0x0 => TwoMilliAmps, + 0x1 => FourMilliAmps, + 0x2 => EightMilliAmps, + 0x3 => TwelveMilliAmps, + _ => unreachable!("invalid drive strength"), + } + } + + #[inline] + fn write_drive_strength(&self, strength: OutputDriveStrength) { + use OutputDriveStrength::*; + let num = self.id().num as usize; + let strength = match strength { + TwoMilliAmps => 0x0, + FourMilliAmps => 0x1, + EightMilliAmps => 0x2, + TwelveMilliAmps => 0x3, + }; + + match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] + .modify(|_, w| w.drive().bits(strength)), + DynGroup::Qspi => qspi_write_drive(num, strength), + }; + } + + #[inline] + fn read_slew_rate(&self) -> OutputSlewRate { + let num = self.id().num as usize; + let slew_fast = match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] + .read() + .slewfast() + .bit_is_set(), + DynGroup::Qspi => qspi_read_slew(num), + }; + if slew_fast { + OutputSlewRate::Fast + } else { + OutputSlewRate::Slow + } + } + + #[inline] + fn write_slew_rate(&self, rate: OutputSlewRate) { + let num = self.id().num as usize; + let slewfast = match rate { + OutputSlewRate::Fast => true, + OutputSlewRate::Slow => false, + }; + + match self.id().group { + DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] + .modify(|_, w| w.slewfast().bit(slewfast)), + DynGroup::Qspi => qspi_write_slew(num, slewfast), + }; + } + + // We have to duplicate code, maybe a fix in the HAL layer can prevent this + #[inline] + fn do_change_mode(&self, mode: DynPinMode) { + let num = self.id().num as usize; + match self.id().group { + DynGroup::Bank0 => gpio_change_mode(num, mode), + DynGroup::Qspi => qspi_change_mode(num, mode), + } + } +} + +#[inline] +fn gpio_change_mode(num: usize, mode: DynPinMode) { + let fields: ModeFields = mode.into(); + let io = unsafe { &(*pac::IO_BANK0::ptr()).gpio[num] }; + let pads = unsafe { &(*pac::PADS_BANK0::ptr()).gpio[num] }; + + pads.write(|w| { + w.pue().bit(fields.pue); + w.pde().bit(fields.pde); + w.ie().bit(fields.inen); + w.od().bit(false) // the SIO oe bit will handle this instead + }); + + io.gpio_ctrl + .write(|w| unsafe { w.funcsel().bits(fields.funcsel) }); + + if fields.funcsel == SIO_FUNCSEL { + if fields.sio_outen { + unsafe { + (*pac::SIO::ptr()).gpio_oe_set.write(|w| w.bits(1 << num)); + } + } else { + unsafe { + (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); + } + } + } else { + unsafe { + (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); + } + } +} + +// TODO: This is really nasty, but there's no single type for the QSPI pins +// I'm not sure if a svd change is even possible to fix this, as these do have +// different reset values +macro_rules! qspi_bits { + ( $( ($num : expr, $suffix : ident) ),+ ) => { + $crate::paste::paste! { + #[inline] + fn qspi_read_drive(num: usize) -> u8 { + match num { + $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().drive().bits(), )+ + _ => unreachable!("invalid ID for QSPI pin") + } + } + + #[inline] + fn qspi_write_drive(num: usize, val : u8) { + match num { + $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.drive().bits(val) ), )+ + _ => unreachable!("invalid ID for QSPI pin") + } + } + + #[inline] + fn qspi_read_slew(num: usize) -> bool { + match num { + $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().slewfast().bit_is_set(), )+ + _ => unreachable!("invalid ID for QSPI pin") + } + } + + #[inline] + fn qspi_write_slew(num: usize, slewfast : bool) { + match num { + $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.slewfast().bit(slewfast) ), )+ + _ => unreachable!("invalid ID for QSPI pin") + } + } + + #[inline] + fn qspi_change_mode(num: usize, mode: DynPinMode) { + let fields : ModeFields = mode.into(); + + match num { + $($num => { + let io = unsafe { &(*pac::IO_QSPI::ptr()).[] }; + let pads = unsafe { &(*pac::PADS_QSPI::ptr()).[] }; + + pads.write(|w| { + w.pue().bit(fields.pue); + w.pde().bit(fields.pde); + w.ie().bit(fields.inen); + w.od().bit(false) + }); + + io.gpio_ctrl.write(|w| unsafe { w.funcsel().bits(fields.funcsel) } ); + }, )+ + _ => unreachable!("invalid ID for QSPI pin") + } + + + // outen is only on SIO + if fields.funcsel == SIO_FUNCSEL { + if fields.sio_outen { + unsafe { (*pac::SIO::ptr()).gpio_hi_oe_set.write(|w| w.bits(1 << num)); } + } else { + unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } + } + } else { + unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } + } + } + } + } +} + +qspi_bits!((0, sclk), (1, ss), (2, sd0), (3, sd1), (4, sd2), (5, sd3)); diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index 24b0fbe..b459b6d 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -10,6 +10,7 @@ extern crate cortex_m; extern crate embedded_hal as hal; extern crate nb; +pub use paste; pub extern crate rp2040_pac as pac; @@ -27,6 +28,7 @@ pub mod sio; pub mod spi; pub mod ssi; pub mod timer; +pub mod typelevel; pub mod uart; pub mod usb; pub mod watchdog; diff --git a/rp2040-hal/src/prelude.rs b/rp2040-hal/src/prelude.rs index 011f347..8e3e645 100644 --- a/rp2040-hal/src/prelude.rs +++ b/rp2040-hal/src/prelude.rs @@ -1,3 +1 @@ //! Prelude -pub use crate::gpio::GpioExt; -pub use crate::sio::Sio; diff --git a/rp2040-hal/src/rom_data.rs b/rp2040-hal/src/rom_data.rs index 77afe98..8b80148 100644 --- a/rp2040-hal/src/rom_data.rs +++ b/rp2040-hal/src/rom_data.rs @@ -52,25 +52,6 @@ macro_rules! rom_funcs { } } -macro_rules! rom_funcs_unsafe { - ( - $( - $(#[$outer:meta])* - $c:literal $name:ident ( - $( $aname:ident : $aty:ty ),* - ) -> $ret:ty ; - )* - ) => { - $( - $(#[$outer])* - pub unsafe fn $name($( $aname:$aty ),*) -> $ret{ - let func: extern "C" fn( $( $aty ),* ) -> $ret = rom_table_lookup(FUNC_TABLE, *$c); - func($( $aname ),*) - } - )* - } -} - rom_funcs! { /// Return a count of the number of 1 bits in value. b"P3" popcount32(value: u32) -> u32; @@ -84,20 +65,6 @@ rom_funcs! { /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. b"T3" ctz32(value: u32) -> u32; - /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: - /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED - /// for the USB Mass Storage Device: - /// * 0 No pins are used as per cold boot. - /// * Otherwise a single bit set indicating which GPIO pin should be set to output and - /// raised whenever there is mass storage activity from the host. - /// * disable_interface_mask may be used to control the exposed USB interfaces: - /// * 0 To enable both interfaces (as per cold boot). - /// * 1 To disable the USB Mass Storage Interface. - /// * 2 to Disable the USB PICOBOOT Interface. - b"UB" reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); -} - -rom_funcs_unsafe! { /// Sets n bytes start at ptr to the value c and returns ptr b"MS" memset(ptr: *mut u8, c: u8, n: u8) -> *mut u8; @@ -146,6 +113,18 @@ rom_funcs_unsafe! { /// know exactly what kind of flash device is connected. b"CX" flash_enter_cmd_xip() -> (); + /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: + /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED + /// for the USB Mass Storage Device: + /// * 0 No pins are used as per cold boot. + /// * Otherwise a single bit set indicating which GPIO pin should be set to output and + /// raised whenever there is mass storage activity from the host. + /// * disable_interface_mask may be used to control the exposed USB interfaces: + /// * 0 To enable both interfaces (as per cold boot). + /// * 1 To disable the USB Mass Storage Interface. + /// * 2 to Disable the USB PICOBOOT Interface. + b"UB" reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); + /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. /// There are few cases where you should call this method (resetting core 1 is much better). /// This method does not return and should only ever be called on core 1. diff --git a/rp2040-hal/src/sio.rs b/rp2040-hal/src/sio.rs index 063df57..154cae2 100644 --- a/rp2040-hal/src/sio.rs +++ b/rp2040-hal/src/sio.rs @@ -3,13 +3,14 @@ //! To be able to partition parts of the SIO block to other modules: //! //! ```rust -//! let sio = Sio::new(pac.SIO); +//! let mut peripherals = pac::Peripherals::take().unwrap(); +//! let sio = Sio::new(peripherals.SIO); //! ``` //! //! And then for example //! //! ```rust -//! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); +//! let pins = gpio:Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS); //! ``` use super::*; @@ -18,6 +19,11 @@ pub struct SioGpioBank0 { _private: (), } +/// Marker struct for ownership of SIO gpio qspi +pub struct SioGpioQspi { + _private: (), +} + /// Marker struct for ownership of divide/modulo module pub struct HwDivider { _private: (), @@ -36,10 +42,11 @@ pub struct Sio { _sio: pac::SIO, /// GPIO Bank 0 registers pub gpio_bank0: SioGpioBank0, + /// GPIO QSPI registers + pub gpio_qspi: SioGpioQspi, /// 8-cycle hardware divide/modulo module pub hwdivider: HwDivider, // we can hand out other things here, for example: - // gpio_qspi // interp0 // interp1 } @@ -50,6 +57,7 @@ impl Sio { _sio: sio, gpio_bank0: SioGpioBank0 { _private: () }, + gpio_qspi: SioGpioQspi { _private: () }, hwdivider: HwDivider { _private: () }, } diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs new file mode 100644 index 0000000..c0d7832 --- /dev/null +++ b/rp2040-hal/src/typelevel.rs @@ -0,0 +1,697 @@ +//! Module supporting type-level programming +//! +//! Copied from [atsamd-hal](https://github.com/atsamd-rs/atsamd). +//! +//! # Introduction +//! +//! Embedded software is often difficult to debug, so there is a strong +//! motivation to catch as many bugs as possible at compile-time. However, the +//! performance requirements of embedded software also make it difficult to +//! justify changes that impose additional overhead in terms of size or speed. +//! Ideally, we would like to add as many compile-time checks as possible, while +//! also producing the fewest possible assembly instructions. +//! +//! The Rust type system can help accomplish this goal. By expressing software +//! constraints within the type system, developers can enforce invariants at +//! compile-time. +//! +//! Sometimes this is done using Rust macros. However, that approach can produce +//! code that is difficult to read and understand. Moreover, macro-generated +//! code can only be extended by *more* macros, which further spreads the +//! problem. In `atsamd-hal` specifically, issue +//! [#214](https://github.com/atsamd-rs/atsamd/issues/214) discussed the extent +//! to which macros were once used in the repository. +//! +//! Alternatively, many of the same goals can be accomplished with the Rust +//! type & trait system directly, which is quite powerful. In fact, it is +//! [turing complete](https://sdleffler.github.io/RustTypeSystemTuringComplete/). +//! By expressing our invariants entirely within the type system, we can encode +//! the desired compile-time checks in a form that is easier to read, understand +//! and document. +//! +//! This module documents some of the type-level programming techniques used +//! throughout this HAL, and it contains a few items used to implement them. +//! +//! ## Contents +//! +//! - [Basics of type-level programming](#basics-of-type-level-programming) +//! - [Type-level enums](#type-level-enums) +//! - [Type classes](#type-classes) +//! - [Type-level containers](#type-level-containers) +//! - [Type-level functions](#type-level-functions) +//! - [`OptionalKind` trait pattern](#optionalkind-trait-pattern) +//! - [`AnyKind` trait pattern](#anykind-trait-pattern) +//! - [Defining an `AnyKind` trait](#defining-an-anykind-trait) +//! - [Using an `AnyKind` trait](#using-an-anykind-trait) +//! +//! # Basics of type-level programming +//! +//! Type-level programming aims to execute a form of compile-time computation. +//! But to perform such computation, we need to map our traditional notions of +//! programming to the Rust type system. +//! +//! In normal Rust, individual values are grouped or categorized into types. For +//! example, `0`, `1`, `2`, etc. are all members of the `usize` type. Similarly, +//! `Enum::A` and `Enum::B` are members of the `Enum` type, defined as +//! +//! ```ignore +//! enum Enum { A, B } +//! ``` +//! +//! We use composite types and containers to create more complex data +//! structures, and we use functions to map between values. +//! +//! All of these concepts can also be expressed within the Rust type system. +//! However, in this case, types are grouped and categorized into traits. For +//! instance, the [`typenum`](https://docs.rs/typenum/1.13.0/typenum/index.html) +//! crate provides the types `U0`, `U1`, `U2`, etc., which are all members of +//! the `Unsigned` trait. Similarly, the following sections will illustrate how +//! to define type-level enums, containers and functions. +//! +//! ## Type-level enums +//! +//! Type-level enums are one of the foundational concepts of type-level +//! programming used in this HAL. +//! +//! At the value-level, a typical Rust enum represents some set of variants that +//! can be assigned to a particular variable. Similarly, a type-level enum +//! represents some set of types that can be assigned to a particular type +//! parameter. +//! +//! To lift an enum from the value level to the type level, you typically map +//! the enum variants to types and the enum itself to a trait. For instance, the +//! value-level enum +//! +//! ```ignore +//! enum Enum { +//! A, +//! B, +//! } +//! ``` +//! +//! would be mapped to the type level like so. +//! +//! ```ignore +//! trait Enum {} +//! +//! enum A {} +//! enum B {} +//! +//! impl Enum for A {} +//! impl Enum for B {} +//! ``` +//! +//! At the value level, the variants `A` and `B` are grouped by the `Enum` type, +//! while at the type level, the types `A` and `B` are grouped by the `Enum` +//! trait. +//! +//! ## Type classes +//! +//! At the value-level, a type restricts the possible values that can be taken +//! by some free variable. While at the type-level, a trait bound restricts the +//! possible types that can be taken by some free type parameter. In effect, +//! trait bounds can be used to create a kind of meta-type, or type class. The +//! type-level enums in the previous section represent the most primitive +//! application of the concept, but type classes can take other forms. The +//! `OptionalKind` and `AnyKind` trait patterns discussed below are more +//! advanced applications of the same concept. +//! +//! ## Type-level containers +//! +//! To represent more complex relationships, we need a way to form composite +//! data structures at the type level. +//! +//! At the value level, a container holds an instance of a particular type. The +//! exact value of that instance is usually not known to the author, it is only +//! known at run-time. +//! +//! At the type level, we don't have the same notion of "run-time", but we do +//! have two different notions of "compile-time" that form a similar +//! relationship. There is compile time for the HAL authors, and there is a +//! separate compile-time for the HAL users. We want to create a type-level +//! container where the exact type is not known at author-time, but it is known +//! at user-time. +//! +//! For example, take the following, value-level container struct. It contains +//! two fields, `a` and `b`, of different types, `EnumOne` and `EnumTwo`. +//! +//! ```ignore +//! struct Container { +//! a: EnumOne, +//! b: EnumTwo, +//! } +//! ``` +//! +//! We can create an instance of this container with specific values. +//! +//! ```ignore +//! let x = Container { a: EnumOne::VariantX, b: EnumTwo::VariantY }; +//! ``` +//! +//! Next, suppose we had already translated `EnumOne` and `EnumTwo` to the type +//! level using the technique in the previous section. If we wanted to create a +//! similar, composite data structure at the type level, we could use type +//! parameters in place of struct fields to represent the unknown types. +//! +//! ```ignore +//! struct Container +//! where +//! A: EnumOne, +//! B: EnumTwo, +//! { +//! a: PhantomData, +//! b: PhantomData, +//! } +//! ``` +//! +//! And we could create an instance of this container with specific types. +//! +//! ```ignore +//! type X = Container; +//! ``` +//! +//! You might notice the use of `PhantomData` in the definition of the +//! type-level container. Because it is geared more toward value-level +//! programming, Rust requires all type parameters actually be used by the +//! corresponding type. However, we don't need to "store" a type in the same way +//! we store values. The compiler is responsible for tracking the concrete type +//! for each type parameter. But the language still requires us to act as if we +//! used each type parameter. `PhantomData` is the solution here, because it +//! lets us make use of the type parameters without actually storing any values. +//! +//! Separately, `PhantomData` also allows us to create "instances" of types that +//! normally can't be instantiated, like empty enums. For example, instances of +//! `Enum` below can never exist directly. +//! +//! ```ignore +//! enum Enum {} +//! ``` +//! +//! But instances of `PhantomData` are perfectly valid. In this way, +//! library authors can create types that only exist at the type level, which +//! can sometimes simplify a design. +//! +//! ## Type-level functions +//! +//! To perform type-level computations, we need some way to map or transform +//! types into other types. +//! +//! At the value level, functions and methods map values of the input types to +//! values of the output types. The same can be accomplished at the type level +//! using traits and associated types. Type-level functions are implemented as +//! traits, where the implementing type and any type parameters are the inputs, +//! and associated types are the outputs. +//! +//! For example, consider the value level `not` method below. +//! +//! ```ignore +//! enum Bool { +//! False, +//! True, +//! } +//! +//! impl Bool { +//! fn not(self) -> Self { +//! use Bool::*; +//! match self { +//! True => False, +//! False => True, +//! } +//! } +//! } +//! ``` +//! +//! We can translate this example to the type level like so. +//! +//! ```ignore +//! trait Bool {} +//! +//! enum True {} +//! enum False {} +//! +//! impl Bool for True {} +//! impl Bool for False {} +//! +//! trait Not: Bool { +//! type Result: Bool; +//! } +//! +//! impl Not for True { +//! type Result = False; +//! } +//! +//! impl Not for False { +//! type Result = True; +//! } +//! ``` +//! +//! We can use the `Not` trait bound to transform one type to another. For +//! instance, we can create a container that accepts one type parameter but +//! stores a different one. +//! +//! ```ignore +//! struct Container { +//! not: PhantomData; +//! } +//! ``` +//! +//! Alternatively, we could redefine the trait and declar a corresponding type +//! alias as +//! +//! ```ignore +//! trait NotFunction: Bool { +//! type Result: Bool; +//! } +//! +//! type Not = ::Result; +//! ``` +//! +//! Doing so would allow us to us reframe the last example as +//! +//! ```ignore +//! struct Container { +//! not: PhantomData>; +//! } +//! ``` +//! +//! Type-level functions can be more complicated than this example, but they +//! ultimately represent a mapping from a set of input types (the implementing +//! type and any type parameters) to a set of output types (the associated +//! types). +//! +//! # `OptionalKind` trait pattern +//! +//! As mentioned above, traits can be used to define a kind of meta-type or type +//! class, essentially forming a set of valid types for a given type parameter. +//! They also represent the concept of types lifted from the value level to the +//! type level. +//! +//! What if we want to define a type class representing either a set of useful +//! types or some useless, null type? Essentially, how do we take the notion of +//! an [`Option`] type and raise it to the type level? +//! +//! Suppose we have some existing type class, defined by the `Class` trait, that +//! we want to make optional. We can define a new type class that includes all +//! instances of `Class` as well as some null type. For the latter we use +//! [`NoneT`], defined in this module. +//! +//! ```ignore +//! trait OptionalClass {} +//! +//! impl OptionalClass for NoneT {} +//! impl OptionalClass for C {} +//! ``` +//! +//! We can use this new type class to store an optional instance of a `Class` +//! type in a struct. +//! +//! ```ignore +//! struct Container { +//! class: PhantomData, +//! } +//! ``` +//! +//! And we can restrict some of its methods to only operate on instances with a +//! valid `Class`. +//! +//! ```ignore +//! impl Container { +//! fn method(self) { ... } +//! } +//! ``` +//! +//! Although it is not strictly necessary, we can also introduce a new type +//! class to differentiate the bare usage of `Class` from instances of some +//! `Class` where an `OptionalClass` is accepted. +//! +//! ```ignore +//! trait SomeClass: OptionalClass + Class {} +//! +//! impl SomeClass for C {} +//! ``` +//! +//! This new trait doesn't add any new information, but it can still help +//! 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 +//! type parameters and represent them with only a single type parameter. It +//! lets you introduce a layer of abstraction, which can simplify interfaces and +//! make them more readable. But most of all, it does so without sacrificing any +//! of our normal, type-level abilities. +//! +//! ## Defining an `AnyKind` trait +//! +//! Suppose you had a composite, type-level data structure. For example, the +//! GPIO `Pin` struct contains instances of two type-level enums, a `PinId` and +//! a `PinMode`. It looks something like this. +//! +//! ```ignore +//! struct Pin { +//! // ... +//! } +//! ``` +//! +//! Rust does not provide any way to speak about a `Pin` generally. Any mention +//! of the `Pin` type must also include its type parameters, i.e. `Pin`. +//! This is not a deal-breaker, but it is less than ideal for type-level +//! programming. It would be nice if there were a way to succinctly refer to any +//! `Pin`, regardless of its type parameters. +//! +//! We've seen above that we can use traits to form a type class. What if we +//! were to introduce a new trait to label all instances of `Pin`? It would look +//! something like this. +//! +//! ```ignore +//! trait AnyPin {} +//! +//! impl AnyPin for Pin {} +//! ``` +//! +//! Now, instead of refering to `Pin`, we can refer to instances of the +//! `AnyPin` type class. +//! +//! ```ignore +//! fn example(pin: P) { ... } +//! ``` +//! +//! Unfortunately, while this is more ergonomic, it is not very useful. As +//! authors of the code, we know that `AnyPin` is only implemented for `Pin` +//! types. But the compiler doesn't know that. Traits in Rust are open, so the +//! compiler must consider that `AnyPin` could be implemented for other types. +//! +//! As a consequence, the compiler knows very little about the type `P` in the +//! function above. In fact, because the `AnyPin` trait is completely empty, the +//! compiler knows *absolutely nothing* about the type `P`. +//! +//! Is there a way to make the `AnyPin` trait more useful? We can see from the +//! current implementation that we are throwing away information. +//! +//! ```ignore +//! impl AnyPin for Pin {} +//! ``` +//! +//! The implementation of `AnyPin` is identical for every `Pin`, regardless of +//! the type parameters `I` and `M`, which erases that information. Instead, we +//! could choose to save that information in the form of associated types. +//! +//! Let's redesign the `AnyPin` trait to record the `PinId` and `PinMode`. +//! +//! ```ignore +//! trait AnyPin { +//! type Id: PinId; +//! type Mode: PinMode; +//! } +//! +//! impl AnyPin for Pin { +//! type Id = I; +//! type Mode = M; +//! } +//! ``` +//! +//! This is better. When `P` implements `AnyPin`, we can at least recover the +//! corresponding `PinId` and `PinMode` types. However, `AnyPin` still doesn't +//! include any trait methods nor any super traits, so the compiler won't allow +//! us to do anything useful with an instances of `P`. +//! +//! We need some way to tell the compiler that when `P` implements `AnyPin`, +//! it is equivalent to saying `P` is exactly `Pin`. +//! Essentially, we want to take a generic type parameter `P` and treat it as if +//! it were an instance of a specific `Pin` type. +//! +//! We can start by defining a trait alias to recover the specific `Pin` type. +//! +//! ```ignore +//! type SpecificPin

= Pin<

::Id,

::Mode>; +//! ``` +//! +//! With this new definition, we can rephrase our statement above. We need some +//! way to tell the compiler that when `P` implements `AnyPin`, +//! `P == SpecificPin

`. There's no way to do that exactly, but we can come +//! close with some useful trait bounds: [`From`], [`Into`], [`AsRef`] and +//! [`AsMut`]. +//! +//! ```ignore +//! trait AnyPin +//! where +//! Self: From>, +//! Self: Into>, +//! Self: AsRef>, +//! Self: AsMut>, +//! { +//! type Id: PinId; +//! type Mode: PinMode; +//! } +//! ``` +//! +//! Now we've given the compiler some useful information. When a type implements +//! `AnyPin`, it can be converted from and into instances of `Pin`. And +//! references to types that implement `AnyPin` can be converted into references +//! to `Pin`s. +//! +//! ```ignore +//! fn example(mut any_pin: P) { +//! // None of the type annotations here are necessary +//! // Everything can be inferred +//! // Remember that SpecificPin

is Pin +//! let pin_mut: &mut SpecificPin

= any_pin.as_mut(); +//! let pin_ref: &SpecificPin

= any_pin.as_ref(); +//! let pin: SpecificPin

= any_pin.into(); +//! } +//! ``` +//! +//! Finally, to simplify this pattern, we can gather all of the super trait +//! bounds into a single, reusable trait. +//! +//! ```ignore +//! trait Is +//! where +//! Self: From>, +//! Self: Into>, +//! Self: AsRef>, +//! Self: AsMut>, +//! { +//! type Type; +//! } +//! +//! type IsType = ::Type; +//! +//! impl + AsMut> Is for T { +//! type Type = T; +//! } +//! ``` +//! +//! And we can rewrite our `AnyPin` trait as +//! +//! ```ignore +//! trait AnyPin: Is> { +//! type Id: PinId; +//! type Mode: PinMode; +//! } +//! ``` +//! +//! ## Using an `AnyKind` trait +//! +//! If a type takes multiple type parameters, storing it within a container +//! requires repeating all of the corresponding type parameters. For instance, +//! imagine a container that stores two completely generic `Pin` types. +//! +//! ```ignore +//! struct TwoPins +//! where +//! I1: PinId, +//! I2: PinId, +//! M1: PinMode, +//! M2: PinMode, +//! { +//! pin1: Pin, +//! pin2: Pin, +//! } +//! ``` +//! +//! This struct has already ballooned to four type parameters, without even +//! doing much useful work. Given its heavy use of type parameters, this +//! limitation can make type-level programming tedious, cumbersome and +//! error-prone. +//! +//! Instead, we can use the `AnyKind` trait pattern to encapsulate each `Pin` +//! with a single type parameter. +//! +//! ```ignore +//! struct TwoPins +//! where +//! P1: AnyPin, +//! P2: AnyPin, +//! { +//! pin1: P1, +//! pin2: P2, +//! } +//! ``` +//! +//! The result is far more readable and generally more comprehensible. Moreover, +//! although we no longer have direct access to the `PinId` and `PinMode` type +//! parameters, we haven't actually lost any expressive power. +//! +//! In the first version of `TwoPins`, suppose we wanted to implement a method +//! for pins in `FloatingInput` mode while simultaneously restricting the +//! possible `PinId`s based on some type class. The result might look like +//! this. +//! +//! ```ignore +//! impl for TwoPins +//! where +//! I1: PinId + Class, +//! I2: PinId + Class, +//! { +//! fn method(&self) { +//! // ... +//! } +//! } +//! ``` +//! +//! The same method could be expressed with the `AnyPin` approach like so +//! +//! ```ignore +//! impl for TwoPins +//! where +//! P1: AnyPin, +//! P2: AnyPin, +//! P1::Id: Class, +//! P2::Id: Class, +//! { +//! fn method(&self) { +//! // ... +//! } +//! } +//! ``` +//! +//! This example demonstrates the simultaneous readability and expressive power +//! of the `AnyKind` pattern. +//! +//! However, remember that when working with a type `P` that implements +//! `AnyPin`, the compiler can only use what it knows about the `AnyPin` trait. +//! But all of the functionality for GPIO pins is defined on the `Pin` type. To +//! make use of a generic type `P` implementing `AnyPin`, you must first convert +//! it to its corresponding `SpecificPin` using [`Into`], [`AsRef`] or +//! [`AsMut`]. And, in some instances, you may also need to convert back to the +//! type `P`. +//! +//! Suppose you wanted to store a completely generic `Pin` within a struct. +//! +//! ```ignore +//! pub struct Example { +//! pin: P, +//! } +//! ``` +//! +//! Next, suppose you want to create a method that would take the `Pin` out of +//! the struct, perform some operations in different `PinMode`s, and put it back +//! into the struct before returning. The `elided` method below shows such an +//! example. However, it can be a bit tricky to follow all of the type +//! conversions here. For clarity, the `expanded` method shows the same behavior +//! with each transformation given its proper type annotation. +//! +//! ```ignore +//! impl Example

{ +//! pub fn elided(mut self) -> Self { +//! let pin = self.pin.into(); +//! let mut pin = pin.into_push_pull_output(); +//! pin.set_high().ok(); +//! let pin = pin.into_floating_input(); +//! let _bit = pin.is_low().unwrap(); +//! let pin = pin.into_mode(); +//! self.pin = pin.into(); +//! self +//! } +//! pub fn expanded(mut self) -> Self { +//! let pin: SpecificPin

= self.pin.into(); +//! let mut pin: Pin = pin.into_push_pull_output(); +//! pin.set_high().ok(); +//! let pin: Pin = pin.into_floating_input(); +//! let _bit = pin.is_low().unwrap(); +//! let pin: SpecificPin

= pin.into_mode::(); +//! self.pin = pin.into(); +//! self +//! } +//! } +//! ``` +//! +//! Notice that it is not enough to simply put back the correct `SpecificPin`. +//! Even though the `SpecificPin` implements +//! `AnyPin` the compiler doesn't understand that +//! `SpecificPin

== P` for all `P`. As far as the compiler is concerned, +//! there could be several different types that implement +//! `AnyPin`. Instead, the compiler requires that +//! you put back an instance of `P` exactly. The final use of [`Into`] is key +//! here. It transforms the `SpecificPin` back into `P` itself. + +mod private { + /// Super trait used to mark traits with an exhaustive set of + /// implementations + pub trait Sealed {} +} + +pub(crate) use private::Sealed; + +/// Type-level version of the [None] variant +#[derive(Default)] +pub struct NoneT; +impl Sealed for NoneT {} + +/// Marker trait for type identity +/// +/// This trait is used as part of the [`AnyKind`] trait pattern. It represents +/// the concept of type identity, because all implementors have +/// `::Type == Self`. When used as a trait bound with a specific +/// type, it guarantees that the corresponding type parameter is exactly the +/// specific type. Stated differently, it guarantees that `T == Specific` in +/// the following example. +/// +/// ```ignore +/// where T: Is +/// ``` +/// +/// Moreover, the super traits guarantee that any instance of or reference to a +/// type `T` can be converted into the `Specific` type. +/// +/// ```ignore +/// fn example(mut any: T) +/// where +/// T: Is, +/// { +/// let specific_mut: &mut Specific = any.as_mut(); +/// let specific_ref: &Specific = any.as_ref(); +/// let specific: Specific = any.into(); +/// } +/// ``` +/// +/// [`AnyKind`]: #anykind-trait-pattern +pub trait Is +where + Self: Sealed, + Self: From>, + Self: Into>, + Self: AsRef>, + Self: AsMut>, +{ + #[allow(missing_docs)] + type Type; +} + +/// Type alias for [`Is::Type`] +pub type IsType = ::Type; + +impl Is for T +where + T: Sealed + AsRef + AsMut, +{ + type Type = T; +}