diff --git a/src/gpio.rs b/src/gpio.rs index edc18f13..f1f06053 100755 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -41,6 +41,63 @@ pub struct Output { /// Push pull output (type state) pub struct PushPull; +mod sealed { + pub trait Sealed {} +} + +/// Marker trait for valid pin modes (type state). +/// +/// It can not be implemented by outside types. +pub trait PinMode: sealed::Sealed { + // These constants are used to implement the pin configuration code. + // They are not part of public API. + + #[doc(hidden)] + const PUPDR: u8; + #[doc(hidden)] + const MODER: u8; + #[doc(hidden)] + const OTYPER: Option = None; +} + +impl sealed::Sealed for Input {} +impl PinMode for Input { + const PUPDR: u8 = 0b00; + const MODER: u8 = 0b00; +} + +impl sealed::Sealed for Input {} +impl PinMode for Input { + const PUPDR: u8 = 0b10; + const MODER: u8 = 0b00; +} + +impl sealed::Sealed for Input {} +impl PinMode for Input { + const PUPDR: u8 = 0b01; + const MODER: u8 = 0b00; +} + +impl sealed::Sealed for Analog {} +impl PinMode for Analog { + const PUPDR: u8 = 0b00; + const MODER: u8 = 0b11; +} + +impl sealed::Sealed for Output {} +impl PinMode for Output { + const PUPDR: u8 = 0b00; + const MODER: u8 = 0b01; + const OTYPER: Option = Some(0b1); +} + +impl sealed::Sealed for Output {} +impl PinMode for Output { + const PUPDR: u8 = 0b00; + const MODER: u8 = 0b01; + const OTYPER: Option = Some(0b0); +} + /// GPIO Pin speed selection #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Speed { @@ -85,7 +142,8 @@ macro_rules! gpio { use crate::rcc::Rcc; use super::{ Floating, GpioExt, Input, OpenDrain, Output, Speed, - PullDown, PullUp, PushPull, AltMode, Analog, Port + PullDown, PullUp, PushPull, AltMode, Analog, Port, + PinMode, }; /// GPIO parts @@ -218,122 +276,188 @@ macro_rules! gpio { } } - impl $PXi { - /// Configures the pin to operate as a floating input pin - pub fn into_floating_input( - self, - ) -> $PXi> { + impl $PXi { + /// Puts `self` into mode `M`. + /// + /// This violates the type state constraints from `MODE`, so callers must + /// ensure they use this properly. + fn mode(&mut self) { let offset = 2 * $i; unsafe { &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) + w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::PUPDR) << offset)) }); + + if let Some(otyper) = M::OTYPER { + &(*$GPIOX::ptr()).otyper.modify(|r, w| { + w.bits(r.bits() & !(0b1 << $i) | (u32::from(otyper) << $i)) + }); + } + &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }) + w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::MODER) << offset)) + }); + } + } + + fn with_mode( + &mut self, + f: F + ) -> R + where + M: PinMode, + F: FnOnce(&mut $PXi) -> R, + { + struct ResetMode<'a, ORIG: PinMode> { + pin: &'a mut $PXi, + } + + impl<'a, ORIG: PinMode> Drop for ResetMode<'a, ORIG> { + fn drop(&mut self) { + self.pin.mode::(); + } + } + + self.mode::(); + + // This will reset the pin back to the original mode when dropped. + // (so either when `with_mode` returns or when `f` unwinds) + let _resetti = ResetMode { pin: self }; + + let mut witness = $PXi { + _mode: PhantomData }; + + f(&mut witness) + } + + /// Configures the pin to operate as a floating input pin. + pub fn into_floating_input( + mut self, + ) -> $PXi> { + self.mode::>(); $PXi { _mode: PhantomData } } - /// Configures the pin to operate as a pulled down input pin + /// Temporarily configures this pin as a floating input. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_floating_input( + &mut self, + f: impl FnOnce(&mut $PXi>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Configures the pin to operate as a pulled-down input pin. pub fn into_pull_down_input( - self, - ) -> $PXi> { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }) - }; + mut self, + ) -> $PXi> { + self.mode::>(); $PXi { _mode: PhantomData } } - /// Configures the pin to operate as a pulled up input pin + /// Temporarily configures this pin as a pulled-down input. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_pull_down_input( + &mut self, + f: impl FnOnce(&mut $PXi>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Configures the pin to operate as a pulled-up input pin. pub fn into_pull_up_input( - self, + mut self, ) -> $PXi> { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }) - }; + self.mode::>(); $PXi { _mode: PhantomData } } - /// Configures the pin to operate as an analog pin + /// Temporarily configures this pin as a pulled-up input. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_pull_up_input( + &mut self, + f: impl FnOnce(&mut $PXi>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Configures the pin to operate as an analog pin. pub fn into_analog( - self, + mut self, ) -> $PXi { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset)) - }); - } + self.mode::(); $PXi { _mode: PhantomData } } - /// Configures the pin to operate as an open drain output pin + /// Temporarily configures this pin as an analog pin. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_analog( + &mut self, + f: impl FnOnce(&mut $PXi) -> R, + ) -> R { + self.with_mode(f) + } + + /// Configures the pin to operate as an open drain output pin. pub fn into_open_drain_output( - self, + mut self, ) -> $PXi> { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }); - &(*$GPIOX::ptr()).otyper.modify(|r, w| { - w.bits(r.bits() | (0b1 << $i)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }) - }; + self.mode::>(); $PXi { _mode: PhantomData } } - /// Configures the pin to operate as an push pull output pin + /// Temporarily configures this pin as an open drain output. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_open_drain_output( + &mut self, + f: impl FnOnce(&mut $PXi>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Configures the pin to operate as an push-pull output pin. pub fn into_push_pull_output( - self, + mut self, ) -> $PXi> { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) - }); - &(*$GPIOX::ptr()).otyper.modify(|r, w| { - w.bits(r.bits() & !(0b1 << $i)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }) - }; + self.mode::>(); $PXi { _mode: PhantomData } } - /// Set pin speed + /// Temporarily configures this pin as a push-pull output. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_push_pull_output( + &mut self, + f: impl FnOnce(&mut $PXi>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Set pin speed. pub fn set_speed(self, speed: Speed) -> Self { let offset = 2 * $i; unsafe { diff --git a/src/pwm.rs b/src/pwm.rs index d88cd18b..4fbcdcdb 100755 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -4,7 +4,7 @@ use core::ops::Deref; use cortex_m::interrupt; use crate::gpio::gpioa::{PA0, PA1, PA2, PA3}; -use crate::gpio::AltMode; +use crate::gpio::{AltMode, PinMode}; use crate::hal; use crate::pac::{tim2, TIM2, TIM3}; use crate::rcc::Rcc; @@ -251,7 +251,7 @@ macro_rules! impl_pin { ) => { $( $( - impl Pin<$instance, $channel> for $name { + impl Pin<$instance, $channel> for $name { fn setup(&self) { self.set_alt_mode(AltMode::$alternate_function); } diff --git a/src/serial.rs b/src/serial.rs index ba769441..e02b931a 100755 --- a/src/serial.rs +++ b/src/serial.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::ptr; use crate::gpio::gpioa::*; -use crate::gpio::AltMode; +use crate::gpio::{PinMode, AltMode}; use crate::hal; use crate::hal::prelude::*; pub use crate::pac::USART2; @@ -159,7 +159,7 @@ pub trait Pins { macro_rules! impl_pins { ($($instance:ty, $tx:ident, $rx:ident, $alt:ident;)*) => { $( - impl Pins<$instance> for ($tx, $rx) { + impl Pins<$instance> for ($tx, $rx) { fn setup(&self) { self.0.set_alt_mode(AltMode::$alt); self.1.set_alt_mode(AltMode::$alt);