From 8eff974fa2a6b7fc34113ea6e1c9975c22c4596c Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 22 Jun 2025 10:59:58 +0300 Subject: [PATCH 1/4] improve Ptr trait --- src/i2c/dma.rs | 4 ++-- src/lib.rs | 12 ++++++++---- src/serial.rs | 2 +- src/spi.rs | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/i2c/dma.rs b/src/i2c/dma.rs index c16d6e10..73b4c54b 100644 --- a/src/i2c/dma.rs +++ b/src/i2c/dma.rs @@ -934,7 +934,7 @@ pub struct Rx { unsafe impl PeriAddress for Rx { #[inline(always)] fn address(&self) -> u32 { - unsafe { (*I2C::ptr()).dr().as_ptr() as u32 } + unsafe { (*I2C::PTR).dr().as_ptr() as u32 } } type MemSize = u8; @@ -943,7 +943,7 @@ unsafe impl PeriAddress for Rx { unsafe impl PeriAddress for Tx { #[inline(always)] fn address(&self) -> u32 { - unsafe { (*I2C::ptr()).dr().as_ptr() as u32 } + unsafe { (*I2C::PTR).dr().as_ptr() as u32 } } type MemSize = u8; diff --git a/src/lib.rs b/src/lib.rs index 95c9d484..ecc0e994 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -210,15 +210,18 @@ impl Sealed for Periph {} pub trait Ptr: Sealed { /// RegisterBlock structure type RB; + /// Pointer to the register block + const PTR: *const Self::RB; /// Return the pointer to the register block - fn ptr() -> *const Self::RB; + #[inline(always)] + fn ptr() -> *const Self::RB { + Self::PTR + } } impl Ptr for Periph { type RB = RB; - fn ptr() -> *const Self::RB { - Self::ptr() - } + const PTR: *const Self::RB = Self::PTR; } pub trait Steal: Sealed { @@ -239,6 +242,7 @@ pub trait Steal: Sealed { } impl Steal for Periph { + #[inline(always)] unsafe fn steal() -> Self { Self::steal() } diff --git a/src/serial.rs b/src/serial.rs index 5bec3451..4d8f4517 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -139,7 +139,7 @@ pub trait Instance: #[doc(hidden)] #[inline(always)] fn peri_address() -> u32 { - unsafe { &*Self::ptr() }.peri_address() + unsafe { &*Self::PTR }.peri_address() } } diff --git a/src/spi.rs b/src/spi.rs index 2a187137..825be012 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -957,7 +957,7 @@ impl DmaBuilder { unsafe impl PeriAddress for Rx { #[inline(always)] fn address(&self) -> u32 { - unsafe { (*SPI::ptr()).dr().as_ptr() as u32 } + unsafe { (*SPI::PTR).dr().as_ptr() as u32 } } type MemSize = u8; @@ -971,7 +971,7 @@ unsafe impl DMASet PeriAddress for Tx { #[inline(always)] fn address(&self) -> u32 { - unsafe { (*SPI::ptr()).dr().as_ptr() as u32 } + unsafe { (*SPI::PTR).dr().as_ptr() as u32 } } type MemSize = u8; From 5f14ffb738037054b6cd93abf832722b22019203 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Thu, 10 Jul 2025 11:45:29 +0300 Subject: [PATCH 2/4] Rename `ErasedPin` to `AnyPin` --- CHANGELOG.md | 2 ++ src/gpio.rs | 6 +++--- src/gpio/convert.rs | 8 ++++---- src/gpio/erased.rs | 30 ++++++++++++------------------ src/gpio/hal_02.rs | 10 +++++----- src/gpio/hal_1.rs | 12 +++++------- src/gpio/partially_erased.rs | 28 ++++++++++++++++++---------- 7 files changed, 49 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a24312e2..b5896caa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Implement `embedded_hal::i2c::I2c` for `I2cMasterDma` [#838] - Back to `stm32f4` - Implement `Ptr`, `Sealed`, `Steal` for generic `Periph` [#834] + - Rename `ErasedPin` to `AnyPin` [#841] - Use `&mut RCC` for `PER::enable/reset` - Unmacro `Adc` [#832] - Use `write` instead of `modify` to clear flags [#829] @@ -31,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#834]: https://github.com/stm32-rs/stm32f4xx-hal/pull/834 [#838]: https://github.com/stm32-rs/stm32f4xx-hal/pull/838 [#839]: https://github.com/stm32-rs/stm32f4xx-hal/pull/839 +[#841]: https://github.com/stm32-rs/stm32f4xx-hal/pull/841 ## [v0.22.1] - 2024-11-03 diff --git a/src/gpio.rs b/src/gpio.rs index 83d58124..1033e82d 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -401,8 +401,8 @@ impl Pin { /// /// This is useful when you want to collect the pins into an array where you /// need all the elements to have the same type - pub fn erase(self) -> ErasedPin { - ErasedPin::new(P as u8 - b'A', N) + pub fn erase(self) -> AnyPin { + AnyPin::new(P as u8 - b'A', N) } } @@ -415,7 +415,7 @@ impl From> for PartiallyErased } } -impl From> for ErasedPin { +impl From> for AnyPin { /// Pin-to-erased pin conversion using the [`From`] trait. /// /// Note that [`From`] is the reciprocal of [`Into`]. diff --git a/src/gpio/convert.rs b/src/gpio/convert.rs index 2ac350c0..97fb04cf 100644 --- a/src/gpio/convert.rs +++ b/src/gpio/convert.rs @@ -166,8 +166,8 @@ macro_rules! change_mode { } use change_mode; -use super::ErasedPin; -impl ErasedPin { +use super::AnyPin; +impl AnyPin { #[inline(always)] pub(super) fn mode(&mut self) { let n = self.pin_id(); @@ -176,9 +176,9 @@ impl ErasedPin { #[inline(always)] /// Converts pin into specified mode - pub fn into_mode(mut self) -> ErasedPin { + pub fn into_mode(mut self) -> AnyPin { self.mode::(); - ErasedPin::from_pin_port(self.into_pin_port()) + AnyPin::from_pin_port(self.into_pin_port()) } } diff --git a/src/gpio/erased.rs b/src/gpio/erased.rs index 163e227f..cb28ad16 100644 --- a/src/gpio/erased.rs +++ b/src/gpio/erased.rs @@ -1,17 +1,17 @@ use super::*; -pub use ErasedPin as AnyPin; +pub use AnyPin as ErasedPin; /// Fully erased pin /// /// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). -pub struct ErasedPin { +pub struct AnyPin { // Bits 0-3: Pin, Bits 4-7: Port pin_port: u8, _mode: PhantomData, } -impl fmt::Debug for ErasedPin { +impl fmt::Debug for AnyPin { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_fmt(format_args!( "P({}{})<{}>", @@ -23,7 +23,7 @@ impl fmt::Debug for ErasedPin { } #[cfg(feature = "defmt")] -impl defmt::Format for ErasedPin { +impl defmt::Format for AnyPin { fn format(&self, f: defmt::Formatter) { defmt::write!( f, @@ -35,7 +35,7 @@ impl defmt::Format for ErasedPin { } } -impl PinExt for ErasedPin { +impl PinExt for AnyPin { type Mode = MODE; #[inline(always)] @@ -48,7 +48,7 @@ impl PinExt for ErasedPin { } } -impl ErasedPin { +impl AnyPin { pub(crate) fn from_pin_port(pin_port: u8) -> Self { Self { pin_port, @@ -93,23 +93,17 @@ impl ErasedPin { } } -impl ErasedPin> { +impl AnyPin> { /// Drives the pin high #[inline(always)] pub fn set_high(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { self.block().bsrr().write(|w| w.bits(1 << self.pin_id())) }; + self.block().bsrr().write(|w| w.bs(self.pin_id()).set_bit()); } /// Drives the pin low #[inline(always)] pub fn set_low(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { - self.block() - .bsrr() - .write(|w| w.bits(1 << (self.pin_id() + 16))) - }; + self.block().bsrr().write(|w| w.br(self.pin_id()).set_bit()); } /// Is the pin in drive high or low mode? @@ -140,7 +134,7 @@ impl ErasedPin> { /// Is the pin in drive low mode? #[inline(always)] pub fn is_set_low(&self) -> bool { - self.block().odr().read().bits() & (1 << self.pin_id()) == 0 + self.block().odr().read().odr(self.pin_id()).bit_is_clear() } /// Toggle pin output @@ -154,7 +148,7 @@ impl ErasedPin> { } } -impl ErasedPin +impl AnyPin where MODE: marker::Readable, { @@ -167,6 +161,6 @@ where /// Is the input pin low? #[inline(always)] pub fn is_low(&self) -> bool { - self.block().idr().read().bits() & (1 << self.pin_id()) == 0 + self.block().idr().read().idr(self.pin_id()).bit_is_clear() } } diff --git a/src/gpio/hal_02.rs b/src/gpio/hal_02.rs index d3254ab3..bf33c356 100644 --- a/src/gpio/hal_02.rs +++ b/src/gpio/hal_02.rs @@ -1,7 +1,7 @@ use core::convert::Infallible; use super::{ - dynamic::PinModeError, marker, DynamicPin, ErasedPin, Input, OpenDrain, Output, + dynamic::PinModeError, marker, AnyPin, DynamicPin, Input, OpenDrain, Output, PartiallyErasedPin, Pin, PinMode, PinState, }; @@ -107,7 +107,7 @@ where // Implementations for `ErasedPin` -impl OutputPin for ErasedPin> { +impl OutputPin for AnyPin> { type Error = core::convert::Infallible; #[inline(always)] @@ -123,7 +123,7 @@ impl OutputPin for ErasedPin> { } } -impl StatefulOutputPin for ErasedPin> { +impl StatefulOutputPin for AnyPin> { #[inline(always)] fn is_set_high(&self) -> Result { Ok(self.is_set_high()) @@ -135,7 +135,7 @@ impl StatefulOutputPin for ErasedPin> { } } -impl ToggleableOutputPin for ErasedPin> { +impl ToggleableOutputPin for AnyPin> { type Error = Infallible; #[inline(always)] @@ -145,7 +145,7 @@ impl ToggleableOutputPin for ErasedPin> { } } -impl InputPin for ErasedPin +impl InputPin for AnyPin where MODE: marker::Readable, { diff --git a/src/gpio/hal_1.rs b/src/gpio/hal_1.rs index 11db87cb..e33e2627 100644 --- a/src/gpio/hal_1.rs +++ b/src/gpio/hal_1.rs @@ -1,8 +1,6 @@ use core::convert::Infallible; -use super::{ - dynamic::PinModeError, marker, DynamicPin, ErasedPin, Output, PartiallyErasedPin, Pin, -}; +use super::{dynamic::PinModeError, marker, AnyPin, DynamicPin, Output, PartiallyErasedPin, Pin}; use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; @@ -53,11 +51,11 @@ where } // Implementations for `ErasedPin` -impl ErrorType for ErasedPin { +impl ErrorType for AnyPin { type Error = core::convert::Infallible; } -impl OutputPin for ErasedPin> { +impl OutputPin for AnyPin> { #[inline(always)] fn set_high(&mut self) -> Result<(), Self::Error> { self.set_high(); @@ -71,7 +69,7 @@ impl OutputPin for ErasedPin> { } } -impl StatefulOutputPin for ErasedPin> { +impl StatefulOutputPin for AnyPin> { #[inline(always)] fn is_set_high(&mut self) -> Result { Ok(Self::is_set_high(self)) @@ -83,7 +81,7 @@ impl StatefulOutputPin for ErasedPin> { } } -impl InputPin for ErasedPin +impl InputPin for AnyPin where MODE: marker::Readable, { diff --git a/src/gpio/partially_erased.rs b/src/gpio/partially_erased.rs index 812ea2c0..7c5863bd 100644 --- a/src/gpio/partially_erased.rs +++ b/src/gpio/partially_erased.rs @@ -68,18 +68,18 @@ impl PartiallyErasedPin> { #[inline(always)] pub fn set_high(&mut self) { // NOTE(unsafe) atomic write to a stateless register - unsafe { - (*gpiox::

()).bsrr().write(|w| w.bits(1 << self.i)); - } + unsafe { &*gpiox::

() } + .bsrr() + .write(|w| w.bs(self.i).set_bit()); } /// Drives the pin low #[inline(always)] pub fn set_low(&mut self) { // NOTE(unsafe) atomic write to a stateless register - unsafe { - (*gpiox::

()).bsrr().write(|w| w.bits(1 << (self.i + 16))); - } + unsafe { &*gpiox::

() } + .bsrr() + .write(|w| w.br(self.i).set_bit()); } /// Is the pin in drive high or low mode? @@ -111,7 +111,11 @@ impl PartiallyErasedPin> { #[inline(always)] pub fn is_set_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects - unsafe { (*gpiox::

()).odr().read().bits() & (1 << self.i) == 0 } + unsafe { &*gpiox::

() } + .odr() + .read() + .odr(self.i) + .bit_is_clear() } /// Toggle pin output @@ -139,15 +143,19 @@ where #[inline(always)] pub fn is_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects - unsafe { (*gpiox::

()).idr().read().bits() & (1 << self.i) == 0 } + unsafe { &*gpiox::

() } + .idr() + .read() + .idr(self.i) + .bit_is_clear() } } -impl From> for ErasedPin { +impl From> for AnyPin { /// Partially erased pin-to-erased pin conversion using the [`From`] trait. /// /// Note that [`From`] is the reciprocal of [`Into`]. fn from(p: PartiallyErasedPin) -> Self { - ErasedPin::new(P as u8 - b'A', p.i) + AnyPin::new(P as u8 - b'A', p.i) } } From af4e59658ab987414f9eab8eea79b3ecec828223 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 12 Aug 2025 07:44:39 +0300 Subject: [PATCH 3/4] CapturePolarity --- src/timer.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/timer.rs b/src/timer.rs index f24e9d9d..334b1cbf 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -685,16 +685,22 @@ macro_rules! hal { if c < Self::CH_NUMBER { match p { CapturePolarity::ActiveLow => { - unsafe { bb::write(tim.ccer(), c*4 + 3, false); } - unsafe { bb::write(tim.ccer(), c*4 + 1, true); } + tim.ccer().modify(|_, w| { + w.ccnp(c).clear_bit(); + w.ccp(c).set_bit() + }); } CapturePolarity::ActiveHigh => { - unsafe { bb::write(tim.ccer(), c*4 + 3, false); } - unsafe { bb::write(tim.ccer(), c*4 + 1, false); } + tim.ccer().modify(|_, w| { + w.ccnp(c).clear_bit(); + w.ccp(c).clear_bit() + }); } CapturePolarity::ActiveBoth => { - unsafe { bb::write(tim.ccer(), c*4 + 3, true); } - unsafe { bb::write(tim.ccer(), c*4 + 1, true); } + tim.ccer().modify(|_, w| { + w.ccnp(c).set_bit(); + w.ccp(c).set_bit() + }); } } From c4171fb944af103d2f2f214716612ac97b14e0a4 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 12 Aug 2025 17:44:45 +0300 Subject: [PATCH 4/4] generic DynPin --- CHANGELOG.md | 1 + src/gpio.rs | 301 ++++++++++++++++++++--------------- src/gpio/convert.rs | 10 +- src/gpio/dynamic.rs | 252 ++++++++++++++++++----------- src/gpio/erased.rs | 86 +++------- src/gpio/hal_02.rs | 6 +- src/gpio/hal_1.rs | 4 +- src/gpio/partially_erased.rs | 97 +++-------- 8 files changed, 382 insertions(+), 375 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5896caa..4d4487ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - FMPI2c APB timings [#770] - Fefactor FMPI2c `embedded-hal` implementations [#784] - remove `NoPin`, use `Option` instead [#813] + - Make `DynPin` be generic over `Pin` and `AnyPin` [#770]: https://github.com/stm32-rs/stm32f4xx-hal/pull/770 [#784]: https://github.com/stm32-rs/stm32f4xx-hal/pull/784 diff --git a/src/gpio.rs b/src/gpio.rs index 1033e82d..d9b7f070 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -67,7 +67,7 @@ pub use erased::{AnyPin, ErasedPin}; mod exti; pub use exti::ExtiPin; mod dynamic; -pub use dynamic::{Dynamic, DynamicPin}; +pub use dynamic::*; mod hal_02; mod hal_1; pub mod outport; @@ -324,22 +324,29 @@ where } } +macro_rules! speed { + () => { + /// Set pin speed + pub fn set_speed(&mut self, speed: Speed) { + unsafe { &*self.block() } + .ospeedr() + .modify(|_, w| w.ospeedr(self.pin_id()).variant(speed.into())); + } + + /// Set pin speed + pub fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self + } + }; +} +use speed; + impl Pin where MODE: marker::OutputSpeed, { - /// Set pin speed - pub fn set_speed(&mut self, speed: Speed) { - unsafe { &(*gpiox::

()) } - .ospeedr() - .modify(|_, w| w.ospeedr(N).variant(speed.into())); - } - - /// Set pin speed - pub fn speed(mut self, speed: Speed) -> Self { - self.set_speed(speed); - self - } + speed!(); } impl PinPull for Pin @@ -352,40 +359,47 @@ where } } -impl Pin -where - MODE: marker::Active, -{ - /// Set the internal pull-up and pull-down resistor - pub fn set_internal_resistor(&mut self, resistor: Pull) { - unsafe { &(*gpiox::

()) } - .pupdr() - .modify(|_, w| w.pupdr(N).variant(resistor.into())); - } +macro_rules! internal_resistor { + () => { + /// Set the internal pull-up and pull-down resistor + pub fn set_internal_resistor(&mut self, resistor: Pull) { + unsafe { &*self.block() } + .pupdr() + .modify(|_, w| w.pupdr(self.pin_id()).variant(resistor.into())); + } - /// Set the internal pull-up and pull-down resistor - pub fn internal_resistor(mut self, resistor: Pull) -> Self { - self.set_internal_resistor(resistor); - self - } + /// Set the internal pull-up and pull-down resistor + pub fn internal_resistor(mut self, resistor: Pull) -> Self { + self.set_internal_resistor(resistor); + self + } - /// Enables / disables the internal pull up - pub fn internal_pull_up(self, on: bool) -> Self { - if on { - self.internal_resistor(Pull::Up) - } else { - self.internal_resistor(Pull::None) + /// Enables / disables the internal pull up + pub fn internal_pull_up(self, on: bool) -> Self { + if on { + self.internal_resistor(Pull::Up) + } else { + self.internal_resistor(Pull::None) + } } - } - /// Enables / disables the internal pull down - pub fn internal_pull_down(self, on: bool) -> Self { - if on { - self.internal_resistor(Pull::Down) - } else { - self.internal_resistor(Pull::None) + /// Enables / disables the internal pull down + pub fn internal_pull_down(self, on: bool) -> Self { + if on { + self.internal_resistor(Pull::Down) + } else { + self.internal_resistor(Pull::None) + } } - } + }; +} +use internal_resistor; + +impl Pin +where + MODE: marker::Active, +{ + internal_resistor!(); } impl Pin { @@ -424,97 +438,132 @@ impl From> for AnyPin { } } -impl Pin { - /// Set the output of the pin regardless of its mode. - /// Primarily used to set the output value of the pin - /// before changing its mode to an output to avoid - /// a short spike of an incorrect value - #[inline(always)] - fn _set_state(&mut self, state: PinState) { - match state { - PinState::High => self._set_high(), - PinState::Low => self._set_low(), +macro_rules! state_inner { + () => { + /// Set the output of the pin regardless of its mode. + /// Primarily used to set the output value of the pin + /// before changing its mode to an output to avoid + /// a short spike of an incorrect value + #[inline(always)] + pub(crate) fn _set_state(&mut self, state: PinState) { + match state { + PinState::High => self._set_high(), + PinState::Low => self._set_low(), + } } - } - #[inline(always)] - fn _set_high(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - let gpio = unsafe { &(*gpiox::

()) }; - gpio.bsrr().write(|w| w.bs(N).set_bit()); - } - #[inline(always)] - fn _set_low(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - let gpio = unsafe { &(*gpiox::

()) }; - gpio.bsrr().write(|w| w.br(N).set_bit()); - } - #[inline(always)] - fn _is_set_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - let gpio = unsafe { &(*gpiox::

()) }; - gpio.odr().read().odr(N).bit_is_clear() - } - #[inline(always)] - fn _is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - let gpio = unsafe { &(*gpiox::

()) }; - gpio.idr().read().idr(N).bit_is_clear() - } + #[inline(always)] + pub(crate) fn _set_high(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + let gpio = unsafe { &*self.block() }; + gpio.bsrr().write(|w| w.bs(self.pin_id()).set_bit()); + } + #[inline(always)] + pub(crate) fn _set_low(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + let gpio = unsafe { &*self.block() }; + gpio.bsrr().write(|w| w.br(self.pin_id()).set_bit()); + } + #[inline(always)] + pub(crate) fn _is_set_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + let gpio = unsafe { &*self.block() }; + gpio.odr().read().odr(self.pin_id()).bit_is_clear() + } + #[inline(always)] + pub(crate) fn _is_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + let gpio = unsafe { &*self.block() }; + gpio.idr().read().idr(self.pin_id()).bit_is_clear() + } + }; } +use state_inner; + +macro_rules! state_output { + () => { + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) { + self._set_high() + } -impl Pin> { - /// Drives the pin high - #[inline(always)] - pub fn set_high(&mut self) { - self._set_high() - } + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) { + self._set_low() + } - /// Drives the pin low - #[inline(always)] - pub fn set_low(&mut self) { - self._set_low() - } + /// Is the pin in drive high or low mode? + #[inline(always)] + pub fn get_state(&self) -> PinState { + if self.is_set_low() { + PinState::Low + } else { + PinState::High + } + } - /// Is the pin in drive high or low mode? - #[inline(always)] - pub fn get_state(&self) -> PinState { - if self.is_set_low() { - PinState::Low - } else { - PinState::High + /// Drives the pin high or low depending on the provided value + #[inline(always)] + pub fn set_state(&mut self, state: PinState) { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } } - } - /// Drives the pin high or low depending on the provided value - #[inline(always)] - pub fn set_state(&mut self, state: PinState) { - match state { - PinState::Low => self.set_low(), - PinState::High => self.set_high(), + /// Is the pin in drive high mode? + #[inline(always)] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() } - } - /// Is the pin in drive high mode? - #[inline(always)] - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } + /// Is the pin in drive low mode? + #[inline(always)] + pub fn is_set_low(&self) -> bool { + self._is_set_low() + } - /// Is the pin in drive low mode? - #[inline(always)] - pub fn is_set_low(&self) -> bool { - self._is_set_low() - } + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } + }; +} +use state_output; + +macro_rules! state_input { + () => { + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> bool { + !self.is_low() + } - /// Toggle pin output - #[inline(always)] - pub fn toggle(&mut self) { - if self.is_set_low() { - self.set_high() - } else { - self.set_low() + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&self) -> bool { + self._is_low() } + }; +} +use state_input; + +impl Pin { + #[inline(always)] + pub(crate) fn block(&self) -> *const crate::pac::gpioa::RegisterBlock { + gpiox::

