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:
Andrea Nall 2021-05-23 21:08:42 -05:00 committed by 9names
parent 71a7057b76
commit e3be4f8025
17 changed files with 2706 additions and 357 deletions

View file

@ -1,4 +1,5 @@
[workspace]
members = [
"rp2040-hal",
"boards/feather_rp2040"
]

View 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"]

View 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 },
);

View file

@ -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" }

View file

@ -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();

View file

@ -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() {

View file

@ -1,5 +1,3 @@
#[macro_use]
macro_rules! int_division {
($name:ident, $div:ident, $u:ty) => {
impl IntegerDivision for $name {

View file

@ -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"),
]
);

View 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()
}
}

View 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
View 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
View 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));

View file

@ -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;

View file

@ -1,3 +1 @@
//! Prelude
pub use crate::gpio::GpioExt;
pub use crate::sio::Sio;

View file

@ -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.

View file

@ -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
View 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;
}