From 526cebaa2c8a45839c70d5107f4a6e48a374c442 Mon Sep 17 00:00:00 2001 From: Andrew Stapleton Date: Tue, 8 Oct 2024 13:49:19 -0700 Subject: [PATCH] gpio: Add basic functionality for GPIOs (#17) This adds the basic outline functionality for the GPIO module and will form the base for follow-up PRs (see #19, #20, #21) to round out the GPIO functionality. The gpio module is largely based on/copied from the implementation in [stm32h7xx-hal](https://github.com/stm32-rs/stm32h7xx-hal). --- Cargo.toml | 12 +- src/gpio.rs | 463 +++++++++++++++++++++++++++++++++++++++++++ src/gpio/gpio_def.rs | 168 ++++++++++++++++ src/lib.rs | 10 + src/prelude.rs | 1 + 5 files changed, 652 insertions(+), 2 deletions(-) create mode 100644 src/gpio.rs create mode 100644 src/gpio/gpio_def.rs diff --git a/Cargo.toml b/Cargo.toml index 5e41f65..05c2bea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,17 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["rt"] + device-selected = [] -rm0492 = [] -rm0481 = [] + +# rmXXXX represent processor subfamilies and their common features +rm0492 = ["gpio-h503"] +rm0481 = ["gpio-h5x"] # STM32H52x/6x/7x + +# Different subfamilies have different GPIOs available +gpio-h503 = [] +gpio-h5x = [] + rt = ["stm32h5/rt"] stm32h503 = ["stm32h5/stm32h503", "device-selected", "rm0492"] stm32h562 = ["stm32h5/stm32h562", "device-selected", "rm0481"] diff --git a/src/gpio.rs b/src/gpio.rs new file mode 100644 index 0000000..901f22c --- /dev/null +++ b/src/gpio.rs @@ -0,0 +1,463 @@ +//! General Purpose Input / Output +//! +//! The GPIO pins are organised into groups of 16 pins which can be accessed through the +//! `gpioa`, `gpiob`... modules. To get access to the pins, you first need to convert them into a +//! HAL designed struct from the `pac` struct using the [split](trait.GpioExt.html#tymethod.split) function. +//! ```rust +//! // Acquire the GPIOA peripheral +//! // NOTE: `dp` is the device peripherals from the `PAC` crate +//! let mut gpioa = dp.GPIOA.split(); +//! ``` +//! +//! This gives you a struct containing all the pins `px0..px15`. +//! By default pins are in floating input mode. You can change their modes. +//! For example, to set `pa5` high, you would call +//! +//! ```rust +//! let output = gpioa.pa5.into_push_pull_output(); +//! output.set_high(); +//! ``` +//! +//! ## Modes +//! +//! Each GPIO pin can be set to various modes: +//! +//! - **Alternate**: Pin mode required when the pin is driven by other peripherals +//! - **Analog**: Analog input to be used with ADC. +//! - **Dynamic**: Pin mode is selected at runtime. See changing configurations for more details +//! - Input +//! - **PullUp**: Input connected to high with a weak pull up resistor. Will be high when nothing +//! is connected +//! - **PullDown**: Input connected to high with a weak pull up resistor. Will be low when nothing +//! is connected +//! - **Floating**: Input not pulled to high or low. Will be undefined when nothing is connected +//! - Output +//! - **PushPull**: Output which either drives the pin high or low +//! - **OpenDrain**: Output which leaves the gate floating, or pulls it do ground in drain +//! mode. Can be used as an input in the `open` configuration +//! +//! ## Changing modes +//! The simplest way to change the pin mode is to use the `into_` functions. These return a +//! new struct with the correct mode that you can use the input or output functions on. +//! +//! If you need a more temporary mode change, and can not use the `into_` functions for +//! ownership reasons, you can use the closure based `with_` functions to temporarily change the pin type, do +//! some output or input, and then have it change back once done. + +mod gpio_def; + +use core::{fmt, marker::PhantomData}; + +pub use embedded_hal::digital::PinState; + +use crate::rcc::ResetEnable; + +pub use gpio_def::*; + +/// A filler pin type +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct NoPin; + +/// Extension trait to split a GPIO peripheral into independent pins and +/// registers +pub trait GpioExt { + /// The parts to split the GPIO into + type Parts; + + /// The Reset and Enable control block for this GPIO block + type Rec: ResetEnable; + + /// Takes the GPIO peripheral and splits it into Zero-Sized Types + /// (ZSTs) representing individual pins. These are public + /// members of the return type. + /// + /// ``` + /// let device_peripherals = stm32::Peripherals.take().unwrap(); + /// let ccdr = ...; // From RCC + /// + /// let gpioa = device_peripherals.GPIOA.split(ccdr.peripheral.GPIOA); + /// + /// let pa0 = gpioa.pa0; // Pin 0 + /// ``` + fn split(self, prec: Self::Rec) -> Self::Parts; + + /// As [split](GpioExt#tymethod.split), but does not reset the GPIO + /// peripheral in the RCC_AHB4RSTR register. However it still enables the + /// peripheral in RCC_AHB4ENR, so our accesses to the peripheral memory will + /// always be valid. + /// + /// This is useful for situations where some GPIO functionality + /// was already activated outside the HAL in early startup code + /// or a bootloader. That might be needed for watchdogs, clock + /// circuits, or executing from an external memory. In this + /// case, `split_without_reset` allows this GPIO HAL to be used + /// without generating unwanted edges on already initialised + /// pins. + /// + /// However, the user takes responsibility that the GPIO + /// peripheral is in a valid state already. Note that the + /// registers accessed and written by this HAL may change in any + /// patch revision. + fn split_without_reset(self, prec: Self::Rec) -> Self::Parts; +} + +/// GPIO peripheral corresponding to GPIOA, GPIOB, etc +pub(crate) struct Gpio; + +/// Id, port and mode for any pin +pub trait PinExt { + /// Current pin mode + type Mode; + /// Pin number + fn pin_id(&self) -> u8; + /// Port number starting from 0 + fn port_id(&self) -> u8; +} + +/// Some alternate mode (type state) +pub struct Alternate(PhantomData); + +/// Input mode (type state) +pub struct Input; + +/// Pull setting for an input. +#[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pull { + /// Floating + None = 0, + /// Pulled up + Up = 1, + /// Pulled down + Down = 2, +} + +/// Open drain input or output (type state) +pub struct OpenDrain; + +/// Output mode (type state) +pub struct Output { + _mode: PhantomData, +} + +/// Push pull output (type state) +pub struct PushPull; + +/// Analog mode (type state) +pub struct Analog; + +/// JTAG/SWD mote (type state) +pub type Debugger = Alternate<0, PushPull>; + +mod marker { + /// Marker trait that show if `ExtiPin` can be implemented + #[allow(dead_code)] // TODO: Remove when EXTI is implemented + pub trait Interruptable {} + /// Marker trait for readable pin modes + pub trait Readable {} + /// Marker trait for slew rate configurable pin modes + pub trait OutputSpeed {} + /// Marker trait for active pin modes + pub trait Active {} + /// Marker trait for all pin modes except alternate + #[allow(dead_code)] // TODO: Remove when alternate function conversion is implemented + pub trait NotAlt {} + /// Marker trait for pins with alternate function `A` mapping + #[allow(dead_code)] // TODO: Remove when alternate function conversion is implemented + pub trait IntoAf {} +} + +impl marker::Interruptable for Output {} +impl marker::Interruptable for Input {} +impl marker::Readable for Input {} +impl marker::Readable for Alternate {} +impl marker::Readable for Output {} +impl marker::Active for Input {} +impl marker::OutputSpeed for Output {} +impl marker::OutputSpeed for Alternate {} +impl marker::Active for Output {} +impl marker::Active for Alternate {} +impl marker::NotAlt for Input {} +impl marker::NotAlt for Output {} +impl marker::NotAlt for Analog {} + +/// GPIO Pin speed selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Speed { + /// Low speed + Low = 0, + /// Medium speed + Medium = 1, + /// High speed + High = 2, + /// Very high speed + VeryHigh = 3, +} + +/// GPIO interrupt trigger edge selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Edge { + /// Rising edge of voltage + Rising, + /// Falling edge of voltage + Falling, + /// Rising and falling edge of voltage + RisingFalling, +} + +#[doc = "Alternate function 0 (type state)"] +pub type AF0 = Alternate<0, Otype>; +#[doc = "Alternate function 1 (type state)"] +pub type AF1 = Alternate<1, Otype>; +#[doc = "Alternate function 2 (type state)"] +pub type AF2 = Alternate<2, Otype>; +#[doc = "Alternate function 3 (type state)"] +pub type AF3 = Alternate<3, Otype>; +#[doc = "Alternate function 4 (type state)"] +pub type AF4 = Alternate<4, Otype>; +#[doc = "Alternate function 5 (type state)"] +pub type AF5 = Alternate<5, Otype>; +#[doc = "Alternate function 6 (type state)"] +pub type AF6 = Alternate<6, Otype>; +#[doc = "Alternate function 7 (type state)"] +pub type AF7 = Alternate<7, Otype>; +#[doc = "Alternate function 8 (type state)"] +pub type AF8 = Alternate<8, Otype>; +#[doc = "Alternate function 9 (type state)"] +pub type AF9 = Alternate<9, Otype>; +#[doc = "Alternate function 10 (type state)"] +pub type AF10 = Alternate<10, Otype>; +#[doc = "Alternate function 11 (type state)"] +pub type AF11 = Alternate<11, Otype>; +#[doc = "Alternate function 12 (type state)"] +pub type AF12 = Alternate<12, Otype>; +#[doc = "Alternate function 13 (type state)"] +pub type AF13 = Alternate<13, Otype>; +#[doc = "Alternate function 14 (type state)"] +pub type AF14 = Alternate<14, Otype>; +#[doc = "Alternate function 15 (type state)"] +pub type AF15 = Alternate<15, Otype>; + +/// Generic pin type +/// +/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. +/// - `N` is pin number: from `0` to `15`. +pub struct Pin { + _mode: PhantomData, +} +impl Pin { + const fn new() -> Self { + Self { _mode: PhantomData } + } +} + +impl fmt::Debug for Pin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P{}{}<{}>", + P, + N, + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Pin { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "P{}{}<{}>", + P, + N, + crate::stripped_type_name::() + ); + } +} + +impl PinExt for Pin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + N + } + #[inline(always)] + fn port_id(&self) -> u8 { + P as u8 - b'A' + } +} + +impl Pin +where + MODE: marker::OutputSpeed, +{ + /// Set pin speed + pub fn set_speed(&mut self, speed: Speed) { + let offset = 2 * { N }; + + unsafe { + (*Gpio::

::ptr()).ospeedr().modify(|r, w| { + w.bits( + (r.bits() & !(0b11 << offset)) | ((speed as u32) << offset), + ) + }); + } + } + + /// Set pin speed + pub fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self + } +} + +impl Pin +where + MODE: marker::Active, +{ + /// Set the internal pull-up and pull-down resistor + pub fn set_internal_resistor(&mut self, resistor: Pull) { + let offset = 2 * { N }; + let value = resistor as u32; + unsafe { + (*Gpio::

