From 02b77020b938df2396d7345987cae8be10d8f69e Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sat, 4 Mar 2023 06:17:21 +0000 Subject: [PATCH 01/26] Leverage typelevel implementation in i2c, pwm (and a little bit in uart). This notably makes the documentation more readable. --- rp2040-hal/examples/uart.rs | 4 +- rp2040-hal/examples/uart_dma.rs | 4 +- rp2040-hal/src/gpio/pin.rs | 62 +++++------- rp2040-hal/src/i2c.rs | 33 +++---- rp2040-hal/src/i2c/controller.rs | 22 ++--- rp2040-hal/src/i2c/peripheral.rs | 31 +++--- rp2040-hal/src/pwm/mod.rs | 157 +++++++++++++++++++----------- rp2040-hal/src/typelevel.rs | 22 +++-- rp2040-hal/src/uart/mod.rs | 4 +- rp2040-hal/src/uart/peripheral.rs | 9 +- rp2040-hal/src/uart/pins.rs | 156 +++++++++++++---------------- 11 files changed, 251 insertions(+), 253 deletions(-) diff --git a/rp2040-hal/examples/uart.rs b/rp2040-hal/examples/uart.rs index febc21ca1..071df17cb 100644 --- a/rp2040-hal/examples/uart.rs +++ b/rp2040-hal/examples/uart.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode::(), + pins.gpio0.into_mode(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode::(), + pins.gpio1.into_mode(), ); let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/uart_dma.rs b/rp2040-hal/examples/uart_dma.rs index 9c9235db8..84d5900fb 100644 --- a/rp2040-hal/examples/uart_dma.rs +++ b/rp2040-hal/examples/uart_dma.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode::(), + pins.gpio0.into_mode(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode::(), + pins.gpio1.into_mode(), ); let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index 0c275cf6d..ce00d9029 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -108,10 +108,8 @@ use eh1_0_alpha::digital as eh1; pub use embedded_hal::digital::v2::PinState; use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; -use core::mem::transmute; - /// Type-level marker for tracking which pin modes are valid for which pins -pub trait ValidPinMode: Sealed {} +pub trait ValidPinMode: Sealed + PinMode {} //============================================================================== // Disabled configurations @@ -437,7 +435,7 @@ impl Registers { /// Provide a type-level equivalent for the /// [`RegisterInterface::change_mode`] method. #[inline] - fn change_mode>(&mut self) { + fn change_mode>(&mut self) { RegisterInterface::do_change_mode(self, M::DYN); } } @@ -450,7 +448,7 @@ impl Registers { pub struct Pin where I: PinId, - M: PinMode + ValidPinMode, + M: ValidPinMode, { regs: Registers, mode: PhantomData, @@ -459,7 +457,7 @@ where impl Pin where I: PinId, - M: PinMode + ValidPinMode, + M: ValidPinMode, { /// Create a new [`Pin`] /// @@ -483,7 +481,7 @@ where /// /// ```no_run /// # use rp2040_hal::gpio::{Pin, PinId, PinMode, ValidPinMode}; - /// # fn get_id> (pin: Pin) -> u8 { + /// # fn get_id> (pin: Pin) -> u8 { /// pin.id().num /// # } /// ```` @@ -494,7 +492,7 @@ where /// Convert the pin to the requested [`PinMode`] #[inline] - pub fn into_mode>(mut self) -> Pin { + pub fn into_mode>(mut self) -> Pin { if N::DYN != M::DYN { self.regs.change_mode::(); } @@ -733,14 +731,14 @@ where impl Sealed for Pin where I: PinId, - M: PinMode + ValidPinMode, + M: ValidPinMode, { } impl AnyPin for Pin where I: PinId, - M: PinMode + ValidPinMode, + M: ValidPinMode, { type Id = I; type Mode = M; @@ -754,28 +752,6 @@ where /// [`AnyKind`]: crate::typelevel#anykind-trait-pattern pub type SpecificPin

= Pin<

::Id,

::Mode>; -impl AsRef

for SpecificPin

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

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

for SpecificPin

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

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

for SpecificPin