() } + state_inner!(); +} + +impl Pin> { + state_output!(); } pub trait ReadPin { @@ -539,17 +588,7 @@ impl Pin where MODE: marker::Readable, { - /// Is the input pin high? - #[inline(always)] - pub fn is_high(&self) -> bool { - !self.is_low() - } - - /// Is the input pin low? - #[inline(always)] - pub fn is_low(&self) -> bool { - self._is_low() - } + state_input!(); } macro_rules! gpio { diff --git a/src/gpio/convert.rs b/src/gpio/convert.rs index 97fb04cf..57a7c6ca 100644 --- a/src/gpio/convert.rs +++ b/src/gpio/convert.rs @@ -112,14 +112,6 @@ impl Pin { self.into_mode() } - /// Configures the pin as a pin that can change between input - /// and output without changing the type. It starts out - /// as a floating input - pub fn into_dynamic(self) -> DynamicPin { - self.into_floating_input(); - DynamicPin::new(Dynamic::InputFloating) - } - /// Puts `self` into mode `M`. /// /// This violates the type state constraints from `MODE`, so callers must @@ -171,7 +163,7 @@ impl AnyPin { #[inline(always)] pub(super) fn mode(&mut self) { let n = self.pin_id(); - change_mode!(self.block(), n); + change_mode!((*self.block()), n); } #[inline(always)] diff --git a/src/gpio/dynamic.rs b/src/gpio/dynamic.rs index fbd33177..b731dd2d 100644 --- a/src/gpio/dynamic.rs +++ b/src/gpio/dynamic.rs @@ -1,29 +1,57 @@ +use core::ops::{Deref, DerefMut}; + use super::*; use embedded_hal::digital::ErrorKind; +pub type DynamicPin = DynPin>; +pub type DynamicAnyPin = DynPin>; + /// Pin type with dynamic mode -/// -/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. -/// - `N` is pin number: from `0` to `15`. -pub struct DynamicPin { +pub struct DynPin { + pin: PIN, /// Current pin mode pub(crate) mode: Dynamic, } /// Tracks the current pin state for dynamic pins pub enum Dynamic { - /// Floating input mode - InputFloating, - /// Pull-up input mode - InputPullUp, - /// Pull-down input mode - InputPullDown, + /// Input mode + Input, /// Push-pull output mode OutputPushPull, /// Open-drain output mode OutputOpenDrain, } +pub trait DynamicMode { + const MODE: Dynamic; +} + +impl DynamicMode for Input { + const MODE: Dynamic = Dynamic::Input; +} + +impl DynamicMode for Output { + const MODE: Dynamic = Dynamic::OutputPushPull; +} + +impl DynamicMode for Output { + const MODE: Dynamic = Dynamic::OutputOpenDrain; +} + +impl Deref for DynPin { + type Target = PIN; + fn deref(&self) -> &Self::Target { + &self.pin + } +} + +impl DerefMut for DynPin { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.pin + } +} + /// Error for [DynamicPin] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PinModeError { @@ -42,8 +70,8 @@ impl Dynamic { pub fn is_input(&self) -> bool { use Dynamic::*; match self { - InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true, OutputPushPull => false, + Input | OutputOpenDrain => true, } } @@ -51,104 +79,150 @@ impl Dynamic { pub fn is_output(&self) -> bool { use Dynamic::*; match self { - InputFloating | InputPullUp | InputPullDown => false, + Input => false, OutputPushPull | OutputOpenDrain => true, } } } // For conversion simplify -struct Unknown; +#[non_exhaustive] +pub struct Unknown; impl crate::Sealed for Unknown {} impl PinMode for Unknown {} +impl marker::Interruptible for Unknown {} +impl marker::OutputSpeed for Unknown {} +impl marker::Active for Unknown {} -impl DynamicPin { - pub(super) const fn new(mode: Dynamic) -> Self { - Self { mode } +impl Pin { + /// Configures the pin as a pin that can change between input + /// and output without changing the type + pub fn into_dynamic(self) -> DynPin> { + DynPin::new(Pin::new(), MODE::MODE) } +} - /// Switch pin into pull-up input - #[inline] - pub fn make_pull_up_input(&mut self) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_pull_up_input(); - self.mode = Dynamic::InputPullUp; - } - /// Switch pin into pull-down input - #[inline] - pub fn make_pull_down_input(&mut self) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_pull_down_input(); - self.mode = Dynamic::InputPullDown; - } - /// Switch pin into floating input - #[inline] - pub fn make_floating_input(&mut self) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_floating_input(); - self.mode = Dynamic::InputFloating; +impl PartiallyErasedPin { + /// Configures the pin as a pin that can change between input + /// and output without changing the type + pub fn into_dynamic(self) -> DynPin> { + DynPin::new(PartiallyErasedPin::new(self.i), MODE::MODE) } - /// Switch pin into push-pull output - #[inline] - pub fn make_push_pull_output(&mut self) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_push_pull_output(); - self.mode = Dynamic::OutputPushPull; - } - /// Switch pin into push-pull output with required voltage state - #[inline] - pub fn make_push_pull_output_in_state(&mut self, state: PinState) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_push_pull_output_in_state(state); - self.mode = Dynamic::OutputPushPull; - } - /// Switch pin into open-drain output - #[inline] - pub fn make_open_drain_output(&mut self) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_open_drain_output(); - self.mode = Dynamic::OutputOpenDrain; +} + +impl AnyPin { + /// Configures the pin as a pin that can change between input + /// and output without changing the type + pub fn into_dynamic(self) -> DynPin> { + DynPin::new(AnyPin::from_pin_port(self.into_pin_port()), MODE::MODE) } - /// Switch pin into open-drain output with required voltage state - #[inline] - pub fn make_open_drain_output_in_state(&mut self, state: PinState) { - // NOTE(unsafe), we have a mutable reference to the current pin - Pin::::new().into_open_drain_output_in_state(state); - self.mode = Dynamic::OutputOpenDrain; +} + +impl DynPin { + pub(super) const fn new(pin: PIN, mode: Dynamic) -> Self { + Self { pin, mode } } +} - /// Drives the pin high - pub fn set_high(&mut self) -> Result<(), PinModeError> { - if self.mode.is_output() { - Pin::::new()._set_state(PinState::High); - Ok(()) - } else { - Err(PinModeError::IncorrectMode) +macro_rules! impldyn { + () => { + pub fn set_mode(&mut self) { + self.pin.mode::(); + self.mode = MODE::MODE } - } - /// Drives the pin low - pub fn set_low(&mut self) -> Result<(), PinModeError> { - if self.mode.is_output() { - Pin::::new()._set_state(PinState::Low); - Ok(()) - } else { - Err(PinModeError::IncorrectMode) + /// Switch pin into input + #[inline] + pub fn make_input(&mut self) { + self.set_mode::(); } - } - /// Is the input pin high? - pub fn is_high(&self) -> Result { - self.is_low().map(|b| !b) - } + /// Switch pin into pull-up input + #[inline] + pub fn make_pull_up_input(&mut self) { + self.set_mode::(); + self.set_internal_resistor(Pull::Up); + } + /// Switch pin into pull-down input + #[inline] + pub fn make_pull_down_input(&mut self) { + self.set_mode::(); + self.set_internal_resistor(Pull::Down); + } + /// Switch pin into floating input + #[inline] + pub fn make_floating_input(&mut self) { + self.set_mode::(); + self.set_internal_resistor(Pull::None); + } + /// Switch pin into push-pull output + #[inline] + pub fn make_push_pull_output(&mut self) { + self.set_mode::(); + } + /// Switch pin into push-pull output with required voltage state + #[inline] + pub fn make_push_pull_output_in_state(&mut self, state: PinState) { + self.pin._set_state(state); + self.set_mode::(); + } + /// Switch pin into open-drain output + #[inline] + pub fn make_open_drain_output(&mut self) { + self.set_mode::>(); + } + /// Switch pin into open-drain output with required voltage state + #[inline] + pub fn make_open_drain_output_in_state(&mut self, state: PinState) { + self.pin._set_state(state); + self.set_mode::>(); + } - /// Is the input pin low? - pub fn is_low(&self) -> Result { - if self.mode.is_input() { - Ok(Pin::::new()._is_low()) - } else { - Err(PinModeError::IncorrectMode) + pub fn set_state(&mut self, state: PinState) -> Result<(), PinModeError> { + if self.mode.is_output() { + self.pin._set_state(state); + Ok(()) + } else { + Err(PinModeError::IncorrectMode) + } } - } + + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) -> Result<(), PinModeError> { + self.set_state(PinState::High) + } + + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) -> Result<(), PinModeError> { + self.set_state(PinState::Low) + } + + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> Result { + self.is_low().map(|b| !b) + } + + /// Is the input pin low? + pub fn is_low(&self) -> Result { + if self.mode.is_input() { + Ok(self.pin._is_low()) + } else { + Err(PinModeError::IncorrectMode) + } + } + }; +} + +impl DynamicPin { + impldyn!(); +} +impl DynPin> { + impldyn!(); +} +impl DynamicAnyPin { + impldyn!(); } diff --git a/src/gpio/erased.rs b/src/gpio/erased.rs index cb28ad16..a6851ef0 100644 --- a/src/gpio/erased.rs +++ b/src/gpio/erased.rs @@ -73,7 +73,7 @@ impl AnyPin { } #[inline] - pub(crate) fn block(&self) -> &crate::pac::gpioa::RegisterBlock { + pub(crate) fn block(&self) -> *const crate::pac::gpioa::RegisterBlock { // This function uses pointer arithmetic instead of branching to be more efficient // The logic relies on the following assumptions: @@ -86,81 +86,33 @@ impl AnyPin { const GPIO_REGISTER_OFFSET: usize = 0x0400; let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize; - let block_ptr = - (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock; - - unsafe { &*block_ptr } + (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock } + + state_inner!(); } impl AnyPin> { - /// Drives the pin high - #[inline(always)] - pub fn set_high(&mut self) { - self.block().bsrr().write(|w| w.bs(self.pin_id()).set_bit()); - } - - /// Drives the pin low - #[inline(always)] - pub fn set_low(&mut self) { - self.block().bsrr().write(|w| w.br(self.pin_id()).set_bit()); - } - - /// Is the pin in drive high or low mode? - #[inline(always)] - pub fn get_state(&self) -> PinState { - if self.is_set_low() { - PinState::Low - } else { - PinState::High - } - } - - /// Drives the pin high or low depending on the provided value - #[inline(always)] - pub fn set_state(&mut self, state: PinState) { - match state { - PinState::Low => self.set_low(), - PinState::High => self.set_high(), - } - } - - /// Is the pin in drive high mode? - #[inline(always)] - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } - - /// Is the pin in drive low mode? - #[inline(always)] - pub fn is_set_low(&self) -> bool { - self.block().odr().read().odr(self.pin_id()).bit_is_clear() - } - - /// Toggle pin output - #[inline(always)] - pub fn toggle(&mut self) { - if self.is_set_low() { - self.set_high() - } else { - self.set_low() - } - } + state_output!(); } impl AnyPin where MODE: marker::Readable, { - /// Is the input pin high? - #[inline(always)] - pub fn is_high(&self) -> bool { - !self.is_low() - } + state_input!(); +} - /// Is the input pin low? - #[inline(always)] - pub fn is_low(&self) -> bool { - self.block().idr().read().idr(self.pin_id()).bit_is_clear() - } +impl AnyPin +where + MODE: marker::OutputSpeed, +{ + speed!(); +} + +impl AnyPin +where + MODE: marker::Active, +{ + internal_resistor!(); } diff --git a/src/gpio/hal_02.rs b/src/gpio/hal_02.rs index bf33c356..e1875a5e 100644 --- a/src/gpio/hal_02.rs +++ b/src/gpio/hal_02.rs @@ -1,8 +1,10 @@ use core::convert::Infallible; +use crate::gpio::DynamicPin; + use super::{ - dynamic::PinModeError, marker, AnyPin, DynamicPin, Input, OpenDrain, Output, - PartiallyErasedPin, Pin, PinMode, PinState, + dynamic::PinModeError, marker, AnyPin, Input, OpenDrain, Output, PartiallyErasedPin, Pin, + PinMode, PinState, }; use embedded_hal_02::digital::v2::{ diff --git a/src/gpio/hal_1.rs b/src/gpio/hal_1.rs index e33e2627..7ea95a58 100644 --- a/src/gpio/hal_1.rs +++ b/src/gpio/hal_1.rs @@ -1,6 +1,8 @@ use core::convert::Infallible; -use super::{dynamic::PinModeError, marker, AnyPin, DynamicPin, Output, PartiallyErasedPin, Pin}; +use crate::gpio::DynamicPin; + +use super::{dynamic::PinModeError, marker, AnyPin, Output, PartiallyErasedPin, Pin}; use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; diff --git a/src/gpio/partially_erased.rs b/src/gpio/partially_erased.rs index 7c5863bd..6c6ce2b2 100644 --- a/src/gpio/partially_erased.rs +++ b/src/gpio/partially_erased.rs @@ -63,92 +63,37 @@ impl PinExt for PartiallyErasedPin { } } -impl PartiallyErasedPin> { - /// Drives the pin high - #[inline(always)] - pub fn set_high(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { &*gpiox::

() } - .bsrr() - .write(|w| w.bs(self.i).set_bit()); - } - - /// Drives the pin low - #[inline(always)] - pub fn set_low(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { &*gpiox::

