mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2025-01-11 13:01:30 +11:00
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.
This commit is contained in:
parent
71a7057b76
commit
e3be4f8025
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"rp2040-hal",
|
||||
"boards/feather_rp2040"
|
||||
]
|
||||
|
|
19
boards/feather_rp2040/Cargo.toml
Normal file
19
boards/feather_rp2040/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "feather_rp2040"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrea Nall <anall@andreanal.com>"]
|
||||
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"]
|
56
boards/feather_rp2040/src/lib.rs
Normal file
56
boards/feather_rp2040/src/lib.rs
Normal file
|
@ -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 },
|
||||
);
|
|
@ -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" }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#[macro_use]
|
||||
|
||||
macro_rules! int_division {
|
||||
($name:ident, $div:ident, $u:ty) => {
|
||||
impl IntegerDivision for $name {
|
||||
|
|
|
@ -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<PADS, SIO> {
|
||||
/// 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<pac::$PADSX, sio::$siotoken> 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<Unknown>,
|
||||
)+
|
||||
}
|
||||
|
||||
type PacDriveStrength = pac::$padsx::gpio::DRIVE_A;
|
||||
|
||||
$(
|
||||
#[doc = "HAL object for GPIO pin "]
|
||||
#[doc = $is]
|
||||
pub struct $PXi<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<MODE> {
|
||||
// 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<Output> {
|
||||
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<Input> {
|
||||
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<Output> {
|
||||
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<Output> {
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(!self.is_set_high()?)
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
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<bool, Self::Error> {
|
||||
Ok(!self.is_high()?)
|
||||
}
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
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<Output> {
|
||||
#[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<Input> {
|
||||
#[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"),
|
||||
]
|
||||
);
|
527
rp2040-hal/src/gpio/dynpin.rs
Normal file
527
rp2040-hal/src/gpio/dynpin.rs
Normal file
|
@ -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, FloatingInput> = 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 [<DYN_FUNCTION_ $Func:upper>]: 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<bool, Error> {
|
||||
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<bool, Error> {
|
||||
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<bool, Error> {
|
||||
Ok(self._read()? == false)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_high(&self) -> Result<bool, Error> {
|
||||
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<bool, Error> {
|
||||
Ok(self._read_out()? == false)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::bool_comparison)] // more explicit this way
|
||||
fn _is_set_high(&self) -> Result<bool, Error> {
|
||||
Ok(self._read_out()? == true)
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Convert between Pin and DynPin
|
||||
//==============================================================================
|
||||
|
||||
impl<I, M> From<Pin<I, M>> for DynPin
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||
/// [`DynPin`]
|
||||
#[inline]
|
||||
fn from(_pin: Pin<I, M>) -> Self {
|
||||
// The `Pin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `DynPin`
|
||||
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, M> TryFrom<DynPin> for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
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<Self, Error> {
|
||||
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<bool, Self::Error> {
|
||||
self._is_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
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<bool, Self::Error> {
|
||||
self._is_set_high()
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
self._is_set_low()
|
||||
}
|
||||
}
|
34
rp2040-hal/src/gpio/mod.rs
Normal file
34
rp2040-hal/src/gpio/mod.rs
Normal file
|
@ -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,
|
||||
}
|
965
rp2040-hal/src/gpio/pin.rs
Normal file
965
rp2040-hal/src/gpio/pin.rs
Normal file
|
@ -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::<FloatingInput>();
|
||||
//! // Specify the target type and use `From`/`Into`
|
||||
//! let gpio12: Pin<Gpio12, FloatingInput> = 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<I: PinId>: 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<C: DisabledConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: DisabledConfig> Sealed for Disabled<C> {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for floating disabled mode
|
||||
pub type FloatingDisabled = Disabled<Floating>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for pull-down disabled mode
|
||||
pub type PullDownDisabled = Disabled<PullDown>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for pull-up disabled mode
|
||||
pub type PullUpDisabled = Disabled<PullUp>;
|
||||
|
||||
impl<I: PinId, C: DisabledConfig> ValidPinMode<I> for Disabled<C> {}
|
||||
|
||||
//==============================================================================
|
||||
// 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<C: InputConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: InputConfig> Sealed for Input<C> {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for floating input mode
|
||||
pub type FloatingInput = Input<Floating>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for pull-down input mode
|
||||
pub type PullDownInput = Input<PullDown>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for pull-up input mode
|
||||
pub type PullUpInput = Input<PullUp>;
|
||||
|
||||
impl<I: PinId, C: InputConfig> ValidPinMode<I> for Input<C> {}
|
||||
|
||||
//==============================================================================
|
||||
// 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<C: OutputConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: OutputConfig> Sealed for Output<C> {}
|
||||
|
||||
/// Type-level variant of [`PinMode`] for push-pull output mode
|
||||
pub type PushPullOutput = Output<PushPull>;
|
||||
|
||||
/// Type-level variant of [`PinMode`] for readable push-pull output mode
|
||||
type ReadableOutput = Output<Readable>;
|
||||
|
||||
impl<I: PinId, C: OutputConfig> ValidPinMode<I> for Output<C> {}
|
||||
|
||||
//
|
||||
|
||||
/// Type-level variant of [`PinMode`] for alternate peripheral functions
|
||||
///
|
||||
/// Type `C` is an [`FunctionConfig`]
|
||||
pub struct Function<C: FunctionConfig> {
|
||||
cfg: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: FunctionConfig> Sealed for Function<C> {}
|
||||
|
||||
/// 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<$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<C: DisabledConfig> PinMode for Disabled<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Disabled(C::DYN);
|
||||
}
|
||||
|
||||
impl<C: InputConfig> PinMode for Input<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
|
||||
}
|
||||
|
||||
impl<C: OutputConfig> PinMode for Output<C> {
|
||||
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
|
||||
}
|
||||
|
||||
impl<C: FunctionConfig> PinMode for Function<C> {
|
||||
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<PinId>`
|
||||
///
|
||||
/// See the [`OptionalKind`] documentation for more details on the pattern.
|
||||
///
|
||||
/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
|
||||
pub trait OptionalPinId {}
|
||||
|
||||
impl OptionalPinId for NoneT {}
|
||||
|
||||
impl<I: PinId> 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<I: PinId> 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<I: PinId> {
|
||||
id: PhantomData<I>,
|
||||
}
|
||||
|
||||
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
|
||||
// each pin is a singleton, so this implementation is safe.
|
||||
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
|
||||
#[inline]
|
||||
fn id(&self) -> DynPinId {
|
||||
I::DYN
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId> Registers<I> {
|
||||
/// 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<M: PinMode + ValidPinMode<I>>(&mut self) {
|
||||
RegisterInterface::do_change_mode(self, M::DYN);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Pin
|
||||
//==============================================================================
|
||||
|
||||
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
|
||||
pub struct Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
regs: Registers<I>,
|
||||
mode: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<I, M> Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
/// 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<I, M> {
|
||||
Pin {
|
||||
regs: Registers::new(),
|
||||
mode: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`PinMode`]
|
||||
#[inline]
|
||||
pub fn into_mode<N: PinMode + ValidPinMode<I>>(mut self) -> Pin<I, N> {
|
||||
if N::DYN != M::DYN {
|
||||
self.regs.change_mode::<N>();
|
||||
}
|
||||
// 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<I, FloatingDisabled> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Disable the pin and set it to pull down
|
||||
#[inline]
|
||||
pub fn into_pull_down_disabled(self) -> Pin<I, PullDownDisabled> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Disable the pin and set it to pull up
|
||||
#[inline]
|
||||
pub fn into_pull_up_disabled(self) -> Pin<I, PullUpDisabled> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a floating input
|
||||
#[inline]
|
||||
pub fn into_floating_input(self) -> Pin<I, FloatingInput> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled down input
|
||||
#[inline]
|
||||
pub fn into_pull_down_input(self) -> Pin<I, PullDownInput> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a pulled up input
|
||||
#[inline]
|
||||
pub fn into_pull_up_input(self) -> Pin<I, PullUpInput> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a push-pull output
|
||||
#[inline]
|
||||
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
/// Configure the pin to operate as a readable push pull output
|
||||
#[inline]
|
||||
pub fn into_readable_output(self) -> Pin<I, ReadableOutput> {
|
||||
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<Type = SpecificPin<Self>>,
|
||||
<Self as AnyPin>::Mode: ValidPinMode<<Self as AnyPin>::Id>,
|
||||
{
|
||||
/// [`PinId`] of the corresponding [`Pin`]
|
||||
type Id: PinId;
|
||||
/// [`PinMode`] of the corresponding [`Pin`]
|
||||
type Mode: PinMode;
|
||||
}
|
||||
|
||||
impl<I, M> Sealed for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<I, M> AnyPin for Pin<I, M>
|
||||
where
|
||||
I: PinId,
|
||||
M: PinMode + ValidPinMode<I>,
|
||||
{
|
||||
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<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||
|
||||
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &P {
|
||||
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||
// 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<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut P {
|
||||
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||
// 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<P::Id> en for repr(Rust).
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Optional pins
|
||||
//==============================================================================
|
||||
|
||||
/// Type-level equivalent of `Option<PinId>`
|
||||
///
|
||||
/// 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<P: AnyPin> 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<P: AnyPin> SomePin for P {}
|
||||
|
||||
//==============================================================================
|
||||
// Embedded HAL traits
|
||||
//==============================================================================
|
||||
|
||||
impl<I, C> OutputPin for Pin<I, Output<C>>
|
||||
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<I> InputPin for Pin<I, ReadableOutput>
|
||||
where
|
||||
I: PinId,
|
||||
{
|
||||
type Error = Infallible;
|
||||
#[inline]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> InputPin for Pin<I, Input<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: InputConfig,
|
||||
{
|
||||
type Error = Infallible;
|
||||
#[inline]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_high())
|
||||
}
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_low())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> ToggleableOutputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig,
|
||||
{
|
||||
type Error = Infallible;
|
||||
#[inline]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self._toggle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
||||
where
|
||||
I: PinId,
|
||||
C: OutputConfig,
|
||||
{
|
||||
#[inline]
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self._is_set_high())
|
||||
}
|
||||
#[inline]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
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::[<SioGpio $Group>];
|
||||
use pac::{[<IO_ $Group:upper>],[<PADS_ $Group:upper>]};
|
||||
|
||||
/// 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<super::$PinFunc> {})+ )*
|
||||
)+
|
||||
|
||||
/// Collection of all the individual [`Pin`]s
|
||||
pub struct Pins {
|
||||
_io: [<IO_ $Group:upper>],
|
||||
_pads: [<PADS_ $Group:upper>],
|
||||
_sio: [<SioGpio $Group>],
|
||||
$(
|
||||
#[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 : [<IO_ $Group:upper>], pads: [<PADS_ $Group:upper>], sio: [<SioGpio $Group>], 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<I: PinId + BankPinId> super::ValidPinMode<I> for super::Function<super::$Func> {} )+
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
)+
|
||||
)?
|
||||
}
|
||||
};
|
||||
}
|
353
rp2040-hal/src/gpio/reg.rs
Normal file
353
rp2040-hal/src/gpio/reg.rs
Normal file
|
@ -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<DynPinMode> 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()).[<gpio_qspi_ $suffix>] }.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()).[<gpio_qspi_ $suffix>] }.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()).[<gpio_qspi_ $suffix>] }.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()).[<gpio_qspi_ $suffix>] }.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()).[<gpio_qspi $suffix>] };
|
||||
let pads = unsafe { &(*pac::PADS_QSPI::ptr()).[<gpio_qspi_ $suffix>] };
|
||||
|
||||
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));
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
//! Prelude
|
||||
pub use crate::gpio::GpioExt;
|
||||
pub use crate::sio::Sio;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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: () },
|
||||
}
|
||||
|
|
697
rp2040-hal/src/typelevel.rs
Normal file
697
rp2040-hal/src/typelevel.rs
Normal file
|
@ -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<A, B>
|
||||
//! where
|
||||
//! A: EnumOne,
|
||||
//! B: EnumTwo,
|
||||
//! {
|
||||
//! a: PhantomData<A>,
|
||||
//! b: PhantomData<B>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we could create an instance of this container with specific types.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! type X = Container<VariantX, VariantY>;
|
||||
//! ```
|
||||
//!
|
||||
//! 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<Enum>` 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<B: Not> {
|
||||
//! not: PhantomData<B::Result>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Alternatively, we could redefine the trait and declar a corresponding type
|
||||
//! alias as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait NotFunction: Bool {
|
||||
//! type Result: Bool;
|
||||
//! }
|
||||
//!
|
||||
//! type Not<B> = <B as NotFunction>::Result;
|
||||
//! ```
|
||||
//!
|
||||
//! Doing so would allow us to us reframe the last example as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! struct Container<B: NotFunction> {
|
||||
//! not: PhantomData<Not<B>>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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<C: Class> 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<C: OptionalClass> {
|
||||
//! class: PhantomData<C>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we can restrict some of its methods to only operate on instances with a
|
||||
//! valid `Class`.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<C: Class> Container<C> {
|
||||
//! 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<C: Class> 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<I: PinId, M: PinMode> {
|
||||
//! // ...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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<I, M>`.
|
||||
//! 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<I: PinId, M: PinMode> AnyPin for Pin<I, M> {}
|
||||
//! ```
|
||||
//!
|
||||
//! Now, instead of refering to `Pin<I, M>`, we can refer to instances of the
|
||||
//! `AnyPin` type class.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! fn example<P: AnyPin>(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<I: PinId, M: PinMode> AnyPin for Pin<I, M> {}
|
||||
//! ```
|
||||
//!
|
||||
//! 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<I: PinId, M: PinMode> AnyPin for Pin<I, M> {
|
||||
//! 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<P::Id, P::Mode>`.
|
||||
//! 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<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::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<P>`. 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<SpecificPin<Self>>,
|
||||
//! Self: Into<SpecificPin<Self>>,
|
||||
//! Self: AsRef<SpecificPin<Self>>,
|
||||
//! Self: AsMut<SpecificPin<Self>>,
|
||||
//! {
|
||||
//! 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<P: AnyPin>(mut any_pin: P) {
|
||||
//! // None of the type annotations here are necessary
|
||||
//! // Everything can be inferred
|
||||
//! // Remember that SpecificPin<P> is Pin<P::Id, P::Mode>
|
||||
//! let pin_mut: &mut SpecificPin<P> = any_pin.as_mut();
|
||||
//! let pin_ref: &SpecificPin<P> = any_pin.as_ref();
|
||||
//! let pin: SpecificPin<P> = 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<IsType<Self>>,
|
||||
//! Self: Into<IsType<Self>>,
|
||||
//! Self: AsRef<IsType<Self>>,
|
||||
//! Self: AsMut<IsType<Self>>,
|
||||
//! {
|
||||
//! type Type;
|
||||
//! }
|
||||
//!
|
||||
//! type IsType<T> = <T as Is>::Type;
|
||||
//!
|
||||
//! impl<T: AsRef<T> + AsMut<T>> Is for T {
|
||||
//! type Type = T;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And we can rewrite our `AnyPin` trait as
|
||||
//!
|
||||
//! ```ignore
|
||||
//! trait AnyPin: Is<Type = SpecificPin<Self>> {
|
||||
//! 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<I1, I2, M1, M2>
|
||||
//! where
|
||||
//! I1: PinId,
|
||||
//! I2: PinId,
|
||||
//! M1: PinMode,
|
||||
//! M2: PinMode,
|
||||
//! {
|
||||
//! pin1: Pin<I1, M1>,
|
||||
//! pin2: Pin<I2, M2>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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<P1, P2>
|
||||
//! 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<I1, I2> for TwoPins<I1, I2, FloatingInput, FloatingInput>
|
||||
//! where
|
||||
//! I1: PinId + Class,
|
||||
//! I2: PinId + Class,
|
||||
//! {
|
||||
//! fn method(&self) {
|
||||
//! // ...
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The same method could be expressed with the `AnyPin` approach like so
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<P1, P2> for TwoPins<P1, P2>
|
||||
//! where
|
||||
//! P1: AnyPin<Mode = FloatingInput>,
|
||||
//! P2: AnyPin<Mode = FloatingInput>,
|
||||
//! 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<P: AnyPin> {
|
||||
//! 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<P: AnyPin> Example<P> {
|
||||
//! 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<P> = self.pin.into();
|
||||
//! let mut pin: Pin<P::Id, PushPullOutput> = pin.into_push_pull_output();
|
||||
//! pin.set_high().ok();
|
||||
//! let pin: Pin<P::Id, FloatingInput> = pin.into_floating_input();
|
||||
//! let _bit = pin.is_low().unwrap();
|
||||
//! let pin: SpecificPin<P> = pin.into_mode::<P::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<Id = P::Id, Mode = P::Mode>` the compiler doesn't understand that
|
||||
//! `SpecificPin<P> == P` for all `P`. As far as the compiler is concerned,
|
||||
//! there could be several different types that implement
|
||||
//! `AnyPin<Id = P::Id, Mode = P::Mode>`. 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
|
||||
/// `<Self as Is>::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<Type = Specific>
|
||||
/// ```
|
||||
///
|
||||
/// 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<T>(mut any: T)
|
||||
/// where
|
||||
/// T: Is<Type = Specific>,
|
||||
/// {
|
||||
/// 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<IsType<Self>>,
|
||||
Self: Into<IsType<Self>>,
|
||||
Self: AsRef<IsType<Self>>,
|
||||
Self: AsMut<IsType<Self>>,
|
||||
{
|
||||
#[allow(missing_docs)]
|
||||
type Type;
|
||||
}
|
||||
|
||||
/// Type alias for [`Is::Type`]
|
||||
pub type IsType<T> = <T as Is>::Type;
|
||||
|
||||
impl<T> Is for T
|
||||
where
|
||||
T: Sealed + AsRef<T> + AsMut<T>,
|
||||
{
|
||||
type Type = T;
|
||||
}
|
Loading…
Reference in a new issue