::ptr()).pupdr().modify(|r, w| { + w.bits((r.bits() & !(0b11 << offset)) | (value << offset)) + }); + } + } + + /// 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 down + pub fn internal_pull_down(self, on: bool) -> Self { + if on { + self.internal_resistor(Pull::Down) + } else { + self.internal_resistor(Pull::None) + } + } +} + +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(), + } + } + #[inline(always)] + fn _set_high(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*Gpio::

::ptr()).bsrr().write(|w| w.bits(1 << N)) } + } + #[inline(always)] + fn _set_low(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*Gpio::

::ptr()).bsrr().write(|w| w.bits(1 << (16 + N))) } + } + #[inline(always)] + fn _is_set_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).odr().read().bits() & (1 << N) == 0 } + } + #[inline(always)] + fn _is_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).idr().read().bits() & (1 << N) == 0 } + } +} + +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() + } + + /// Is the pin in drive high or low mode? + #[inline(always)] + pub fn get_state(&mut 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(&mut self) -> bool { + !self.is_set_low() + } + + /// Is the pin in drive low mode? + #[inline(always)] + pub fn is_set_low(&mut 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() + } + } +} + +impl Pin +where + MODE: marker::Readable, +{ + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&mut self) -> bool { + !self.is_low() + } + + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&mut self) -> bool { + self._is_low() + } +} diff --git a/src/gpio/gpio_def.rs b/src/gpio/gpio_def.rs new file mode 100644 index 0000000..32245d8 --- /dev/null +++ b/src/gpio/gpio_def.rs @@ -0,0 +1,168 @@ +use super::Gpio; + +macro_rules! gpio { + ($GPIOX:ident, $gpiox:ident, $Rec:ident, $PEPin:ident, $port_id:expr, $PXn:ident, [ + $($PXi:ident: ($pxi:ident, $i:expr, [$($A:literal),*] $(, $MODE:ty)?),)+ + ]) => { + #[doc=concat!("Port ", $port_id)] + pub mod $gpiox { + use crate::pac::$GPIOX; + use crate::rcc::{rec, ResetEnable}; + + /// GPIO parts + pub struct Parts { + $( + /// Pin + pub $pxi: $PXi $(<$MODE>)?, + )+ + } + + impl crate::gpio::GpioExt for $GPIOX { + type Parts = Parts; + type Rec = rec::$Rec; + + fn split(self, prec: rec::$Rec) -> Parts { + prec.enable().reset(); + + Parts { + $( + $pxi: $PXi::new(), + )+ + } + } + + fn split_without_reset(self, prec: rec::$Rec) -> Parts { + prec.enable(); + + Parts { + $( + $pxi: $PXi::new(), + )+ + } + } + } + + $( + #[doc=concat!("P", $port_id, $i, " pin")] + pub type $PXi = crate::gpio::Pin<$port_id, $i, MODE>; + + $( + impl crate::gpio::marker::IntoAf<$A> for $PXi { } + )* + )+ + + } + + pub use $gpiox::{ $($PXi,)+ }; + } +} + +#[cfg(feature = "gpio-h503")] +pub use h503::*; + +#[cfg(feature = "gpio-h503")] +mod h503 { + use super::Gpio; + + gpio!(GPIOA, gpioa, Gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PA1: (pa1, 1, [1, 4, 5, 6, 7, 8, 9, 11, 14, 15]), + PA2: (pa2, 2, [1, 2, 3, 4, 6, 7, 8, 14, 15]), + PA3: (pa3, 3, [1, 3, 4, 5, 6, 7, 8, 13, 14, 15]), + PA4: (pa4, 4, [1, 3, 4, 5, 6, 7, 8, 10, 13, 14, 15]), + PA5: (pa5, 5, [1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PA6: (pa6, 6, [1, 2, 5, 15]), + PA7: (pa7, 7, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PA8: (pa8, 8, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PA9: (pa9, 9, [0, 1, 3, 4, 5, 7, 10, 13, 15]), + PA10: (pa10, 10, [1, 3, 4, 7, 15]), + PA11: (pa11, 11, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15]), + PA12: (pa12, 12, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15]), + PA13: (pa13, 13, [0, 1, 2, 7, 8, 9, 12, 14, 15], crate::gpio::Debugger), + PA14: (pa14, 14, [0, 1, 2, 3, 4, 7, 8, 9, 14, 15], crate::gpio::Debugger), + PA15: (pa15, 15, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15], crate::gpio::Debugger), + ]); + + gpio!(GPIOB, gpiob, Gpiob, PB, 'B', PBn, [ + PB0: (pb0, 0, [1, 2, 4, 9, 14, 15]), + PB1: (pb1, 1, [1, 2, 3, 5, 6, 9, 12, 14, 15]), + PB2: (pb2, 2, [0, 1, 2, 4, 5, 6, 7, 9, 14, 15]), + PB3: (pb3, 3, [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15], crate::gpio::Debugger), + PB4: (pb4, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15], crate::gpio::Debugger), + PB5: (pb5, 5, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PB6: (pb6, 6, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15]), + PB7: (pb7, 7, [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PB8: (pb8, 8, [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PB10: (pb10, 10, [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PB12: (pb12, 12, [1, 4, 5, 7, 8, 9, 15]), + PB13: (pb13, 13, [1, 3, 4, 5, 7, 8, 9, 10, 11, 15]), + PB14: (pb14, 14, [1, 2, 4, 5, 7, 8, 15]), + PB15: (pb15, 15, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + ]); + + gpio!(GPIOC, gpioc, Gpioc, PC, 'C', PCn, [ + PC0: (pc0, 0, [1, 2, 5, 7, 15]), + PC1: (pc1, 1, [0, 2, 4, 5, 7, 8, 14, 15]), + PC2: (pc2, 2, [0, 4, 5, 7, 8, 15]), + PC3: (pc3, 3, [0, 3, 4, 5, 7, 14, 15]), + PC4: (pc4, 4, [1, 3, 4, 5, 7, 15]), + PC5: (pc5, 5, [1, 5, 12, 14, 15]), + PC6: (pc6, 6, [1, 2, 3, 4, 5, 8, 9, 13, 15]), + PC7: (pc7, 7, [0, 1, 2, 3, 5, 6, 8, 9, 13, 15]), + PC8: (pc8, 8, [0, 1, 2, 3, 4, 5, 8, 9, 13, 15]), + PC9: (pc9, 9, [0, 1, 2, 3, 4, 5, 6, 7, 9, 13, 15]), + PC10: (pc10, 10, [1, 3, 5, 6, 7, 8, 9, 13, 15]), + PC11: (pc11, 11, [1, 3, 4, 5, 6, 7, 8, 14, 15]), + PC12: (pc12, 12, [0, 1, 2, 3, 6, 7, 8, 14, 15]), + PC13: (pc13, 13, [15]), + PC14: (pc14, 14, [15]), + PC15: (pc15, 15, [15]), + ]); + + gpio!(GPIOD, gpiod, Gpiod, PD, 'D', PDn, [ + PD2: (pd2, 2, [0, 1, 2, 6, 7, 9, 14, 15]), + ]); + + gpio!(GPIOH, gpioh, Gpioh, PH, 'H', PHn, [ + PH0: (ph0, 0, [15]), + PH1: (ph1, 1, [15]), + ]); + + impl Gpio