{ /// See the [`OptionalKind`] documentation for more details on the pattern. /// /// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPin: Sealed { +pub trait OptionalPin: Sealed { #[allow(missing_docs)] type Id: OptionalPinId; + #[allow(missing_docs)] + const IS_NONE: bool; } -impl OptionalPin for NoneT { +impl OptionalPin for NoneT { type Id = NoneT; + const IS_NONE: bool = true; } -impl OptionalPin for P { +impl OptionalPin for P { type Id = P::Id; + /// Value-level translation of the Type-level equivalent of [`Option::is_none`]. + const IS_NONE: bool = false; } /// Type-level equivalent of `Some(PinId)` @@ -803,8 +784,13 @@ impl OptionalPin for P { /// See the [`OptionalKind`] documentation for more details on the pattern. /// /// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePin: AnyPin + Sealed {} -impl SomePin for P {} +pub trait SomePin: OptionalPin + Sealed { + /// Value-level translation of the Type-level equivalent of [`Option::is_some`]. + const IS_SOME: bool; +} +impl> SomePin for P { + const IS_SOME: bool = !P::IS_NONE; +} //============================================================================== // Embedded HAL traits @@ -1048,7 +1034,7 @@ macro_rules! gpio { } } - $( impl super::ValidPinMode for super::[] {} )+ + $( impl super::ValidPinMode for super::[] {} )+ } } } diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index 54ce88721..76b32e4b2 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -47,11 +47,11 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ gpio::pin::bank0::{ - BankPinId, Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, - Gpio18, Gpio19, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, - Gpio28, Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Gpio9, + Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, + Gpio19, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, + Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Gpio9, }, - gpio::pin::{FunctionI2C, Pin, PinId}, + gpio::pin::{AnyPin, FunctionI2C}, resets::SubsystemReset, typelevel::Sealed, }; @@ -223,19 +223,13 @@ fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 } -impl I2C, Pin), Mode> +impl I2C where Block: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, - Mode: I2CMode, { /// Releases the I2C peripheral and associated pins #[allow(clippy::type_complexity)] - pub fn free( - self, - resets: &mut RESETS, - ) -> (Block, (Pin, Pin)) { + pub fn free(self, resets: &mut RESETS) -> (Block, (Sda, Scl)) { self.i2c.reset_bring_down(resets); (self.i2c, self.pins) @@ -283,20 +277,23 @@ impl, PINS, Mode> I2C { macro_rules! hal { ($($I2CX:ident: ($i2cX:ident),)+) => { $( - impl - I2C<$I2CX, (Pin, Pin)> { + impl I2C<$I2CX, (Sda, Scl)> + where + Sda: AnyPin, + Scl: AnyPin, + { /// Configures the I2C peripheral to work in master mode pub fn $i2cX( i2c: $I2CX, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, freq: F, resets: &mut RESETS, system_clock: SystemF) -> Self where F: Into, - Sda: SdaPin<$I2CX>, - Scl: SclPin<$I2CX>, + Sda::Id: SdaPin<$I2CX>, + Scl::Id: SclPin<$I2CX>, SystemF: Into, { Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into()) diff --git a/rp2040-hal/src/i2c/controller.rs b/rp2040-hal/src/i2c/controller.rs index afee8c24d..f61f7b0f8 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,8 +1,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::pin::bank0::BankPinId, - gpio::pin::{FunctionI2C, Pin, PinId}, + gpio::{pin::FunctionI2C, AnyPin}, resets::SubsystemReset, }; use fugit::HertzU32; @@ -14,22 +13,23 @@ use eh1_0_alpha::i2c as eh1; use super::{i2c_reserved_addr, Controller, Error, SclPin, SdaPin, I2C}; -impl, Sda: PinId + BankPinId, Scl: PinId + BankPinId> - I2C, Pin), Controller> +impl I2C +where + T: SubsystemReset + Deref, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: SdaPin, + Scl::Id: SclPin, { /// Configures the I2C peripheral to work in controller mode pub fn new_controller( i2c: T, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, freq: HertzU32, resets: &mut RESETS, system_clock: HertzU32, - ) -> Self - where - Sda: SdaPin, - Scl: SclPin, - { + ) -> Self { let freq = freq.to_Hz(); assert!(freq <= 1_000_000); assert!(freq > 0); diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index cd633f47b..ff3011824 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -1,8 +1,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::pin::bank0::BankPinId, - gpio::pin::{FunctionI2C, Pin, PinId}, + gpio::{pin::FunctionI2C, AnyPin}, resets::SubsystemReset, }; use pac::{i2c0::RegisterBlock as I2CBlock, RESETS}; @@ -38,11 +37,13 @@ pub struct I2CPeripheralEventIterator { state: State, } -impl I2C, Pin), Peripheral> +impl I2C where T: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: SdaPin, + Scl::Id: SclPin, { /// Configures the I2C peripheral to work in peripheral mode /// @@ -50,15 +51,11 @@ where #[allow(clippy::type_complexity)] pub fn new_peripheral_event_iterator( i2c: T, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, resets: &mut RESETS, addr: u16, - ) -> I2CPeripheralEventIterator, Pin)> - where - Sda: SdaPin, - Scl: SclPin, - { + ) -> I2CPeripheralEventIterator { i2c.reset_bring_down(resets); i2c.reset_bring_up(resets); @@ -184,19 +181,13 @@ impl, PINS> Iterator for I2CPeripheralEventIterator< } } -impl - I2CPeripheralEventIterator, Pin)> +impl I2CPeripheralEventIterator where Block: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, { /// Releases the I2C peripheral and associated pins #[allow(clippy::type_complexity)] - pub fn free( - self, - resets: &mut RESETS, - ) -> (Block, (Pin, Pin)) { + pub fn free(self, resets: &mut RESETS) -> (Block, (Sda, Scl)) { self.i2c.free(resets) } } diff --git a/rp2040-hal/src/pwm/mod.rs b/rp2040-hal/src/pwm/mod.rs index 5c2dafee6..6faf294ca 100644 --- a/rp2040-hal/src/pwm/mod.rs +++ b/rp2040-hal/src/pwm/mod.rs @@ -79,9 +79,9 @@ use core::marker::PhantomData; use crate::{ - gpio::{bank0::*, FunctionPwm, Pin, PinId, PinMode, ValidPinMode}, + gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidPinMode}, resets::SubsystemReset, - typelevel::Sealed, + typelevel::{Is, Sealed}, }; use embedded_hal::PwmPin; use pac::PWM; @@ -130,7 +130,7 @@ pub struct CountRisingEdge; pub struct CountFallingEdge; /// Type-level marker for tracking which slice modes are valid for which slices -pub trait ValidSliceMode: Sealed {} +pub trait ValidSliceMode: Sealed + SliceMode {} /// Type-level marker for tracking which slice modes are valid for which slices pub trait ValidSliceInputMode: Sealed + ValidSliceMode {} @@ -192,6 +192,54 @@ macro_rules! slice_id { }; } +//============================================================================== +// AnySlice +//============================================================================== + +/// Type class for [`Slice`] types +/// +/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for +/// [`Slice`] 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 AnySlice +where + Self: Sealed, + Self: Is>, + ::Mode: ValidSliceMode<::Id>, +{ + /// [`SliceId`] of the corresponding [`Slice`] + type Id: SliceId; + /// [`SliceMode`] of the corresponding [`Slice`] + type Mode: SliceMode; +} + +impl Sealed for Slice +where + S: SliceId, + M: ValidSliceMode, +{ +} + +impl AnySlice for Slice +where + S: SliceId, + M: ValidSliceMode, +{ + type Id = S; + type Mode = M; +} + +/// Type alias to recover the specific [`Slice`] type from an implementation of +/// [`AnySlice`] +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +type SpecificSlice = Slice<::Id, ::Mode>; + //============================================================================== // Registers //============================================================================== @@ -228,7 +276,7 @@ impl Registers { /// Provide a type-level equivalent for the /// [`RegisterInterface::change_mode`] method. #[inline] - fn change_mode>(&mut self) { + fn change_mode>(&mut self) { RegisterInterface::do_change_mode(self, M::DYN); } } @@ -237,20 +285,20 @@ impl Registers { pub struct Slice where I: SliceId, - M: SliceMode + ValidSliceMode, + M: ValidSliceMode, { regs: Registers, mode: PhantomData, /// Channel A (always output) - pub channel_a: Channel, + pub channel_a: Channel, /// Channel B (input or output) - pub channel_b: Channel, + pub channel_b: Channel, } impl Slice where I: SliceId, - M: SliceMode + ValidSliceMode, + M: ValidSliceMode, { /// Create a new [`Slice`] /// @@ -271,7 +319,7 @@ where /// Convert the slice to the requested [`SliceMode`] #[inline] - pub fn into_mode>(mut self) -> Slice { + pub fn into_mode>(mut self) -> Slice { if N::DYN != M::DYN { self.regs.change_mode::(); } @@ -519,7 +567,7 @@ impl Slices { // C: ChannelId, // G: PinId + BankPinId + ValidPwmOutputPin, // PM: PinMode + ValidPinMode, - // SM: SliceMode + ValidSliceMode, + // SM: ValidSliceMode, // >(&mut self, _: &Pin) -> &mut Slice{ // match S::DYN { // DynSliceId{num} if num == 0 => &mut self.pwm0, @@ -538,15 +586,15 @@ impl Slices { /// A Channel from the Pwm subsystem. /// /// Its attached to one of the eight slices and can be an A or B side channel -pub struct Channel { - regs: Registers, - slice_mode: PhantomData, +pub struct Channel { + regs: Registers, + slice_mode: PhantomData, channel_id: PhantomData, duty_cycle: u16, enabled: bool, } -impl Channel { +impl Channel { pub(super) unsafe fn new(duty_cycle: u16) -> Self { Channel { regs: Registers::new(), @@ -558,9 +606,9 @@ impl Channel { } } -impl Sealed for Channel {} +impl Sealed for Channel {} -impl PwmPin for Channel { +impl PwmPin for Channel { type Duty = u16; /// We cant disable the channel without disturbing the other channel. @@ -600,7 +648,7 @@ impl PwmPin for Channel { } } -impl PwmPin for Channel { +impl PwmPin for Channel { type Duty = u16; /// We cant disable the channel without disturbing the other channel. @@ -640,16 +688,14 @@ impl PwmPin for Channel { } } -impl> Channel { +impl Channel { /// Capture a gpio pin and use it as pwm output for channel A - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + P::Mode: ValidPinMode, + { + pin.into().into_mode() } /// Invert channel output @@ -665,16 +711,14 @@ impl> Channel { } } -impl> Channel { +impl Channel { /// Capture a gpio pin and use it as pwm output for channel B - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + P::Mode: ValidPinMode, + { + pin.into().into_mode() } /// Invert channel output @@ -690,36 +734,35 @@ impl> Channel { } } -impl> Channel { +impl Channel +where + S::Mode: ValidSliceInputMode, +{ /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from, PM: PinMode + ValidPinMode>( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn input_from(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmInputPin, + { + pin.into().into_mode() } } -impl> Slice { +impl> Slice { /// Capture a gpio pin and use it as pwm output - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - C: ChannelId, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + { + pin.into().into_mode() } } -impl> Slice { +impl> Slice { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from, PM: PinMode + ValidPinMode>( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn input_from(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmInputPin, + { + pin.into().into_mode() } } diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index c0d783275..3eae9d4fd 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -436,16 +436,16 @@ //! With this new definition, we can rephrase our statement above. We need some //! way to tell the compiler that when `P` implements `AnyPin`, //! `P == SpecificPin

`. There's no way to do that exactly, but we can come -//! close with some useful trait bounds: [`From`], [`Into`], [`AsRef`] and -//! [`AsMut`]. +//! close with some useful trait bounds: [`From`], [`Into`], [`Borrow`] and +//! [`BorrowMut`]. //! //! ```ignore //! trait AnyPin //! where //! Self: From>, //! Self: Into>, -//! Self: AsRef>, -//! Self: AsMut>, +//! Self: Borrow>, +//! Self: BorrowMut>, //! { //! type Id: PinId; //! type Mode: PinMode; @@ -476,15 +476,15 @@ //! where //! Self: From>, //! Self: Into>, -//! Self: AsRef>, -//! Self: AsMut>, +//! Self: Borrow>, +//! Self: BorrowMut>, //! { //! type Type; //! } //! //! type IsType = ::Type; //! -//! impl + AsMut> Is for T { +//! impl + BorrowMut> Is for T { //! type Type = T; //! } //! ``` @@ -639,6 +639,8 @@ mod private { pub trait Sealed {} } +use core::borrow::{Borrow, BorrowMut}; + pub(crate) use private::Sealed; /// Type-level version of the [None] variant @@ -679,8 +681,8 @@ where Self: Sealed, Self: From>, Self: Into>, - Self: AsRef>, - Self: AsMut>, + Self: Borrow>, + Self: BorrowMut>, { #[allow(missing_docs)] type Type; @@ -691,7 +693,7 @@ pub type IsType = ::Type; impl Is for T where - T: Sealed + AsRef + AsMut, + T: Sealed + Borrow + BorrowMut, { type Type = T; } diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index f75095526..f9696a562 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -19,8 +19,8 @@ //! //! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) //! let pins = ( -//! pins.gpio0.into_mode::(), -//! pins.gpio1.into_mode::(), +//! pins.gpio0.into_mode(), +//! pins.gpio1.into_mode(), //! ); //! // Need to perform clock init before using UART or it will freeze. //! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS) diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index c46bae42e..ae2c990fe 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -4,6 +4,7 @@ //! UartPeripheral object that can both read and write. use super::*; +use crate::gpio::SomePin; use crate::pac::uart0::uartlcr_h::W as UART_LCR_H_Writer; use core::convert::Infallible; use core::fmt; @@ -73,10 +74,10 @@ impl> UartPeripheral { // Enable the UART, and the TX,RC,CTS and RTS based on the pins device.uartcr.write(|w| { w.uarten().set_bit(); - w.txe().bit(P::TX_ENABLED); - w.rxe().bit(P::RX_ENABLED); - w.ctsen().bit(P::CTS_ENABLED); - w.rtsen().bit(P::RTS_ENABLED); + w.txe().bit(P::Tx::IS_SOME); + w.rxe().bit(P::Rx::IS_SOME); + w.ctsen().bit(P::Cts::IS_SOME); + w.rtsen().bit(P::Rts::IS_SOME); w }); diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index ec02546e8..201258e55 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,59 +1,69 @@ -use crate::gpio::{bank0, FunctionUart, Pin}; +use crate::gpio::{bank0, AnyPin, FunctionUart, OptionalPin}; use crate::pac::{UART0, UART1}; -use crate::typelevel::Sealed; +use crate::typelevel::{NoneT, Sealed}; use super::UartDevice; /// Declares a valid UART pinout. pub trait ValidUartPinout { - /// Indicates TX should be enabled for this pinout - const TX_ENABLED: bool; - /// Indicates RX should be enabled for this pinout - const RX_ENABLED: bool; - /// Indicates CTS should be enabled for this pinout - const CTS_ENABLED: bool; - /// Indicates RTS should be enabled for this pinout - const RTS_ENABLED: bool; + #[allow(missing_docs)] + type Rx: OptionalPin; + #[allow(missing_docs)] + type Tx: OptionalPin; + #[allow(missing_docs)] + type Cts: OptionalPin; + #[allow(missing_docs)] + type Rts: OptionalPin; } impl ValidUartPinout for Pins where UART: UartDevice, - TX: Tx, - RX: Rx, - CTS: Cts, - RTS: Rts, + TX: OptionalPin, + RX: OptionalPin, + CTS: OptionalPin, + RTS: OptionalPin, + TX::Id: ValidTxPin, + RX::Id: ValidRxPin, + CTS::Id: ValidCtsPin, + RTS::Id: ValidRtsPin, { - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = CTS::ENABLED; - const RTS_ENABLED: bool = RTS::ENABLED; + type Rx = RX; + type Tx = TX; + type Cts = CTS; + type Rts = RTS; } impl ValidUartPinout for (TX, RX) where UART: UartDevice, - TX: Tx, - RX: Rx, + TX: AnyPin, + RX: AnyPin, + TX::Id: ValidTxPin, + RX::Id: ValidRxPin, { - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = false; - const RTS_ENABLED: bool = false; + type Rx = RX; + type Tx = TX; + type Cts = NoneT; + type Rts = NoneT; } impl ValidUartPinout for (TX, RX, CTS, RTS) where UART: UartDevice, - TX: Tx, - RX: Rx, - CTS: Cts, - RTS: Rts, + TX: AnyPin, + RX: AnyPin, + CTS: AnyPin, + RTS: AnyPin, + TX::Id: ValidTxPin, + RX::Id: ValidRxPin, + CTS::Id: ValidCtsPin, + RTS::Id: ValidRtsPin, { - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = CTS::ENABLED; - const RTS_ENABLED: bool = RTS::ENABLED; + type Rx = RX; + type Tx = TX; + type Cts = CTS; + type Rts = RTS; } /// Customizable Uart pinout, allowing you to set the pins individually. @@ -65,7 +75,7 @@ where /// |UART0|0, 12, 16, 28|1, 13, 17, 29|2, 14, 18 |3, 15, 19 | /// |UART1|4, 8, 20, 24 |5, 9, 21, 25 |6, 10, 22, 26|7, 11, 23, 27| /// -/// Every field can be set to `()` to not configure them. +/// Every field can be set to [`NoneT`] to not configure them. /// /// Note that you can also use tuples `(RX, TX)` or `(RX, TX, CTS, RTS)` instead of this type. /// @@ -91,18 +101,24 @@ pub struct Pins { pub cts: CTS, } -impl Default for Pins<(), (), (), ()> { +impl Default for Pins { fn default() -> Self { Self { - tx: (), - rx: (), - rts: (), - cts: (), + tx: NoneT, + rx: NoneT, + rts: NoneT, + cts: NoneT, } } } -impl Pins { +impl Pins +where + TX: OptionalPin, + RX: OptionalPin, + CTS: OptionalPin, + RTS: OptionalPin, +{ /// Set the TX pin pub fn tx(self, tx: NTX) -> Pins { Pins { @@ -142,39 +158,13 @@ impl Pins { } /// Indicates a valid TX pin for UART0 or UART1 -pub trait Tx: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} +pub trait ValidTxPin: Sealed {} /// Indicates a valid RX pin for UART0 or UART1 -pub trait Rx: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} +pub trait ValidRxPin: Sealed {} /// Indicates a valid CTS pin for UART0 or UART1 -pub trait Cts: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} +pub trait ValidCtsPin: Sealed {} /// Indicates a valid RTS pin for UART0 or UART1 -pub trait Rts: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} - -impl Tx for () { - const ENABLED: bool = false; -} -impl Rx for () { - const ENABLED: bool = false; -} -impl Cts for () { - const ENABLED: bool = false; -} -impl Rts for () { - const ENABLED: bool = false; -} -impl Sealed for () {} +pub trait ValidRtsPin: Sealed {} macro_rules! impl_valid_uart { ($($uart:ident: { @@ -184,26 +174,14 @@ macro_rules! impl_valid_uart { rts: [$($rts:ident),*], }),*) => { $( - $( - impl Tx<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Rx<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Cts<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Rts<$uart> for Pin { - const ENABLED: bool = true; - } - )* + impl ValidRxPin<$uart> for NoneT {} + impl ValidTxPin<$uart> for NoneT {} + impl ValidCtsPin<$uart> for NoneT {} + impl ValidRtsPin<$uart> for NoneT {} + $(impl ValidTxPin<$uart> for bank0::$tx {})* + $(impl ValidRxPin<$uart> for bank0::$rx {})* + $(impl ValidCtsPin<$uart> for bank0::$cts {})* + $(impl ValidRtsPin<$uart> for bank0::$rts {})* )* }; } From f4e4deefa41cc1d198acb661576b7758ae910713 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 21 May 2023 04:56:43 +0100 Subject: [PATCH 02/26] Use an enum for core identification --- rp2040-hal/CHANGELOG.md | 8 ++++---- rp2040-hal/src/sio.rs | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index 55fc77af7..a80b1f57e 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -19,12 +19,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - multicore: remove the requirement on the closure to never return - #594 @ithinuel - Updated dependency on rp2040-boot2 to version 0.3.0. - @jannic - - This doubles the flash access speed to the value used by the C SDK by - default. So it should usually be safe. However, if you are overclocking - the RP2040, you might need to lower the flash speed accordingly. -- timer: Make sure clocks are initialized before creating a timer - #618 @jannic + This doubles the flash access speed to the value used by the C SDK by + default. So it should usually be safe. However, if you are overclocking + the RP2040, you might need to lower the flash speed accordingly. - Doc: Several improvements have been made to documentation: #607 #597 - DMA: Check for valid word sizes at compile time - #600 @jannic +- Use an enum for core identification. - @ithinuel ### Added diff --git a/rp2040-hal/src/sio.rs b/rp2040-hal/src/sio.rs index 9238dd3f6..3dbd59abd 100644 --- a/rp2040-hal/src/sio.rs +++ b/rp2040-hal/src/sio.rs @@ -23,6 +23,17 @@ use crate::typelevel::Sealed; use super::*; use core::convert::Infallible; +/// Id of the core. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + #[allow(missing_docs)] + Core0 = 0, + #[allow(missing_docs)] + Core1 = 1, +} + /// Marker struct for ownership of SIO gpio bank0 pub struct SioGpioBank0 { _private: (), @@ -89,9 +100,13 @@ impl Sio { } /// Returns whether we are running on Core 0 (`0`) or Core 1 (`1`). - pub fn core() -> u8 { + pub fn core() -> CoreId { // Safety: it is always safe to read this read-only register - unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 } + match unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 } { + 0 => CoreId::Core0, + 1 => CoreId::Core1, + _ => unreachable!("This MCU only has 2 cores."), + } } } From be0a95d30f06ab08af80f921f9114eb56ee74259 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Tue, 16 May 2023 07:52:51 +0100 Subject: [PATCH 03/26] Overhaul pin modules and related changes. - Added `AdcPin` wrapper to disable digital function for ADC operations - Added `Sealed` supertrait to `PIOExt` - Added pins to `Spi` to fix inconsistencies in gpio bounds in peripheral (i2c, uart, spi) - Merge DynPin and Pin into Pin. The type class used in Pin now have a runtime variant allowing for the creation of uniform array of pins (eg: `[Pin]`). - Fix miss defined ValidPinMode bound allowing any Bank0 pin to be Xip and any Qspi pin to be any other function (except for clock). - Use `let _ = ` to ignore result rather than `.ok();` as this gives a false sense the result is checked. Addresses: #140, #446, #481, #485, #556 --- rp2040-hal/CHANGELOG.md | 11 + rp2040-hal/examples/adc.rs | 6 +- rp2040-hal/examples/dht11.rs | 48 +- rp2040-hal/examples/gpio_irq_example.rs | 8 +- rp2040-hal/examples/i2c.rs | 4 +- rp2040-hal/examples/pio_blink.rs | 4 +- rp2040-hal/examples/pio_dma.rs | 4 +- rp2040-hal/examples/pio_proc_blink.rs | 4 +- rp2040-hal/examples/pio_side_set.rs | 2 +- rp2040-hal/examples/pio_synchronized.rs | 8 +- rp2040-hal/examples/pwm_irq_input.rs | 16 +- rp2040-hal/examples/rom_funcs.rs | 4 +- rp2040-hal/examples/spi.rs | 8 +- rp2040-hal/examples/spi_dma.rs | 8 +- rp2040-hal/examples/uart.rs | 4 +- rp2040-hal/examples/uart_dma.rs | 4 +- rp2040-hal/examples/vector_table.rs | 2 +- rp2040-hal/src/adc.rs | 124 ++- rp2040-hal/src/clocks/clock_sources.rs | 10 +- rp2040-hal/src/critical_section_impl.rs | 2 +- rp2040-hal/src/dma/single_channel.rs | 2 - rp2040-hal/src/gpio/dynpin.rs | 650 ----------- rp2040-hal/src/gpio/func.rs | 185 +++ rp2040-hal/src/gpio/mod.rs | 1271 ++++++++++++++++++++- rp2040-hal/src/gpio/pin.rs | 1359 ++--------------------- rp2040-hal/src/gpio/pin/pin_sealed.rs | 192 ++++ rp2040-hal/src/gpio/pull.rs | 57 + rp2040-hal/src/gpio/reg.rs | 530 --------- rp2040-hal/src/i2c.rs | 84 +- rp2040-hal/src/i2c/controller.rs | 12 +- rp2040-hal/src/i2c/peripheral.rs | 12 +- rp2040-hal/src/pio.rs | 32 +- rp2040-hal/src/pwm/mod.rs | 28 +- rp2040-hal/src/sio.rs | 5 + rp2040-hal/src/spi.rs | 75 +- rp2040-hal/src/spi/pins.rs | 129 +++ rp2040-hal/src/typelevel.rs | 32 +- rp2040-hal/src/uart/mod.rs | 14 +- rp2040-hal/src/uart/peripheral.rs | 2 +- rp2040-hal/src/uart/pins.rs | 267 +++-- 40 files changed, 2406 insertions(+), 2813 deletions(-) delete mode 100644 rp2040-hal/src/gpio/dynpin.rs create mode 100644 rp2040-hal/src/gpio/func.rs create mode 100644 rp2040-hal/src/gpio/pin/pin_sealed.rs create mode 100644 rp2040-hal/src/gpio/pull.rs delete mode 100644 rp2040-hal/src/gpio/reg.rs create mode 100644 rp2040-hal/src/spi/pins.rs diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index a80b1f57e..c46e0e521 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -25,11 +25,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Doc: Several improvements have been made to documentation: #607 #597 - DMA: Check for valid word sizes at compile time - #600 @jannic - Use an enum for core identification. - @ithinuel +- Merge DynPin and Pin into Pin. The type class used in Pin now have a runtime variant allowing for + the creation of uniform array of pins (eg: `[Pin]`). - @ithinuel +- Fix miss defined ValidPinMode bound allowing any Bank0 pin to be Xip and any Qspi pin to be any + other function (except for clock). - @ithinuel +- Use `let _ =` to ignore result rather than `.ok();` as this gives a false sense the result is + checked. - @ithinuel +- Reduce code repetition in i2c modules. - @ithinuel ### Added - timer::Timer implements the embedded-hal delay traits and Copy/Clone - #614 @ithinuel @jannic - DMA: Allow access to the DMA engine's byteswapping feature - #603 @Gip-Gip +- Added `AdcPin` wrapper to disable digital function for ADC operations - @ithinuel +- Added `Sealed` supertrait to `PIOExt` - @ithinuel +- Added pins to `Spi` to fix inconsistencies in gpio bounds in peripheral (i2c, uart, spi) - @ithinuel +- Added `sio::Sio::read_bank0() -> u32` to provide single instruction multiple io read. ## [0.8.1] - 2023-05-05 diff --git a/rp2040-hal/examples/adc.rs b/rp2040-hal/examples/adc.rs index bdb773f1f..96e66b82f 100644 --- a/rp2040-hal/examples/adc.rs +++ b/rp2040-hal/examples/adc.rs @@ -88,8 +88,8 @@ fn main() -> ! { // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1) let uart_pins = ( - pins.gpio0.into_mode::(), - pins.gpio1.into_mode::(), + pins.gpio0.into_function::(), + pins.gpio1.into_function::(), ); // Create a UART driver @@ -110,7 +110,7 @@ fn main() -> ! { let mut temperature_sensor = adc.enable_temp_sensor(); // Configure GPIO26 as an ADC input - let mut adc_pin_0 = pins.gpio26.into_floating_input(); + let mut adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26); loop { // Read the raw ADC counts from the temperature sensor channel. let temp_sens_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap(); diff --git a/rp2040-hal/examples/dht11.rs b/rp2040-hal/examples/dht11.rs index e6c205f7c..d506b99d1 100644 --- a/rp2040-hal/examples/dht11.rs +++ b/rp2040-hal/examples/dht11.rs @@ -24,9 +24,7 @@ use rp2040_hal as hal; use hal::pac; // Some traits we need -use embedded_hal::digital::v2::InputPin; use embedded_hal::digital::v2::OutputPin; -use hal::gpio::dynpin::DynPin; use hal::Clock; /// The linker will place this boot block at the start of our program image. We @@ -43,48 +41,6 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; use dht_sensor::{dht11, DhtReading}; -/// A wrapper for DynPin, implementing both InputPin and OutputPin, to simulate -/// an open-drain pin as needed by the wire protocol the DHT11 sensor speaks. -/// https://how2electronics.com/interfacing-dht11-temperature-humidity-sensor-with-raspberry-pi-pico/ -struct InOutPin { - inner: DynPin, -} - -impl InOutPin { - fn new(inner: DynPin) -> Self { - Self { inner } - } -} - -impl InputPin for InOutPin { - type Error = rp2040_hal::gpio::Error; - fn is_high(&self) -> Result::Error> { - self.inner.is_high() - } - fn is_low(&self) -> Result::Error> { - self.inner.is_low() - } -} - -impl OutputPin for InOutPin { - type Error = rp2040_hal::gpio::Error; - fn set_low(&mut self) -> Result<(), ::Error> { - // To actively pull the pin low, it must also be configured as a (readable) output pin - self.inner.into_readable_output(); - // In theory, we should set the pin to low first, to make sure we never actively - // pull it up. But if we try it on the input pin, we get Err(Gpio(InvalidPinType)). - self.inner.set_low()?; - Ok(()) - } - fn set_high(&mut self) -> Result<(), ::Error> { - // To set the open-drain pin to high, just disable the output driver by changing the - // pin to input mode with pull-up. That way, the DHT11 can still pull the data line down - // to send its response. - self.inner.into_pull_up_input(); - Ok(()) - } -} - /// Entry point to our bare-metal application. /// /// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function @@ -128,8 +84,8 @@ fn main() -> ! { let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); // Use GPIO 28 as an InOutPin - let mut pin = InOutPin::new(pins.gpio28.into()); - pin.set_high().ok(); + let mut pin = hal::gpio::InOutPin::new(pins.gpio28); + let _ = pin.set_high(); // Perform a sensor reading let _measurement = dht11::Reading::read(&mut delay, &mut pin); diff --git a/rp2040-hal/examples/gpio_irq_example.rs b/rp2040-hal/examples/gpio_irq_example.rs index 35ccc0072..0eaed59ac 100644 --- a/rp2040-hal/examples/gpio_irq_example.rs +++ b/rp2040-hal/examples/gpio_irq_example.rs @@ -63,11 +63,11 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; // We'll create some type aliases using `type` to help with that /// This pin will be our output - it will drive an LED if you run this on a Pico -type LedPin = gpio::Pin; +type LedPin = gpio::Pin; /// This pin will be our interrupt source. /// It will trigger an interrupt if pulled to ground (via a switch or jumper wire) -type ButtonPin = gpio::Pin; +type ButtonPin = gpio::Pin; /// Since we're always accessing these pins together we'll store them in a tuple. /// Giving this tuple a type alias means we won't need to use () when putting them @@ -121,10 +121,10 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_mode(); + let led = pins.gpio25.into(); // Set up the GPIO pin that will be our input - let in_pin = pins.gpio26.into_mode(); + let in_pin = pins.gpio26.into(); // Trigger on the 'falling edge' of the input pin. // This will happen as the button is being pressed diff --git a/rp2040-hal/examples/i2c.rs b/rp2040-hal/examples/i2c.rs index 1eb720438..790ab3bf2 100644 --- a/rp2040-hal/examples/i2c.rs +++ b/rp2040-hal/examples/i2c.rs @@ -75,8 +75,8 @@ fn main() -> ! { ); // Configure two pins as being I²C, not GPIO - let sda_pin = pins.gpio18.into_mode::(); - let scl_pin = pins.gpio19.into_mode::(); + let sda_pin = pins.gpio18.into_function::(); + let scl_pin = pins.gpio19.into_function::(); // let not_an_scl_pin = pins.gpio20.into_mode::(); // Create the I²C drive, using the two pre-configured pins. This will fail diff --git a/rp2040-hal/examples/pio_blink.rs b/rp2040-hal/examples/pio_blink.rs index 1338ccc4c..18e69220b 100644 --- a/rp2040-hal/examples/pio_blink.rs +++ b/rp2040-hal/examples/pio_blink.rs @@ -38,9 +38,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // Define some simple PIO program. const MAX_DELAY: u8 = 31; diff --git a/rp2040-hal/examples/pio_dma.rs b/rp2040-hal/examples/pio_dma.rs index 7850a9a87..78e37cd69 100644 --- a/rp2040-hal/examples/pio_dma.rs +++ b/rp2040-hal/examples/pio_dma.rs @@ -35,9 +35,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // HELLO WORLD in morse code: // .... . .-.. .-.. --- / .-- --- .-. .-.. -.. diff --git a/rp2040-hal/examples/pio_proc_blink.rs b/rp2040-hal/examples/pio_proc_blink.rs index 6f9da4623..7f5c9d245 100644 --- a/rp2040-hal/examples/pio_proc_blink.rs +++ b/rp2040-hal/examples/pio_proc_blink.rs @@ -36,9 +36,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // Define some simple PIO program. let program = pio_proc::pio_asm!( diff --git a/rp2040-hal/examples/pio_side_set.rs b/rp2040-hal/examples/pio_side_set.rs index 25539f2b9..89f01b940 100644 --- a/rp2040-hal/examples/pio_side_set.rs +++ b/rp2040-hal/examples/pio_side_set.rs @@ -38,7 +38,7 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO let led_pin_id = led.id().num; diff --git a/rp2040-hal/examples/pio_synchronized.rs b/rp2040-hal/examples/pio_synchronized.rs index 8258905a9..9759162b5 100644 --- a/rp2040-hal/examples/pio_synchronized.rs +++ b/rp2040-hal/examples/pio_synchronized.rs @@ -38,12 +38,12 @@ fn main() -> ! { ); // configure pins for Pio0. - let _: Pin<_, FunctionPio0> = pins.gpio0.into_mode(); - let _: Pin<_, FunctionPio0> = pins.gpio1.into_mode(); + let gp0: Pin<_, FunctionPio0, _> = pins.gpio0.into_function(); + let gp1: Pin<_, FunctionPio0, _> = pins.gpio1.into_function(); // PIN id for use inside of PIO - let pin0 = 0; - let pin1 = 1; + let pin0 = gp0.id().num; + let pin1 = gp1.id().num; // Define some simple PIO program. let program = pio_proc::pio_asm!( diff --git a/rp2040-hal/examples/pwm_irq_input.rs b/rp2040-hal/examples/pwm_irq_input.rs index d4a51937d..291214912 100644 --- a/rp2040-hal/examples/pwm_irq_input.rs +++ b/rp2040-hal/examples/pwm_irq_input.rs @@ -63,10 +63,10 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; /// We'll create some type aliases using `type` to help with that /// This pin will be our output - it will drive an LED if you run this on a Pico -type LedPin = gpio::Pin; +type LedPin = gpio::Pin, gpio::PullNone>; /// This pin will be our input for a 50 Hz servo PWM signal -type InputPwmPin = gpio::Pin; +type InputPwmPin = gpio::Pin; /// This will be our PWM Slice - it will interpret the PWM signal from the pin type PwmSlice = pwm::Slice; @@ -134,8 +134,8 @@ fn main() -> ! { pwm.enable(); // Connect to GPI O1 as the input to channel B on PWM0 + let input_pin = pins.gpio1.into(); let channel = &mut pwm.channel_b; - let input_pin = channel.input_from(pins.gpio1); channel.enable(); // Enable an interrupt whenever GPI O1 goes from high to low (the end of a pulse) @@ -144,7 +144,7 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_mode(); + let led = pins.gpio25.into(); // Give away our pins by moving them into the `GLOBAL_PINS` variable. // We won't need to access them in the main thread again @@ -193,14 +193,14 @@ fn IO_IRQ_BANK0() { // if the PWM signal indicates low, turn off the LED if pulse_width_us < LOW_US { // set_low can't fail, but the embedded-hal traits always allow for it - // we can discard the Result by transforming it to an Option - led.set_low().ok(); + // we can discard the Result + let _ = led.set_low(); } // if the PWM signal indicates low, turn on the LED else if pulse_width_us > HIGH_US { // set_high can't fail, but the embedded-hal traits always allow for it - // we can discard the Result by transforming it to an Option - led.set_high().ok(); + // we can discard the Result + let _ = led.set_high(); } // If the PWM signal was in the dead-zone between LOW and HIGH, don't change the LED's diff --git a/rp2040-hal/examples/rom_funcs.rs b/rp2040-hal/examples/rom_funcs.rs index 281025df7..fe81839ca 100644 --- a/rp2040-hal/examples/rom_funcs.rs +++ b/rp2040-hal/examples/rom_funcs.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode::(), + pins.gpio0.into_function::(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode::(), + pins.gpio1.into_function::(), ); let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/spi.rs b/rp2040-hal/examples/spi.rs index 05aa8ae8f..eb212dc2c 100644 --- a/rp2040-hal/examples/spi.rs +++ b/rp2040-hal/examples/spi.rs @@ -80,10 +80,10 @@ fn main() -> ! { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::Spi::<_, _, 8>::new(pac.SPI0); + let spi_mosi = pins.gpio7.into_function::(); + let spi_miso = pins.gpio4.into_function::(); + let spi_sclk = pins.gpio6.into_function::(); + let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let mut spi = spi.init( diff --git a/rp2040-hal/examples/spi_dma.rs b/rp2040-hal/examples/spi_dma.rs index 9d48fb79b..67ba993d1 100644 --- a/rp2040-hal/examples/spi_dma.rs +++ b/rp2040-hal/examples/spi_dma.rs @@ -58,10 +58,10 @@ fn main() -> ! { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::spi::Spi::<_, _, 8>::new(pac.SPI0); + let spi_mosi = pins.gpio7.into_function::(); + let spi_miso = pins.gpio4.into_function::(); + let spi_sclk = pins.gpio6.into_function::(); + let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let spi = spi.init( diff --git a/rp2040-hal/examples/uart.rs b/rp2040-hal/examples/uart.rs index 071df17cb..178979eae 100644 --- a/rp2040-hal/examples/uart.rs +++ b/rp2040-hal/examples/uart.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode(), + pins.gpio0.into_function(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode(), + pins.gpio1.into_function(), ); let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/uart_dma.rs b/rp2040-hal/examples/uart_dma.rs index 84d5900fb..35dc770a1 100644 --- a/rp2040-hal/examples/uart_dma.rs +++ b/rp2040-hal/examples/uart_dma.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode(), + pins.gpio0.into_function(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode(), + pins.gpio1.into_function(), ); let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/vector_table.rs b/rp2040-hal/examples/vector_table.rs index 16f2a053f..b1c083069 100644 --- a/rp2040-hal/examples/vector_table.rs +++ b/rp2040-hal/examples/vector_table.rs @@ -33,7 +33,7 @@ static mut RAM_VTABLE: VectorTable = VectorTable::new(); // Give our LED and Alarm a type alias to make it easier to refer to them type LedAndAlarm = ( - hal::gpio::Pin, + hal::gpio::Pin, hal::timer::Alarm0, ); diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 1e2827580..1c438b4c8 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -7,14 +7,14 @@ //! Capture ADC reading from a pin //! ```no_run //! use embedded_hal::adc::OneShot; -//! use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio}; +//! use rp2040_hal::{adc::Adc, adc::AdcPin, gpio::Pins, pac, Sio}; //! let mut peripherals = pac::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); //! // Enable adc //! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS); //! // Configure one of the pins as an ADC input -//! let mut adc_pin_0 = pins.gpio26.into_floating_input(); +//! let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()); //! // Read the ADC counts from the ADC channel //! let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap(); //! ``` @@ -37,20 +37,57 @@ //! See [examples/adc.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/adc.rs) and //! [pimoroni_pico_explorer_showcase.rs](https://github.com/rp-rs/rp-hal-boards/tree/main/boards/pimoroni-pico-explorer/examples/pimoroni_pico_explorer_showcase.rs) for more complete examples +use core::convert::Infallible; + use hal::adc::{Channel, OneShot}; use pac::{ADC, RESETS}; use crate::{ - gpio::Pin, gpio::{ bank0::{Gpio26, Gpio27, Gpio28, Gpio29}, - FloatingInput, + AnyPin, DynPinId, Function, OutputEnableOverride, Pin, PullType, ValidFunction, }, resets::SubsystemReset, }; const TEMPERATURE_SENSOR_CHANNEL: u8 = 4; +/// A pin locked in use with the ADC. +pub struct AdcPin

+where + P: AnyPin, +{ + pin: P, + output_disable: bool, + input_enable: bool, +} + +impl

AdcPin

+where + P: AnyPin, +{ + /// Captures the pin to be used with an ADC and disables its digital circuitery. + pub fn new(pin: P) -> Self { + let mut p = pin.into(); + let (od, ie) = (p.get_output_disable(), p.get_input_enable()); + p.set_output_enable_override(OutputEnableOverride::Disable); + p.set_input_enable(false); + Self { + pin: P::from(p), + output_disable: od, + input_enable: ie, + } + } + + /// Release the pin and restore its digital circuitery's state. + pub fn release(self) -> P { + let mut p = self.pin.into(); + p.set_output_disable(self.output_disable); + p.set_input_enable(self.input_enable); + P::from(p) + } +} + /// Adc pub struct Adc { device: ADC, @@ -92,11 +129,30 @@ impl Adc { pub fn disable_temp_sensor(&mut self, _: TempSense) { self.device.cs.modify(|_, w| w.ts_en().clear_bit()); } + + fn read(&mut self, chan: u8) -> u16 { + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + self.device + .cs + .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + self.device.result.read().result().bits() + } } macro_rules! channel { ($pin:ident, $channel:expr) => { - impl Channel for Pin<$pin, FloatingInput> { + impl Channel for AdcPin> + where + $pin: crate::gpio::ValidFunction, + { type ID = u8; // ADC channels are identified numerically fn channel() -> u8 { @@ -110,6 +166,13 @@ channel!(Gpio26, 0); channel!(Gpio27, 1); channel!(Gpio28, 2); channel!(Gpio29, 3); +impl Channel for AdcPin> +where + DynPinId: crate::gpio::ValidFunction, +{ + type ID = (); // ADC channels are identified at run time + fn channel() {} +} /// Internal temperature sensor type pub struct TempSense { @@ -124,32 +187,49 @@ impl Channel for TempSense { } } -impl OneShot for Adc +// Implementation for TempSense and type-checked pins +impl OneShot for Adc where WORD: From, - PIN: Channel, + SRC: Channel, { - type Error = (); - - fn read(&mut self, _pin: &mut PIN) -> nb::Result { - let chan = PIN::channel(); + type Error = Infallible; + fn read(&mut self, _pin: &mut SRC) -> nb::Result { + let chan = SRC::channel(); if chan == TEMPERATURE_SENSOR_CHANNEL { self.device.cs.modify(|_, w| w.ts_en().set_bit()) } - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } - - self.device - .cs - .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + Ok(self.read(chan).into()) + } +} - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } +/// The pin was invalid for the requested operation +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidPinError; - Ok(self.device.result.read().result().bits().into()) +// Implementation for dyn-pins +impl OneShot>> for Adc +where + WORD: From, + F: Function, + M: PullType, + DynPinId: ValidFunction, + AdcPin>: Channel, +{ + type Error = InvalidPinError; + + fn read(&mut self, _pin: &mut AdcPin>) -> nb::Result { + use crate::gpio::DynBankId; + let pin_id = _pin.pin.id(); + let chan = if (26..=29).contains(&pin_id.num) && pin_id.bank == DynBankId::Bank0 { + pin_id.num - 26 + } else { + return Err(nb::Error::Other(InvalidPinError)); + }; + + Ok(self.read(chan).into()) } } diff --git a/rp2040-hal/src/clocks/clock_sources.rs b/rp2040-hal/src/clocks/clock_sources.rs index d4661310b..c399f5766 100644 --- a/rp2040-hal/src/clocks/clock_sources.rs +++ b/rp2040-hal/src/clocks/clock_sources.rs @@ -4,7 +4,7 @@ use super::*; use crate::{ gpio::{ bank0::{Gpio20, Gpio22}, - FunctionClock, Pin, + FunctionClock, Pin, PullNone, PullType, }, pll::{Locked, PhaseLockedLoop}, rosc::{Enabled, RingOscillator}, @@ -77,16 +77,16 @@ impl ClockSource for RingOscillator { } // GPIN0 -pub(crate) type GPin0 = Pin; -impl ClockSource for GPin0 { +pub(crate) type GPin0 = Pin; +impl ClockSource for GPin0 { fn get_freq(&self) -> HertzU32 { todo!() } } // GPIN1 -pub(crate) type GPin1 = Pin; -impl ClockSource for Pin { +pub(crate) type GPin1 = Pin; +impl ClockSource for Pin { fn get_freq(&self) -> HertzU32 { todo!() } diff --git a/rp2040-hal/src/critical_section_impl.rs b/rp2040-hal/src/critical_section_impl.rs index 6673f9e94..a121059c3 100644 --- a/rp2040-hal/src/critical_section_impl.rs +++ b/rp2040-hal/src/critical_section_impl.rs @@ -36,7 +36,7 @@ impl RpSpinlockCs { // Store the initial interrupt state and current core id in stack variables let interrupts_active = cortex_m::register::primask::read().is_active(); // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1. - let core = crate::Sio::core() + 1_u8; + let core = crate::Sio::core() as u8 + 1_u8; // Do we already own the spinlock? if LOCK_OWNER.load(Ordering::Acquire) == core { // We already own the lock, so we must have called acquire within a critical_section. diff --git a/rp2040-hal/src/dma/single_channel.rs b/rp2040-hal/src/dma/single_channel.rs index 3f717ce61..429eb101f 100644 --- a/rp2040-hal/src/dma/single_channel.rs +++ b/rp2040-hal/src/dma/single_channel.rs @@ -116,8 +116,6 @@ impl SingleChannel for (Channel, Chan } } -impl Sealed for (Channel, Channel) {} - pub(crate) trait ChannelConfig { fn config( &mut self, diff --git a/rp2040-hal/src/gpio/dynpin.rs b/rp2040-hal/src/gpio/dynpin.rs deleted file mode 100644 index 6b24b5220..000000000 --- a/rp2040-hal/src/gpio/dynpin.rs +++ /dev/null @@ -1,650 +0,0 @@ -//! # 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`]. -//! -//! ```no_run -//! // Move a pin out of the Pins struct and convert to a DynPin -//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pins}, Sio}; -//! # let mut peripherals = pac::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); -//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT; -//! let gpio12: DynPin = pins.gpio12.into(); -//! ``` -//! -//! Conversions between pin modes use a value-level version of the type-level -//! API. -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::{DynPin, Pins}, Sio}; -//! # let mut peripherals = pac::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); -//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT; -//! # let mut gpio12: DynPin = pins.gpio12.into(); -//! // Use one of the literal function names -//! gpio12.into_floating_input(); -//! // Use a method and a DynPinMode variant -//! gpio12.try_into_mode(DYN_FLOATING_INPUT).unwrap(); -//! ``` -//! -//! 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. -//! -//! ```no_run -//! # use core::convert::TryInto; -//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pin, Pins, FloatingInput}, Sio}; -//! # let mut peripherals = pac::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); -//! // Convert to a `DynPin` -//! let mut gpio12: DynPin = pins.gpio12.into(); -//! // Change pin mode -//! gpio12.into_floating_input(); -//! // Convert back to a `Pin` -//! let gpio12: Pin = gpio12.try_into().unwrap(); -//! ``` -//! -//! # Embedded HAL traits -//! -//! This module implements all of the embedded HAL GPIO traits for [`DynPin`]. -//! However, whereas the type-level API uses -//! `Error = core::convert::Infallible`, the value-level API can return a real -//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the -//! operation, the trait functions will return -//! [`InvalidPinType`](Error::InvalidPinType). -use super::pin::{Pin, PinId, PinMode, ValidPinMode}; -use super::reg::RegisterInterface; -use super::{Interrupt, InterruptOverride}; -use core::convert::TryFrom; - -#[cfg(feature = "eh1_0_alpha")] -use eh1_0_alpha::digital as eh1; -use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - -//============================================================================== -// DynPinMode configurations -//============================================================================== - -/// Value-level `enum` for disabled configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynDisabled { - Floating, - PullDown, - PullUp, - BusKeep, -} - -/// Value-level `enum` for input configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynInput { - Floating, - PullDown, - PullUp, - BusKeep, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynOutput { - PushPull, - Readable, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynFunction { - Spi, - Xip, - Uart, - I2C, - Pwm, - Pio0, - Pio1, - Clock, - UsbAux, -} - -//============================================================================== -// DynPinMode -//============================================================================== - -/// Value-level `enum` representing pin modes -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynPinMode { - Disabled(DynDisabled), - Input(DynInput), - Output(DynOutput), - Function(DynFunction), -} - -impl DynPinMode { - #[inline] - fn valid_for(&self, id: DynPinId) -> bool { - use DynFunction::*; - use DynGroup::*; - use DynPinMode::*; - match self { - Disabled(_) => true, - Input(_) => true, - Output(_) => true, - Function(alt) => match id.group { - Bank0 => match alt { - Spi | Uart | I2C | Pwm | Pio0 | Pio1 | UsbAux => true, - Clock if id.num >= 20 && id.num <= 25 => true, - _ => false, - }, - #[allow(clippy::match_like_matches_macro)] - Qspi => match alt { - Xip => true, - _ => false, - }, - }, - } - } -} - -/// Value-level variant of [`DynPinMode`] for floating disabled mode -pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down disabled mode -pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up disabled mode -pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp); - -/// Value-level variant of [`DynPinMode`] for floating input mode -pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down input mode -pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up input mode -pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); - -/// Value-level variant of [`DynPinMode`] for push-pull output mode -pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); -/// Value-level variant of [`DynPinMode`] for readable push-pull output mode -pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable); - -macro_rules! dyn_function { - ( $($Func:ident),+ ) => { - crate::paste::paste! { - $( - #[ - doc = "Value-level variant of [`DynPinMode`] for alternate " - "peripheral function " $Func - ] - pub const []: DynPinMode = - DynPinMode::Function(DynFunction::$Func); - )+ - } - }; -} - -dyn_function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux); - -//============================================================================== -// DynGroup & DynPinId -//============================================================================== - -/// Value-level `enum` for pin groups -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynGroup { - Bank0, - Qspi, -} - -/// Value-level `struct` representing pin IDs -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -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 registers. -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)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -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, -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::Error for Error { - fn kind(&self) -> eh1::ErrorKind { - eh1::ErrorKind::Other - } -} - -//============================================================================== -// 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) - } - } - - /// Clear interrupt. - #[inline] - pub fn clear_interrupt(&self, interrupt: Interrupt) { - self.regs.clear_interrupt(interrupt) - } - - /// Interrupt status. - #[inline] - pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { - self.regs.interrupt_status(interrupt) - } - - /// Is interrupt enabled. - #[inline] - pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_enabled(interrupt) - } - - /// Enable or disable interrupt - #[inline] - pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - self.regs.set_interrupt_enabled(interrupt, enabled) - } - - /// Is interrupt forced. - #[inline] - pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_forced(interrupt) - } - - /// Force or release interrupt. - #[inline] - pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - self.regs.set_interrupt_forced(interrupt, forced); - } - - /// Set the interrupt override. - #[inline] - pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { - self.regs.set_interrupt_override(override_value); - } - - /// Disable the pin and set it to float - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_floating_disabled(&mut self) { - self.try_into_mode(DYN_FLOATING_DISABLED).unwrap(); // always valid - } - - /// Disable the pin and set it to pull down - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_down_disabled(&mut self) { - self.try_into_mode(DYN_PULL_DOWN_DISABLED).unwrap(); // always valid - } - - /// Disable the pin and set it to pull up - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_up_disabled(&mut self) { - self.try_into_mode(DYN_PULL_UP_DISABLED).unwrap(); // always valid - } - - /// Configure the pin to operate as a floating input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_floating_input(&mut self) { - self.try_into_mode(DYN_FLOATING_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a pulled down input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_down_input(&mut self) { - self.try_into_mode(DYN_PULL_DOWN_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a pulled up input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_up_input(&mut self) { - self.try_into_mode(DYN_PULL_UP_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a push-pull output - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_push_pull_output(&mut self) { - self.try_into_mode(DYN_PUSH_PULL_OUTPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a readable push pull output - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_readable_output(&mut self) { - self.try_into_mode(DYN_READABLE_OUTPUT).unwrap(); // always valid - } - - #[inline] - fn _read(&self) -> Result { - match self.mode { - DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()), - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _write(&mut self, bit: bool) -> Result<(), Error> { - match self.mode { - DynPinMode::Output(_) => { - self.regs.write_pin(bit); - Ok(()) - } - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _toggle(&mut self) -> Result<(), Error> { - match self.mode { - DynPinMode::Output(_) => { - self.regs.toggle_pin(); - Ok(()) - } - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _read_out(&self) -> Result { - match self.mode { - DynPinMode::Output(_) => Ok(self.regs.read_out_pin()), - _ => Err(Error::InvalidPinType), - } - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_low(&self) -> Result { - Ok(self._read()? == false) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_high(&self) -> Result { - Ok(self._read()? == true) - } - #[inline] - fn _set_low(&mut self) -> Result<(), Error> { - self._write(false) - } - #[inline] - fn _set_high(&mut self) -> Result<(), Error> { - self._write(true) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_set_low(&self) -> Result { - Ok(self._read_out()? == false) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_set_high(&self) -> Result { - Ok(self._read_out()? == true) - } -} - -//============================================================================== -// Convert between Pin and DynPin -//============================================================================== - -impl From> for DynPin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - /// Erase the type-level information in a [`Pin`] and return a value-level - /// [`DynPin`] - #[inline] - fn from(_pin: Pin) -> Self { - // The `Pin` is consumed, so it is safe to replace it with the - // corresponding `DynPin` - unsafe { DynPin::new(I::DYN, M::DYN) } - } -} - -impl TryFrom for Pin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - type Error = Error; - - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - fn try_from(pin: DynPin) -> Result { - if pin.regs.id == I::DYN && pin.mode == M::DYN { - // The `DynPin` is consumed, so it is safe to replace it with the - // corresponding `Pin` - Ok(unsafe { Self::new() }) - } else { - Err(Error::InvalidPinType) - } - } -} - -//============================================================================== -// Embedded HAL traits -//============================================================================== - -impl OutputPin for DynPin { - type Error = Error; - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high() - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low() - } -} - -impl InputPin for DynPin { - type Error = Error; - #[inline] - fn is_high(&self) -> Result { - self._is_high() - } - #[inline] - fn is_low(&self) -> Result { - self._is_low() - } -} - -impl ToggleableOutputPin for DynPin { - type Error = Error; - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle() - } -} - -impl StatefulOutputPin for DynPin { - #[inline] - fn is_set_high(&self) -> Result { - self._is_set_high() - } - #[inline] - fn is_set_low(&self) -> Result { - self._is_set_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for DynPin { - type Error = Error; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::OutputPin for DynPin { - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high() - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for DynPin { - #[inline] - fn is_high(&self) -> Result { - self._is_high() - } - #[inline] - fn is_low(&self) -> Result { - self._is_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ToggleableOutputPin for DynPin { - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::StatefulOutputPin for DynPin { - #[inline] - fn is_set_high(&self) -> Result { - self._is_set_high() - } - #[inline] - fn is_set_low(&self) -> Result { - self._is_set_low() - } -} diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs new file mode 100644 index 000000000..2d042b331 --- /dev/null +++ b/rp2040-hal/src/gpio/func.rs @@ -0,0 +1,185 @@ +use core::marker::PhantomData; + +use paste::paste; + +use super::pin::DynBankId; + +pub(crate) mod func_sealed { + use super::DynFunction; + + pub trait Function { + fn from(f: DynFunction) -> Self; + fn as_dyn(&self) -> DynFunction; + } + pub trait TypeLevelFunction {} +} + +/// Type-level `enum` for pin function. +pub trait Function: func_sealed::Function {} + +/// Value-level `enum` for pin function. +#[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynFunction { + Xip, + Spi, + Uart, + I2c, + Pwm, + Sio(DynSioConfig), + Pio0, + Pio1, + Clock, + Usb, + Null, +} + +/// Value-level `enum` for SIO configuration. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynSioConfig { + /// Pin is configured as Input. + Input, + /// Pin is configured as Output. + Output, +} + +impl Function for DynFunction {} +impl func_sealed::Function for DynFunction { + fn from(f: DynFunction) -> Self { + f + } + + fn as_dyn(&self) -> DynFunction { + *self + } +} + +macro_rules! pin_func { + ($($fn:ident $(as $alias:ident)?),*) => { + $(paste! { + /// Type-level `variant` for pin [`Function`]. + pub struct [](pub(super) ()); + impl Function for [] {} + impl func_sealed::TypeLevelFunction for [] {} + impl func_sealed::Function for [] { + fn from(_f: DynFunction) -> Self { + Self(()) + } + fn as_dyn(&self) -> DynFunction { + DynFunction::[<$fn>] + } + } + $( + #[doc = "Alias to [`Function" $fn "`]."] + pub type [] = []; + )? + })* + }; +} +pin_func!(Xip, Spi, Uart, I2c as I2C, Pwm, Pio0, Pio1, Clock, Usb, Null); + +// ============================= +// SIO sub-types + +/// Type-level `variant` for pin [`Function`]. +pub struct FunctionSio(PhantomData); +impl Function for FunctionSio {} +impl func_sealed::TypeLevelFunction for FunctionSio {} +impl func_sealed::Function for FunctionSio { + fn from(_f: DynFunction) -> Self { + FunctionSio(PhantomData) + } + fn as_dyn(&self) -> DynFunction { + DynFunction::Sio(C::DYN) + } +} +/// Alias to [`FunctionSio`]. +pub type FunctionSioInput = FunctionSio; +/// Alias to [`FunctionSio`]. +pub type FunctionSioOutput = FunctionSio; + +/// Type-level `enum` for SIO configuration. +pub trait SioConfig { + #[allow(missing_docs)] + const DYN: DynSioConfig; +} + +/// Type-level `variant` for SIO configuration. +pub enum SioInput {} +impl SioConfig for SioInput { + #[allow(missing_docs)] + const DYN: DynSioConfig = DynSioConfig::Input; +} +/// Type-level `variant` for SIO configuration. +pub enum SioOutput {} +impl SioConfig for SioOutput { + #[allow(missing_docs)] + const DYN: DynSioConfig = DynSioConfig::Output; +} + +// ============================= +// Pin to function mapping + +/// Error type for invalid function conversion. +pub struct InvalidFunction; + +/// Marker of valid pin -> function combination. +/// +/// Read as `F is a valid function implemented for the pin F` +pub trait ValidFunction: super::pin::PinId {} + +impl DynFunction { + pub(crate) fn is_valid(&self, id: &P) -> bool { + use DynBankId::*; + use DynFunction::*; + + let dyn_pin = id.as_dyn(); + match (self, dyn_pin.bank, dyn_pin.num) { + (Xip, Bank0, _) => false, + (Clock, _, 0..=19 | 26..=29) => false, + (_, Bank0, 0..=29) => true, + + (Xip | Sio(_), Qspi, 0..=5) => true, + (_, Qspi, 0..=5) => false, + + _ => unreachable!(), + } + } +} +macro_rules! pin_valid_func { + ($bank:ident as $prefix:ident, [$head:ident $(, $func:ident)*], [$($name:tt),+]) => { + pin_valid_func!($bank as $prefix, [$($func),*], [$($name),+]); + paste::paste!{$( + impl ValidFunction<[]> for super::pin::[<$bank:lower>]::[<$prefix $name>] {} + )+} + }; + ($bank:ident as $prefix:ident, [], [$($name:tt),+]) => {}; +} +pin_valid_func!( + bank0 as Gpio, + [Spi, Uart, I2c, Pwm, Pio0, Pio1, Usb, Null], + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29 + ] +); +pin_valid_func!(bank0 as Gpio, [Clock], [20, 21, 22, 23, 24, 25]); +pin_valid_func!(qspi as Qspi, [Xip, Null], [Sclk, Sd0, Sd1, Sd2, Sd3, Ss]); + +macro_rules! pin_valid_func_sio { + ($bank:ident as $prefix:ident, [$($name:tt),+]) => { + paste::paste!{$( + impl ValidFunction> for super::pin::[<$bank:lower>]::[<$prefix $name>] {} + )+} + }; +} +pin_valid_func_sio!( + bank0 as Gpio, + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29 + ] +); +pin_valid_func_sio!(qspi as Qspi, [Sclk, Sd0, Sd1, Sd2, Sd3, Ss]); diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 4d7329859..06d919462 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -1,7 +1,5 @@ //! General Purpose Input and Output (GPIO) //! -//! See [`pin`](self::pin) for implementation details and in-depth documentation. -//! //! ## Basic usage //! ```no_run //! use embedded_hal::digital::v2::{InputPin, OutputPin}; @@ -32,18 +30,34 @@ //! ``` //! See [examples/gpio_in_out.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/gpio_in_out.rs) for a more practical example -// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 -pub mod pin; -pub use pin::*; +// Design Notes: +// +// - The user must not be able to instantiate by themselves nor obtain an instance of the Type-level +// structure. +// - non-typestated features (overides, irq configuration, pads' output disable, pad's input +// enable, drive strength, schmitt, slew rate, sio's in sync bypass) are considered somewhat +// advanced usage of the pin (relative to reading/writing a gpio) and it is the responsibility of +// the user to make sure these are in a correct state when converting and passing the pin around. + +pub use embedded_hal::digital::v2::PinState; + +use crate::{ + atomic_register_access::{write_bitmask_clear, write_bitmask_set}, + sio::Sio, + typelevel::{self, Sealed}, +}; -pub mod dynpin; -pub use dynpin::*; +mod func; +pub(crate) mod pin; +mod pull; -mod reg; +pub use func::*; +pub use pin::{DynBankId, DynPinId, PinId}; +pub use pull::*; +/// The amount of current that a pin can drive when used as an output. #[allow(clippy::enum_variant_names)] #[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, @@ -55,8 +69,8 @@ pub enum OutputDriveStrength { TwelveMilliAmps, } +/// The slew rate of a pin when used as an output. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// The slew rate of a pin when used as an output pub enum OutputSlewRate { /// Slew slow Slow, @@ -64,8 +78,8 @@ pub enum OutputSlewRate { Fast, } +/// Interrupt kind. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// Interrupt kind pub enum Interrupt { /// While low LevelLow, @@ -76,9 +90,19 @@ pub enum Interrupt { /// On rising edge EdgeHigh, } +impl Interrupt { + fn mask(&self) -> u32 { + match self { + Interrupt::LevelLow => 0b0001, + Interrupt::LevelHigh => 0b0010, + Interrupt::EdgeLow => 0b0100, + Interrupt::EdgeHigh => 0b1000, + } + } +} -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// Interrupt override state. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InterruptOverride { /// Don't invert the interrupt. DontInvert = 0, @@ -90,8 +114,8 @@ pub enum InterruptOverride { AlwaysHigh = 3, } -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// Input override state. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InputOverride { /// Don't invert the peripheral input. DontInvert = 0, @@ -128,3 +152,1224 @@ pub enum OutputOverride { /// Drive output high. AlwaysHigh = 3, } + +/// Pin with validation of [`Function`] and [`PullType`]. +pub struct Pin { + id: I, + function: F, + pull_type: P, +} + +/// Create a new pin instance. +/// +/// # Safety +/// The unicity of the pin is not verified. User must make sure no other instance of that specific +/// pin exists at the same time. +pub unsafe fn new_pin(id: DynPinId) -> Pin { + use pin::pin_sealed::PinIdOps; + use rp2040_pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A; + + let funcsel = id + .io_ctrl() + .read() + .funcsel() + .variant() + .expect("Invalid funcsel read from register."); + let function = match funcsel { + FUNCSEL_A::JTAG => DynFunction::Xip, + FUNCSEL_A::SPI => DynFunction::Spi, + FUNCSEL_A::UART => DynFunction::Uart, + FUNCSEL_A::I2C => DynFunction::I2c, + FUNCSEL_A::PWM => DynFunction::Pwm, + FUNCSEL_A::SIO => { + let mask = id.mask(); + let cfg = if id.sio_oe().read().bits() & mask == mask { + DynSioConfig::Output + } else { + DynSioConfig::Input + }; + DynFunction::Sio(cfg) + } + FUNCSEL_A::PIO0 => DynFunction::Pio0, + FUNCSEL_A::PIO1 => DynFunction::Pio1, + FUNCSEL_A::CLOCK => DynFunction::Clock, + FUNCSEL_A::USB => DynFunction::Usb, + FUNCSEL_A::NULL => DynFunction::Null, + }; + let pad = id.pad_ctrl().read(); + let pull_type = match (pad.pue().bit_is_set(), pad.pde().bit_is_set()) { + (true, true) => DynPullType::Both, + (true, false) => DynPullType::Up, + (false, true) => DynPullType::Down, + (false, false) => DynPullType::None, + }; + + Pin { + id, + function, + pull_type, + } +} + +impl Pin { + /// Pin ID. + pub fn id(&self) -> DynPinId { + self.id.as_dyn() + } + + /// # Safety + /// This method does not check if the pin is actually configured as the target function or pull + /// mode. This may lead to inconcistencies between the type-state and the actual state of the + /// pin's configuration. + pub unsafe fn into_unchecked(self) -> Pin { + Pin { + id: self.id, + function: F2::from(self.function.as_dyn()), + pull_type: P2::from(self.pull_type.as_dyn()), + } + } + + /// Convert the pin from one state to the other. + pub fn into(self) -> Pin + where + F2: func::Function, + P2: PullType, + I: func::ValidFunction, + { + self.into_function().into_pull_type() + } + + /// Convert the pin function. + #[deprecated( + note = "Misleading name `mode` when it changes the `function`. Please use `into_function` instead.", + since = "0.9.0" + )] + pub fn into_mode(self) -> Pin + where + F2: func::Function, + I: func::ValidFunction, + { + self.into_function() + } + /// Convert the pin function. + pub fn into_function(self) -> Pin + where + F2: func::Function, + I: func::ValidFunction, + { + // Thanks to type-level validation, we know F2 is valid for I + let prev_function = self.function.as_dyn(); + let function = F2::from(prev_function); + let new_function = function.as_dyn(); + + if prev_function != new_function { + pin::set_function(&self.id, new_function); + } + + Pin { + function, + id: self.id, + pull_type: self.pull_type, + } + } + /// Convert the pin pull type. + pub fn into_pull_type(self) -> Pin { + let prev_pull_type = self.pull_type.as_dyn(); + let pull_type = M2::from(prev_pull_type); + let new_pull_type = pull_type.as_dyn(); + + if prev_pull_type != new_pull_type { + pin::set_pull_type(&self.id, new_pull_type); + } + + Pin { + pull_type, + id: self.id, + function: self.function, + } + } + /// Erase the Pin ID type check. + pub fn into_dyn_pin(self) -> Pin { + Pin { + id: self.id.as_dyn(), + function: self.function, + pull_type: self.pull_type, + } + } + + /// Get the pin's pull type. + pub fn pull_type(&self) -> DynPullType { + self.pull_type.as_dyn() + } + + // ========================================== + // Typical pin conversions. + + /// Disable the pin and set it to float + #[inline] + pub fn into_floating_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Disable the pin and set it to pull down + #[inline] + pub fn into_pull_down_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Disable the pin and set it to pull up + #[inline] + pub fn into_pull_up_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Configure the pin to operate as a floating input + #[inline] + pub fn into_floating_input(self) -> Pin, PullNone> + where + I: ValidFunction>, + { + self.into_function().into_pull_type() + } + /// Configure the pin to operate as a pulled down input + #[inline] + pub fn into_pull_down_input(self) -> Pin, PullDown> + where + I: ValidFunction>, + { + self.into_function().into_pull_type() + } + /// Configure the pin to operate as a pulled up input + #[inline] + pub fn into_pull_up_input(self) -> Pin, PullUp> + where + I: ValidFunction>, + { + self.into_function().into_pull_type() + } + /// Configure the pin to operate as a bus keep input + #[inline] + pub fn into_bus_keep_input(self) -> Pin, PullBoth> + where + I: ValidFunction>, + { + self.into_function().into_pull_type() + } + + /// Configure the pin to operate as a push-pull output. + /// + /// If you want to specify the initial pin state, use [`Pin::into_push_pull_output_in_state`]. + #[inline] + pub fn into_push_pull_output(self) -> Pin, P> + where + I: ValidFunction>, + { + self.into_function() + } + /// Configure the pin to operate as a push-pull output, specifying an initial + /// state which is applied immediately. + #[inline] + pub fn into_push_pull_output_in_state( + mut self, + state: PinState, + ) -> Pin, P> + where + I: ValidFunction>, + { + match state { + PinState::High => self._set_high(), + PinState::Low => self._set_low(), + } + self.into_push_pull_output() + } + + /// Configure the pin to operate as a readable push pull output. + /// + /// If you want to specify the initial pin state, use [`Pin::into_readable_output_in_state`]. + #[inline] + #[deprecated(note = "All gpio are readable, use `.into_push_pull_output()` instead.")] + pub fn into_readable_output(self) -> Pin, P> + where + I: ValidFunction>, + { + self.into_function() + } + /// Configure the pin to operate as a readable push pull output, specifying an initial + /// state which is applied immediately. + #[inline] + #[deprecated(note = "All gpio are readable, use `.into_push_pull_output_in_state()` instead.")] + pub fn into_readable_output_in_state(self, state: PinState) -> Pin, P> + where + I: ValidFunction>, + { + self.into_push_pull_output_in_state(state) + } + + // ========================================== + // methods available for all pins. + + // =================================== + // Pad related methods + + /// Get the current drive strength of the pin. + #[inline] + pub fn get_drive_strength(&self) -> OutputDriveStrength { + use pac::pads_bank0::gpio::DRIVE_A; + match self.id.pad_ctrl().read().drive().variant() { + DRIVE_A::_2MA => OutputDriveStrength::TwoMilliAmps, + DRIVE_A::_4MA => OutputDriveStrength::FourMilliAmps, + DRIVE_A::_8MA => OutputDriveStrength::EightMilliAmps, + DRIVE_A::_12MA => OutputDriveStrength::TwelveMilliAmps, + } + } + + /// Set the drive strength for the pin. + #[inline] + pub fn set_drive_strength(&mut self, strength: OutputDriveStrength) { + use pac::pads_bank0::gpio::DRIVE_A; + let variant = match strength { + OutputDriveStrength::TwoMilliAmps => DRIVE_A::_2MA, + OutputDriveStrength::FourMilliAmps => DRIVE_A::_4MA, + OutputDriveStrength::EightMilliAmps => DRIVE_A::_8MA, + OutputDriveStrength::TwelveMilliAmps => DRIVE_A::_12MA, + }; + self.id.pad_ctrl().modify(|_, w| w.drive().variant(variant)) + } + + /// Get the slew rate for the pin. + #[inline] + pub fn get_slew_rate(&self) -> OutputSlewRate { + if self.id.pad_ctrl().read().slewfast().bit_is_set() { + OutputSlewRate::Fast + } else { + OutputSlewRate::Slow + } + } + + /// Set the slew rate for the pin. + #[inline] + pub fn set_slew_rate(&mut self, rate: OutputSlewRate) { + self.id + .pad_ctrl() + .modify(|_, w| w.slewfast().bit(OutputSlewRate::Fast == rate)); + } + + /// Get wether the schmitt trigger (hysteresis) is enabled. + #[inline] + pub fn get_schimtt_enabled(&self) -> bool { + self.id.pad_ctrl().read().schmitt().bit_is_set() + } + + /// Enable/Disable the schmitt trigger. + #[inline] + pub fn set_schmitt_enabled(&self, enable: bool) { + self.id.pad_ctrl().modify(|_, w| w.schmitt().bit(enable)); + } + + /// Get the state of the digital output circuitery of the pad. + #[inline] + pub fn get_output_disable(&mut self) -> bool { + self.id.pad_ctrl().read().od().bit_is_set() + } + + /// Set the digital output circuitery of the pad. + #[inline] + pub fn set_output_disable(&mut self, disable: bool) { + self.id.pad_ctrl().modify(|_, w| w.od().bit(disable)); + } + + /// Get the state of the digital input circuitery of the pad. + #[inline] + pub fn get_input_enable(&mut self) -> bool { + self.id.pad_ctrl().read().ie().bit_is_set() + } + + /// Set the digital input circuitery of the pad. + #[inline] + pub fn set_input_enable(&mut self, enable: bool) { + self.id.pad_ctrl().modify(|_, w| w.ie().bit(enable)); + } + + // =================================== + // IO related methods + + /// Set the input override. + #[inline] + pub fn set_input_override(&mut self, override_value: InputOverride) { + use pac::io_bank0::gpio::gpio_ctrl::INOVER_A; + let variant = match override_value { + InputOverride::DontInvert => INOVER_A::NORMAL, + InputOverride::Invert => INOVER_A::INVERT, + InputOverride::AlwaysLow => INOVER_A::LOW, + InputOverride::AlwaysHigh => INOVER_A::HIGH, + }; + self.id.io_ctrl().modify(|_, w| w.inover().variant(variant)); + } + + /// Set the output enable override. + #[inline] + pub fn set_output_enable_override(&mut self, override_value: OutputEnableOverride) { + use pac::io_bank0::gpio::gpio_ctrl::OEOVER_A; + let variant = match override_value { + OutputEnableOverride::DontInvert => OEOVER_A::NORMAL, + OutputEnableOverride::Invert => OEOVER_A::INVERT, + OutputEnableOverride::Disable => OEOVER_A::DISABLE, + OutputEnableOverride::Enable => OEOVER_A::ENABLE, + }; + self.id.io_ctrl().modify(|_, w| w.oeover().variant(variant)); + } + + /// Set the output override. + #[inline] + pub fn set_output_override(&mut self, override_value: OutputOverride) { + use pac::io_bank0::gpio::gpio_ctrl::OUTOVER_A; + let variant = match override_value { + OutputOverride::DontInvert => OUTOVER_A::NORMAL, + OutputOverride::Invert => OUTOVER_A::INVERT, + OutputOverride::AlwaysLow => OUTOVER_A::LOW, + OutputOverride::AlwaysHigh => OUTOVER_A::HIGH, + }; + self.id + .io_ctrl() + .modify(|_, w| w.outover().variant(variant)); + } + + /// Set the interrupt override. + #[inline] + pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { + use pac::io_bank0::gpio::gpio_ctrl::IRQOVER_A; + let variant = match override_value { + InterruptOverride::DontInvert => IRQOVER_A::NORMAL, + InterruptOverride::Invert => IRQOVER_A::INVERT, + InterruptOverride::AlwaysLow => IRQOVER_A::LOW, + InterruptOverride::AlwaysHigh => IRQOVER_A::HIGH, + }; + self.id + .io_ctrl() + .modify(|_, w| w.irqover().variant(variant)); + } + + // =================================== + // SIO related methods + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_low(&self) -> bool { + let mask = self.id.mask(); + self.id.sio_in().read().bits() & mask == 0 + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_high(&self) -> bool { + !self._is_low() + } + + #[inline] + pub(crate) fn _set_low(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_clr() + .write(|w| unsafe { w.gpio_out_clr().bits(mask) }); + } + + #[inline] + pub(crate) fn _set_high(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_set() + .write(|w| unsafe { w.gpio_out_set().bits(mask) }); + } + + #[inline] + pub(crate) fn _toggle(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_xor() + .write(|w| unsafe { w.gpio_out_xor().bits(mask) }); + } + + #[inline] + pub(crate) fn _is_set_low(&self) -> bool { + let mask = self.id.mask(); + self.id.sio_out().read().bits() & mask == 0 + } + + #[inline] + pub(crate) fn _is_set_high(&self) -> bool { + !self._is_set_low() + } + + // =================================== + // Interrupt related methods + + /// Clear interrupt. + #[inline] + pub fn clear_interrupt(&mut self, interrupt: Interrupt) { + let (reg, offset) = self.id.intr(); + let mask = interrupt.mask(); + reg.write(|w| unsafe { w.bits(mask << offset) }); + } + + /// Interrupt status. + #[inline] + pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_ints(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Is interrupt enabled. + #[inline] + pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_inte(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Enable or disable interrupt. + #[inline] + pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { + let (reg, offset) = self.id.proc_inte(Sio::core()); + let mask = interrupt.mask(); + unsafe { + if enabled { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Is interrupt forced. + #[inline] + pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_intf(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Force or release interrupt. + #[inline] + pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { + let (reg, offset) = self.id.proc_intf(Sio::core()); + let mask = interrupt.mask(); + unsafe { + if forced { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Dormant wake status. + #[inline] + pub fn dormant_wake_status(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_ints(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Is dormant wake enabled. + #[inline] + pub fn is_dormant_wake_enabled(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_inte(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Enable or disable dormant wake. + #[inline] + pub fn set_dormant_wake_enabled(&self, interrupt: Interrupt, enabled: bool) { + let (reg, offset) = self.id.dormant_wake_inte(); + let mask = interrupt.mask(); + unsafe { + if enabled { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Is dormant wake forced. + #[inline] + pub fn is_dormant_wake_forced(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_intf(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Force dormant wake. + #[inline] + pub fn set_dormant_wake_forced(&mut self, interrupt: Interrupt, forced: bool) { + let (reg, offset) = self.id.dormant_wake_intf(); + let mask = interrupt.mask(); + unsafe { + if forced { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } +} +impl Pin { + /// Try to return to a type-checked pin id. + /// + /// This method may fail if the target pin id differs from the current dynamic one. + pub fn try_into_pin(self) -> Result, Self> { + if P2::ID == self.id { + Ok(Pin { + id: P2::new(), + function: self.function, + pull_type: self.pull_type, + }) + } else { + Err(self) + } + } +} +impl Pin { + /// Try to set the pin's function. + /// + /// This method may fail if the requested function is not supported by the pin, eg `FunctionXiP` + /// on a gpio from `Bank0`. + pub fn try_set_function(&mut self, function: DynFunction) -> Result<(), func::InvalidFunction> { + use func::func_sealed::Function; + if !function.is_valid(&self.id) { + return Err(func::InvalidFunction); + } else if function != self.function.as_dyn() { + pin::set_function(&self.id, function); + self.function = function; + } + Ok(()) + } + /// Gets the pin's function. + pub fn function(&self) -> DynFunction { + use func::func_sealed::Function; + self.function.as_dyn() + } +} + +impl Pin { + /// Set the pin's pull type. + pub fn set_pull_type(&mut self, pull_type: DynPullType) { + if pull_type != self.pull_type { + pin::set_pull_type(&self.id, pull_type); + self.pull_type = pull_type; + } + } +} + +//============================================================================== +// Embedded-HAL +//============================================================================== + +/// GPIO error type. +pub type Error = core::convert::Infallible; + +impl embedded_hal::digital::v2::OutputPin for Pin, P> +where + I: PinId, + P: PullType, +{ + type Error = Error; + + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } +} + +impl embedded_hal::digital::v2::InputPin for Pin, P> +where + I: PinId, + P: PullType, +{ + type Error = Error; + + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +impl embedded_hal::digital::v2::StatefulOutputPin for Pin, P> +where + I: PinId, + P: PullType, +{ + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } +} + +impl embedded_hal::digital::v2::ToggleableOutputPin for Pin, P> +where + I: PinId, + P: PullType, +{ + type Error = Error; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } +} +impl embedded_hal::digital::v2::InputPin for Pin, P> +where + I: PinId, + P: PullType, +{ + type Error = Error; + + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +//============================================================================== +// Pins +//============================================================================== + +/// Default type state of a pin after reset of the pads, io and sio. +pub trait DefaultTypeState: crate::typelevel::Sealed { + /// Default function. + type Function: Function; + /// Default pull type. + type PullType: PullType; +} + +macro_rules! gpio { + ( $bank:ident:$prefix:ident, [ $(($id:expr, $pull_type:ident, $func:ident)),* ] ) => { + paste::paste!{ + #[doc = "Pin bank " [<$bank>] ] + pub mod [<$bank:snake>] { + use crate::sio::[]; + use pac::{[],[]}; + use super::{Pin, pin, pull, func}; + $(pub use super::pin::[<$bank:lower>]::[<$prefix $id>];)* + + $( + impl super::DefaultTypeState for [<$prefix $id>] { + type Function = super::[]; + type PullType = super::[]; + } + )* + gpio!(struct: $bank $prefix $([<$prefix $id>], $id, $func, $pull_type),*); + + impl Pins { + /// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s + pub fn new(io : [], pads: [], sio: [], reset : &mut pac::RESETS) -> Self { + use crate::resets::SubsystemReset; + pads.reset_bring_down(reset); + io.reset_bring_down(reset); + + { + use $crate::gpio::pin::DynBankId; + // SAFETY: this function owns the whole bank that will be affected. + let sio = unsafe { &*pac::SIO::PTR }; + if DynBankId::$bank == DynBankId::Bank0 { + sio.gpio_oe.reset(); + sio.gpio_out.reset(); + } else { + sio.gpio_hi_oe.reset(); + sio.gpio_hi_out.reset(); + } + } + + io.reset_bring_up(reset); + pads.reset_bring_up(reset); + gpio!(members: io, pads, sio, $(([<$prefix $id>], $func, $pull_type)),+) + } + } + } + } + }; + (struct: $bank:ident $prefix:ident $($PXi:ident, $id:expr, $func:ident, $pull_type:ident),*) => { + paste::paste!{ + /// Collection of all the individual [`Pin`]s + pub struct Pins { + _io: [], + _pads: [], + _sio: [], + $( + #[doc = "Pin " [<$PXi>] ] + pub [<$PXi:snake>]: Pin]::[<$prefix $id>] , func::[], pull::[]>, + )* + } + } + }; + (members: $io:ident, $pads:ident, $sio:ident, $(($PXi:ident, $func:ident, $pull_type:ident)),+) => { + paste::paste!{ + Self { + _io: $io, + _pads: $pads, + _sio: $sio, + $( + [<$PXi:snake>]: Pin { + id: [<$PXi>] (()), + function: func::[] (()), + pull_type: pull::[] (()) + }, + )+ + } + } + }; +} + +gpio!( + Bank0: Gpio, + [ + (0, Down, Null), + (1, Down, Null), + (2, Down, Null), + (3, Down, Null), + (4, Down, Null), + (5, Down, Null), + (6, Down, Null), + (7, Down, Null), + (8, Down, Null), + (9, Down, Null), + (10, Down, Null), + (11, Down, Null), + (12, Down, Null), + (13, Down, Null), + (14, Down, Null), + (15, Down, Null), + (16, Down, Null), + (17, Down, Null), + (18, Down, Null), + (19, Down, Null), + (20, Down, Null), + (21, Down, Null), + (22, Down, Null), + (23, Down, Null), + (24, Down, Null), + (25, Down, Null), + (26, Down, Null), + (27, Down, Null), + (28, Down, Null), + (29, Down, Null) + ] +); + +gpio!( + Qspi: Qspi, + [ + (Sclk, Down, Null), + (Ss, Up, Null), + (Sd0, None, Null), + (Sd1, None, Null), + (Sd2, None, Null), + (Sd3, None, Null) + ] +); + +pub use bank0::Pins; + +//============================================================================== +// 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: Sealed +where + Self: typelevel::Sealed, + Self: typelevel::Is>, + ::Id: ValidFunction<::Function>, +{ + /// [`PinId`] of the corresponding [`Pin`] + type Id: PinId; + /// [`func::Function`] of the corresponding [`Pin`] + type Function: func::Function; + /// [`PullType`] of the corresponding [`Pin`] + type Pull: PullType; +} + +impl Sealed for Pin +where + I: PinId + func::ValidFunction, + F: func::Function, + P: PullType, +{ +} + +impl AnyPin for Pin +where + I: func::ValidFunction, + F: func::Function, + P: PullType, +{ + type Id = I; + type Function = F; + type Pull = P; +} + +/// Type alias to recover the specific [`Pin`] type from an implementation of [`AnyPin`]. +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +pub type SpecificPin

= Pin<

::Id,

::Function,

::Pull>; + +//============================================================================== +// bsp_pins helper macro +//============================================================================== + +/// 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`]. +/// +/// # Example +/// Calling the macro like this: +/// ```rust +/// use rp2040_hal::bsp_pins; +/// bsp_pins! { +/// #[cfg(feature = "gpio")] +/// Gpio0 { +/// /// Doc gpio0 +/// name: gpio0, +/// aliases: { FunctionPio0, PullNone: PioPin } +/// }, +/// Gpio1 { +/// name: led, +/// aliases: { FunctionPwm, PullDown: LedPwm } +/// }, +/// } +/// ``` +/// +/// Is roughly equivalent to the following source code (excluding the docs strings below): +/// ``` +/// use ::rp2040_hal as hal; +/// use hal::gpio; +/// pub struct Pins { +/// /// Doc gpio0 +/// #[cfg(feature = "gpio")] +/// pub gpio0: gpio::Pin< +/// gpio::bank0::Gpio0, +/// ::Function, +/// ::PullType, +/// >, +/// pub led: gpio::Pin< +/// gpio::bank0::Gpio1, +/// ::Function, +/// ::PullType, +/// >, +/// } +/// impl Pins { +/// #[inline] +/// pub fn new( +/// io: hal::pac::IO_BANK0, +/// pads: hal::pac::PADS_BANK0, +/// sio: hal::sio::SioGpioBank0, +/// reset: &mut hal::pac::RESETS, +/// ) -> Self { +/// let mut pins = gpio::Pins::new(io, pads, sio, reset); +/// Self { +/// #[cfg(feature = "gpio")] +/// gpio0: pins.gpio0, +/// led: pins.gpio1, +/// } +/// } +/// } +/// pub type PioPin = gpio::Pin; +/// pub type LedPwm = gpio::Pin; +/// ``` +#[macro_export] +macro_rules! bsp_pins { + ( + $( + $( #[$id_cfg:meta] )* + $Id:ident { + $( #[$name_doc:meta] )* + name: $name:ident $(,)? + $( + aliases: { + $( + $( #[$alias_cfg:meta] )* + $Function:ty, $PullType: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. + /// + /// To enable specific functions of the pins you can use the + /// [rp2040_hal::gpio::pin::Pin::into_function] function with + /// one of: + /// - [rp2040_hal::gpio::FunctionI2C] + /// - [rp2040_hal::gpio::FunctionPwm] + /// - [rp2040_hal::gpio::FunctionSpi] + /// - [rp2040_hal::gpio::FunctionXip] + /// - [rp2040_hal::gpio::FunctionPio0] + /// - [rp2040_hal::gpio::FunctionPio1] + /// - [rp2040_hal::gpio::FunctionUart] + /// + /// like this: + ///```no_run + /// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; + /// + /// let mut peripherals = pac::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); + /// + /// let _spi_sclk = pins.gpio2.into_function::(); + /// let _spi_mosi = pins.gpio3.into_function::(); + /// let _spi_miso = pins.gpio4.into_function::(); + ///``` + /// + /// **See also [rp2040_hal::gpio] for more in depth information about this**! + pub struct Pins { + $( + $( #[$id_cfg] )* + $( #[$name_doc] )* + pub $name: $crate::gpio::Pin< + $crate::gpio::bank0::$Id, + <$crate::gpio::bank0::$Id as $crate::gpio::DefaultTypeState>::Function, + <$crate::gpio::bank0::$Id as $crate::gpio::DefaultTypeState>::PullType, + >, + )+ + } + + 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 $Function $PullType $Alias )+ )? ); + )+ + } + }; + ( @aliases, $( $( $( #[$attr:meta] )* $Id:ident $Function:ident $PullType: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::$Function, + $crate::gpio::$PullType + >; + )+ + )? + } + }; +} + +//============================================================================== +// InOutPin +//============================================================================== + +/// A wrapper [`AnyPin`]`` emulating open-drain function. +/// +/// This wrapper implements both InputPin and OutputPin, to simulate an open-drain pin as needed for +/// example by the wire protocol the DHT11 sensor speaks. +/// +/// +pub struct InOutPin { + inner: Pin, +} + +impl InOutPin { + /// Create a new wrapper + pub fn new(inner: T) -> InOutPin + where + T::Id: ValidFunction, + { + let mut inner = inner.into(); + inner.set_output_enable_override(OutputEnableOverride::Disable); + + // into Pin<_, FunctionSioOutput, _> + let inner = inner.into_push_pull_output_in_state(PinState::Low); + + Self { + inner: inner.into(), + } + } + + /// Releases the pin reverting to its previous function. + pub fn release(self) -> T { + let mut inner = self.inner.into(); + inner.set_output_enable_override(OutputEnableOverride::DontInvert); + T::from(inner) + } +} + +impl embedded_hal::digital::v2::InputPin for InOutPin { + type Error = Error; + fn is_high(&self) -> Result { + self.inner.is_high() + } + fn is_low(&self) -> Result { + self.inner.is_low() + } +} + +impl embedded_hal::digital::v2::OutputPin for InOutPin { + type Error = Error; + fn set_low(&mut self) -> Result<(), Error> { + // The pin is already set to output low but this is inhibited by the override. + self.inner + .set_output_enable_override(OutputEnableOverride::Enable); + Ok(()) + } + fn set_high(&mut self) -> Result<(), Error> { + // To set the open-drain pin to high, just disable the output driver by configuring the + // output override. That way, the DHT11 can still pull the data line down to send its response. + self.inner + .set_output_enable_override(OutputEnableOverride::Disable); + Ok(()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +mod eh1 { + use eh1_0_alpha::digital::{ + ErrorType, InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin, + }; + + use super::{Error, FunctionSio, Pin, PinId, PullType, SioConfig, SioInput, SioOutput}; + + impl ErrorType for Pin, P> + where + I: PinId, + P: PullType, + S: SioConfig, + { + type Error = Error; + } + + impl OutputPin for Pin, P> + where + I: PinId, + P: PullType, + { + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } + } + + impl InputPin for Pin, P> + where + I: PinId, + P: PullType, + { + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } + } + + impl StatefulOutputPin for Pin, P> + where + I: PinId, + P: PullType, + { + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } + } + + impl ToggleableOutputPin for Pin, P> + where + I: PinId, + P: PullType, + { + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } + } + impl InputPin for Pin, P> + where + I: PinId, + P: PullType, + { + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } + } +} diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index ce00d9029..7579bebf6 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -1,1275 +1,150 @@ -//! # Type-level module for GPIO pins +//! ## Note 1 //! -//! Based heavily on `atsamd-hal`. +//! QSPI registers are ordered differently on pads, io & SIO: +//! - PADS: sclk, sd0, sd1, sd2, sd3, ss +//! - IO bank: sclk, ss, sd0, sd1, sd2, sd3 +//! - SIO: sclk, ss, sd0, sd1, sd2, sd3 //! -//! 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`], -//! [`PullUp`] and [`BusKeep`] are its type-level variants. +//! This HAL will use the order shared by IO bank & SIO. The main reason for that being the bit +//! shift operation used in SIO and interrupt related registers. //! -//! 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. +//! ## Note 2 //! -//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and -//! [`PinMode`]. +//! The SWD and SWCLK pin only appear on the pad control and cannot be used as gpio. +//! They are therefore absent from this implementation. //! -//! A `PinId` identifies a pin by it's group (BANK0 or QSPI) and pin number. Each -//! `PinId` instance is named according to its datasheet identifier, e.g. -//! [`Gpio0`](`bank0::Gpio0`). +//! ## Note 3 //! -//! A `PinMode` represents the various pin modes. The available `PinMode` -//! variants are [`Disabled`], [`Input`], [`Output`] and -//! [`Function`], each with its own corresponding configurations. +//! Dues to limitations in svd2rust and svdtools (and their shared dependencies) it is not possible +//! to fully express the relations between the gpio registers in the different banks on the RP2040 +//! at the PAC level. //! -//! 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. +//! These limitations are respectively: +//! - Inability to derive register with different reset values +//! - Inability to derive from path including clusters and/or arrays //! -//! 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. -//! -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::Pins, sio::Sio}; -//! let mut peripherals = pac::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. -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, FloatingInput}, sio::Sio}; -//! # let mut peripherals = pac::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); -//! // 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 = gpio12.into_mode::(); -//! // Specify the target type and use `.into_mode()` -//! let gpio12: Pin = gpio12.into_mode(); -//! ``` -//! -//! # 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 -//! -//! ```no_run -//! use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; -//! use embedded_hal::digital::v2::OutputPin; -//! -//! let mut peripherals = pac::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); -//! -//! let mut pin12: Pin = pins.gpio12.into_mode(); -//! pin12.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::{ - InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, - OutputOverride, 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; -#[cfg(feature = "eh1_0_alpha")] -use eh1_0_alpha::digital as eh1; -pub use embedded_hal::digital::v2::PinState; -use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - -/// Type-level marker for tracking which pin modes are valid for which pins -pub trait ValidPinMode: Sealed + PinMode {} - -//============================================================================== -// 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 {} -/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] -pub enum BusKeep {} - -impl Sealed for Floating {} -impl Sealed for PullDown {} -impl Sealed for PullUp {} -impl Sealed for BusKeep {} - -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; -} -impl DisabledConfig for BusKeep { - const DYN: DynDisabled = DynDisabled::BusKeep; -} - -/// Type-level variant of [`PinMode`] for disabled modes -/// -/// Type `C` is one of four configurations: [`Floating`], [`PullDown`], -/// [`PullUp`] or [`BusKeep`] -pub struct Disabled { - cfg: PhantomData, -} - -impl Sealed for Disabled {} - -/// Type-level variant of [`PinMode`] for floating disabled mode -pub type FloatingDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for pull-down disabled mode -pub type PullDownDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for pull-up disabled mode -pub type PullUpDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for bus keep disabled mode -pub type BusKeepDisabled = Disabled; - -impl ValidPinMode for Disabled {} - -//============================================================================== -// Input configurations -//============================================================================== - -/// Type-level `enum` for input configurations -pub trait InputConfig: Sealed { - /// Corresponding [`DynInput`](super::DynInput) - const DYN: DynInput; -} - -impl InputConfig for Floating { - const DYN: DynInput = DynInput::Floating; -} -impl InputConfig for PullDown { - const DYN: DynInput = DynInput::PullDown; -} -impl InputConfig for PullUp { - const DYN: DynInput = DynInput::PullUp; -} -impl InputConfig for BusKeep { - const DYN: DynInput = DynInput::BusKeep; -} - -/// Type-level variant of [`PinMode`] for input modes -/// -/// Type `C` is one of four input configurations: [`Floating`], [`PullDown`], -/// [`PullUp`] or [`BusKeep`] -pub struct Input { - cfg: PhantomData, -} - -impl Sealed for Input {} - -/// Type-level variant of [`PinMode`] for floating input mode -pub type FloatingInput = Input; - -/// Type-level variant of [`PinMode`] for pull-down input mode -pub type PullDownInput = Input; - -/// Type-level variant of [`PinMode`] for pull-up input mode -pub type PullUpInput = Input; - -/// Type-level variant of [`PinMode`] for bus keep input mode -pub type BusKeepInput = Input; - -impl ValidPinMode for Input {} - -//============================================================================== -// Output configurations -//============================================================================== - -/// Type-level `enum` for output configurations -pub trait OutputConfig: Sealed { - /// Corresponding [`DynOutput`](super::DynOutput) - const DYN: DynOutput; -} - -/// Type-level variant of [`OutputConfig`] for a push-pull configuration -pub enum PushPull {} -/// Type-level variant of [`OutputConfig`] for a readable push-pull -/// configuration -pub enum Readable {} - -impl Sealed for PushPull {} -impl Sealed for Readable {} - -impl OutputConfig for PushPull { - const DYN: DynOutput = DynOutput::PushPull; -} -impl OutputConfig for Readable { - const DYN: DynOutput = DynOutput::Readable; -} - -/// Type-level variant of [`PinMode`] for output modes -/// -/// Type `C` is one of two output configurations: [`PushPull`] or [`Readable`] -pub struct Output { - cfg: PhantomData, -} - -impl Sealed for Output {} - -/// Type-level variant of [`PinMode`] for push-pull output mode -pub type PushPullOutput = Output; - -/// Type-level variant of [`PinMode`] for readable push-pull output mode -pub type ReadableOutput = Output; - -impl ValidPinMode for Output {} - -// - -/// Type-level variant of [`PinMode`] for alternate peripheral functions -/// -/// Type `C` is an [`FunctionConfig`] -pub struct Function { - cfg: PhantomData, -} - -impl Sealed for Function {} - -/// Type-level enum for alternate peripheral function configurations -pub trait FunctionConfig: Sealed { - /// Corresponding [`DynFunction`](super::DynFunction) - const DYN: DynFunction; -} - -macro_rules! function { - ( - $( - $Func:ident - ),+ - ) => { - $crate::paste::paste! { - $( - #[ - doc = "Type-level variant of [`FunctionConfig`] for \ - alternate peripheral function " $Func - ] - pub enum $Func {} - impl Sealed for $Func {} - impl FunctionConfig for $Func { - const DYN: DynFunction = DynFunction::$Func; - } - #[ - doc = "Type-level variant of [`PinMode`] for alternate \ - peripheral function [`" $Func "`]" - ] - pub type [] = Function<$Func>; - )+ - } - }; -} - -function!(Spi, Xip, Uart, I2C, Pwm, Clock, UsbAux); - -impl Sealed for pac::PIO0 {} -impl FunctionConfig for pac::PIO0 { - const DYN: DynFunction = DynFunction::Pio0; -} -/// Type-level variant of [`PinMode`] for alternate peripheral function `pac::PIO0` -pub type FunctionPio0 = Function; - -impl Sealed for pac::PIO1 {} -impl FunctionConfig for pac::PIO1 { - const DYN: DynFunction = DynFunction::Pio1; -} -/// Type-level variant of [`PinMode`] for alternate peripheral function `pac::PIO1` -pub type FunctionPio1 = Function; - -//============================================================================== -// Pin modes -//============================================================================== - -/// Type-level `enum` representing pin modes -pub trait PinMode: Sealed + Sized { - /// Corresponding [`DynPinMode`](super::DynPinMode) - const DYN: DynPinMode; -} - -impl PinMode for Disabled { - const DYN: DynPinMode = DynPinMode::Disabled(C::DYN); -} - -impl PinMode for Input { - const DYN: DynPinMode = DynPinMode::Input(C::DYN); -} - -impl PinMode for Output { - const DYN: DynPinMode = DynPinMode::Output(C::DYN); -} - -impl PinMode for Function { - const DYN: DynPinMode = DynPinMode::Function(C::DYN); -} - -//============================================================================== -// Pin IDs -//============================================================================== - -/// Type-level `enum` for pin IDs -pub trait PinId: Sealed { - /// Corresponding [`DynPinId`](super::DynPinId) - const DYN: DynPinId; - /// [`PinMode`] at reset - type Reset; -} - -macro_rules! pin_id { - ($Group:ident, $Id:ident, $NUM:literal, $reset : ident) => { - #[doc = "Pin ID representing pin "] - pub enum $Id {} - impl Sealed for $Id {} - impl PinId for $Id { - type Reset = $reset; - const DYN: DynPinId = DynPinId { - group: DynGroup::$Group, - num: $NUM, - }; - } - }; -} - -//============================================================================== -// OptionalPinId -//============================================================================== - -/// Type-level equivalent of `Option` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPinId: Sealed {} - -impl OptionalPinId for NoneT {} - -impl OptionalPinId for I {} - -/// Type-level equivalent of `Some(PinId)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePinId: OptionalPinId + PinId + Sealed {} - -impl SomePinId for I {} - -//============================================================================== -// Registers -//============================================================================== - -/// Provide a safe register interface for [`Pin`]s -/// -/// This `struct` takes ownership of a [`PinId`] and provides an API to -/// access the corresponding registers. -struct Registers { - id: PhantomData, -} - -// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that -// each pin is a singleton, so this implementation is safe. -unsafe impl RegisterInterface for Registers { - #[inline] - fn id(&self) -> DynPinId { - I::DYN - } -} - -impl Registers { - /// Create a new instance of [`Registers`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`PinId`] - #[inline] - unsafe fn new() -> Self { - Registers { id: PhantomData } - } - - /// Provide a type-level equivalent for the - /// [`RegisterInterface::change_mode`] method. - #[inline] - fn change_mode>(&mut self) { - RegisterInterface::do_change_mode(self, M::DYN); - } -} - -//============================================================================== -// Pin -//============================================================================== - -/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types -pub struct Pin -where - I: PinId, - M: ValidPinMode, -{ - regs: Registers, - mode: PhantomData, -} - -impl Pin -where - I: PinId, - M: ValidPinMode, -{ - /// Create a new [`Pin`] - /// - /// # Safety - /// - /// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be - /// at most one corresponding [`Pin`] in existence at any given time. - /// Violating this requirement is `unsafe`. - #[inline] - pub(crate) unsafe fn new() -> Pin { - Pin { - regs: Registers::new(), - mode: PhantomData, - } - } - - /// Return the [`DynPinId`] corresponding to this pin. - /// - /// To get the numeric pin number, access the num field - /// directly: - /// - /// ```no_run - /// # use rp2040_hal::gpio::{Pin, PinId, PinMode, ValidPinMode}; - /// # fn get_id> (pin: Pin) -> u8 { - /// pin.id().num - /// # } - /// ```` - #[inline] - pub fn id(&self) -> DynPinId { - I::DYN - } - - /// Convert the pin to the requested [`PinMode`] - #[inline] - pub fn into_mode>(mut self) -> Pin { - if N::DYN != M::DYN { - self.regs.change_mode::(); - } - // Safe because we drop the existing Pin - unsafe { Pin::new() } - } - - /// Disable the pin and set it to float - #[inline] - pub fn into_floating_disabled(self) -> Pin { - self.into_mode() - } - - /// Disable the pin and set it to pull down - #[inline] - pub fn into_pull_down_disabled(self) -> Pin { - self.into_mode() - } - - /// Disable the pin and set it to pull up - #[inline] - pub fn into_pull_up_disabled(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a floating input - #[inline] - pub fn into_floating_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled down input - #[inline] - pub fn into_pull_down_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled up input - #[inline] - pub fn into_pull_up_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a bus keep input - #[inline] - pub fn into_bus_keep_input(self) -> Pin { - self.into_mode() - } +//! This modules bridges that gap by adding a trait definition per register type and implementing it +//! for each of the relevant registers. - /// Configure the pin to operate as a push-pull output. - /// - /// If you want to specify the initial pin state, use [`Pin::into_push_pull_output_in_state`]. - #[inline] - pub fn into_push_pull_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a push-pull output, specifying an initial - /// state which is applied immediately. - #[inline] - pub fn into_push_pull_output_in_state(mut self, state: PinState) -> Pin { - match state { - PinState::High => self._set_high(), - PinState::Low => self._set_low(), - } - self.into_mode() - } - - /// Configure the pin to operate as a readable push pull output. - /// - /// If you want to specify the initial pin state, use [`Pin::into_readable_output_in_state`]. - #[inline] - pub fn into_readable_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a readable push pull output, specifying an initial - /// state which is applied immediately. - #[inline] - pub fn into_readable_output_in_state(mut self, state: PinState) -> Pin { - match state { - PinState::High => self._set_high(), - PinState::Low => self._set_low(), - } - 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) - } - - /// Clear interrupt. - #[inline] - pub fn clear_interrupt(&mut self, interrupt: Interrupt) { - self.regs.clear_interrupt(interrupt); - } - - /// Interrupt status. - #[inline] - pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { - self.regs.interrupt_status(interrupt) - } - - /// Is interrupt enabled. - #[inline] - pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_enabled(interrupt) - } - - /// Enable or disable interrupt. - #[inline] - pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - self.regs.set_interrupt_enabled(interrupt, enabled); - } - - /// Is interrupt forced. - #[inline] - pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_forced(interrupt) - } +use super::{DynFunction, DynPullType}; - /// Force or release interrupt. - #[inline] - pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - self.regs.set_interrupt_forced(interrupt, forced); - } - - /// Set the interrupt override. - #[inline] - pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { - self.regs.set_interrupt_override(override_value); - } - - /// Set the input override. - #[inline] - pub fn set_input_override(&mut self, override_value: InputOverride) { - self.regs.set_input_override(override_value); - } - - /// Set the output enable override. - #[inline] - pub fn set_output_enable_override(&mut self, override_value: OutputEnableOverride) { - self.regs.set_output_enable_override(override_value); - } - - /// Set the output override. - #[inline] - pub fn set_output_override(&mut self, override_value: OutputOverride) { - self.regs.set_output_override(override_value); - } +pub(crate) mod pin_sealed; - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_low(&self) -> bool { - self.regs.read_pin() == false - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_high(&self) -> bool { - self.regs.read_pin() == true - } - - #[inline] - pub(crate) fn _set_low(&mut self) { - self.regs.write_pin(false); - } - - #[inline] - pub(crate) fn _set_high(&mut self) { - self.regs.write_pin(true); - } - - #[inline] - pub(crate) fn _toggle(&mut self) { - self.regs.toggle_pin(); - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_set_low(&self) -> bool { - self.regs.read_out_pin() == false - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_set_high(&self) -> bool { - self.regs.read_out_pin() == true - } -} - -//============================================================================== -// AnyPin -//============================================================================== - -/// Type class for [`Pin`] types -/// -/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for -/// [`Pin`] types. See the `AnyKind` documentation for more details on the -/// pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -/// [type class]: crate::typelevel#type-classes -pub trait AnyPin -where - Self: Sealed, - Self: Is>, - ::Mode: ValidPinMode<::Id>, -{ - /// [`PinId`] of the corresponding [`Pin`] - type Id: PinId; - /// [`PinMode`] of the corresponding [`Pin`] - type Mode: PinMode; -} - -impl Sealed for Pin -where - I: PinId, - M: ValidPinMode, -{ +/// Type-level `enum` for the pin Id (pin number + bank). +pub trait PinId: pin_sealed::PinIdOps { + /// This pin as a `DynPinId`. + fn as_dyn(&self) -> DynPinId; } -impl AnyPin for Pin -where - I: PinId, - M: ValidPinMode, -{ - type Id = I; - type Mode = M; +/// Value-level `enum` for the pin's bank. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DynBankId { + /// GPIO Pins bank + Bank0, + /// QSPI Pins bank + Qspi, } -/// Type alias to recover the specific [`Pin`] type from an implementation of -/// [`AnyPin`] -/// -/// See the [`AnyKind`] documentation for more details on the pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -pub type SpecificPin

= Pin<

::Id,

::Mode>; - -//============================================================================== -// Optional pins -//============================================================================== - -/// Type-level equivalent of `Option` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPin: Sealed { +/// Value-level representation for the pin (bank + id). +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct DynPinId { #[allow(missing_docs)] - type Id: OptionalPinId; + pub bank: DynBankId, #[allow(missing_docs)] - const IS_NONE: bool; -} - -impl OptionalPin for NoneT { - type Id = NoneT; - const IS_NONE: bool = true; -} - -impl OptionalPin for P { - type Id = P::Id; - /// Value-level translation of the Type-level equivalent of [`Option::is_none`]. - const IS_NONE: bool = false; -} - -/// Type-level equivalent of `Some(PinId)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePin: OptionalPin + Sealed { - /// Value-level translation of the Type-level equivalent of [`Option::is_some`]. - const IS_SOME: bool; -} -impl> SomePin for P { - const IS_SOME: bool = !P::IS_NONE; + pub num: u8, } - -//============================================================================== -// Embedded HAL traits -//============================================================================== - -impl OutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high(); - Ok(()) - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low(); - Ok(()) - } -} - -impl InputPin for Pin -where - I: PinId, -{ - type Error = Infallible; - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) +impl PinId for DynPinId { + fn as_dyn(&self) -> DynPinId { + *self } } -impl InputPin for Pin> -where - I: PinId, - C: InputConfig, -{ - type Error = Infallible; - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -impl ToggleableOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle(); - Ok(()) - } -} - -impl StatefulOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn is_set_high(&self) -> Result { - Ok(self._is_set_high()) - } - #[inline] - fn is_set_low(&self) -> Result { - Ok(self._is_set_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::OutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[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(()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for Pin -where - I: PinId, -{ - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for Pin> -where - I: PinId, - C: InputConfig, -{ - type Error = Infallible; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for Pin> -where - I: PinId, - C: InputConfig, -{ - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ToggleableOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle(); - Ok(()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::StatefulOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn is_set_high(&self) -> Result { - Ok(self._is_set_high()) - } - #[inline] - fn is_set_low(&self) -> Result { - Ok(self._is_set_low()) - } -} - -//============================================================================== -// Pin definitions -//============================================================================== - -macro_rules! gpio { - ($Group:ident, [ $($Func:ident),+ ], [ - $($PXi:ident: ($i:expr, $is:expr, $reset:ident $(, [ $($PinFunc:ident),+ ])? )),+ - ]) => { - $crate::paste::paste! { - #[doc = "GPIO Pins for " $Group] - pub mod [<$Group:lower>] { - use crate::sio::[]; - use pac::{[],[]}; - - /// Bank0 bank pin id - pub trait BankPinId {} - use crate::typelevel::Sealed; - use crate::gpio::dynpin::{DynGroup,DynPinId}; - - // FIXME: Somehow just import what we need - #[allow(unused_imports)] - use super::{PullDownDisabled,PullUpDisabled,FloatingDisabled,BusKeepDisabled}; - - use super::{Pin,PinId}; - use crate::resets::SubsystemReset; - - $( - pin_id!($Group, $PXi, $i, $reset); - impl BankPinId for $PXi {} - - $( $(impl super::ValidPinMode<$PXi> for super::Function {})+ )* - )+ - - /// Collection of all the individual [`Pin`]s - pub struct Pins { - _io: [], - _pads: [], - _sio: [], - $( - #[doc = "Pin " $PXi] - pub [<$PXi:lower>] : Pin<$PXi,<$PXi as PinId>::Reset>, - )+ - } - - impl Pins { - /// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s - pub fn new(io : [], pads: [], sio: [], reset : &mut pac::RESETS) -> Self { - pads.reset_bring_down(reset); - io.reset_bring_down(reset); - - io.reset_bring_up(reset); - pads.reset_bring_up(reset); - unsafe { - Self { - _io: io, - _pads: pads, - _sio: sio, - $( - [<$PXi:lower>]: Pin::new(), - )+ - } +macro_rules! pin_ids { + ($bank:ident: $($id:expr;$name:ident),*) => { + pin_ids!($bank as $bank: $($id;$name),*); + }; + ($bank:ident as $prefix:ident: $($id:tt),*) => { + pin_ids!($bank as $prefix: $($id;$id),*); + }; + ($bank:ident as $prefix:ident: $($id:expr;$name:tt),*) => { + paste::paste!{ + $( + #[doc = "Type level variant for the pin `" $name "` in bank `" $prefix "`."] + pub struct [<$prefix $name>] (pub(crate) ()); + impl crate::typelevel::Sealed for [<$prefix $name>] {} + impl PinId for [<$prefix $name>] { + fn as_dyn(&self) -> DynPinId { + DynPinId { + bank: DynBankId::$bank, + num: $id } } } - - $( impl super::ValidPinMode for super::[] {} )+ - } + impl pin_sealed::TypeLevelPinId for [<$prefix $name>] { + const ID: DynPinId = DynPinId { + bank: DynBankId::$bank, + num: $id + }; + + fn new() -> Self { + Self(()) + } + } + )* } - } + }; } - -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 -/// Calling the macro like this: -/// ```rust -/// use rp2040_hal::bsp_pins; -/// bsp_pins! { -/// #[cfg(feature = "gpio")] -/// Gpio0 { -/// /// Doc gpio0 -/// name: gpio0, -/// aliases: { FunctionPio0: PioPin } -/// }, -/// Gpio1 { -/// name: led, -/// aliases: { FunctionPwm: LedPwm } -/// }, -/// } -/// ``` -/// -/// Is roughly equivalent to the following source code (excluding the docs strings below): -/// ``` -/// use ::rp2040_hal as hal; -/// use hal::gpio; -/// pub struct Pins { -/// #[cfg(feature = "gpio")] -/// /// Doc gpio0 -/// pub gpio0: gpio::Pin::Reset>, -/// pub led: gpio::Pin::Reset>, -/// } -/// impl Pins { -/// #[inline] -/// pub fn new( -/// io: hal::pac::IO_BANK0, -/// pads: hal::pac::PADS_BANK0, -/// sio: hal::sio::SioGpioBank0, -/// reset: &mut hal::pac::RESETS, -/// ) -> Self { -/// let mut pins = gpio::Pins::new(io, pads, sio, reset); -/// Self { -/// #[cfg(feature = "gpio")] -/// gpio0: pins.gpio0, -/// led: pins.gpio1, -/// } -/// } -/// } -/// pub type PioPin = gpio::Pin; -/// pub const PIO_PIN_ID: gpio::DynPinId = ::DYN; -/// pub const PIO_PIN_MODE: gpio::DynPinMode = ::DYN; -/// pub type LedPwm = gpio::Pin; -/// pub const LED_PWM_ID: gpio::DynPinId = ::DYN; -/// pub const LED_PWM_MODE: gpio::DynPinMode = ::DYN; -/// ``` -#[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. - /// - /// To enable specific functions of the pins you can use the - /// [rp2040_hal::gpio::pin::Pin::into_mode] function with - /// one of: - /// - [rp2040_hal::gpio::pin::FunctionI2C] - /// - [rp2040_hal::gpio::pin::FunctionPwm] - /// - [rp2040_hal::gpio::pin::FunctionSpi] - /// - [rp2040_hal::gpio::pin::FunctionXip] - /// - [rp2040_hal::gpio::pin::FunctionPio0] - /// - [rp2040_hal::gpio::pin::FunctionPio1] - /// - [rp2040_hal::gpio::pin::FunctionUart] - /// - /// like this: - ///```no_run - /// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; - /// - /// let mut peripherals = pac::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); - /// - /// let _spi_sclk = pins.gpio2.into_mode::(); - /// let _spi_mosi = pins.gpio3.into_mode::(); - /// let _spi_miso = pins.gpio4.into_mode::(); - ///``` - /// - /// **See also [rp2040_hal::gpio::pin] for more in depth information - /// about this**! - 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>], - )+ - } +/// Bank of all the GPIOs. +pub mod bank0 { + use super::{pin_sealed, DynBankId, DynPinId, PinId}; + pin_ids!(Bank0 as Gpio: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29); +} +/// Bank of the QSPI related pins. +pub mod qspi { + use super::{pin_sealed, DynBankId, DynPinId, PinId}; + pin_ids!(Qspi: 0;Sclk, 1;Ss, 2;Sd0, 3;Sd1, 4;Sd2, 5;Sd3); +} + +pub(crate) fn set_function(pin: &P, function: DynFunction) { + use pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A; + let funcsel = match function { + DynFunction::Xip => FUNCSEL_A::JTAG, + DynFunction::Spi => FUNCSEL_A::SPI, + DynFunction::Uart => FUNCSEL_A::UART, + DynFunction::I2c => FUNCSEL_A::I2C, + DynFunction::Pwm => FUNCSEL_A::PWM, + DynFunction::Sio(sio) => { + let mask = pin.mask(); + match sio { + crate::gpio::DynSioConfig::Input => { + pin.sio_oe_clr().write(|w| unsafe { w.bits(mask) }); + } + crate::gpio::DynSioConfig::Output => { + pin.sio_oe_set().write(|w| unsafe { w.bits(mask) }); } } - $( - $( #[$id_cfg] )* - $crate::bsp_pins!(@aliases, $( $( $( #[$alias_cfg] )* $Id $Mode $Alias )+ )? ); - )+ + + FUNCSEL_A::SIO } + DynFunction::Pio0 => FUNCSEL_A::PIO0, + DynFunction::Pio1 => FUNCSEL_A::PIO1, + DynFunction::Clock => FUNCSEL_A::CLOCK, + DynFunction::Usb => FUNCSEL_A::USB, + DynFunction::Null => FUNCSEL_A::NULL, }; - ( @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; - )+ - )? - } + pin.io_ctrl().modify(|_, w| w.funcsel().variant(funcsel)); +} +pub(crate) fn set_pull_type(pin: &P, pull_type: DynPullType) { + let (pue, pde) = match pull_type { + DynPullType::None => (false, false), + DynPullType::Up => (true, false), + DynPullType::Down => (false, true), + DynPullType::Both => (true, true), }; + + pin.pad_ctrl() + .modify(|_, w| w.pue().bit(pue).pde().bit(pde)); } diff --git a/rp2040-hal/src/gpio/pin/pin_sealed.rs b/rp2040-hal/src/gpio/pin/pin_sealed.rs new file mode 100644 index 000000000..bee520d32 --- /dev/null +++ b/rp2040-hal/src/gpio/pin/pin_sealed.rs @@ -0,0 +1,192 @@ +use crate::sio::CoreId; + +use super::{DynBankId, DynPinId}; + +pub trait TypeLevelPinId: super::PinId { + const ID: DynPinId; + + fn new() -> Self; +} + +pub trait PinIdOps { + fn mask(&self) -> u32; + fn io_status(&self) -> &pac::io_bank0::gpio::GPIO_STATUS; + fn io_ctrl(&self) -> &pac::io_bank0::gpio::GPIO_CTRL; + fn pad_ctrl(&self) -> &pac::pads_bank0::GPIO; + + fn sio_in(&self) -> &pac::sio::GPIO_IN; + fn sio_out(&self) -> &pac::sio::GPIO_OUT; + fn sio_out_set(&self) -> &pac::sio::GPIO_OUT_SET; + fn sio_out_clr(&self) -> &pac::sio::GPIO_OUT_CLR; + fn sio_out_xor(&self) -> &pac::sio::GPIO_OUT_XOR; + fn sio_oe(&self) -> &pac::sio::GPIO_OE; + fn sio_oe_set(&self) -> &pac::sio::GPIO_OE_SET; + fn sio_oe_clr(&self) -> &pac::sio::GPIO_OE_CLR; + fn sio_oe_xor(&self) -> &pac::sio::GPIO_OE_XOR; + + fn intr(&self) -> (&pac::io_bank0::INTR, usize); + fn proc_ints(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTS, usize); + fn proc_inte(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTE, usize); + fn proc_intf(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTF, usize); + fn dormant_wake_ints(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTS, usize); + fn dormant_wake_inte(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTE, usize); + fn dormant_wake_intf(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTF, usize); +} + +macro_rules! accessor_fns { + (sio $reg:ident) => { + paste::paste! { + fn [](&self) -> &pac::sio::[] { + let pin = self.as_dyn(); + unsafe { + let sio = &*pac::SIO::PTR; + match pin.bank { + DynBankId::Bank0 => &sio.[], + DynBankId::Qspi => core::mem::transmute(&sio.[]), + } + } + } + } + }; + (io $reg:ident) => { + paste::paste! { + fn [](&self) -> &pac::io_bank0::gpio::[] { + let pin = self.as_dyn(); + match pin.bank { + DynBankId::Bank0 => { + let gpio = unsafe { &*pac::IO_BANK0::PTR }; + &gpio.gpio[usize::from(pin.num)].[] + } + DynBankId::Qspi => unsafe { + let qspi = &*pac::IO_QSPI::PTR; + match pin.num { + 0 => core::mem::transmute(&qspi.gpio_qspisclk.[]), + 1 => core::mem::transmute(&qspi.gpio_qspiss.[]), + 2 => core::mem::transmute(&qspi.gpio_qspisd0.[]), + 3 => core::mem::transmute(&qspi.gpio_qspisd1.[]), + 4 => core::mem::transmute(&qspi.gpio_qspisd2.[]), + 5 => core::mem::transmute(&qspi.gpio_qspisd3.[]), + _ => unreachable!("Invalid QSPI bank pin number."), + } + }, + } + } + } + }; + (int $reg:ident) => { + paste::paste! { + fn [](&self, proc: CoreId) -> (&pac::io_bank0::[], usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + match proc { + CoreId::Core0 => &bank.[][usize::from(index)], + CoreId::Core1 => core::mem::transmute(&bank.[][usize::from(index)]), + } + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + match proc { + CoreId::Core0 => core::mem::transmute(&bank.[]), + CoreId::Core1 => core::mem::transmute(&bank.[]), + } + } + }; + (reg, usize::from(offset)) + } + } + } + }; + (dormant $reg:ident) => { + paste::paste! { + fn [< dormant_wake_ $reg:lower>](&self) -> (&pac::io_bank0::[< DORMANT_WAKE_ $reg:upper >], usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + &bank.[< dormant_wake_ $reg:lower>][usize::from(index)] + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + core::mem::transmute(&bank.[< dormant_wake_ $reg:lower>]) + } + }; + (reg, usize::from(offset)) + } + } + } + }; +} +impl PinIdOps for T +where + T: super::PinId, +{ + fn mask(&self) -> u32 { + 1 << self.as_dyn().num + } + fn pad_ctrl(&self) -> &pac::pads_bank0::GPIO { + let pin = self.as_dyn(); + match pin.bank { + DynBankId::Bank0 => { + let gpio = unsafe { &*pac::PADS_BANK0::PTR }; + &gpio.gpio[usize::from(pin.num)] + } + DynBankId::Qspi => unsafe { + let qspi = &*pac::PADS_QSPI::PTR; + match pin.num { + 0 => core::mem::transmute(&qspi.gpio_qspi_sclk), + 1 => core::mem::transmute(&qspi.gpio_qspi_ss), + 2 => core::mem::transmute(&qspi.gpio_qspi_sd0), + 3 => core::mem::transmute(&qspi.gpio_qspi_sd1), + 4 => core::mem::transmute(&qspi.gpio_qspi_sd2), + 5 => core::mem::transmute(&qspi.gpio_qspi_sd3), + _ => unreachable!("Invalid QSPI bank pin number."), + } + }, + } + } + accessor_fns!(io ctrl); + accessor_fns!(io status); + + accessor_fns!(sio in); + accessor_fns!(sio out); + accessor_fns!(sio out_set); + accessor_fns!(sio out_clr); + accessor_fns!(sio out_xor); + accessor_fns!(sio oe); + accessor_fns!(sio oe_set); + accessor_fns!(sio oe_clr); + accessor_fns!(sio oe_xor); + + fn intr(&self) -> (&pac::io_bank0::INTR, usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + &bank.intr[usize::from(index)] + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + core::mem::transmute(&bank.intr) + } + }; + + (reg, usize::from(offset)) + } + } + + accessor_fns!(int ints); + accessor_fns!(int inte); + accessor_fns!(int intf); + + accessor_fns!(dormant ints); + accessor_fns!(dormant inte); + accessor_fns!(dormant intf); +} diff --git a/rp2040-hal/src/gpio/pull.rs b/rp2040-hal/src/gpio/pull.rs new file mode 100644 index 000000000..2c4b16711 --- /dev/null +++ b/rp2040-hal/src/gpio/pull.rs @@ -0,0 +1,57 @@ +use paste::paste; + +pub(crate) mod pull_sealed { + use super::DynPullType; + + pub trait PullType { + fn from(pm: DynPullType) -> Self; + fn as_dyn(&self) -> DynPullType; + } +} +/// Type-level `enum` for pull resitor types. +pub trait PullType: pull_sealed::PullType {} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Value-level `enum` for pull resistor types. +pub enum DynPullType { + #[allow(missing_docs)] + None, + #[allow(missing_docs)] + Up, + #[allow(missing_docs)] + Down, + /// This enables both pull resistor. This setup is described as bus-keep in the RP2040 + /// datasheet. + Both, +} + +impl PullType for DynPullType {} +impl pull_sealed::PullType for DynPullType { + fn from(pm: DynPullType) -> Self { + pm + } + + fn as_dyn(&self) -> DynPullType { + *self + } +} + +macro_rules! pin_pull_type { + ($($pull_type:ident),*) => { + $(paste! { + /// Type-level `variant` of [`PullType`]. + pub struct [](pub(super) ()); + impl PullType for [] {} + impl pull_sealed::PullType for [] { + fn from(_pm: DynPullType) -> Self { + Self(()) + } + fn as_dyn(&self) -> DynPullType { + DynPullType::[<$pull_type>] + } + } + })* + }; +} +pin_pull_type!(None, Up, Down, Both); diff --git a/rp2040-hal/src/gpio/reg.rs b/rp2040-hal/src/gpio/reg.rs deleted file mode 100644 index 6e2350e6f..000000000 --- a/rp2040-hal/src/gpio/reg.rs +++ /dev/null @@ -1,530 +0,0 @@ -// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 -use super::dynpin::{DynGroup, DynPinId}; -use super::{ - InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, - OutputOverride, OutputSlewRate, -}; -use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set}; -use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode}; -use crate::pac; - -//============================================================================== -// ModeFields -//============================================================================== - -/// Collect all fields needed to set the [`PinMode`](super::PinMode) -#[derive(Default)] -struct ModeFields { - inen: bool, - pue: bool, - pde: bool, - - sio_outen: bool, - funcsel: u8, -} - -const SIO_FUNCSEL: u8 = 5; -const NULL_FUNCSEL: u8 = 0x1f; - -impl From for ModeFields { - #[inline] - fn from(mode: DynPinMode) -> Self { - use DynPinMode::*; - let mut fields = Self::default(); - match mode { - Disabled(config) => { - use DynDisabled::*; - fields.funcsel = NULL_FUNCSEL; - fields.sio_outen = false; - fields.inen = false; - - match config { - Floating => (), - PullDown => { - fields.pde = true; - } - PullUp => { - fields.pue = true; - } - BusKeep => { - fields.pde = true; - 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; - } - BusKeep => { - fields.pde = true; - 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, - // SIO is 5, but isn't an alternate function but is instead for using the pin as GPIO - Pio0 => 6, - Pio1 => 7, - Clock => 8, - UsbAux => 9, - }; - fields.inen = true; - if func == I2C { - fields.pue = true; - } - } - }; - - fields - } -} - -/// # Safety -/// -/// Users should only implement the [`id`] function. No default function -/// implementations should be overridden. The implementing type must also have -/// "control" over the corresponding pin ID, i.e. it must guarantee that each -/// pin ID is a singleton -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), - } - } - - /// Clear interrupt. - #[inline] - fn clear_interrupt(&self, interrupt: Interrupt) { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers in total. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - io.intr[num >> 3].write(|w| w.bits(1 << bit_in_reg)); - } - } - - /// Interrupt status. - #[inline] - fn interrupt_status(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - let cpuid = *(pac::SIO::ptr() as *const u32); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = ((num % 8) * 4) + (interrupt as usize); - if cpuid == 0 { - (io.proc0_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Is interrupt enabled. - #[inline] - fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - let cpuid = *(pac::SIO::ptr() as *const u32); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if cpuid == 0 { - (io.proc0_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Enable or disable interrupt. - #[inline] - fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let reg = if cpuid == 0 { - io.proc0_inte[num >> 3].as_ptr() - } else { - io.proc1_inte[num >> 3].as_ptr() - }; - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if enabled { - write_bitmask_set(reg, 1 << bit_in_reg); - } else { - write_bitmask_clear(reg, 1 << bit_in_reg); - } - } - } - - /// Is interrupt forced. - #[inline] - fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if cpuid == 0 { - (io.proc0_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Force or release interrupt. - #[inline] - fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let reg = if cpuid == 0 { - io.proc0_intf[num >> 3].as_ptr() - } else { - io.proc1_intf[num >> 3].as_ptr() - }; - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if forced { - write_bitmask_set(reg, 1 << bit_in_reg); - } else { - write_bitmask_clear(reg, 1 << bit_in_reg); - } - } - } - - /// Set the interrupt override. - #[inline] - fn set_interrupt_override(&self, override_value: InterruptOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.irqover().bits(override_value as u8)); - } - - /// Set the input override. - #[inline] - fn set_input_override(&self, override_value: InputOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.inover().bits(override_value as u8)); - } - - /// Set the output enable override. - #[inline] - fn set_output_enable_override(&self, override_value: OutputEnableOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.oeover().bits(override_value as u8)); - } - - /// Set the output override. - #[inline] - fn set_output_override(&self, override_value: OutputOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.outover().bits(override_value as u8)); - } -} - -#[inline] -fn gpio_change_mode(num: usize, mode: DynPinMode) { - let fields: ModeFields = mode.into(); - let io = unsafe { &(*pac::IO_BANK0::ptr()).gpio[num] }; - let pads = unsafe { &(*pac::PADS_BANK0::ptr()).gpio[num] }; - - pads.write(|w| { - w.pue().bit(fields.pue); - w.pde().bit(fields.pde); - w.ie().bit(fields.inen); - w.od().bit(false) // the SIO oe bit will handle this instead - }); - - io.gpio_ctrl - .write(|w| unsafe { w.funcsel().bits(fields.funcsel) }); - - if fields.funcsel == SIO_FUNCSEL { - if fields.sio_outen { - unsafe { - (*pac::SIO::ptr()).gpio_oe_set.write(|w| w.bits(1 << num)); - } - } else { - unsafe { - (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); - } - } - } else { - unsafe { - (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); - } - } -} - -// TODO: This is really nasty, but there's no single type for the QSPI pins -// I'm not sure if a svd change is even possible to fix this, as these do have -// different reset values -macro_rules! qspi_bits { - ( $( ($num : expr, $suffix : ident) ),+ ) => { - $crate::paste::paste! { - #[inline] - fn qspi_read_drive(num: usize) -> u8 { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().drive().bits(), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_write_drive(num: usize, val : u8) { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.drive().bits(val) ), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_read_slew(num: usize) -> bool { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().slewfast().bit_is_set(), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_write_slew(num: usize, slewfast : bool) { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.slewfast().bit(slewfast) ), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_change_mode(num: usize, mode: DynPinMode) { - let fields : ModeFields = mode.into(); - - match num { - $($num => { - let io = unsafe { &(*pac::IO_QSPI::ptr()).[] }; - let pads = unsafe { &(*pac::PADS_QSPI::ptr()).[] }; - - pads.write(|w| { - w.pue().bit(fields.pue); - w.pde().bit(fields.pde); - w.ie().bit(fields.inen); - w.od().bit(false) - }); - - io.gpio_ctrl.write(|w| unsafe { w.funcsel().bits(fields.funcsel) } ); - }, )+ - _ => unreachable!("invalid ID for QSPI pin") - } - - - // outen is only on SIO - if fields.funcsel == SIO_FUNCSEL { - if fields.sio_outen { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_set.write(|w| w.bits(1 << num)); } - } else { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } - } - } else { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } - } - } - } - } -} - -qspi_bits!((0, sclk), (1, ss), (2, sd0), (3, sd1), (4, sd2), (5, sd3)); diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index 76b32e4b2..be289a0c7 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -46,12 +46,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::pin::bank0::{ - Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, - Gpio19, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, - Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Gpio9, - }, - gpio::pin::{AnyPin, FunctionI2C}, + gpio::{bank0::*, AnyPin, FunctionI2C}, resets::SubsystemReset, typelevel::Sealed, }; @@ -141,55 +136,32 @@ impl eh1_0_alpha::i2c::Error for Error { } /// SCL pin -pub trait SclPin: Sealed {} +pub trait ValidSclPin: Sealed {} /// SDA pin -pub trait SdaPin: Sealed {} - -impl SdaPin for Gpio0 {} -impl SclPin for Gpio1 {} - -impl SdaPin for Gpio2 {} -impl SclPin for Gpio3 {} - -impl SdaPin for Gpio4 {} -impl SclPin for Gpio5 {} - -impl SdaPin for Gpio6 {} -impl SclPin for Gpio7 {} - -impl SdaPin for Gpio8 {} -impl SclPin for Gpio9 {} - -impl SdaPin for Gpio10 {} -impl SclPin for Gpio11 {} - -impl SdaPin for Gpio12 {} -impl SclPin for Gpio13 {} - -impl SdaPin for Gpio14 {} -impl SclPin for Gpio15 {} - -impl SdaPin for Gpio16 {} -impl SclPin for Gpio17 {} +pub trait ValidSdaPin: Sealed {} -impl SdaPin for Gpio18 {} -impl SclPin for Gpio19 {} - -impl SdaPin for Gpio20 {} -impl SclPin for Gpio21 {} - -impl SdaPin for Gpio22 {} -impl SclPin for Gpio23 {} - -impl SdaPin for Gpio24 {} -impl SclPin for Gpio25 {} - -impl SdaPin for Gpio26 {} -impl SclPin for Gpio27 {} - -impl SdaPin for Gpio28 {} -impl SclPin for Gpio29 {} +macro_rules! valid_pins { + ($($i2c:ident: { + sda: [$($sda:ident),*], + scl: [$($scl:ident),*] + }),*) => { + $( + $(impl ValidSdaPin<$i2c> for $sda {})* + $(impl ValidSclPin<$i2c> for $scl {})* + )* + }; +} +valid_pins! { + I2C0: { + sda: [Gpio0, Gpio4, Gpio8, Gpio12, Gpio16, Gpio20, Gpio24, Gpio28], + scl: [Gpio1, Gpio5, Gpio9, Gpio13, Gpio17, Gpio21, Gpio25, Gpio29] + }, + I2C1: { + sda: [Gpio2, Gpio6, Gpio10, Gpio14, Gpio18, Gpio22, Gpio26], + scl: [Gpio3, Gpio7, Gpio11, Gpio15, Gpio19, Gpio23, Gpio27] + } +} /// Operational mode of the I2C peripheral. pub trait I2CMode: Sealed { @@ -279,8 +251,8 @@ macro_rules! hal { $( impl I2C<$I2CX, (Sda, Scl)> where - Sda: AnyPin, - Scl: AnyPin, + Sda: AnyPin, + Scl: AnyPin, { /// Configures the I2C peripheral to work in master mode pub fn $i2cX( @@ -292,8 +264,8 @@ macro_rules! hal { system_clock: SystemF) -> Self where F: Into, - Sda::Id: SdaPin<$I2CX>, - Scl::Id: SclPin<$I2CX>, + Sda::Id: ValidSdaPin<$I2CX>, + Scl::Id: ValidSclPin<$I2CX>, SystemF: Into, { Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into()) diff --git a/rp2040-hal/src/i2c/controller.rs b/rp2040-hal/src/i2c/controller.rs index f61f7b0f8..e306118f9 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,7 +1,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::{pin::FunctionI2C, AnyPin}, + gpio::{AnyPin, FunctionI2C}, resets::SubsystemReset, }; use fugit::HertzU32; @@ -11,15 +11,15 @@ use pac::{i2c0::RegisterBlock as Block, RESETS}; #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::i2c as eh1; -use super::{i2c_reserved_addr, Controller, Error, SclPin, SdaPin, I2C}; +use super::{i2c_reserved_addr, Controller, Error, ValidSclPin, ValidSdaPin, I2C}; impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: SdaPin, - Scl::Id: SclPin, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: ValidSdaPin, + Scl::Id: ValidSclPin, { /// Configures the I2C peripheral to work in controller mode pub fn new_controller( diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index ff3011824..5ee3ac731 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -1,12 +1,12 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::{pin::FunctionI2C, AnyPin}, + gpio::{AnyPin, FunctionI2C}, resets::SubsystemReset, }; use pac::{i2c0::RegisterBlock as I2CBlock, RESETS}; -use super::{Peripheral, SclPin, SdaPin, I2C}; +use super::{Peripheral, ValidSclPin, ValidSdaPin, I2C}; /// I2C bus events #[derive(Debug, PartialEq, Eq)] @@ -40,10 +40,10 @@ pub struct I2CPeripheralEventIterator { impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: SdaPin, - Scl::Id: SclPin, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: ValidSdaPin, + Scl::Id: ValidSclPin, { /// Configures the I2C peripheral to work in peripheral mode /// diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 967f13bbd..505c83356 100644 --- a/rp2040-hal/src/pio.rs +++ b/rp2040-hal/src/pio.rs @@ -1,21 +1,27 @@ //! Programmable IO (PIO) //! See [Chapter 3 of the datasheet](https://rptl.io/rp2040-datasheet#section_pio) for more details. +use core::ops::Deref; + use crate::{ atomic_register_access::{write_bitmask_clear, write_bitmask_set}, dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget}, + gpio::{Function, FunctionPio0, FunctionPio1}, resets::SubsystemReset, typelevel::Sealed, }; use pio::{Instruction, InstructionOperands, Program, SideSet, Wrap}; -use rp2040_pac::dma::ch::ch_ctrl_trig::TREQ_SEL_A; -use rp2040_pac::{PIO0, PIO1}; +use rp2040_pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, pio0::RegisterBlock, PIO0, PIO1}; const PIO_INSTRUCTION_COUNT: usize = 32; +impl crate::typelevel::Sealed for PIO0 {} +impl crate::typelevel::Sealed for PIO1 {} + /// PIO Instance -pub trait PIOExt: - core::ops::Deref + SubsystemReset + Sized + Send + Sealed -{ +pub trait PIOExt: Deref + SubsystemReset + Sized + Send + Sealed { + /// Associated Pin Function. + type PinFunction: Function; + /// Create a new PIO wrapper and split the state machines into individual objects. #[allow(clippy::type_complexity)] // Required for symmetry with PIO::free(). fn split( @@ -68,11 +74,13 @@ pub trait PIOExt: } impl PIOExt for PIO0 { + type PinFunction = FunctionPio0; fn id() -> usize { 0 } } impl PIOExt for PIO1 { + type PinFunction = FunctionPio1; fn id() -> usize { 1 } @@ -447,8 +455,6 @@ impl ValidStateMachine for (P, SM) { } } -impl Sealed for (P, SM) {} - /// Pin State in the PIO /// /// Note the GPIO is able to override/invert that. @@ -474,7 +480,7 @@ pub enum PinDir { /// PIO State Machine (uninitialized, without a program). #[derive(Debug)] pub struct UninitStateMachine { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, sm: *const rp2040_pac::pio0::SM, _phantom: core::marker::PhantomData, } @@ -536,7 +542,7 @@ impl UninitStateMachine { &*self.sm } - unsafe fn pio(&self) -> &rp2040_pac::pio0::RegisterBlock { + unsafe fn pio(&self) -> &RegisterBlock { &*self.block } } @@ -1273,7 +1279,7 @@ impl StateMachine { /// PIO RX FIFO handle. pub struct Rx { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData, } @@ -1411,7 +1417,7 @@ impl EndlessReadTarget for Rx {} /// PIO TX FIFO handle. pub struct Tx { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData, } @@ -1604,7 +1610,7 @@ impl EndlessWriteTarget for Tx {} /// PIO Interrupt controller. #[derive(Debug)] pub struct Interrupt<'a, P: PIOExt, const IRQ: usize> { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData<&'a P>, } @@ -1770,7 +1776,7 @@ impl<'a, P: PIOExt, const IRQ: usize> Interrupt<'a, P, IRQ> { ) } - unsafe fn block(&self) -> &rp2040_pac::pio0::RegisterBlock { + unsafe fn block(&self) -> &RegisterBlock { &*self.block } diff --git a/rp2040-hal/src/pwm/mod.rs b/rp2040-hal/src/pwm/mod.rs index 6faf294ca..61ffc223e 100644 --- a/rp2040-hal/src/pwm/mod.rs +++ b/rp2040-hal/src/pwm/mod.rs @@ -79,7 +79,7 @@ use core::marker::PhantomData; use crate::{ - gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidPinMode}, + gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidFunction}, resets::SubsystemReset, typelevel::{Is, Sealed}, }; @@ -539,9 +539,9 @@ pwm! { } /// Marker trait for valid input pins (Channel B only) -pub trait ValidPwmInputPin: Sealed {} +pub trait ValidPwmInputPin: ValidFunction + Sealed {} /// Marker trait for valid output pins -pub trait ValidPwmOutputPin: Sealed {} +pub trait ValidPwmOutputPin: ValidFunction + Sealed {} impl Slices { /// Free the pwm registers from the pwm hal struct while consuming it. @@ -690,12 +690,11 @@ impl PwmPin for Channel { impl Channel { /// Capture a gpio pin and use it as pwm output for channel A - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, - P::Mode: ValidPinMode, { - pin.into().into_mode() + pin.into().into_function() } /// Invert channel output @@ -713,12 +712,11 @@ impl Channel { impl Channel { /// Capture a gpio pin and use it as pwm output for channel B - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, - P::Mode: ValidPinMode, { - pin.into().into_mode() + pin.into().into_function() } /// Invert channel output @@ -739,30 +737,30 @@ where S::Mode: ValidSliceInputMode, { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from(&mut self, pin: P) -> Pin + pub fn input_from(&mut self, pin: P) -> Pin where P::Id: ValidPwmInputPin, { - pin.into().into_mode() + pin.into().into_function() } } impl> Slice { /// Capture a gpio pin and use it as pwm output - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, { - pin.into().into_mode() + pin.into().into_function() } } impl> Slice { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from(&mut self, pin: P) -> Pin + pub fn input_from(&mut self, pin: P) -> Pin where P::Id: ValidPwmInputPin, { - pin.into().into_mode() + pin.into().into_function() } } diff --git a/rp2040-hal/src/sio.rs b/rp2040-hal/src/sio.rs index 3dbd59abd..ead75c858 100644 --- a/rp2040-hal/src/sio.rs +++ b/rp2040-hal/src/sio.rs @@ -99,6 +99,11 @@ impl Sio { } } + /// Reads the whole bank0 at once. + pub fn read_bank0() -> u32 { + unsafe { (*pac::SIO::PTR).gpio_in.read().bits() } + } + /// Returns whether we are running on Core 0 (`0`) or Core 1 (`1`). pub fn core() -> CoreId { // Safety: it is always safe to read this read-only register diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index c6304a1f1..eb7e8eb17 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -13,26 +13,30 @@ //! let sio = Sio::new(peripherals.SIO); //! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS); //! -//! let _ = pins.gpio2.into_mode::(); -//! let _ = pins.gpio3.into_mode::(); +//! let sclk = pins.gpio2.into_mode::(); +//! let mosi = pins.gpio3.into_mode::(); //! //! let spi = Spi::<_, _, 8>::new(peripherals.SPI0).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), MODE_0); //! ``` use crate::dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget}; -use crate::resets::SubsystemReset; -use crate::typelevel::Sealed; use core::{convert::Infallible, marker::PhantomData, ops::Deref}; + #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::spi as eh1; #[cfg(feature = "eh1_0_alpha")] use eh_nb_1_0_alpha::spi as eh1nb; -use embedded_hal::blocking::spi; -use embedded_hal::spi::{FullDuplex, Phase, Polarity}; -use fugit::HertzU32; -use fugit::RateExtU32; -use pac::dma::ch::ch_ctrl_trig::TREQ_SEL_A; -use pac::RESETS; +use embedded_hal::{ + blocking::spi, + spi::{FullDuplex, Phase, Polarity}, +}; +use fugit::{HertzU32, RateExtU32}; +use pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, RESETS}; + +use crate::{resets::SubsystemReset, typelevel::Sealed}; + +mod pins; +pub use pins::*; /// Spi mode pub struct Mode(embedded_hal::spi::Mode); @@ -128,15 +132,17 @@ impl Sealed for u8 {} impl Sealed for u16 {} /// Spi -pub struct Spi { +pub struct Spi, const DS: u8> { device: D, + pins: P, state: PhantomData, } -impl Spi { - fn transition(self, _: To) -> Spi { +impl, const DS: u8> Spi { + fn transition(self, _: To) -> Spi { Spi { device: self.device, + pins: self.pins, state: PhantomData, } } @@ -195,11 +201,12 @@ impl Spi { } } -impl Spi { +impl, const DS: u8> Spi { /// Create new spi device - pub fn new(device: D) -> Spi { + pub fn new(device: D, pins: P) -> Spi { Spi { device, + pins, state: PhantomData, } } @@ -232,7 +239,7 @@ impl Spi { baudrate: B, mode: Mode, slave: bool, - ) -> Spi { + ) -> Spi { self.device.reset_bring_down(resets); self.device.reset_bring_up(resets); @@ -257,12 +264,12 @@ impl Spi { peri_frequency: F, baudrate: B, mode: M, - ) -> Spi { + ) -> Spi { self.init_spi(resets, peri_frequency, baudrate, mode.into(), false) } /// Initialize the SPI in slave mode - pub fn init_slave>(self, resets: &mut RESETS, mode: M) -> Spi { + pub fn init_slave>(self, resets: &mut RESETS, mode: M) -> Spi { // Use dummy values for frequency and baudrate. // With both values 0, set_baudrate will set prescale == u8::MAX, which will break if debug assertions are enabled. // u8::MAX is outside the allowed range 2..=254 for CPSDVSR, which might interfere with proper operation in slave mode. @@ -270,7 +277,7 @@ impl Spi { } } -impl Spi { +impl, const DS: u8> Spi { fn is_writable(&self) -> bool { self.device.sspsr.read().tnf().bit_is_set() } @@ -284,7 +291,7 @@ impl Spi { } /// Disable the spi to reset its configuration - pub fn disable(self) -> Spi { + pub fn disable(self) -> Spi { self.device.sspcr1.modify(|_, w| w.sse().clear_bit()); self.transition(Disabled { __private: () }) @@ -295,7 +302,7 @@ macro_rules! impl_write { ($type:ident, [$($nr:expr),+]) => { $( - impl FullDuplex<$type> for Spi { + impl> FullDuplex<$type> for Spi { type Error = Infallible; fn read(&mut self) -> Result<$type, nb::Error> { @@ -320,17 +327,17 @@ macro_rules! impl_write { } } - impl spi::write::Default<$type> for Spi {} - impl spi::transfer::Default<$type> for Spi {} - impl spi::write_iter::Default<$type> for Spi {} + impl> spi::write::Default<$type> for Spi {} + impl> spi::transfer::Default<$type> for Spi {} + impl> spi::write_iter::Default<$type> for Spi {} #[cfg(feature = "eh1_0_alpha")] - impl eh1::ErrorType for Spi { + impl> eh1::ErrorType for Spi { type Error = Infallible; } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusFlush for Spi { + impl> eh1::SpiBusFlush for Spi { fn flush(&mut self) -> Result<(), Self::Error> { while self.is_busy() {} Ok(()) @@ -338,7 +345,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusRead<$type> for Spi { + impl> eh1::SpiBusRead<$type> for Spi { fn read(&mut self, words: &mut [$type]) -> Result<(), Self::Error> { for word in words.iter_mut() { // write empty word @@ -356,7 +363,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusWrite<$type> for Spi { + impl> eh1::SpiBusWrite<$type> for Spi { fn write(&mut self, words: &[$type]) -> Result<(), Self::Error> { for word in words.iter() { // write one word @@ -374,7 +381,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBus<$type> for Spi { + impl> eh1::SpiBus<$type> for Spi { fn transfer(&mut self, read: &mut [$type], write: &[$type]) -> Result<(), Self::Error>{ let len = read.len().max(write.len()); for i in 0..len { @@ -414,7 +421,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1nb::FullDuplex<$type> for Spi { + impl> eh1nb::FullDuplex<$type> for Spi { fn read(&mut self) -> Result<$type, nb::Error> { if !self.is_readable() { return Err(nb::Error::WouldBlock); @@ -437,7 +444,7 @@ macro_rules! impl_write { } } - impl ReadTarget for Spi { + impl> ReadTarget for Spi { type ReceivedWord = $type; fn rx_treq() -> Option { @@ -456,9 +463,9 @@ macro_rules! impl_write { } } - impl EndlessReadTarget for Spi {} + impl> EndlessReadTarget for Spi {} - impl WriteTarget for Spi { + impl> WriteTarget for Spi { type TransmittedWord = $type; fn tx_treq() -> Option { @@ -477,7 +484,7 @@ macro_rules! impl_write { } } - impl EndlessWriteTarget for Spi {} + impl> EndlessWriteTarget for Spi {} )+ }; diff --git a/rp2040-hal/src/spi/pins.rs b/rp2040-hal/src/spi/pins.rs new file mode 100644 index 000000000..198bf215b --- /dev/null +++ b/rp2040-hal/src/spi/pins.rs @@ -0,0 +1,129 @@ +use super::SpiDevice; +use crate::gpio::{AnyPin, FunctionSpi}; +use crate::typelevel::{OptionTSome, Sealed}; +use crate::{gpio::bank0::*, typelevel::OptionTNone}; +use pac::{SPI0, SPI1}; + +/// Indicates a valid Rx pin for SPI0 or SPI1 +pub trait ValidPinRx: Sealed {} +/// Indicates a valid Tx pin for SPI0 or SPI1 +pub trait ValidPinTx: Sealed {} +/// Indicates a valid SCLK pin for SPI0 or SPI1 +pub trait ValidPinSck: Sealed {} +/// Indicates a valid CS pin for SPI0 or SPI1 +pub trait ValidPinCs: Sealed {} + +macro_rules! impl_valid_spi { + ($($spi:ident: { + rx: [$($rx:ident),*], + cs: [$($cs:ident),*], + sck: [$($sck:ident),*], + tx: [$($tx:ident),*], + }),*) => { + $( + $(impl ValidPinRx<$spi> for $rx {})* + $(impl ValidPinTx<$spi> for $tx {})* + $(impl ValidPinSck<$spi> for $sck {})* + $(impl ValidPinCs<$spi> for $cs {})* + )* + }; +} + +impl_valid_spi!( + SPI0: { + rx: [Gpio0, Gpio4, Gpio16, Gpio20], + cs: [Gpio1, Gpio5, Gpio17, Gpio21], + sck: [Gpio2, Gpio6, Gpio18, Gpio22], + tx: [Gpio3, Gpio7, Gpio19, Gpio23], + }, + SPI1: { + rx: [Gpio8, Gpio12, Gpio24, Gpio28], + cs: [Gpio9, Gpio13, Gpio25, Gpio29], + sck: [Gpio10, Gpio14, Gpio26], + tx: [Gpio11, Gpio15, Gpio27], + } +); + +/// Indicates a valid optional Rx pin for SPI0 or SPI1 +pub trait ValidOptionRx: Sealed {} +/// Indicates a valid optional Tx pin for SPI0 or SPI1 +pub trait ValidOptionTx: Sealed {} +/// Indicates a valid optional SCLK pin for SPI0 or SPI1 +pub trait ValidOptionSck: Sealed {} +/// Indicates a valid optional CS pin for SPI0 or SPI1 +pub trait ValidOptionCs: Sealed {} +impl ValidOptionRx for OptionTNone {} +impl ValidOptionCs for OptionTNone {} +impl ValidOptionSck for OptionTNone {} +impl ValidOptionTx for OptionTNone {} + +impl ValidOptionRx for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinRx, +{ +} +impl ValidOptionCs for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinCs, +{ +} +impl ValidOptionSck for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinSck, +{ +} +impl ValidOptionTx for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinTx, +{ +} + +/// Declares a valid SPI pinout. +pub trait ValidSpiPinout: Sealed { + #[allow(missing_docs)] + type Rx: ValidOptionRx; + #[allow(missing_docs)] + type Cs: ValidOptionCs; + #[allow(missing_docs)] + type Sck: ValidOptionSck; + #[allow(missing_docs)] + type Tx: ValidOptionTx; +} + +impl ValidSpiPinout for (Tx, Sck) +where + Spi: SpiDevice, + Tx: AnyPin, + Sck: AnyPin, + Tx::Id: ValidPinTx, + Sck::Id: ValidPinSck, +{ + type Rx = OptionTNone; + type Cs = OptionTNone; + type Sck = OptionTSome; + type Tx = OptionTSome; +} + +impl ValidSpiPinout for (Tx, Rx, Sck) +where + Spi: SpiDevice, + Tx: AnyPin, + Sck: AnyPin, + Rx: AnyPin, + Tx::Id: ValidPinTx, + Sck::Id: ValidPinSck, + Rx::Id: ValidPinRx, +{ + type Rx = OptionTSome; + type Cs = OptionTNone; + type Sck = OptionTSome; + type Tx = OptionTSome; +} diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index 3eae9d4fd..ddf17b4a8 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -604,7 +604,7 @@ //! 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.set_high(); //! let pin = pin.into_floating_input(); //! let _bit = pin.is_low().unwrap(); //! let pin = pin.into_mode(); @@ -614,7 +614,7 @@ //! pub fn expanded(mut self) -> Self { //! let pin: SpecificPin

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

= pin.into_mode::(); @@ -643,6 +643,10 @@ use core::borrow::{Borrow, BorrowMut}; pub(crate) use private::Sealed; +impl Sealed for (A, B) {} +impl Sealed for (A, B, C) {} +impl Sealed for (A, B, C, D) {} + /// Type-level version of the [None] variant #[derive(Default)] pub struct NoneT; @@ -697,3 +701,27 @@ where { type Type = T; } + +// ===================== +// Type level option +// ===================== + +/// Type-level `enum` for Option. +pub trait OptionT: Sealed { + /// Is this Some or None ? + const IS_SOME: bool; +} + +/// Type-level variant for `OptionT` +pub struct OptionTNone; +impl Sealed for OptionTNone {} +impl OptionT for OptionTNone { + const IS_SOME: bool = false; +} + +/// Type-level variant for `OptionT` +pub struct OptionTSome(pub T); +impl Sealed for OptionTSome {} +impl OptionT for OptionTSome { + const IS_SOME: bool = true; +} diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index f9696a562..5b1f65223 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -19,8 +19,8 @@ //! //! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) //! let pins = ( -//! pins.gpio0.into_mode(), -//! pins.gpio1.into_mode(), +//! pins.gpio0.into_function(), +//! pins.gpio1.into_function(), //! ); //! // Need to perform clock init before using UART or it will freeze. //! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS) @@ -38,11 +38,11 @@ mod reader; mod utils; mod writer; -pub use self::peripheral::UartPeripheral; -pub use self::pins::*; -pub use self::reader::{ReadError, ReadErrorType, Reader}; -pub use self::utils::*; -pub use self::writer::Writer; +pub use peripheral::UartPeripheral; +pub use pins::*; +pub use reader::{ReadError, ReadErrorType, Reader}; +pub use utils::*; +pub use writer::Writer; /// Common configurations for UART. #[deprecated(note = "Use UartConfig::new(...) instead.")] diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index ae2c990fe..94b151e51 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -4,8 +4,8 @@ //! UartPeripheral object that can both read and write. use super::*; -use crate::gpio::SomePin; use crate::pac::uart0::uartlcr_h::W as UART_LCR_H_Writer; +use crate::typelevel::OptionT; use core::convert::Infallible; use core::fmt; use embedded_hal::serial::{Read, Write}; diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index 201258e55..515f347b2 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,69 +1,133 @@ -use crate::gpio::{bank0, AnyPin, FunctionUart, OptionalPin}; +use crate::gpio::{bank0::*, AnyPin, FunctionUart}; use crate::pac::{UART0, UART1}; -use crate::typelevel::{NoneT, Sealed}; +use crate::typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}; use super::UartDevice; +/// Indicates a valid TX pin for UART0 or UART1 +pub trait ValidPinTx: Sealed {} +/// Indicates a valid RX pin for UART0 or UART1 +pub trait ValidPinRx: Sealed {} +/// Indicates a valid CTS pin for UART0 or UART1 +pub trait ValidPinCts: Sealed {} +/// Indicates a valid RTS pin for UART0 or UART1 +pub trait ValidPinRts: Sealed {} + +macro_rules! impl_valid_uart { + ($($uart:ident: { + tx: [$($tx:ident),*], + rx: [$($rx:ident),*], + cts: [$($cts:ident),*], + rts: [$($rts:ident),*], + }),*) => { + $( + $(impl ValidPinTx<$uart> for $tx {})* + $(impl ValidPinRx<$uart> for $rx {})* + $(impl ValidPinCts<$uart> for $cts {})* + $(impl ValidPinRts<$uart> for $rts {})* + )* + }; +} + +impl_valid_uart!( + UART0: { + tx: [Gpio0, Gpio12, Gpio16, Gpio28], + rx: [Gpio1, Gpio13, Gpio17, Gpio29], + cts: [Gpio2, Gpio14, Gpio18], + rts: [Gpio3, Gpio15, Gpio19], + }, + UART1: { + tx: [Gpio4, Gpio8, Gpio20, Gpio24], + rx: [Gpio5, Gpio9, Gpio21, Gpio25], + cts: [Gpio6, Gpio10, Gpio22, Gpio26], + rts: [Gpio7, Gpio11, Gpio23, Gpio27], + } +); + +/// Indicates a valid optional Tx pin for UART0 or UART1 +pub trait ValidOptionTx: OptionT {} +/// Indicates a valid optional Rx pin for UART0 or UART1 +pub trait ValidOptionRx: OptionT {} +/// Indicates a valid optional Cts pin for UART0 or UART1 +pub trait ValidOptionCts: OptionT {} +/// Indicates a valid optional Rts pin for UART0 or UART1 +pub trait ValidOptionRts: OptionT {} +impl ValidOptionTx for OptionTNone {} +impl ValidOptionRx for OptionTNone {} +impl ValidOptionCts for OptionTNone {} +impl ValidOptionRts for OptionTNone {} + +impl ValidOptionTx for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinTx, +{ +} +impl ValidOptionRx for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinRx, +{ +} +impl ValidOptionCts for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinCts, +{ +} +impl ValidOptionRts for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinRts, +{ +} + /// Declares a valid UART pinout. -pub trait ValidUartPinout { +pub trait ValidUartPinout: Sealed { #[allow(missing_docs)] - type Rx: OptionalPin; + type Rx: ValidOptionRx; #[allow(missing_docs)] - type Tx: OptionalPin; + type Tx: ValidOptionTx; #[allow(missing_docs)] - type Cts: OptionalPin; + type Cts: ValidOptionCts; #[allow(missing_docs)] - type Rts: OptionalPin; + type Rts: ValidOptionRts; } -impl ValidUartPinout for Pins +impl ValidUartPinout for (Tx, Rx) where - UART: UartDevice, - TX: OptionalPin, - RX: OptionalPin, - CTS: OptionalPin, - RTS: OptionalPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, - CTS::Id: ValidCtsPin, - RTS::Id: ValidRtsPin, + Uart: UartDevice, + Tx: AnyPin, + Rx: AnyPin, + Tx::Id: ValidPinTx, + Rx::Id: ValidPinRx, { - type Rx = RX; - type Tx = TX; - type Cts = CTS; - type Rts = RTS; + type Tx = OptionTSome; + type Rx = OptionTSome; + type Cts = OptionTNone; + type Rts = OptionTNone; } -impl ValidUartPinout for (TX, RX) +impl ValidUartPinout for (Tx, Rx, Cts, Rts) where - UART: UartDevice, - TX: AnyPin, - RX: AnyPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, + Uart: UartDevice, + Tx: AnyPin, + Rx: AnyPin, + Cts: AnyPin, + Rts: AnyPin, + Tx::Id: ValidPinTx, + Rx::Id: ValidPinRx, + Cts::Id: ValidPinCts, + Rts::Id: ValidPinRts, { - type Rx = RX; - type Tx = TX; - type Cts = NoneT; - type Rts = NoneT; -} - -impl ValidUartPinout for (TX, RX, CTS, RTS) -where - UART: UartDevice, - TX: AnyPin, - RX: AnyPin, - CTS: AnyPin, - RTS: AnyPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, - CTS::Id: ValidCtsPin, - RTS::Id: ValidRtsPin, -{ - type Rx = RX; - type Tx = TX; - type Cts = CTS; - type Rts = RTS; + type Rx = OptionTSome; + type Tx = OptionTSome; + type Cts = OptionTSome; + type Rts = OptionTSome; } /// Customizable Uart pinout, allowing you to set the pins individually. @@ -75,7 +139,7 @@ where /// |UART0|0, 12, 16, 28|1, 13, 17, 29|2, 14, 18 |3, 15, 19 | /// |UART1|4, 8, 20, 24 |5, 9, 21, 25 |6, 10, 22, 26|7, 11, 23, 27| /// -/// Every field can be set to [`NoneT`] to not configure them. +/// Every field can be set to [`OptionTNone`] to not configure them. /// /// Note that you can also use tuples `(RX, TX)` or `(RX, TX, CTS, RTS)` instead of this type. /// @@ -86,117 +150,82 @@ where /// # use rp2040_hal::pac::UART0; /// # let gpio_pins: rp2040_hal::gpio::Pins = unsafe { core::mem::zeroed() }; /// let pins = Pins::default() -/// .tx(gpio_pins.gpio0.into_mode()) -/// .rx(gpio_pins.gpio1.into_mode()); +/// .tx(gpio_pins.gpio0.into_function()) +/// .rx(gpio_pins.gpio1.into_function()); /// /// fn assert_is_valid_uart0>(_: T) {} /// /// assert_is_valid_uart0(pins); /// ``` #[allow(missing_docs)] -pub struct Pins { - pub tx: TX, - pub rx: RX, - pub rts: RTS, - pub cts: CTS, +pub struct Pins { + pub tx: Tx, + pub rx: Rx, + pub cts: Cts, + pub rts: Rts, } -impl Default for Pins { +impl Default for Pins { fn default() -> Self { Self { - tx: NoneT, - rx: NoneT, - rts: NoneT, - cts: NoneT, + tx: OptionTNone, + rx: OptionTNone, + rts: OptionTNone, + cts: OptionTNone, } } } -impl Pins -where - TX: OptionalPin, - RX: OptionalPin, - CTS: OptionalPin, - RTS: OptionalPin, -{ +impl Pins { /// Set the TX pin - pub fn tx(self, tx: NTX) -> Pins { + pub fn tx(self, tx: NewTx) -> Pins, Rx, Cts, Rts> { Pins { - tx, + tx: OptionTSome(tx), rx: self.rx, rts: self.rts, cts: self.cts, } } /// Set the RX pin - pub fn rx(self, rx: NRX) -> Pins { + pub fn rx(self, rx: NewRx) -> Pins, Cts, Rts> { Pins { tx: self.tx, - rx, + rx: OptionTSome(rx), rts: self.rts, cts: self.cts, } } /// Set the CTS pin - pub fn cts(self, cts: NCTS) -> Pins { + pub fn cts(self, cts: NewCts) -> Pins, Rts> { Pins { tx: self.tx, rx: self.rx, rts: self.rts, - cts, + cts: OptionTSome(cts), } } /// Set the RTS pin - pub fn rts(self, rts: NRTS) -> Pins { + pub fn rts(self, rts: NewRts) -> Pins> { Pins { tx: self.tx, rx: self.rx, - rts, + rts: OptionTSome(rts), cts: self.cts, } } } -/// Indicates a valid TX pin for UART0 or UART1 -pub trait ValidTxPin: Sealed {} -/// Indicates a valid RX pin for UART0 or UART1 -pub trait ValidRxPin: Sealed {} -/// Indicates a valid CTS pin for UART0 or UART1 -pub trait ValidCtsPin: Sealed {} -/// Indicates a valid RTS pin for UART0 or UART1 -pub trait ValidRtsPin: Sealed {} - -macro_rules! impl_valid_uart { - ($($uart:ident: { - tx: [$($tx:ident),*], - rx: [$($rx:ident),*], - cts: [$($cts:ident),*], - rts: [$($rts:ident),*], - }),*) => { - $( - impl ValidRxPin<$uart> for NoneT {} - impl ValidTxPin<$uart> for NoneT {} - impl ValidCtsPin<$uart> for NoneT {} - impl ValidRtsPin<$uart> for NoneT {} - $(impl ValidTxPin<$uart> for bank0::$tx {})* - $(impl ValidRxPin<$uart> for bank0::$rx {})* - $(impl ValidCtsPin<$uart> for bank0::$cts {})* - $(impl ValidRtsPin<$uart> for bank0::$rts {})* - )* - }; +impl Sealed for Pins {} +impl ValidUartPinout for Pins +where + Uart: UartDevice, + Tx: ValidOptionTx, + Rx: ValidOptionRx, + Cts: ValidOptionCts, + Rts: ValidOptionRts, +{ + type Rx = Rx; + type Tx = Tx; + type Cts = Cts; + type Rts = Rts; } - -impl_valid_uart!( - UART0: { - tx: [Gpio0, Gpio12, Gpio16, Gpio28], - rx: [Gpio1, Gpio13, Gpio17, Gpio29], - cts: [Gpio2, Gpio14, Gpio18], - rts: [Gpio3, Gpio15, Gpio19], - }, - UART1: { - tx: [Gpio4, Gpio8, Gpio20, Gpio24], - rx: [Gpio5, Gpio9, Gpio21, Gpio25], - cts: [Gpio6, Gpio10, Gpio22, Gpio26], - rts: [Gpio7, Gpio11, Gpio23, Gpio27], - } -); From c6b8db407d4c34c4b55661cf37c14f581eadaa8c Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 23 Apr 2023 08:35:48 +0100 Subject: [PATCH 04/26] Address first review round from @thejpster. --- rp2040-hal/src/gpio/func.rs | 2 +- rp2040-hal/src/gpio/mod.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs index 2d042b331..5aff61d40 100644 --- a/rp2040-hal/src/gpio/func.rs +++ b/rp2040-hal/src/gpio/func.rs @@ -127,7 +127,7 @@ pub struct InvalidFunction; /// Marker of valid pin -> function combination. /// -/// Read as `F is a valid function implemented for the pin F` +/// Where `impl ValidFunction for I` reads as `F is a valid function implemented for the pin I`. pub trait ValidFunction: super::pin::PinId {} impl DynFunction { diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 06d919462..f05b626a6 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -153,7 +153,8 @@ pub enum OutputOverride { AlwaysHigh = 3, } -/// Pin with validation of [`Function`] and [`PullType`]. +/// Represents a pin, with a given ID (e.g. Xx), a given function (e.g. Uart) and a given pull type +/// (e.g. pull-down). pub struct Pin { id: I, function: F, From 5706a0b81cbb5584caaf3d281fa1b0cce54676f3 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 23 Apr 2023 13:40:55 +0100 Subject: [PATCH 05/26] Add a few inline to enable better optimisation. --- rp2040-hal/src/gpio/func.rs | 4 ++++ rp2040-hal/src/gpio/pin.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs index 5aff61d40..fcf6593f6 100644 --- a/rp2040-hal/src/gpio/func.rs +++ b/rp2040-hal/src/gpio/func.rs @@ -47,10 +47,12 @@ pub enum DynSioConfig { impl Function for DynFunction {} impl func_sealed::Function for DynFunction { + #[inline] fn from(f: DynFunction) -> Self { f } + #[inline] fn as_dyn(&self) -> DynFunction { *self } @@ -64,9 +66,11 @@ macro_rules! pin_func { impl Function for [] {} impl func_sealed::TypeLevelFunction for [] {} impl func_sealed::Function for [] { + #[inline] fn from(_f: DynFunction) -> Self { Self(()) } + #[inline] fn as_dyn(&self) -> DynFunction { DynFunction::[<$fn>] } diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index 7579bebf6..d2cf67720 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -56,6 +56,7 @@ pub struct DynPinId { pub num: u8, } impl PinId for DynPinId { + #[inline] fn as_dyn(&self) -> DynPinId { *self } @@ -75,6 +76,7 @@ macro_rules! pin_ids { pub struct [<$prefix $name>] (pub(crate) ()); impl crate::typelevel::Sealed for [<$prefix $name>] {} impl PinId for [<$prefix $name>] { + #[inline] fn as_dyn(&self) -> DynPinId { DynPinId { bank: DynBankId::$bank, From 2512b10f2877757204ee67ff3d6aeb1aa62dd21e Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 23 Apr 2023 15:05:46 +0100 Subject: [PATCH 06/26] Expand documentation for the `Adc` Co-authored-by: Jonathan 'theJPster' Pallant --- rp2040-hal/src/adc.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 1c438b4c8..4596536e5 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -88,7 +88,14 @@ where } } -/// Adc +/// Analog to Digital Convertor (ADC). +/// +/// Represents an ADC within the RP2040. Each ADC has multiple channels, and each +/// channel is either associated with a specific GPIO pin or attached to the internal +/// temperature sensor. You should put the relevant pin into ADC mode by creating an +/// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode" +/// by calling [`Adc::enable_temp_sensor()`]. Either way, the resulting objects can be +/// passed to the [`OneShot::read()`] trait method to actually do the read. pub struct Adc { device: ADC, } From cd1ba03b3fa8cd429fedd2f7efc2ce949fbe35b1 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 23 Apr 2023 15:18:38 +0100 Subject: [PATCH 07/26] Refactor the ADC module and remove redundant ts_en.set_bit() - The channel types and their traits impl have been moved to the top of the file and reassembled following the "Type -> impl Trait for Type" pattern. - The temperature sensor bias was enabled twice, `enable_temp_sensor` and in the Adc::read function. The latter has now been removed because reading the Temp channel requires to call `enable_temp_sensor` anyway. --- rp2040-hal/src/adc.rs | 94 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 4596536e5..60a917b40 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -52,6 +52,11 @@ use crate::{ const TEMPERATURE_SENSOR_CHANNEL: u8 = 4; +/// The pin was invalid for the requested operation +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidPinError; + /// A pin locked in use with the ADC. pub struct AdcPin

where @@ -88,6 +93,47 @@ where } } +macro_rules! channel { + ($pin:ident, $channel:expr) => { + impl Channel for AdcPin> + where + $pin: crate::gpio::ValidFunction, + { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + $channel + } + } + }; +} + +channel!(Gpio26, 0); +channel!(Gpio27, 1); +channel!(Gpio28, 2); +channel!(Gpio29, 3); + +impl Channel for AdcPin> +where + DynPinId: crate::gpio::ValidFunction, +{ + type ID = (); // ADC channels are identified at run time + fn channel() {} +} + +/// Internal temperature sensor type +pub struct TempSense { + __private: (), +} + +impl Channel for TempSense { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + TEMPERATURE_SENSOR_CHANNEL + } +} + /// Analog to Digital Convertor (ADC). /// /// Represents an ADC within the RP2040. Each ADC has multiple channels, and each @@ -154,46 +200,6 @@ impl Adc { } } -macro_rules! channel { - ($pin:ident, $channel:expr) => { - impl Channel for AdcPin> - where - $pin: crate::gpio::ValidFunction, - { - type ID = u8; // ADC channels are identified numerically - - fn channel() -> u8 { - $channel - } - } - }; -} - -channel!(Gpio26, 0); -channel!(Gpio27, 1); -channel!(Gpio28, 2); -channel!(Gpio29, 3); -impl Channel for AdcPin> -where - DynPinId: crate::gpio::ValidFunction, -{ - type ID = (); // ADC channels are identified at run time - fn channel() {} -} - -/// Internal temperature sensor type -pub struct TempSense { - __private: (), -} - -impl Channel for TempSense { - type ID = u8; // ADC channels are identified numerically - - fn channel() -> u8 { - TEMPERATURE_SENSOR_CHANNEL - } -} - // Implementation for TempSense and type-checked pins impl OneShot for Adc where @@ -204,19 +210,11 @@ where fn read(&mut self, _pin: &mut SRC) -> nb::Result { let chan = SRC::channel(); - if chan == TEMPERATURE_SENSOR_CHANNEL { - self.device.cs.modify(|_, w| w.ts_en().set_bit()) - } Ok(self.read(chan).into()) } } -/// The pin was invalid for the requested operation -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidPinError; - // Implementation for dyn-pins impl OneShot>> for Adc where From be59518e36903c78712cc3426fe62d81a4c8e36c Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 23 Apr 2023 15:32:08 +0100 Subject: [PATCH 08/26] Spacersssss :) and unicity -> uniqueness --- rp2040-hal/src/gpio/func.rs | 6 ++++-- rp2040-hal/src/gpio/mod.rs | 27 ++++++++++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs index fcf6593f6..41e424fed 100644 --- a/rp2040-hal/src/gpio/func.rs +++ b/rp2040-hal/src/gpio/func.rs @@ -84,8 +84,9 @@ macro_rules! pin_func { } pin_func!(Xip, Spi, Uart, I2c as I2C, Pwm, Pio0, Pio1, Clock, Usb, Null); -// ============================= +//============================================================================== // SIO sub-types +//============================================================================== /// Type-level `variant` for pin [`Function`]. pub struct FunctionSio(PhantomData); @@ -123,8 +124,9 @@ impl SioConfig for SioOutput { const DYN: DynSioConfig = DynSioConfig::Output; } -// ============================= +//============================================================================== // Pin to function mapping +//============================================================================== /// Error type for invalid function conversion. pub struct InvalidFunction; diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index f05b626a6..214a5c0f4 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -164,7 +164,7 @@ pub struct Pin { /// Create a new pin instance. /// /// # Safety -/// The unicity of the pin is not verified. User must make sure no other instance of that specific +/// The uniqueness of the pin is not verified. User must make sure no other instance of that specific /// pin exists at the same time. pub unsafe fn new_pin(id: DynPinId) -> Pin { use pin::pin_sealed::PinIdOps; @@ -252,6 +252,7 @@ impl Pin { { self.into_function() } + /// Convert the pin function. pub fn into_function(self) -> Pin where @@ -273,6 +274,7 @@ impl Pin { pull_type: self.pull_type, } } + /// Convert the pin pull type. pub fn into_pull_type(self) -> Pin { let prev_pull_type = self.pull_type.as_dyn(); @@ -289,6 +291,7 @@ impl Pin { function: self.function, } } + /// Erase the Pin ID type check. pub fn into_dyn_pin(self) -> Pin { Pin { @@ -303,8 +306,9 @@ impl Pin { self.pull_type.as_dyn() } - // ========================================== + //============================================================================== // Typical pin conversions. + //============================================================================== /// Disable the pin and set it to float #[inline] @@ -341,6 +345,7 @@ impl Pin { { self.into_function().into_pull_type() } + /// Configure the pin to operate as a pulled down input #[inline] pub fn into_pull_down_input(self) -> Pin, PullDown> @@ -349,6 +354,7 @@ impl Pin { { self.into_function().into_pull_type() } + /// Configure the pin to operate as a pulled up input #[inline] pub fn into_pull_up_input(self) -> Pin, PullUp> @@ -357,6 +363,7 @@ impl Pin { { self.into_function().into_pull_type() } + /// Configure the pin to operate as a bus keep input #[inline] pub fn into_bus_keep_input(self) -> Pin, PullBoth> @@ -376,6 +383,7 @@ impl Pin { { self.into_function() } + /// Configure the pin to operate as a push-pull output, specifying an initial /// state which is applied immediately. #[inline] @@ -404,6 +412,7 @@ impl Pin { { self.into_function() } + /// Configure the pin to operate as a readable push pull output, specifying an initial /// state which is applied immediately. #[inline] @@ -415,10 +424,11 @@ impl Pin { self.into_push_pull_output_in_state(state) } - // ========================================== + //============================================================================== // methods available for all pins. + //============================================================================== - // =================================== + // ======================= // Pad related methods /// Get the current drive strength of the pin. @@ -500,7 +510,7 @@ impl Pin { self.id.pad_ctrl().modify(|_, w| w.ie().bit(enable)); } - // =================================== + // ======================= // IO related methods /// Set the input override. @@ -559,7 +569,7 @@ impl Pin { .modify(|_, w| w.irqover().variant(variant)); } - // =================================== + // ======================= // SIO related methods #[inline] @@ -610,7 +620,7 @@ impl Pin { !self._is_set_low() } - // =================================== + // ======================= // Interrupt related methods /// Clear interrupt. @@ -756,6 +766,7 @@ impl Pin { } Ok(()) } + /// Gets the pin's function. pub fn function(&self) -> DynFunction { use func::func_sealed::Function; @@ -1267,6 +1278,7 @@ impl embedded_hal::digital::v2::InputPin for InOutPin { fn is_high(&self) -> Result { self.inner.is_high() } + fn is_low(&self) -> Result { self.inner.is_low() } @@ -1280,6 +1292,7 @@ impl embedded_hal::digital::v2::OutputPin for InOutPin { .set_output_enable_override(OutputEnableOverride::Enable); Ok(()) } + fn set_high(&mut self) -> Result<(), Error> { // To set the open-drain pin to high, just disable the output driver by configuring the // output override. That way, the DHT11 can still pull the data line down to send its response. From 509160aeeaacf51f5f4d650b4c816e5a150ec25c Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Mon, 24 Apr 2023 18:50:00 +0100 Subject: [PATCH 09/26] Add sio bypass when pin is in Sio Function --- rp2040-hal/src/gpio/mod.rs | 24 ++++++++++++++++++++++++ rp2040-hal/src/gpio/pin/pin_sealed.rs | 12 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 214a5c0f4..8ebd765c5 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -735,6 +735,30 @@ impl Pin { } } } +impl Pin, P> { + /// Is bypass enabled + #[inline] + pub fn is_sync_bypass(&self) -> bool { + let mask = self.id.mask(); + self.id.proc_in_by_pass().read().bits() & mask == mask + } + + /// Bypass the input sync stages. + /// + /// This saves two clock cycles in the input signal's path at the risks of intruducing metastability. + #[inline] + pub fn set_sync_bypass(&mut self, bypass: bool) { + let mask = self.id.mask(); + let reg = self.id.proc_in_by_pass(); + unsafe { + if bypass { + write_bitmask_set(reg.as_ptr(), mask); + } else { + write_bitmask_clear(reg.as_ptr(), mask); + } + } + } +} impl Pin { /// Try to return to a type-checked pin id. /// diff --git a/rp2040-hal/src/gpio/pin/pin_sealed.rs b/rp2040-hal/src/gpio/pin/pin_sealed.rs index bee520d32..0180f4e66 100644 --- a/rp2040-hal/src/gpio/pin/pin_sealed.rs +++ b/rp2040-hal/src/gpio/pin/pin_sealed.rs @@ -23,6 +23,7 @@ pub trait PinIdOps { fn sio_oe_set(&self) -> &pac::sio::GPIO_OE_SET; fn sio_oe_clr(&self) -> &pac::sio::GPIO_OE_CLR; fn sio_oe_xor(&self) -> &pac::sio::GPIO_OE_XOR; + fn proc_in_by_pass(&self) -> &pac::syscfg::PROC_IN_SYNC_BYPASS; fn intr(&self) -> (&pac::io_bank0::INTR, usize); fn proc_ints(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTS, usize); @@ -163,6 +164,17 @@ where accessor_fns!(sio oe_clr); accessor_fns!(sio oe_xor); + fn proc_in_by_pass(&self) -> &rp2040_pac::syscfg::PROC_IN_SYNC_BYPASS { + let pin = self.as_dyn(); + unsafe { + let syscfg = &*pac::SYSCFG::PTR; + match pin.bank { + DynBankId::Bank0 => &syscfg.proc_in_sync_bypass, + DynBankId::Qspi => core::mem::transmute(&syscfg.proc_in_sync_bypass_hi), + } + } + } + fn intr(&self) -> (&pac::io_bank0::INTR, usize) { let pin = self.as_dyn(); let (index, offset) = (pin.num / 8, pin.num % 8 * 4); From f7c9bab60f5256c807b945fb306a9a200a75fd3a Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Wed, 26 Apr 2023 07:36:40 +0100 Subject: [PATCH 10/26] Rename `DontInvert` to `Normal` --- rp2040-hal/CHANGELOG.md | 1 + rp2040-hal/src/gpio/mod.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index c46e0e521..1d6861e47 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `let _ =` to ignore result rather than `.ok();` as this gives a false sense the result is checked. - @ithinuel - Reduce code repetition in i2c modules. - @ithinuel +- Rename `DontInvert` to `Normal`. - @ithinuel ### Added diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 8ebd765c5..c4ca929a3 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -105,7 +105,7 @@ impl Interrupt { #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InterruptOverride { /// Don't invert the interrupt. - DontInvert = 0, + Normal = 0, /// Invert the interrupt. Invert = 1, /// Drive interrupt low. @@ -118,7 +118,7 @@ pub enum InterruptOverride { #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InputOverride { /// Don't invert the peripheral input. - DontInvert = 0, + Normal = 0, /// Invert the peripheral input. Invert = 1, /// Drive peripheral input low. @@ -131,7 +131,7 @@ pub enum InputOverride { /// Output enable override state. pub enum OutputEnableOverride { /// Use the original output enable signal from selected peripheral. - DontInvert = 0, + Normal = 0, /// Invert the output enable signal from selected peripheral. Invert = 1, /// Disable output. @@ -518,7 +518,7 @@ impl Pin { pub fn set_input_override(&mut self, override_value: InputOverride) { use pac::io_bank0::gpio::gpio_ctrl::INOVER_A; let variant = match override_value { - InputOverride::DontInvert => INOVER_A::NORMAL, + InputOverride::Normal => INOVER_A::NORMAL, InputOverride::Invert => INOVER_A::INVERT, InputOverride::AlwaysLow => INOVER_A::LOW, InputOverride::AlwaysHigh => INOVER_A::HIGH, @@ -531,7 +531,7 @@ impl Pin { pub fn set_output_enable_override(&mut self, override_value: OutputEnableOverride) { use pac::io_bank0::gpio::gpio_ctrl::OEOVER_A; let variant = match override_value { - OutputEnableOverride::DontInvert => OEOVER_A::NORMAL, + OutputEnableOverride::Normal => OEOVER_A::NORMAL, OutputEnableOverride::Invert => OEOVER_A::INVERT, OutputEnableOverride::Disable => OEOVER_A::DISABLE, OutputEnableOverride::Enable => OEOVER_A::ENABLE, @@ -559,7 +559,7 @@ impl Pin { pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { use pac::io_bank0::gpio::gpio_ctrl::IRQOVER_A; let variant = match override_value { - InterruptOverride::DontInvert => IRQOVER_A::NORMAL, + InterruptOverride::Normal => IRQOVER_A::NORMAL, InterruptOverride::Invert => IRQOVER_A::INVERT, InterruptOverride::AlwaysLow => IRQOVER_A::LOW, InterruptOverride::AlwaysHigh => IRQOVER_A::HIGH, @@ -1292,7 +1292,7 @@ impl InOutPin { /// Releases the pin reverting to its previous function. pub fn release(self) -> T { let mut inner = self.inner.into(); - inner.set_output_enable_override(OutputEnableOverride::DontInvert); + inner.set_output_enable_override(OutputEnableOverride::Normal); T::from(inner) } } From 24b3774622da3df6d28d5951dc3f4e726be3d58d Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 4 May 2023 23:44:03 +0100 Subject: [PATCH 11/26] Prevent the creation of multiple instances of TempSensor --- rp2040-hal/CHANGELOG.md | 1 + rp2040-hal/examples/adc.rs | 2 +- rp2040-hal/src/adc.rs | 25 ++++++++++++++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index 1d6861e47..e120b7bd5 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 checked. - @ithinuel - Reduce code repetition in i2c modules. - @ithinuel - Rename `DontInvert` to `Normal`. - @ithinuel +- Prevent the creation of multiple instances of `adc::TempSensor` - @ithinuel ### Added diff --git a/rp2040-hal/examples/adc.rs b/rp2040-hal/examples/adc.rs index 96e66b82f..773de7e36 100644 --- a/rp2040-hal/examples/adc.rs +++ b/rp2040-hal/examples/adc.rs @@ -107,7 +107,7 @@ fn main() -> ! { let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS); // Enable the temperature sense channel - let mut temperature_sensor = adc.enable_temp_sensor(); + let mut temperature_sensor = adc.take_temp_sensor().unwrap(); // Configure GPIO26 as an ADC input let mut adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26); diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 60a917b40..cb1919318 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -29,7 +29,7 @@ //! // Enable adc //! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS); //! // Enable the temperature sensor -//! let mut temperature_sensor = adc.enable_temp_sensor(); +//! let mut temperature_sensor = adc.take_temp_sensor().unwrap(); //! // Read the ADC counts from the ADC channel //! let temperature_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap(); //! ``` @@ -140,7 +140,7 @@ impl Channel for TempSense { /// channel is either associated with a specific GPIO pin or attached to the internal /// temperature sensor. You should put the relevant pin into ADC mode by creating an /// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode" -/// by calling [`Adc::enable_temp_sensor()`]. Either way, the resulting objects can be +/// by calling [`Adc::take_temp_sensor()`]. Either way, the resulting objects can be /// passed to the [`OneShot::read()`] trait method to actually do the read. pub struct Adc { device: ADC, @@ -171,11 +171,26 @@ impl Adc { self.device.result.read().result().bits() } - /// Enable temperature sensor, returns a channel to use + /// Enable temperature sensor, returns a channel to use. + /// + /// This can only be done once before calling [`disable_temp_sensor`]. If the sensor has already + /// been enabled, this method will panic. pub fn enable_temp_sensor(&mut self) -> TempSense { - self.device.cs.modify(|_, w| w.ts_en().set_bit()); + self.take_temp_sensor() + .expect("Temp sensor is already enabled.") + } - TempSense { __private: () } + /// Enable temperature sensor, returns a channel to use + /// + /// If the sensor has already been enabled, this method returns `None`. + pub fn take_temp_sensor(&mut self) -> Option { + let mut disabled = false; + self.device.cs.modify(|r, w| { + disabled = r.ts_en().bit_is_clear(); + // if bit was already set, this is a nop + w.ts_en().set_bit() + }); + disabled.then_some(TempSense { __private: () }) } /// Disable temperature sensor, consumes channel From 54c9ae1c934ff8571f9ce0561f04f67c80c531d8 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Fri, 5 May 2023 17:39:17 +0100 Subject: [PATCH 12/26] Add missing updates on on-target-tests --- on-target-tests/tests/dma_spi_loopback_u16.rs | 14 +++++++++----- on-target-tests/tests/dma_spi_loopback_u8.rs | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/on-target-tests/tests/dma_spi_loopback_u16.rs b/on-target-tests/tests/dma_spi_loopback_u16.rs index 98bc2ea2b..df264b65d 100644 --- a/on-target-tests/tests/dma_spi_loopback_u16.rs +++ b/on-target-tests/tests/dma_spi_loopback_u16.rs @@ -7,6 +7,7 @@ use crate::hal::dma::Channels; use defmt_rtt as _; // defmt transport use defmt_test as _; +use hal::gpio::{self, Pin}; use panic_probe as _; use rp2040_hal as hal; // memory layout // panic handler use rp2040_hal::pac::SPI0; @@ -24,9 +25,12 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; /// if your board has a different frequency const XTAL_FREQ_HZ: u32 = 12_000_000u32; +type MISO = Pin; +type MOSI = Pin; +type SCLK = Pin; struct State { channels: Option, - spi: Option>, + spi: Option>, } mod testdata { @@ -91,10 +95,10 @@ mod tests { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::spi::Spi::<_, _, 16>::new(pac.SPI0); + let spi_sclk = pins.gpio6.into(); + let spi_mosi = pins.gpio7.into(); + let spi_miso = pins.gpio4.into(); + let spi = hal::spi::Spi::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let spi = spi.init( diff --git a/on-target-tests/tests/dma_spi_loopback_u8.rs b/on-target-tests/tests/dma_spi_loopback_u8.rs index 138b5d141..b6fef3fe3 100644 --- a/on-target-tests/tests/dma_spi_loopback_u8.rs +++ b/on-target-tests/tests/dma_spi_loopback_u8.rs @@ -9,6 +9,7 @@ use defmt_rtt as _; // defmt transport use defmt_test as _; use panic_probe as _; use rp2040_hal as hal; // memory layout // panic handler +use rp2040_hal::gpio::{self, Pin}; use rp2040_hal::pac::SPI0; use rp2040_hal::spi; @@ -24,9 +25,12 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; /// if your board has a different frequency const XTAL_FREQ_HZ: u32 = 12_000_000u32; +type MISO = Pin; +type MOSI = Pin; +type SCLK = Pin; struct State { channels: Option, - spi: Option>, + spi: Option>, } mod testdata { @@ -92,10 +96,10 @@ mod tests { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::spi::Spi::<_, _, 8>::new(pac.SPI0); + let spi_sclk = pins.gpio6.into(); + let spi_mosi = pins.gpio7.into(); + let spi_miso = pins.gpio4.into(); + let spi = hal::spi::Spi::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let spi = spi.init( From e2e7ebdc6bddd33bb85c720ce8250c0ab0391b43 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Tue, 9 May 2023 00:25:46 +0100 Subject: [PATCH 13/26] rename `PullBoth` to the more accurate `PullBusKeep` --- rp2040-hal/src/gpio/mod.rs | 4 ++-- rp2040-hal/src/gpio/pin.rs | 2 +- rp2040-hal/src/gpio/pull.rs | 14 ++++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index c4ca929a3..99a795f76 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -199,7 +199,7 @@ pub unsafe fn new_pin(id: DynPinId) -> Pin { }; let pad = id.pad_ctrl().read(); let pull_type = match (pad.pue().bit_is_set(), pad.pde().bit_is_set()) { - (true, true) => DynPullType::Both, + (true, true) => DynPullType::BusKeep, (true, false) => DynPullType::Up, (false, true) => DynPullType::Down, (false, false) => DynPullType::None, @@ -366,7 +366,7 @@ impl Pin { /// Configure the pin to operate as a bus keep input #[inline] - pub fn into_bus_keep_input(self) -> Pin, PullBoth> + pub fn into_bus_keep_input(self) -> Pin, PullBusKeep> where I: ValidFunction>, { diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index d2cf67720..8036f56e9 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -144,7 +144,7 @@ pub(crate) fn set_pull_type(pin: &P, pull_type: DynPullType) { DynPullType::None => (false, false), DynPullType::Up => (true, false), DynPullType::Down => (false, true), - DynPullType::Both => (true, true), + DynPullType::BusKeep => (true, true), }; pin.pad_ctrl() diff --git a/rp2040-hal/src/gpio/pull.rs b/rp2040-hal/src/gpio/pull.rs index 2c4b16711..718a229d6 100644 --- a/rp2040-hal/src/gpio/pull.rs +++ b/rp2040-hal/src/gpio/pull.rs @@ -21,9 +21,15 @@ pub enum DynPullType { Up, #[allow(missing_docs)] Down, - /// This enables both pull resistor. This setup is described as bus-keep in the RP2040 - /// datasheet. - Both, + /// This enables bus-keep mode. + /// + /// This is not documented in the datasheet but discribed in the + /// [c-sdk](https://github.com/raspberrypi/pico-sdk/blob/e7267f99febc70486923e17a8210088af058c915/src/rp2_common/hardware_gpio/gpio.c#L53) + /// as: + /// + /// > […] on RP2040, setting both pulls enables a "bus keep" function, + /// > i.e. weak pull to whatever is current high/low state of GPIO. + BusKeep, } impl PullType for DynPullType {} @@ -54,4 +60,4 @@ macro_rules! pin_pull_type { })* }; } -pin_pull_type!(None, Up, Down, Both); +pin_pull_type!(None, Up, Down, BusKeep); From f8567f6939aa0bd96af5fff4618edd68ae7ea141 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Wed, 10 May 2023 07:48:07 +0100 Subject: [PATCH 14/26] Enable dyn-pin to be used with peripherals --- rp2040-hal/src/gpio/mod.rs | 11 ++- rp2040-hal/src/i2c.rs | 83 ++++++++++++++--- rp2040-hal/src/i2c/controller.rs | 13 +-- rp2040-hal/src/i2c/peripheral.rs | 13 +-- rp2040-hal/src/spi.rs | 9 +- rp2040-hal/src/spi/pins.rs | 148 ++++++++++++++++-------------- rp2040-hal/src/uart/peripheral.rs | 4 +- rp2040-hal/src/uart/pins.rs | 142 ++++++++++++++-------------- rp2040-hal/src/uart/utils.rs | 7 ++ 9 files changed, 256 insertions(+), 174 deletions(-) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 99a795f76..039605e88 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -1047,7 +1047,6 @@ pub trait AnyPin: Sealed where Self: typelevel::Sealed, Self: typelevel::Is>, - ::Id: ValidFunction<::Function>, { /// [`PinId`] of the corresponding [`Pin`] type Id: PinId; @@ -1059,7 +1058,7 @@ where impl Sealed for Pin where - I: PinId + func::ValidFunction, + I: PinId, F: func::Function, P: PullType, { @@ -1067,7 +1066,7 @@ where impl AnyPin for Pin where - I: func::ValidFunction, + I: PinId, F: func::Function, P: PullType, { @@ -1288,7 +1287,13 @@ impl InOutPin { inner: inner.into(), } } +} +impl InOutPin +where + T: AnyPin, + T::Id: ValidFunction, +{ /// Releases the pin reverting to its previous function. pub fn release(self) -> T { let mut inner = self.inner.into(); diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index be289a0c7..e95f19497 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -46,7 +46,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::{bank0::*, AnyPin, FunctionI2C}, + gpio::{bank0::*, pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionI2c}, resets::SubsystemReset, typelevel::Sealed, }; @@ -58,6 +58,20 @@ pub mod controller; /// Peripheral implementation pub mod peripheral; +/// Pac I2C device +pub trait I2cDevice: Deref + SubsystemReset + Sealed { + /// Index of the peripheral. + const ID: usize; +} +impl Sealed for pac::I2C0 {} +impl I2cDevice for pac::I2C0 { + const ID: usize = 0; +} +impl Sealed for pac::I2C1 {} +impl I2cDevice for pac::I2C1 { + const ID: usize = 1; +} + /// I2C error #[non_exhaustive] #[cfg_attr(not(feature = "eh1_0_alpha"), derive(Debug))] @@ -135,11 +149,53 @@ impl eh1_0_alpha::i2c::Error for Error { } } -/// SCL pin -pub trait ValidSclPin: Sealed {} +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Marker for PinId that can serve as " $p] + pub trait []: Sealed {} + + #[doc = "Valid " $p] + pub trait []: Sealed {} + + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } + + #[doc = "A runtime validated " $p " pin for I2C."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + S: I2cDevice, + { + /// Validate a pin's function on a i2c peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that I2C."] + pub fn validate(p: P, _u: &S) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, S::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; +} -/// SDA pin -pub trait ValidSdaPin: Sealed {} +pin_validation!(Scl, Sda); macro_rules! valid_pins { ($($i2c:ident: { @@ -147,9 +203,12 @@ macro_rules! valid_pins { scl: [$($scl:ident),*] }),*) => { $( - $(impl ValidSdaPin<$i2c> for $sda {})* - $(impl ValidSclPin<$i2c> for $scl {})* + $(impl ValidPinIdSda<$i2c> for $sda {})* + $(impl ValidPinIdScl<$i2c> for $scl {})* )* + + const SDA: &[(u8, usize)] = &[$($(($sda::ID.num, $i2c::ID)),*),*]; + const SCL: &[(u8, usize)] = &[$($(($scl::ID.num, $i2c::ID)),*),*]; }; } valid_pins! { @@ -249,11 +308,7 @@ impl, PINS, Mode> I2C { macro_rules! hal { ($($I2CX:ident: ($i2cX:ident),)+) => { $( - impl I2C<$I2CX, (Sda, Scl)> - where - Sda: AnyPin, - Scl: AnyPin, - { + impl I2C<$I2CX, (Sda, Scl)> { /// Configures the I2C peripheral to work in master mode pub fn $i2cX( i2c: $I2CX, @@ -264,8 +319,8 @@ macro_rules! hal { system_clock: SystemF) -> Self where F: Into, - Sda::Id: ValidSdaPin<$I2CX>, - Scl::Id: ValidSclPin<$I2CX>, + Sda: ValidPinSda<$I2CX>, + Scl: ValidPinScl<$I2CX>, SystemF: Into, { Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into()) diff --git a/rp2040-hal/src/i2c/controller.rs b/rp2040-hal/src/i2c/controller.rs index e306118f9..130efdb6b 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,9 +1,6 @@ use core::{marker::PhantomData, ops::Deref}; -use crate::{ - gpio::{AnyPin, FunctionI2C}, - resets::SubsystemReset, -}; +use crate::resets::SubsystemReset; use fugit::HertzU32; use hal::blocking::i2c::{Read, Write, WriteRead}; use pac::{i2c0::RegisterBlock as Block, RESETS}; @@ -11,15 +8,13 @@ use pac::{i2c0::RegisterBlock as Block, RESETS}; #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::i2c as eh1; -use super::{i2c_reserved_addr, Controller, Error, ValidSclPin, ValidSdaPin, I2C}; +use super::{i2c_reserved_addr, Controller, Error, ValidPinScl, ValidPinSda, I2C}; impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: ValidSdaPin, - Scl::Id: ValidSclPin, + Sda: ValidPinSda, + Scl: ValidPinScl, { /// Configures the I2C peripheral to work in controller mode pub fn new_controller( diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index 5ee3ac731..b61dad9c5 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -1,12 +1,9 @@ use core::{marker::PhantomData, ops::Deref}; -use crate::{ - gpio::{AnyPin, FunctionI2C}, - resets::SubsystemReset, -}; +use crate::resets::SubsystemReset; use pac::{i2c0::RegisterBlock as I2CBlock, RESETS}; -use super::{Peripheral, ValidSclPin, ValidSdaPin, I2C}; +use super::{Peripheral, ValidPinScl, ValidPinSda, I2C}; /// I2C bus events #[derive(Debug, PartialEq, Eq)] @@ -40,10 +37,8 @@ pub struct I2CPeripheralEventIterator { impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: ValidSdaPin, - Scl::Id: ValidSclPin, + Sda: ValidPinSda, + Scl: ValidPinScl, { /// Configures the I2C peripheral to work in peripheral mode /// diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index eb7e8eb17..4aeab688d 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -98,13 +98,18 @@ impl Sealed for Enabled {} /// Pac SPI device pub trait SpiDevice: Deref + SubsystemReset + Sealed { + /// Index of the peripheral. + const ID: usize; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8; /// The DREQ number for which RX DMA requests are triggered. fn rx_dreq() -> u8; } +impl Sealed for pac::SPI0 {} impl SpiDevice for pac::SPI0 { + const ID: usize = 0; fn tx_dreq() -> u8 { TREQ_SEL_A::SPI0_TX.into() } @@ -112,8 +117,9 @@ impl SpiDevice for pac::SPI0 { TREQ_SEL_A::SPI0_RX.into() } } -impl Sealed for pac::SPI0 {} +impl Sealed for pac::SPI1 {} impl SpiDevice for pac::SPI1 { + const ID: usize = 1; fn tx_dreq() -> u8 { TREQ_SEL_A::SPI1_TX.into() } @@ -121,7 +127,6 @@ impl SpiDevice for pac::SPI1 { TREQ_SEL_A::SPI1_RX.into() } } -impl Sealed for pac::SPI1 {} /// Data size used in spi pub trait DataSize: Sealed {} diff --git a/rp2040-hal/src/spi/pins.rs b/rp2040-hal/src/spi/pins.rs index 198bf215b..5a1bac718 100644 --- a/rp2040-hal/src/spi/pins.rs +++ b/rp2040-hal/src/spi/pins.rs @@ -1,17 +1,71 @@ -use super::SpiDevice; -use crate::gpio::{AnyPin, FunctionSpi}; -use crate::typelevel::{OptionTSome, Sealed}; -use crate::{gpio::bank0::*, typelevel::OptionTNone}; +use core::marker::PhantomData; + +use crate::gpio::{pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionSpi}; +use crate::{ + gpio::bank0::*, + typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}, +}; use pac::{SPI0, SPI1}; -/// Indicates a valid Rx pin for SPI0 or SPI1 -pub trait ValidPinRx: Sealed {} -/// Indicates a valid Tx pin for SPI0 or SPI1 -pub trait ValidPinTx: Sealed {} -/// Indicates a valid SCLK pin for SPI0 or SPI1 -pub trait ValidPinSck: Sealed {} -/// Indicates a valid CS pin for SPI0 or SPI1 -pub trait ValidPinCs: Sealed {} +use super::SpiDevice; + +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Indicates a valid " $p " pin for SPI0 or SPI1"] + pub trait []: Sealed {} + + #[doc = "Indicates a valid " $p " pin for SPI0 or SPI1"] + pub trait []: Sealed {} + + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } + + #[doc = "A runtime validated " $p " pin for spi."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + S: SpiDevice, + { + /// Validate a pin's function on a spi peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that Spi."] + pub fn validate(p: P, _u: &S) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, S::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + + #[doc = "Indicates a valid optional " $p " pin for SPI0 or SPI1"] + pub trait []: OptionT {} + + impl [] for OptionTNone {} + impl [] for OptionTSome + where + U: SpiDevice, + T: [], + { + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; +} +pin_validation!(Tx, Rx, Sck, Cs); macro_rules! impl_valid_spi { ($($spi:ident: { @@ -21,11 +75,16 @@ macro_rules! impl_valid_spi { tx: [$($tx:ident),*], }),*) => { $( - $(impl ValidPinRx<$spi> for $rx {})* - $(impl ValidPinTx<$spi> for $tx {})* - $(impl ValidPinSck<$spi> for $sck {})* - $(impl ValidPinCs<$spi> for $cs {})* + $(impl ValidPinIdRx<$spi> for $rx {})* + $(impl ValidPinIdTx<$spi> for $tx {})* + $(impl ValidPinIdSck<$spi> for $sck {})* + $(impl ValidPinIdCs<$spi> for $cs {})* )* + + const RX: &[(u8, usize)] = &[$($(($rx::ID.num, $spi::ID)),*),*]; + const TX: &[(u8, usize)] = &[$($(($tx::ID.num, $spi::ID)),*),*]; + const SCK: &[(u8, usize)] = &[$($(($sck::ID.num, $spi::ID)),*),*]; + const CS: &[(u8, usize)] = &[$($(($cs::ID.num, $spi::ID)),*),*]; }; } @@ -44,48 +103,6 @@ impl_valid_spi!( } ); -/// Indicates a valid optional Rx pin for SPI0 or SPI1 -pub trait ValidOptionRx: Sealed {} -/// Indicates a valid optional Tx pin for SPI0 or SPI1 -pub trait ValidOptionTx: Sealed {} -/// Indicates a valid optional SCLK pin for SPI0 or SPI1 -pub trait ValidOptionSck: Sealed {} -/// Indicates a valid optional CS pin for SPI0 or SPI1 -pub trait ValidOptionCs: Sealed {} -impl ValidOptionRx for OptionTNone {} -impl ValidOptionCs for OptionTNone {} -impl ValidOptionSck for OptionTNone {} -impl ValidOptionTx for OptionTNone {} - -impl ValidOptionRx for OptionTSome -where - U: SpiDevice, - T: AnyPin, - T::Id: ValidPinRx, -{ -} -impl ValidOptionCs for OptionTSome -where - U: SpiDevice, - T: AnyPin, - T::Id: ValidPinCs, -{ -} -impl ValidOptionSck for OptionTSome -where - U: SpiDevice, - T: AnyPin, - T::Id: ValidPinSck, -{ -} -impl ValidOptionTx for OptionTSome -where - U: SpiDevice, - T: AnyPin, - T::Id: ValidPinTx, -{ -} - /// Declares a valid SPI pinout. pub trait ValidSpiPinout: Sealed { #[allow(missing_docs)] @@ -101,10 +118,8 @@ pub trait ValidSpiPinout: Sealed { impl ValidSpiPinout for (Tx, Sck) where Spi: SpiDevice, - Tx: AnyPin, - Sck: AnyPin, - Tx::Id: ValidPinTx, - Sck::Id: ValidPinSck, + Tx: ValidPinTx, + Sck: ValidPinSck, { type Rx = OptionTNone; type Cs = OptionTNone; @@ -115,12 +130,9 @@ where impl ValidSpiPinout for (Tx, Rx, Sck) where Spi: SpiDevice, - Tx: AnyPin, - Sck: AnyPin, - Rx: AnyPin, - Tx::Id: ValidPinTx, - Sck::Id: ValidPinSck, - Rx::Id: ValidPinRx, + Tx: ValidPinTx, + Sck: ValidPinSck, + Rx: ValidPinRx, { type Rx = OptionTSome; type Cs = OptionTNone; diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index 94b151e51..ed7d2c71a 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -275,8 +275,8 @@ fn calculate_baudrate_dividers( } /// Baudrate configuration. Code loosely inspired from the C SDK. -fn configure_baudrate( - device: &mut dyn UartDevice, +fn configure_baudrate( + device: &mut U, wanted_baudrate: HertzU32, frequency: HertzU32, ) -> Result { diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index 515f347b2..7e7c022b4 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,17 +1,69 @@ -use crate::gpio::{bank0::*, AnyPin, FunctionUart}; +use core::marker::PhantomData; + +use crate::gpio::{bank0::*, pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionUart}; use crate::pac::{UART0, UART1}; use crate::typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}; use super::UartDevice; -/// Indicates a valid TX pin for UART0 or UART1 -pub trait ValidPinTx: Sealed {} -/// Indicates a valid RX pin for UART0 or UART1 -pub trait ValidPinRx: Sealed {} -/// Indicates a valid CTS pin for UART0 or UART1 -pub trait ValidPinCts: Sealed {} -/// Indicates a valid RTS pin for UART0 or UART1 -pub trait ValidPinRts: Sealed {} +// All type level checked pins are inherently valid. +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Indicates a valid " $p " pin for UART0 or UART1"] + pub trait []: Sealed {} + + #[doc = "Indicates a valid " $p " pin for UART0 or UART1"] + pub trait []: Sealed {} + + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } + + #[doc = "A runtime validated " $p " pin for uart."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + U: UartDevice, + { + /// Validate a pin's function on a uart peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that Uart."] + pub fn validate(p: P, _u: &U) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, U::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + + #[doc = "Indicates a valid optional " $p " pin for UART0 or UART1"] + pub trait []: OptionT {} + + impl [] for OptionTNone {} + impl [] for OptionTSome + where + U: UartDevice, + T: [], + { + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; +} +pin_validation!(Tx, Rx, Cts, Rts); macro_rules! impl_valid_uart { ($($uart:ident: { @@ -21,11 +73,15 @@ macro_rules! impl_valid_uart { rts: [$($rts:ident),*], }),*) => { $( - $(impl ValidPinTx<$uart> for $tx {})* - $(impl ValidPinRx<$uart> for $rx {})* - $(impl ValidPinCts<$uart> for $cts {})* - $(impl ValidPinRts<$uart> for $rts {})* + $(impl ValidPinIdTx<$uart> for $tx {})* + $(impl ValidPinIdRx<$uart> for $rx {})* + $(impl ValidPinIdCts<$uart> for $cts {})* + $(impl ValidPinIdRts<$uart> for $rts {})* )* + const RX: &[(u8, usize)] = &[$($(($rx::ID.num, $uart::ID)),*),*]; + const TX: &[(u8, usize)] = &[$($(($tx::ID.num, $uart::ID)),*),*]; + const CTS: &[(u8, usize)] = &[$($(($cts::ID.num, $uart::ID)),*),*]; + const RTS: &[(u8, usize)] = &[$($(($rts::ID.num, $uart::ID)),*),*]; }; } @@ -44,48 +100,6 @@ impl_valid_uart!( } ); -/// Indicates a valid optional Tx pin for UART0 or UART1 -pub trait ValidOptionTx: OptionT {} -/// Indicates a valid optional Rx pin for UART0 or UART1 -pub trait ValidOptionRx: OptionT {} -/// Indicates a valid optional Cts pin for UART0 or UART1 -pub trait ValidOptionCts: OptionT {} -/// Indicates a valid optional Rts pin for UART0 or UART1 -pub trait ValidOptionRts: OptionT {} -impl ValidOptionTx for OptionTNone {} -impl ValidOptionRx for OptionTNone {} -impl ValidOptionCts for OptionTNone {} -impl ValidOptionRts for OptionTNone {} - -impl ValidOptionTx for OptionTSome -where - U: UartDevice, - T: AnyPin, - T::Id: ValidPinTx, -{ -} -impl ValidOptionRx for OptionTSome -where - U: UartDevice, - T: AnyPin, - T::Id: ValidPinRx, -{ -} -impl ValidOptionCts for OptionTSome -where - U: UartDevice, - T: AnyPin, - T::Id: ValidPinCts, -{ -} -impl ValidOptionRts for OptionTSome -where - U: UartDevice, - T: AnyPin, - T::Id: ValidPinRts, -{ -} - /// Declares a valid UART pinout. pub trait ValidUartPinout: Sealed { #[allow(missing_docs)] @@ -101,10 +115,8 @@ pub trait ValidUartPinout: Sealed { impl ValidUartPinout for (Tx, Rx) where Uart: UartDevice, - Tx: AnyPin, - Rx: AnyPin, - Tx::Id: ValidPinTx, - Rx::Id: ValidPinRx, + Tx: ValidPinTx, + Rx: ValidPinRx, { type Tx = OptionTSome; type Rx = OptionTSome; @@ -115,14 +127,10 @@ where impl ValidUartPinout for (Tx, Rx, Cts, Rts) where Uart: UartDevice, - Tx: AnyPin, - Rx: AnyPin, - Cts: AnyPin, - Rts: AnyPin, - Tx::Id: ValidPinTx, - Rx::Id: ValidPinRx, - Cts::Id: ValidPinCts, - Rts::Id: ValidPinRts, + Tx: ValidPinTx, + Rx: ValidPinRx, + Cts: ValidPinCts, + Rts: ValidPinRts, { type Rx = OptionTSome; type Tx = OptionTSome; diff --git a/rp2040-hal/src/uart/utils.rs b/rp2040-hal/src/uart/utils.rs index 135969048..0c0e54bf2 100644 --- a/rp2040-hal/src/uart/utils.rs +++ b/rp2040-hal/src/uart/utils.rs @@ -17,6 +17,9 @@ pub trait State: Sealed {} /// Trait to handle both underlying devices (UART0 & UART1) pub trait UartDevice: Deref + SubsystemReset + Sealed + 'static { + /// Index of the Uart. + const ID: usize; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 where @@ -28,6 +31,8 @@ pub trait UartDevice: Deref + SubsystemReset + Sealed + } impl UartDevice for UART0 { + const ID: usize = 0; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 { TREQ_SEL_A::UART0_TX.into() @@ -39,6 +44,8 @@ impl UartDevice for UART0 { } impl Sealed for UART0 {} impl UartDevice for UART1 { + const ID: usize = 1; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 { TREQ_SEL_A::UART1_TX.into() From b5f209991994c3eb512f936ab402c7c8b3761402 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Wed, 10 May 2023 12:13:26 +0100 Subject: [PATCH 15/26] update deprecation notice. --- rp2040-hal/src/adc.rs | 4 ++++ rp2040-hal/src/gpio/mod.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index cb1919318..4d13d349d 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -175,6 +175,10 @@ impl Adc { /// /// This can only be done once before calling [`disable_temp_sensor`]. If the sensor has already /// been enabled, this method will panic. + #[deprecated( + note = "This method may panic, use `take_temp_sensor()` instead.", + since = "0.9.0" + )] pub fn enable_temp_sensor(&mut self) -> TempSense { self.take_temp_sensor() .expect("Temp sensor is already enabled.") diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 039605e88..1994458f3 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -405,7 +405,10 @@ impl Pin { /// /// If you want to specify the initial pin state, use [`Pin::into_readable_output_in_state`]. #[inline] - #[deprecated(note = "All gpio are readable, use `.into_push_pull_output()` instead.")] + #[deprecated( + note = "All gpio are readable, use `.into_push_pull_output()` instead.", + since = "0.9.0" + )] pub fn into_readable_output(self) -> Pin, P> where I: ValidFunction>, @@ -416,7 +419,10 @@ impl Pin { /// Configure the pin to operate as a readable push pull output, specifying an initial /// state which is applied immediately. #[inline] - #[deprecated(note = "All gpio are readable, use `.into_push_pull_output_in_state()` instead.")] + #[deprecated( + note = "All gpio are readable, use `.into_push_pull_output_in_state()` instead.", + since = "0.9.0" + )] pub fn into_readable_output_in_state(self, state: PinState) -> Pin, P> where I: ValidFunction>, From ff0dc594d118ec5ae0fe9ebefa9975dc4153af34 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Wed, 10 May 2023 12:13:44 +0100 Subject: [PATCH 16/26] Add `try_into_function` for dynpin'ed Pins. --- rp2040-hal/src/gpio/mod.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 1994458f3..be943d07b 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -780,6 +780,31 @@ impl Pin { Err(self) } } + + /// Try to change the pin's function. + pub fn try_into_function(self) -> Result, Pin> + where + F2: func::Function, + { + // Thanks to type-level validation, we know F2 is valid for I + let prev_function = self.function.as_dyn(); + let function = F2::from(prev_function); + let function_as_dyn = function.as_dyn(); + + use func_sealed::Function; + if function_as_dyn.is_valid(&self.id) { + if function_as_dyn != prev_function.as_dyn() { + pin::set_function(&self.id, function_as_dyn); + } + Ok(Pin { + function, + id: self.id, + pull_type: self.pull_type, + }) + } else { + Err(self) + } + } } impl Pin { /// Try to set the pin's function. @@ -787,7 +812,7 @@ impl Pin { /// This method may fail if the requested function is not supported by the pin, eg `FunctionXiP` /// on a gpio from `Bank0`. pub fn try_set_function(&mut self, function: DynFunction) -> Result<(), func::InvalidFunction> { - use func::func_sealed::Function; + use func_sealed::Function; if !function.is_valid(&self.id) { return Err(func::InvalidFunction); } else if function != self.function.as_dyn() { @@ -799,7 +824,7 @@ impl Pin { /// Gets the pin's function. pub fn function(&self) -> DynFunction { - use func::func_sealed::Function; + use func_sealed::Function; self.function.as_dyn() } } From 34c4cdacaeb6bf011b635981050bb8d50f0f315b Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 11 May 2023 12:04:15 +0100 Subject: [PATCH 17/26] Fix some doc warnings --- rp2040-hal/src/adc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 4d13d349d..205966c96 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -139,9 +139,11 @@ impl Channel for TempSense { /// Represents an ADC within the RP2040. Each ADC has multiple channels, and each /// channel is either associated with a specific GPIO pin or attached to the internal /// temperature sensor. You should put the relevant pin into ADC mode by creating an -/// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode" +/// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode` /// by calling [`Adc::take_temp_sensor()`]. Either way, the resulting objects can be -/// passed to the [`OneShot::read()`] trait method to actually do the read. +/// passed to the [`OneShot::read()`][a] trait method to actually do the read. +/// +/// [a]: embedded_hal::adc::OneShot::read pub struct Adc { device: ADC, } @@ -173,7 +175,7 @@ impl Adc { /// Enable temperature sensor, returns a channel to use. /// - /// This can only be done once before calling [`disable_temp_sensor`]. If the sensor has already + /// This can only be done once before calling [`Adc::disable_temp_sensor()`]. If the sensor has already /// been enabled, this method will panic. #[deprecated( note = "This method may panic, use `take_temp_sensor()` instead.", From c5d5357a297ec9d461833440078162b37df74a98 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 11 May 2023 12:04:01 +0100 Subject: [PATCH 18/26] Implement `PinGroup` --- rp2040-hal/Cargo.toml | 2 + rp2040-hal/src/gpio/mod.rs | 2 + rp2040-hal/src/gpio/pin.rs | 31 +++-- rp2040-hal/src/gpio/pin/pin_sealed.rs | 2 + rp2040-hal/src/gpio/pin_group.rs | 170 ++++++++++++++++++++++++++ rp2040-hal/src/typelevel.rs | 3 + 6 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 rp2040-hal/src/gpio/pin_group.rs diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index eb5757dae..63026199a 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -40,6 +40,8 @@ defmt = { version = ">=0.2.0, <0.4", optional = true } rtic-monotonic = { version = "1.0.0", optional = true } +frunk = { version = "0.4.1", default-features = false } + [dev-dependencies] cortex-m-rt = "0.7" panic-halt = "0.2.0" diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index be943d07b..bd42a3173 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -49,10 +49,12 @@ use crate::{ mod func; pub(crate) mod pin; +mod pin_group; mod pull; pub use func::*; pub use pin::{DynBankId, DynPinId, PinId}; +pub use pin_group::PinGroup; pub use pull::*; /// The amount of current that a pin can drive when used as an output. diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index 8036f56e9..debea1dc6 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -30,12 +30,6 @@ use super::{DynFunction, DynPullType}; pub(crate) mod pin_sealed; -/// Type-level `enum` for the pin Id (pin number + bank). -pub trait PinId: pin_sealed::PinIdOps { - /// This pin as a `DynPinId`. - fn as_dyn(&self) -> DynPinId; -} - /// Value-level `enum` for the pin's bank. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -46,6 +40,25 @@ pub enum DynBankId { Qspi, } +/// Type-level `enum` for the pin's bank ID. +pub trait BankId: crate::typelevel::Sealed {} + +/// Type-level `variant` of `BankId` +pub struct BankBank0; +impl crate::typelevel::Sealed for BankBank0 {} +impl BankId for BankBank0 {} + +/// Type-level `variant` of `BankId` +pub struct BankQspi; +impl crate::typelevel::Sealed for BankQspi {} +impl BankId for BankQspi {} + +/// Type-level `enum` for the pin Id (pin number + bank). +pub trait PinId: pin_sealed::PinIdOps { + /// This pin as a `DynPinId`. + fn as_dyn(&self) -> DynPinId; +} + /// Value-level representation for the pin (bank + id). #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -85,6 +98,8 @@ macro_rules! pin_ids { } } impl pin_sealed::TypeLevelPinId for [<$prefix $name>] { + type Bank = []; + const ID: DynPinId = DynPinId { bank: DynBankId::$bank, num: $id @@ -100,12 +115,12 @@ macro_rules! pin_ids { } /// Bank of all the GPIOs. pub mod bank0 { - use super::{pin_sealed, DynBankId, DynPinId, PinId}; + use super::{pin_sealed, BankBank0, DynBankId, DynPinId, PinId}; pin_ids!(Bank0 as Gpio: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29); } /// Bank of the QSPI related pins. pub mod qspi { - use super::{pin_sealed, DynBankId, DynPinId, PinId}; + use super::{pin_sealed, BankQspi, DynBankId, DynPinId, PinId}; pin_ids!(Qspi: 0;Sclk, 1;Ss, 2;Sd0, 3;Sd1, 4;Sd2, 5;Sd3); } diff --git a/rp2040-hal/src/gpio/pin/pin_sealed.rs b/rp2040-hal/src/gpio/pin/pin_sealed.rs index 0180f4e66..fc1f56e53 100644 --- a/rp2040-hal/src/gpio/pin/pin_sealed.rs +++ b/rp2040-hal/src/gpio/pin/pin_sealed.rs @@ -3,6 +3,8 @@ use crate::sio::CoreId; use super::{DynBankId, DynPinId}; pub trait TypeLevelPinId: super::PinId { + type Bank: super::BankId; + const ID: DynPinId; fn new() -> Self; diff --git a/rp2040-hal/src/gpio/pin_group.rs b/rp2040-hal/src/gpio/pin_group.rs new file mode 100644 index 000000000..61657818a --- /dev/null +++ b/rp2040-hal/src/gpio/pin_group.rs @@ -0,0 +1,170 @@ +use embedded_hal::digital::v2::PinState; +use frunk::{hlist::Plucker, HCons, HNil}; + +use crate::typelevel::Sealed; + +use super::{ + pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionSio, FunctionSioInput, FunctionSioOutput, Pin, + PinId, PullType, SioConfig, +}; + +/// Generate a read mask for a pin list. +pub trait ReadPinHList: Sealed { + /// Generate a mask for a pin list. + fn read_mask(&self) -> u32; +} +impl ReadPinHList for HNil { + fn read_mask(&self) -> u32 { + 0 + } +} +impl ReadPinHList for HCons { + fn read_mask(&self) -> u32 { + (1 << self.head.borrow().id().num) | self.tail.read_mask() + } +} + +/// Generate a write mask for a pin list. +pub trait WritePinHList: Sealed { + /// Generate a mask for a pin list. + fn write_mask(&self) -> u32; +} +impl WritePinHList for HNil { + fn write_mask(&self) -> u32 { + 0 + } +} +impl WritePinHList + for HCons, T> +{ + fn write_mask(&self) -> u32 { + self.tail.write_mask() + } +} +impl WritePinHList + for HCons, T> +{ + fn write_mask(&self) -> u32 { + self.tail.write_mask() + } +} + +/// A group of pins to be controlled together and guaranty single cycle control of several pins. +/// +/// ```no_run +/// # macro_rules! defmt { ($($a:tt)*) => {}} +/// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PinState, PinGroup}, sio::Sio}; +/// +/// let mut peripherals = pac::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); +/// +/// let group = PinGroup::new(); +/// let group = group.add_pin(pins.gpio0.into_pull_up_input()); +/// let mut group = group.add_pin(pins.gpio4.into_push_pull_output_in_state(PinState::High)); +/// +/// defmt!("Group's state is: {}", group.read()); +/// group.toggle(); +/// defmt!("Group's state is: {}", group.read()); +/// ``` +pub struct PinGroup(T); +impl PinGroup { + /// Creates an empty pin group. + pub fn new() -> Self { + PinGroup(HNil) + } + + /// Add a pin to the group. + pub fn add_pin(self, pin: P) -> PinGroup> + where + C: SioConfig, + P: AnyPin>, + P::Id: TypeLevelPinId, + { + PinGroup(HCons { + head: pin, + tail: self.0, + }) + } +} +impl PinGroup> +where + H::Id: TypeLevelPinId, + H: AnyPin, +{ + /// Add a pin to the group. + pub fn add_pin(self, pin: P) -> PinGroup>> + where + C: SioConfig, + P: AnyPin>, + P::Id: TypeLevelPinId::Bank>, + { + PinGroup(HCons { + head: pin, + tail: self.0, + }) + } + + /// Pluck a pin from the group. + #[allow(clippy::type_complexity)] + pub fn remove_pin( + self, + ) -> (P, PinGroup< as Plucker>::Remainder>) + where + HCons: Plucker, + { + let (p, rest): (P, _) = self.0.pluck(); + (p, PinGroup(rest)) + } +} +impl PinGroup> +where + HCons: ReadPinHList + WritePinHList, + H: AnyPin, +{ + /// Read the whole group at once. + /// + /// The returned value is a bit field where each pin populates its own index. Therefore, there + /// might be "holes" in the value. Unoccupied bits will always read as 0. + /// + /// For example, if the group contains Gpio1 and Gpio3, a read may yield: + /// ```text + /// 0b0000_0000__0000_0000__0000_0000__0000_1010 + /// This is Gpio3 ↑↑↑ + /// Gpio2 is not used || + /// This is Gpio1 | + /// ``` + pub fn read(&self) -> u32 { + let mask = self.0.read_mask(); + crate::sio::Sio::read_bank0() & mask + } + + /// Write this set of pins all at the same time. + pub fn set(&mut self, state: PinState) { + use super::pin::pin_sealed::PinIdOps; + let mask = self.0.write_mask(); + let head_id = self.0.head.borrow().id(); + if state == PinState::Low { + head_id.sio_out_clr().write(|w| unsafe { w.bits(mask) }); + } else { + head_id.sio_out_set().write(|w| unsafe { w.bits(mask) }); + } + } + + /// Toggles this set of pins all at the same time. + pub fn toggle(&mut self) { + use super::pin::pin_sealed::PinIdOps; + let mask = self.0.write_mask(); + self.0 + .head + .borrow() + .id() + .sio_out_xor() + .write(|w| unsafe { w.bits(mask) }); + } +} +impl Default for PinGroup { + fn default() -> Self { + Self::new() + } +} diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index ddf17b4a8..04769c417 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -647,6 +647,9 @@ impl Sealed for (A, B) {} impl Sealed for (A, B, C) {} impl Sealed for (A, B, C, D) {} +impl Sealed for frunk::HNil {} +impl Sealed for frunk::HCons {} + /// Type-level version of the [None] variant #[derive(Default)] pub struct NoneT; From 074dde460643e9ddf8c7e2b8eb5fc51e491707d0 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 11 May 2023 17:21:56 +0100 Subject: [PATCH 19/26] Update type-level documentation. There was too many changes required to keep it in sync with the current implementation. However, this is not required to understand the essence of the type-level techniques. Therefore it is less maintenance costly to forward the reader to the upstream of the idea where the documentation is already excellent. --- rp2040-hal/src/typelevel.rs | 645 +----------------------------------- 1 file changed, 9 insertions(+), 636 deletions(-) diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index 04769c417..ebdb13136 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -1,637 +1,8 @@ //! Module supporting type-level programming //! -//! Copied from [atsamd-hal](https://github.com/atsamd-rs/atsamd). -//! -//! # Introduction -//! -//! Embedded software is often difficult to debug, so there is a strong -//! motivation to catch as many bugs as possible at compile-time. However, the -//! performance requirements of embedded software also make it difficult to -//! justify changes that impose additional overhead in terms of size or speed. -//! Ideally, we would like to add as many compile-time checks as possible, while -//! also producing the fewest possible assembly instructions. -//! -//! The Rust type system can help accomplish this goal. By expressing software -//! constraints within the type system, developers can enforce invariants at -//! compile-time. -//! -//! Sometimes this is done using Rust macros. However, that approach can produce -//! code that is difficult to read and understand. Moreover, macro-generated -//! code can only be extended by *more* macros, which further spreads the -//! problem. In `atsamd-hal` specifically, issue -//! [#214](https://github.com/atsamd-rs/atsamd/issues/214) discussed the extent -//! to which macros were once used in the repository. -//! -//! Alternatively, many of the same goals can be accomplished with the Rust -//! type & trait system directly, which is quite powerful. In fact, it is -//! [turing complete](https://sdleffler.github.io/RustTypeSystemTuringComplete/). -//! By expressing our invariants entirely within the type system, we can encode -//! the desired compile-time checks in a form that is easier to read, understand -//! and document. -//! -//! This module documents some of the type-level programming techniques used -//! throughout this HAL, and it contains a few items used to implement them. -//! -//! ## Contents -//! -//! - [Basics of type-level programming](#basics-of-type-level-programming) -//! - [Type-level enums](#type-level-enums) -//! - [Type classes](#type-classes) -//! - [Type-level containers](#type-level-containers) -//! - [Type-level functions](#type-level-functions) -//! - [`OptionalKind` trait pattern](#optionalkind-trait-pattern) -//! - [`AnyKind` trait pattern](#anykind-trait-pattern) -//! - [Defining an `AnyKind` trait](#defining-an-anykind-trait) -//! - [Using an `AnyKind` trait](#using-an-anykind-trait) -//! -//! # Basics of type-level programming -//! -//! Type-level programming aims to execute a form of compile-time computation. -//! But to perform such computation, we need to map our traditional notions of -//! programming to the Rust type system. -//! -//! In normal Rust, individual values are grouped or categorized into types. For -//! example, `0`, `1`, `2`, etc. are all members of the `usize` type. Similarly, -//! `Enum::A` and `Enum::B` are members of the `Enum` type, defined as -//! -//! ```ignore -//! enum Enum { A, B } -//! ``` -//! -//! We use composite types and containers to create more complex data -//! structures, and we use functions to map between values. -//! -//! All of these concepts can also be expressed within the Rust type system. -//! However, in this case, types are grouped and categorized into traits. For -//! instance, the [`typenum`](https://docs.rs/typenum/1.13.0/typenum/index.html) -//! crate provides the types `U0`, `U1`, `U2`, etc., which are all members of -//! the `Unsigned` trait. Similarly, the following sections will illustrate how -//! to define type-level enums, containers and functions. -//! -//! ## Type-level enums -//! -//! Type-level enums are one of the foundational concepts of type-level -//! programming used in this HAL. -//! -//! At the value-level, a typical Rust enum represents some set of variants that -//! can be assigned to a particular variable. Similarly, a type-level enum -//! represents some set of types that can be assigned to a particular type -//! parameter. -//! -//! To lift an enum from the value level to the type level, you typically map -//! the enum variants to types and the enum itself to a trait. For instance, the -//! value-level enum -//! -//! ```ignore -//! enum Enum { -//! A, -//! B, -//! } -//! ``` -//! -//! would be mapped to the type level like so. -//! -//! ```ignore -//! trait Enum {} -//! -//! enum A {} -//! enum B {} -//! -//! impl Enum for A {} -//! impl Enum for B {} -//! ``` -//! -//! At the value level, the variants `A` and `B` are grouped by the `Enum` type, -//! while at the type level, the types `A` and `B` are grouped by the `Enum` -//! trait. -//! -//! ## Type classes -//! -//! At the value-level, a type restricts the possible values that can be taken -//! by some free variable. While at the type-level, a trait bound restricts the -//! possible types that can be taken by some free type parameter. In effect, -//! trait bounds can be used to create a kind of meta-type, or type class. The -//! type-level enums in the previous section represent the most primitive -//! application of the concept, but type classes can take other forms. The -//! `OptionalKind` and `AnyKind` trait patterns discussed below are more -//! advanced applications of the same concept. -//! -//! ## Type-level containers -//! -//! To represent more complex relationships, we need a way to form composite -//! data structures at the type level. -//! -//! At the value level, a container holds an instance of a particular type. The -//! exact value of that instance is usually not known to the author, it is only -//! known at run-time. -//! -//! At the type level, we don't have the same notion of "run-time", but we do -//! have two different notions of "compile-time" that form a similar -//! relationship. There is compile time for the HAL authors, and there is a -//! separate compile-time for the HAL users. We want to create a type-level -//! container where the exact type is not known at author-time, but it is known -//! at user-time. -//! -//! For example, take the following, value-level container struct. It contains -//! two fields, `a` and `b`, of different types, `EnumOne` and `EnumTwo`. -//! -//! ```ignore -//! struct Container { -//! a: EnumOne, -//! b: EnumTwo, -//! } -//! ``` -//! -//! We can create an instance of this container with specific values. -//! -//! ```ignore -//! let x = Container { a: EnumOne::VariantX, b: EnumTwo::VariantY }; -//! ``` -//! -//! Next, suppose we had already translated `EnumOne` and `EnumTwo` to the type -//! level using the technique in the previous section. If we wanted to create a -//! similar, composite data structure at the type level, we could use type -//! parameters in place of struct fields to represent the unknown types. -//! -//! ```ignore -//! struct Container -//! where -//! A: EnumOne, -//! B: EnumTwo, -//! { -//! a: PhantomData, -//! b: PhantomData, -//! } -//! ``` -//! -//! And we could create an instance of this container with specific types. -//! -//! ```ignore -//! type X = Container; -//! ``` -//! -//! You might notice the use of `PhantomData` in the definition of the -//! type-level container. Because it is geared more toward value-level -//! programming, Rust requires all type parameters actually be used by the -//! corresponding type. However, we don't need to "store" a type in the same way -//! we store values. The compiler is responsible for tracking the concrete type -//! for each type parameter. But the language still requires us to act as if we -//! used each type parameter. `PhantomData` is the solution here, because it -//! lets us make use of the type parameters without actually storing any values. -//! -//! Separately, `PhantomData` also allows us to create "instances" of types that -//! normally can't be instantiated, like empty enums. For example, instances of -//! `Enum` below can never exist directly. -//! -//! ```ignore -//! enum Enum {} -//! ``` -//! -//! But instances of `PhantomData` are perfectly valid. In this way, -//! library authors can create types that only exist at the type level, which -//! can sometimes simplify a design. -//! -//! ## Type-level functions -//! -//! To perform type-level computations, we need some way to map or transform -//! types into other types. -//! -//! At the value level, functions and methods map values of the input types to -//! values of the output types. The same can be accomplished at the type level -//! using traits and associated types. Type-level functions are implemented as -//! traits, where the implementing type and any type parameters are the inputs, -//! and associated types are the outputs. -//! -//! For example, consider the value level `not` method below. -//! -//! ```ignore -//! enum Bool { -//! False, -//! True, -//! } -//! -//! impl Bool { -//! fn not(self) -> Self { -//! use Bool::*; -//! match self { -//! True => False, -//! False => True, -//! } -//! } -//! } -//! ``` -//! -//! We can translate this example to the type level like so. -//! -//! ```ignore -//! trait Bool {} -//! -//! enum True {} -//! enum False {} -//! -//! impl Bool for True {} -//! impl Bool for False {} -//! -//! trait Not: Bool { -//! type Result: Bool; -//! } -//! -//! impl Not for True { -//! type Result = False; -//! } -//! -//! impl Not for False { -//! type Result = True; -//! } -//! ``` -//! -//! We can use the `Not` trait bound to transform one type to another. For -//! instance, we can create a container that accepts one type parameter but -//! stores a different one. -//! -//! ```ignore -//! struct Container { -//! not: PhantomData; -//! } -//! ``` -//! -//! Alternatively, we could redefine the trait and declar a corresponding type -//! alias as -//! -//! ```ignore -//! trait NotFunction: Bool { -//! type Result: Bool; -//! } -//! -//! type Not = ::Result; -//! ``` -//! -//! Doing so would allow us to us reframe the last example as -//! -//! ```ignore -//! struct Container { -//! not: PhantomData>; -//! } -//! ``` -//! -//! Type-level functions can be more complicated than this example, but they -//! ultimately represent a mapping from a set of input types (the implementing -//! type and any type parameters) to a set of output types (the associated -//! types). -//! -//! # `OptionalKind` trait pattern -//! -//! As mentioned above, traits can be used to define a kind of meta-type or type -//! class, essentially forming a set of valid types for a given type parameter. -//! They also represent the concept of types lifted from the value level to the -//! type level. -//! -//! What if we want to define a type class representing either a set of useful -//! types or some useless, null type? Essentially, how do we take the notion of -//! an [`Option`] type and raise it to the type level? -//! -//! Suppose we have some existing type class, defined by the `Class` trait, that -//! we want to make optional. We can define a new type class that includes all -//! instances of `Class` as well as some null type. For the latter we use -//! [`NoneT`], defined in this module. -//! -//! ```ignore -//! trait OptionalClass {} -//! -//! impl OptionalClass for NoneT {} -//! impl OptionalClass for C {} -//! ``` -//! -//! We can use this new type class to store an optional instance of a `Class` -//! type in a struct. -//! -//! ```ignore -//! struct Container { -//! class: PhantomData, -//! } -//! ``` -//! -//! And we can restrict some of its methods to only operate on instances with a -//! valid `Class`. -//! -//! ```ignore -//! impl Container { -//! fn method(self) { ... } -//! } -//! ``` -//! -//! Although it is not strictly necessary, we can also introduce a new type -//! class to differentiate the bare usage of `Class` from instances of some -//! `Class` where an `OptionalClass` is accepted. -//! -//! ```ignore -//! trait SomeClass: OptionalClass + Class {} -//! -//! impl SomeClass for C {} -//! ``` -//! -//! This new trait doesn't add any new information, but it can still help -//! readers understand that a particular type parameter is restricted to an -//! instances of `Class` when an `OptionalClass` could be accepted. -//! -//! Note that when `Class` and `OptionalClass` contain associated types, name -//! clashes may occur when using `SomeClass` as a trait bound. This can be -//! avoided by removing the `OptionalClass` super trait from `SomeClass`. -//! Ultimately, it is redundant anyway, because any implementer of `Class` also -//! implements `OptionalClass`. -//! -//! # `AnyKind` trait pattern -//! -//! The `AnyKind` trait pattern allows you to encapsulate types with multiple -//! type parameters and represent them with only a single type parameter. It -//! lets you introduce a layer of abstraction, which can simplify interfaces and -//! make them more readable. But most of all, it does so without sacrificing any -//! of our normal, type-level abilities. -//! -//! ## Defining an `AnyKind` trait -//! -//! Suppose you had a composite, type-level data structure. For example, the -//! GPIO `Pin` struct contains instances of two type-level enums, a `PinId` and -//! a `PinMode`. It looks something like this. -//! -//! ```ignore -//! struct Pin { -//! // ... -//! } -//! ``` -//! -//! Rust does not provide any way to speak about a `Pin` generally. Any mention -//! of the `Pin` type must also include its type parameters, i.e. `Pin`. -//! This is not a deal-breaker, but it is less than ideal for type-level -//! programming. It would be nice if there were a way to succinctly refer to any -//! `Pin`, regardless of its type parameters. -//! -//! We've seen above that we can use traits to form a type class. What if we -//! were to introduce a new trait to label all instances of `Pin`? It would look -//! something like this. -//! -//! ```ignore -//! trait AnyPin {} -//! -//! impl AnyPin for Pin {} -//! ``` -//! -//! Now, instead of refering to `Pin`, we can refer to instances of the -//! `AnyPin` type class. -//! -//! ```ignore -//! fn example(pin: P) { ... } -//! ``` -//! -//! Unfortunately, while this is more ergonomic, it is not very useful. As -//! authors of the code, we know that `AnyPin` is only implemented for `Pin` -//! types. But the compiler doesn't know that. Traits in Rust are open, so the -//! compiler must consider that `AnyPin` could be implemented for other types. -//! -//! As a consequence, the compiler knows very little about the type `P` in the -//! function above. In fact, because the `AnyPin` trait is completely empty, the -//! compiler knows *absolutely nothing* about the type `P`. -//! -//! Is there a way to make the `AnyPin` trait more useful? We can see from the -//! current implementation that we are throwing away information. -//! -//! ```ignore -//! impl AnyPin for Pin {} -//! ``` -//! -//! The implementation of `AnyPin` is identical for every `Pin`, regardless of -//! the type parameters `I` and `M`, which erases that information. Instead, we -//! could choose to save that information in the form of associated types. -//! -//! Let's redesign the `AnyPin` trait to record the `PinId` and `PinMode`. -//! -//! ```ignore -//! trait AnyPin { -//! type Id: PinId; -//! type Mode: PinMode; -//! } -//! -//! impl AnyPin for Pin { -//! type Id = I; -//! type Mode = M; -//! } -//! ``` -//! -//! This is better. When `P` implements `AnyPin`, we can at least recover the -//! corresponding `PinId` and `PinMode` types. However, `AnyPin` still doesn't -//! include any trait methods nor any super traits, so the compiler won't allow -//! us to do anything useful with an instances of `P`. -//! -//! We need some way to tell the compiler that when `P` implements `AnyPin`, -//! it is equivalent to saying `P` is exactly `Pin`. -//! Essentially, we want to take a generic type parameter `P` and treat it as if -//! it were an instance of a specific `Pin` type. -//! -//! We can start by defining a trait alias to recover the specific `Pin` type. -//! -//! ```ignore -//! type SpecificPin

= Pin<

::Id,

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

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

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

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

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

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

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

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

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

== P` for all `P`. As far as the compiler is concerned, -//! there could be several different types that implement -//! `AnyPin`. Instead, the compiler requires that -//! you put back an instance of `P` exactly. The final use of [`Into`] is key -//! here. It transforms the `SpecificPin` back into `P` itself. +//! This is heavily inspired by the work in [`atsamd-rs`](https://github.com/atsamd-rs/atsamd). Please refer to the +//! [documentation](https://github.com/atsamd-rs/atsamd/blob/d2ba3da08daa807ee5adc0e17546551bbfe7c1f2/hal/src/typelevel.rs) +//! over there for more details. mod private { /// Super trait used to mark traits with an exhaustive set of @@ -664,20 +35,22 @@ impl Sealed for NoneT {} /// specific type. Stated differently, it guarantees that `T == Specific` in /// the following example. /// -/// ```ignore +/// ```text /// where T: Is /// ``` /// /// Moreover, the super traits guarantee that any instance of or reference to a /// type `T` can be converted into the `Specific` type. /// -/// ```ignore +/// ``` +/// # use rp2040_hal::typelevel::Is; +/// # struct Specific; /// fn example(mut any: T) /// where /// T: Is, /// { -/// let specific_mut: &mut Specific = any.as_mut(); -/// let specific_ref: &Specific = any.as_ref(); +/// let specific_mut: &mut Specific = any.borrow_mut(); +/// let specific_ref: &Specific = any.borrow(); /// let specific: Specific = any.into(); /// } /// ``` From 23e728e57bf95d5899f01351a8cdd818e1075803 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 11 May 2023 18:02:25 +0100 Subject: [PATCH 20/26] rename `Pin::into()` to `Pin::into_typestate()` to avoid conflicts with `Into::into` --- rp2040-hal/examples/gpio_irq_example.rs | 4 ++-- rp2040-hal/examples/pwm_irq_input.rs | 4 ++-- rp2040-hal/src/gpio/mod.rs | 17 +++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/rp2040-hal/examples/gpio_irq_example.rs b/rp2040-hal/examples/gpio_irq_example.rs index 0eaed59ac..6b4d305ce 100644 --- a/rp2040-hal/examples/gpio_irq_example.rs +++ b/rp2040-hal/examples/gpio_irq_example.rs @@ -121,10 +121,10 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into(); + let led = pins.gpio25.into_typestate(); // Set up the GPIO pin that will be our input - let in_pin = pins.gpio26.into(); + let in_pin = pins.gpio26.into_typestate(); // Trigger on the 'falling edge' of the input pin. // This will happen as the button is being pressed diff --git a/rp2040-hal/examples/pwm_irq_input.rs b/rp2040-hal/examples/pwm_irq_input.rs index 291214912..4021088e5 100644 --- a/rp2040-hal/examples/pwm_irq_input.rs +++ b/rp2040-hal/examples/pwm_irq_input.rs @@ -134,7 +134,7 @@ fn main() -> ! { pwm.enable(); // Connect to GPI O1 as the input to channel B on PWM0 - let input_pin = pins.gpio1.into(); + let input_pin = pins.gpio1.into_typestate(); let channel = &mut pwm.channel_b; channel.enable(); @@ -144,7 +144,7 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into(); + let led = pins.gpio25.into_typestate(); // Give away our pins by moving them into the `GLOBAL_PINS` variable. // We won't need to access them in the main thread again diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index bd42a3173..23c2750c1 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -233,7 +233,7 @@ impl Pin { } /// Convert the pin from one state to the other. - pub fn into(self) -> Pin + pub fn into_typestate(self) -> Pin where F2: func::Function, P2: PullType, @@ -318,7 +318,7 @@ impl Pin { where I: ValidFunction, { - self.into() + self.into_typestate() } /// Disable the pin and set it to pull down @@ -327,7 +327,7 @@ impl Pin { where I: ValidFunction, { - self.into() + self.into_typestate() } /// Disable the pin and set it to pull up @@ -336,7 +336,7 @@ impl Pin { where I: ValidFunction, { - self.into() + self.into_typestate() } /// Configure the pin to operate as a floating input @@ -1316,9 +1316,7 @@ impl InOutPin { // into Pin<_, FunctionSioOutput, _> let inner = inner.into_push_pull_output_in_state(PinState::Low); - Self { - inner: inner.into(), - } + Self { inner } } } @@ -1329,8 +1327,11 @@ where { /// Releases the pin reverting to its previous function. pub fn release(self) -> T { - let mut inner = self.inner.into(); + // restore the previous typestate first + let mut inner = self.inner.into_typestate(); + // disable override inner.set_output_enable_override(OutputEnableOverride::Normal); + // typelevel-return T::from(inner) } } From 6c101ed7e3a3185f4b284535b280b7eee1b72296 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Fri, 12 May 2023 05:27:26 +0100 Subject: [PATCH 21/26] Update rp2040-hal/src/gpio/mod.rs Co-authored-by: Jan Niehusmann --- rp2040-hal/src/gpio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 23c2750c1..28f2a1a91 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -484,7 +484,7 @@ impl Pin { /// Get wether the schmitt trigger (hysteresis) is enabled. #[inline] - pub fn get_schimtt_enabled(&self) -> bool { + pub fn get_schmitt_enabled(&self) -> bool { self.id.pad_ctrl().read().schmitt().bit_is_set() } From 4a7b9d6ddbbc2c12c57f1b7479fe3c165bdbca83 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Mon, 15 May 2023 03:12:57 +0100 Subject: [PATCH 22/26] Remove unused NoneT and fix AnyKind documentation's link --- rp2040-hal/src/typelevel.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index ebdb13136..eb509d408 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -1,7 +1,7 @@ //! Module supporting type-level programming //! //! This is heavily inspired by the work in [`atsamd-rs`](https://github.com/atsamd-rs/atsamd). Please refer to the -//! [documentation](https://github.com/atsamd-rs/atsamd/blob/d2ba3da08daa807ee5adc0e17546551bbfe7c1f2/hal/src/typelevel.rs) +//! [documentation](https://docs.rs/atsamd-hal/0.15.1/atsamd_hal/typelevel/index.html) //! over there for more details. mod private { @@ -21,11 +21,6 @@ impl Sealed for (A, B, C, D) {} impl Sealed for frunk::HNil {} impl Sealed for frunk::HCons {} -/// 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 @@ -55,7 +50,7 @@ impl Sealed for NoneT {} /// } /// ``` /// -/// [`AnyKind`]: #anykind-trait-pattern +/// [`AnyKind`]: https://docs.rs/atsamd-hal/0.15.1/atsamd_hal/typelevel/index.html#anykind-trait-pattern pub trait Is where Self: Sealed, From 62af23ed1ae47017fdea5bd8c9bd0f52e286a83d Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Tue, 16 May 2023 07:56:29 +0100 Subject: [PATCH 23/26] Remove remaining occurences of "into_mode" in examples --- rp2040-hal/examples/gpio_irq_example.rs | 2 +- rp2040-hal/examples/i2c.rs | 2 +- rp2040-hal/examples/pwm_irq_input.rs | 2 +- rp2040-hal/src/i2c.rs | 4 ++-- rp2040-hal/src/spi.rs | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rp2040-hal/examples/gpio_irq_example.rs b/rp2040-hal/examples/gpio_irq_example.rs index 6b4d305ce..d632d6aba 100644 --- a/rp2040-hal/examples/gpio_irq_example.rs +++ b/rp2040-hal/examples/gpio_irq_example.rs @@ -119,7 +119,7 @@ fn main() -> ! { ); // Configure GPIO 25 as an output to drive our LED. - // we can use into_mode() instead of into_pull_up_input() + // we can use into_typestate() instead of into_pull_up_input() // since the variable we're pushing it into has that type let led = pins.gpio25.into_typestate(); diff --git a/rp2040-hal/examples/i2c.rs b/rp2040-hal/examples/i2c.rs index 790ab3bf2..54b815f20 100644 --- a/rp2040-hal/examples/i2c.rs +++ b/rp2040-hal/examples/i2c.rs @@ -77,7 +77,7 @@ fn main() -> ! { // Configure two pins as being I²C, not GPIO let sda_pin = pins.gpio18.into_function::(); let scl_pin = pins.gpio19.into_function::(); - // let not_an_scl_pin = pins.gpio20.into_mode::(); + // let not_an_scl_pin = pins.gpio20.into_function::(); // Create the I²C drive, using the two pre-configured pins. This will fail // at compile time if the pins are in the wrong mode, or if this I²C diff --git a/rp2040-hal/examples/pwm_irq_input.rs b/rp2040-hal/examples/pwm_irq_input.rs index 4021088e5..1e7870569 100644 --- a/rp2040-hal/examples/pwm_irq_input.rs +++ b/rp2040-hal/examples/pwm_irq_input.rs @@ -142,7 +142,7 @@ fn main() -> ! { input_pin.set_interrupt_enabled(gpio::Interrupt::EdgeLow, true); // Configure GPIO 25 as an output to drive our LED. - // we can use into_mode() instead of into_pull_up_input() + // we can use into_typestate() instead of into_pull_up_input() // since the variable we're pushing it into has that type let led = pins.gpio25.into_typestate(); diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index e95f19497..5bfc52fc7 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -12,8 +12,8 @@ //! //! let mut i2c = I2C::i2c1( //! peripherals.I2C1, -//! pins.gpio18.into_mode(), // sda -//! pins.gpio19.into_mode(), // scl +//! pins.gpio18.into_function(), // sda +//! pins.gpio19.into_function(), // scl //! 400.kHz(), //! &mut peripherals.RESETS, //! 125_000_000.Hz(), diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index 4aeab688d..c9c816e31 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -13,10 +13,10 @@ //! let sio = Sio::new(peripherals.SIO); //! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS); //! -//! let sclk = pins.gpio2.into_mode::(); -//! let mosi = pins.gpio3.into_mode::(); +//! let sclk = pins.gpio2.into_function::(); +//! let mosi = pins.gpio3.into_function::(); //! -//! let spi = Spi::<_, _, 8>::new(peripherals.SPI0).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), MODE_0); +//! let spi = Spi::<_, _, _, 8>::new(peripherals.SPI0, (mosi, sclk)).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), MODE_0); //! ``` use crate::dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget}; From 94b1c03f3f9c3b92911a3d10a9828ebaf31d730d Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Thu, 18 May 2023 05:11:45 +0100 Subject: [PATCH 24/26] Enhance documentation --- rp2040-hal/src/gpio/func.rs | 23 +++++++++++++++++++++-- rp2040-hal/src/gpio/pin.rs | 4 ++-- rp2040-hal/src/gpio/pull.rs | 6 +++--- rp2040-hal/src/uart/pins.rs | 5 ++++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs index 41e424fed..6eb32c188 100644 --- a/rp2040-hal/src/gpio/func.rs +++ b/rp2040-hal/src/gpio/func.rs @@ -17,21 +17,40 @@ pub(crate) mod func_sealed { /// Type-level `enum` for pin function. pub trait Function: func_sealed::Function {} -/// Value-level `enum` for pin function. -#[allow(missing_docs)] +/// Describes the function currently assigned to a pin with a dynamic type. +/// +/// A 'pin' on the RP2040 can be connected to different parts of the chip +/// internally - for example, it could be configured as a GPIO pin and connected +/// to the SIO block, or it could be configured as a UART pin and connected to +/// the UART block. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DynFunction { + /// The 'XIP' (or Execute-in-place) function, which means talking to the QSPI Flash. Xip, + /// The 'SPI' (or serial-peripheral-interface) function. Spi, + /// The 'UART' (or serial-port) function. Uart, + /// The 'I2C' (or inter-integrated circuit) function. This is sometimes also called TWI (for + /// two-wire-interface). I2c, + /// The 'PWM' (or pulse-width-modulation) function. Pwm, + /// The 'SIO' (or single-cycle input-output) function. This is the function to use for + /// 'manually' controlling the GPIO. Sio(DynSioConfig), + /// The 'PIO' (or programmable-input-output) function for the PIO0 peripheral block. Pio0, + /// The 'PIO' (or programmable-input-output) function for the PIO1 peripheral block. Pio1, + /// The 'Clock' function. This can be used to input or output clock references to or from the + /// rp2040. Clock, + /// The 'USB' function. Only VBUS detect, VBUS enable and overcurrent detect are configurable. + /// Other USB io have dedicated pins. Usb, + /// The 'Null' function for unused pins. Null, } diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index debea1dc6..95b3e3c90 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -63,9 +63,9 @@ pub trait PinId: pin_sealed::PinIdOps { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct DynPinId { - #[allow(missing_docs)] + /// Pin bank. pub bank: DynBankId, - #[allow(missing_docs)] + /// Pin number. pub num: u8, } impl PinId for DynPinId { diff --git a/rp2040-hal/src/gpio/pull.rs b/rp2040-hal/src/gpio/pull.rs index 718a229d6..4b1061a2e 100644 --- a/rp2040-hal/src/gpio/pull.rs +++ b/rp2040-hal/src/gpio/pull.rs @@ -15,11 +15,11 @@ pub trait PullType: pull_sealed::PullType {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// Value-level `enum` for pull resistor types. pub enum DynPullType { - #[allow(missing_docs)] + /// No pull enabled. None, - #[allow(missing_docs)] + /// Enable pull up. Up, - #[allow(missing_docs)] + /// Enable pull down. Down, /// This enables bus-keep mode. /// diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index 7e7c022b4..3c742b14a 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -165,11 +165,14 @@ where /// /// assert_is_valid_uart0(pins); /// ``` -#[allow(missing_docs)] pub struct Pins { + #[allow(missing_docs)] pub tx: Tx, + #[allow(missing_docs)] pub rx: Rx, + #[allow(missing_docs)] pub cts: Cts, + #[allow(missing_docs)] pub rts: Rts, } From b83633f24dd13891a937d5c9522024dfad6aa0a2 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sat, 20 May 2023 21:59:53 +0100 Subject: [PATCH 25/26] doc: fix placeholder names --- rp2040-hal/src/gpio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 28f2a1a91..82fbaacaf 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -155,7 +155,7 @@ pub enum OutputOverride { AlwaysHigh = 3, } -/// Represents a pin, with a given ID (e.g. Xx), a given function (e.g. Uart) and a given pull type +/// Represents a pin, with a given ID (e.g. Gpio3), a given function (e.g. FunctionUart) and a given pull type /// (e.g. pull-down). pub struct Pin { id: I, From 5971c03e462d49e7c0e79f9630e45765f98c5afa Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Sun, 28 May 2023 19:57:11 +0100 Subject: [PATCH 26/26] Rename `into_typestate` to the more explicit `reconfigure`. --- rp2040-hal/examples/gpio_irq_example.rs | 6 +++--- rp2040-hal/examples/pwm_irq_input.rs | 6 +++--- rp2040-hal/src/gpio/mod.rs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rp2040-hal/examples/gpio_irq_example.rs b/rp2040-hal/examples/gpio_irq_example.rs index d632d6aba..5421c32ba 100644 --- a/rp2040-hal/examples/gpio_irq_example.rs +++ b/rp2040-hal/examples/gpio_irq_example.rs @@ -119,12 +119,12 @@ fn main() -> ! { ); // Configure GPIO 25 as an output to drive our LED. - // we can use into_typestate() instead of into_pull_up_input() + // we can use reconfigure() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_typestate(); + let led = pins.gpio25.reconfigure(); // Set up the GPIO pin that will be our input - let in_pin = pins.gpio26.into_typestate(); + let in_pin = pins.gpio26.reconfigure(); // Trigger on the 'falling edge' of the input pin. // This will happen as the button is being pressed diff --git a/rp2040-hal/examples/pwm_irq_input.rs b/rp2040-hal/examples/pwm_irq_input.rs index 1e7870569..8cb088202 100644 --- a/rp2040-hal/examples/pwm_irq_input.rs +++ b/rp2040-hal/examples/pwm_irq_input.rs @@ -134,7 +134,7 @@ fn main() -> ! { pwm.enable(); // Connect to GPI O1 as the input to channel B on PWM0 - let input_pin = pins.gpio1.into_typestate(); + let input_pin = pins.gpio1.reconfigure(); let channel = &mut pwm.channel_b; channel.enable(); @@ -142,9 +142,9 @@ fn main() -> ! { input_pin.set_interrupt_enabled(gpio::Interrupt::EdgeLow, true); // Configure GPIO 25 as an output to drive our LED. - // we can use into_typestate() instead of into_pull_up_input() + // we can use reconfigure() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_typestate(); + let led = pins.gpio25.reconfigure(); // Give away our pins by moving them into the `GLOBAL_PINS` variable. // We won't need to access them in the main thread again diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 82fbaacaf..dae579ce7 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -233,7 +233,7 @@ impl Pin { } /// Convert the pin from one state to the other. - pub fn into_typestate(self) -> Pin + pub fn reconfigure(self) -> Pin where F2: func::Function, P2: PullType, @@ -318,7 +318,7 @@ impl Pin { where I: ValidFunction, { - self.into_typestate() + self.reconfigure() } /// Disable the pin and set it to pull down @@ -327,7 +327,7 @@ impl Pin { where I: ValidFunction, { - self.into_typestate() + self.reconfigure() } /// Disable the pin and set it to pull up @@ -336,7 +336,7 @@ impl Pin { where I: ValidFunction, { - self.into_typestate() + self.reconfigure() } /// Configure the pin to operate as a floating input @@ -1328,7 +1328,7 @@ where /// Releases the pin reverting to its previous function. pub fn release(self) -> T { // restore the previous typestate first - let mut inner = self.inner.into_typestate(); + let mut inner = self.inner.reconfigure(); // disable override inner.set_output_enable_override(OutputEnableOverride::Normal); // typelevel-return