() } - .bsrr() - .write(|w| w.br(self.i).set_bit()); - } - - /// Is the pin in drive high or low mode? - #[inline(always)] - pub fn get_state(&self) -> PinState { - if self.is_set_low() { - PinState::Low - } else { - PinState::High - } - } - - /// Drives the pin high or low depending on the provided value - #[inline(always)] - pub fn set_state(&mut self, state: PinState) { - match state { - PinState::Low => self.set_low(), - PinState::High => self.set_high(), - } - } - - /// Is the pin in drive high mode? - #[inline(always)] - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } - - /// Is the pin in drive low mode? +impl PartiallyErasedPin { #[inline(always)] - pub fn is_set_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { &*gpiox::

() } - .odr() - .read() - .odr(self.i) - .bit_is_clear() + pub(crate) fn block(&self) -> *const crate::pac::gpioa::RegisterBlock { + gpiox::

() } + state_inner!(); +} - /// Toggle pin output - #[inline(always)] - pub fn toggle(&mut self) { - if self.is_set_low() { - self.set_high() - } else { - self.set_low() - } - } +impl PartiallyErasedPin> { + state_output!(); } impl PartiallyErasedPin where MODE: marker::Readable, { - /// Is the input pin high? - #[inline(always)] - pub fn is_high(&self) -> bool { - !self.is_low() - } + state_input!(); +} - /// Is the input pin low? - #[inline(always)] - pub fn is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { &*gpiox::

() } - .idr() - .read() - .idr(self.i) - .bit_is_clear() - } +impl PartiallyErasedPin +where + MODE: marker::OutputSpeed, +{ + speed!(); +} + +impl PartiallyErasedPin +where + MODE: marker::Active, +{ + internal_resistor!(); } impl From> for AnyPin {