{ + pub(crate) const fn ptr() -> *const crate::pac::gpioa::RegisterBlock { + match P { + 'A' => crate::pac::GPIOA::ptr(), + 'B' => crate::pac::GPIOB::ptr() as _, + 'C' => crate::pac::GPIOC::ptr() as _, + 'D' => crate::pac::GPIOD::ptr() as _, + 'H' => crate::pac::GPIOH::ptr() as _, + _ => panic!("Unknown GPIO port"), + } + } + } +} + +#[cfg(feature = "gpio-h5x")] +pub use h5x::*; + +#[cfg(feature = "gpio-h5x")] +mod h5x { + use super::Gpio; + + impl Gpio

{ + pub(crate) const fn ptr() -> *const crate::pac::gpioa::RegisterBlock { + match P { + 'A' => crate::pac::GPIOA::ptr(), + 'B' => crate::pac::GPIOB::ptr() as _, + 'C' => crate::pac::GPIOC::ptr() as _, + 'D' => crate::pac::GPIOD::ptr() as _, + 'E' => crate::pac::GPIOE::ptr() as _, + 'F' => crate::pac::GPIOF::ptr() as _, + 'G' => crate::pac::GPIOG::ptr() as _, + 'H' => crate::pac::GPIOH::ptr() as _, + 'I' => crate::pac::GPIOI::ptr() as _, + _ => panic!("Unknown GPIO port"), + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index a72b082..83b330a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,3 +48,13 @@ pub mod time; #[cfg(feature = "device-selected")] pub mod rcc; + +#[cfg(feature = "device-selected")] +pub mod gpio; + +/// Get the name of the type without the module prefix(es) +fn stripped_type_name() -> &'static str { + let s = core::any::type_name::(); + let p = s.split("::"); + p.last().unwrap() +} diff --git a/src/prelude.rs b/src/prelude.rs index 9ab0257..e2fdf8b 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,6 @@ //! Prelude +pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt;