From eaa3721442ca3800cf957b6739394e813de4eac4 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Wed, 28 May 2025 14:46:52 +0300 Subject: [PATCH 1/4] generic Pins --- Cargo.toml | 16 +- examples/button.rs | 4 +- examples/spi-sd.rs | 2 +- src/comparator.rs | 10 +- src/gpio.rs | 1303 +++++++++++++--------------------- src/gpio/convert.rs | 362 ++++++++++ src/gpio/dynamic.rs | 154 ++++ src/gpio/erased.rs | 172 +++++ src/gpio/exti.rs | 116 +++ src/gpio/g4.rs | 369 ++++++++++ src/gpio/hal_02.rs | 242 +++++++ src/gpio/hal_1.rs | 168 +++++ src/gpio/outport.rs | 108 +++ src/gpio/partially_erased.rs | 153 ++++ src/lib.rs | 6 + src/rcc/clockout.rs | 6 +- 16 files changed, 2352 insertions(+), 839 deletions(-) create mode 100644 src/gpio/convert.rs create mode 100644 src/gpio/dynamic.rs create mode 100644 src/gpio/erased.rs create mode 100644 src/gpio/exti.rs create mode 100644 src/gpio/g4.rs create mode 100644 src/gpio/hal_02.rs create mode 100644 src/gpio/hal_1.rs create mode 100644 src/gpio/outport.rs create mode 100644 src/gpio/partially_erased.rs diff --git a/Cargo.toml b/Cargo.toml index 368fb015..b1cb7421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Vitaly Domnikov "] categories = ["embedded", "hardware-support", "no-std"] description = "Peripheral access API for STM32G4 series microcontrollers" documentation = "https://docs.rs/stm32g4xx-hal" -edition = "2018" +edition = "2021" keywords = ["arm", "cortex-m", "stm32g4xx", "hal"] license = "MIT/Apache-2.0" name = "stm32g4xx-hal" @@ -86,6 +86,14 @@ stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"] stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5"] stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"] stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"] + + +gpio-g43x = [] + +gpio-g47x = [] + +gpio-g49x = [] + log-itm = ["cortex-m-log/itm"] log-rtt = [] log-semihost = ["cortex-m-log/semihosting"] @@ -103,9 +111,9 @@ adc4 = [] adc5 = [] # Device category -cat2 = [] -cat3 = [] -cat4 = [] +cat2 = ["gpio-g43x"] +cat3 = ["gpio-g47x"] +cat4 = ["gpio-g49x"] can = ["dep:fdcan"] diff --git a/examples/button.rs b/examples/button.rs index 766cf33a..c3530b90 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -3,7 +3,7 @@ use stm32g4xx_hal::{ //delay::{DelayExt, SYSTDelayExt}, - gpio::{self, ExtiPin, GpioExt, Input, PullDown, SignalEdge}, + gpio::{self, ExtiPin, GpioExt, Input, SignalEdge}, rcc::RccExt, stm32, stm32::{interrupt, Interrupt}, @@ -15,7 +15,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use cortex_m::{asm::wfi, interrupt::Mutex}; use cortex_m_rt::entry; -type ButtonPin = gpio::PC13>; +type ButtonPin = gpio::PC13; // Make LED pin globally available static G_BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); diff --git a/examples/spi-sd.rs b/examples/spi-sd.rs index 89305077..46e10df2 100644 --- a/examples/spi-sd.rs +++ b/examples/spi-sd.rs @@ -34,7 +34,7 @@ fn main() -> ! { let gpiof = dp.GPIOF.split(&mut rcc); let cs = { - let mut cs = gpiof.pf8.into_push_pull_output(); + let mut cs = gpiof.pf0.into_push_pull_output(); cs.set_high(); cs }; diff --git a/src/comparator.rs b/src/comparator.rs index 2fb4772f..e5603f4e 100644 --- a/src/comparator.rs +++ b/src/comparator.rs @@ -621,7 +621,6 @@ output_pin! { COMP1: PA6, 8, COMP1: PA11, 8, COMP1: PB8, 8, - COMP1: PF4, 2, COMP2: PA2, 8, COMP2: PA7, 8, @@ -637,13 +636,10 @@ output_pin! { COMP4: PB14, 8, } -#[cfg(any( - feature = "stm32g473", - feature = "stm32g483", - feature = "stm32g474", - feature = "stm32g484", -))] +#[cfg(feature = "gpio-g47x")] output_pin! { + COMP1: PF4, 2, + COMP5: PA9, 8, COMP5: PC7, 7, diff --git a/src/gpio.rs b/src/gpio.rs index 9f374b0d..0ffe1047 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -1,14 +1,90 @@ //! General Purpose Input / Output -use core::marker::PhantomData; +//! +//! 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 GPIOC 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. +//! +//! ### Dynamic Mode Change +//! The above mode change methods guarantee that you can only call input functions when the pin is +//! in input mode, and output when in output modes, but can lead to some issues. Therefore, there +//! is also a mode where the state is kept track of at runtime, allowing you to change the mode +//! often, and without problems with ownership, or references, at the cost of some performance and +//! the risk of runtime errors. +//! +//! To make a pin dynamic, use the `into_dynamic` function, and then use the `make_` functions to +//! change the mode -use crate::rcc::{Enable, Rcc, Reset}; -use crate::stm32::EXTI; -use crate::syscfg::SysCfg; +use core::marker::PhantomData; use crate::pac; - -/// Default pin mode -pub type DefaultMode = Input; +use crate::rcc::Rcc; +mod convert; +pub use convert::PinMode; +mod partially_erased; +pub use partially_erased::{PEPin, PartiallyErasedPin}; +mod erased; +pub use erased::{AnyPin, ErasedPin}; +mod exti; +pub use exti::ExtiPin; +mod dynamic; +pub use dynamic::{Dynamic, DynamicPin}; +mod hal_02; +mod hal_1; +pub mod outport; + +pub use embedded_hal_old::digital::v2::PinState; + +use core::fmt; + +/// A filler pin type +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct NoPin(PhantomData); +impl NoPin { + pub fn new() -> Self { + Self(PhantomData) + } +} /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { @@ -19,37 +95,98 @@ pub trait GpioExt { fn split(self, rcc: &mut Rcc) -> Self::Parts; } -/// Input mode (type state) -pub struct Input { - _mode: PhantomData, +/// 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) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Alternate(PhantomData); -/// Floating input (type state) -pub struct Floating; +/// Input mode (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Input; -/// Pulled down input (type state) -pub struct PullDown; +/// Pull setting for an input. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pull { + /// Floating + None = 0, + /// Pulled up + Up = 1, + /// Pulled down + Down = 2, +} -/// Pulled up input (type state) -pub struct PullUp; +impl From for pac::gpioa::pupdr::PULL { + fn from(value: Pull) -> Self { + match value { + Pull::Down => Self::PullDown, + Pull::Up => Self::PullUp, + Pull::None => Self::Floating, + } + } +} /// Open drain input or output (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OpenDrain; -/// Analog mode (type state) -pub struct Analog; - /// Output mode (type state) -pub struct Output { - _mode: PhantomData, +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Output { + _mode: PhantomData, } /// Push pull output (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PushPull; +/// Analog mode (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Analog; + +/// JTAG/SWD mote (type state) +pub type Debugger = Alternate<0, PushPull>; + +pub(crate) mod marker { + /// Marker trait that show if `ExtiPin` can be implemented + pub trait Interruptible {} + /// 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 + pub trait IntoAf {} +} + +impl marker::Interruptible for Output {} +impl marker::Interruptible for Input {} +impl marker::Readable for Input {} +impl marker::Readable for Output {} +impl marker::Interruptible for Alternate {} +impl marker::Readable for Alternate {} +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 {} + /// GPIO Pin speed selection #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -115,859 +252,381 @@ af!( 15: AF15 ); -/// External Interrupt Pin -pub trait ExtiPin { - fn make_interrupt_source(&mut self, syscfg: &mut SysCfg); - fn trigger_on_edge(&mut self, exti: &mut EXTI, level: SignalEdge); - fn enable_interrupt(&mut self, exti: &mut EXTI); - fn disable_interrupt(&mut self, exti: &mut EXTI); - fn clear_interrupt_pending_bit(&mut self); - fn check_interrupt(&self) -> bool; -} - -macro_rules! exti_erased { - ($PIN:ty, $extigpionr:expr) => { - impl ExtiPin for $PIN { - /// Make corresponding EXTI line sensitive to this pin - fn make_interrupt_source(&mut self, syscfg: &mut SysCfg) { - let offset = 4 * (self.i % 4); - match self.i { - 0..=3 => { - syscfg.exticr1().modify(|r, w| unsafe { - w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) - }); - } - 4..=7 => { - syscfg.exticr2().modify(|r, w| unsafe { - w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) - }); - } - 8..=11 => { - syscfg.exticr3().modify(|r, w| unsafe { - w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) - }); - } - 12..=15 => { - syscfg.exticr4().modify(|r, w| unsafe { - w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) - }); - } - _ => {} - } - } - - /// Generate interrupt on rising edge, falling edge or both - fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: SignalEdge) { - match edge { - SignalEdge::Rising => { - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); - } - SignalEdge::Falling => { - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); - } - SignalEdge::RisingFalling => { - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); - } - } - } - - /// Enable external interrupts from this pin. - fn enable_interrupt(&mut self, exti: &mut EXTI) { - exti.imr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); - } - - /// Disable external interrupts from this pin - fn disable_interrupt(&mut self, exti: &mut EXTI) { - exti.imr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); - } - - /// Clear the interrupt pending bit for this pin - fn clear_interrupt_pending_bit(&mut self) { - unsafe { (*EXTI::ptr()).pr1().write(|w| w.bits(1 << self.i)) }; - } - - /// Reads the interrupt pending bit for this pin - fn check_interrupt(&self) -> bool { - unsafe { ((*EXTI::ptr()).pr1().read().bits() & (1 << self.i)) != 0 } - } - } - }; +/// 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 } + } } -macro_rules! exti { - ($PIN:ty, $extigpionr:expr, $i:expr, $exticri:ident) => { - impl ExtiPin for $PIN { - /// Configure EXTI Line $i to trigger from this pin. - fn make_interrupt_source(&mut self, syscfg: &mut SysCfg) { - let offset = 4 * ($i % 4); - syscfg.$exticri().modify(|r, w| unsafe { - let mut exticr = r.bits(); - exticr = (exticr & !(0xf << offset)) | ($extigpionr << offset); //FIXME: clears other pins - w.bits(exticr) - }); - } +impl crate::Sealed for Pin {} - /// Generate interrupt on rising edge, falling edge or both - fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: SignalEdge) { - match edge { - SignalEdge::Rising => { - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); - } - SignalEdge::Falling => { - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); - } - SignalEdge::RisingFalling => { - exti.rtsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); - exti.ftsr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); - } - } - } +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::() + )) + } +} - /// Enable external interrupts from this pin. - fn enable_interrupt(&mut self, exti: &mut EXTI) { - exti.imr1() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); - } +#[cfg(feature = "defmt")] +impl defmt::Format for Pin { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "P{}{}<{}>", P, N, crate::stripped_type_name::()); + } +} - /// Disable external interrupts from this pin - fn disable_interrupt(&mut self, exti: &mut EXTI) { - exti.imr1() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); - } +impl PinExt for Pin { + type Mode = MODE; - /// Clear the interrupt pending bit for this pin - fn clear_interrupt_pending_bit(&mut self) { - unsafe { (*EXTI::ptr()).pr1().write(|w| w.bits(1 << $i)) }; - } - - /// Reads the interrupt pending bit for this pin - fn check_interrupt(&self) -> bool { - unsafe { ((*EXTI::ptr()).pr1().read().bits() & (1 << $i)) != 0 } - } - } - }; + #[inline(always)] + fn pin_id(&self) -> u8 { + N + } + #[inline(always)] + fn port_id(&self) -> u8 { + P as u8 - b'A' + } } -macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $iopxenr:ident, $PXx:ident, $Pxn:expr, [ - $($PXi:ident: ($pxi:ident, $i:expr, $exticri:ident),)+ - ]) => { - /// GPIO - pub mod $gpiox { - use core::marker::PhantomData; - use crate::stm32::{EXTI, $GPIOX}; - use crate::exti::{ExtiExt, Event}; - use crate::rcc::Rcc; - use super::*; +pub trait PinSpeed: Sized { + /// Set pin speed + fn set_speed(&mut self, speed: Speed); - /// GPIO parts - pub struct Parts { - $( - pub $pxi: $PXi>, - )+ - } + #[inline(always)] + fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self + } +} - impl GpioExt for $GPIOX { - type Parts = Parts; +pub trait PinPull: Sized { + /// Set the internal pull-up and pull-down resistor + fn set_internal_resistor(&mut self, resistor: Pull); - fn split(self, rcc: &mut Rcc) -> Parts { - $GPIOX::enable(&rcc.rb); - $GPIOX::reset(&rcc.rb); + #[inline(always)] + fn internal_resistor(mut self, resistor: Pull) -> Self { + self.set_internal_resistor(resistor); + self + } +} - Parts { - $( - $pxi: $PXi { _mode: PhantomData }, - )+ - } - } - } +impl PinSpeed for Pin +where + MODE: marker::OutputSpeed, +{ + #[inline(always)] + fn set_speed(&mut self, speed: Speed) { + self.set_speed(speed) + } +} - /// Partially erased pin - pub struct $PXx { - i: u8, - _mode: PhantomData, - } +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())); + } - impl $PXx> { - pub fn set_high(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr().write(|w| w.bs(self.i).set_bit()) }; - } + /// Set pin speed + pub fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self + } +} - pub fn set_low(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr().write(|w| w.br(self.i).set_bit()) }; - } +impl PinPull for Pin +where + MODE: marker::Active, +{ + #[inline(always)] + fn set_internal_resistor(&mut self, resistor: Pull) { + self.set_internal_resistor(resistor) + } +} - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } +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())); + } - pub fn is_set_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).odr().read().odr(self.i).is_low()} - } + /// Set the internal pull-up and pull-down resistor + pub fn internal_resistor(mut self, resistor: Pull) -> Self { + self.set_internal_resistor(resistor); + self + } - pub fn toggle(&mut self) { - if self.is_set_low() { - self.set_high() - } else { - self.set_low() - } - } - } + /// 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) + } + } - impl embedded_hal_old::digital::v2::OutputPin for $PXx> { - type Error = (); + /// 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) + } + } +} - fn set_high(&mut self) -> Result<(), ()> { - self.set_high(); - Ok(()) - } +impl Pin { + /// Erases the pin number from the type + /// + /// 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_number(self) -> PartiallyErasedPin { + PartiallyErasedPin::new(N) + } - fn set_low(&mut self) -> Result<(), ()> { - self.set_low(); - Ok(()) - } - } + /// Erases the pin number and the port from the type + /// + /// 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) + } +} - impl embedded_hal::digital::ErrorType for $PXx { - type Error = core::convert::Infallible; - } +impl From> for PartiallyErasedPin { + /// Pin-to-partially erased pin conversion using the [`From`] trait. + /// + /// Note that [`From`] is the reciprocal of [`Into`]. + fn from(p: Pin) -> Self { + p.erase_number() + } +} - impl embedded_hal::digital::OutputPin for $PXx> { - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } +impl From> for ErasedPin { + /// Pin-to-erased pin conversion using the [`From`] trait. + /// + /// Note that [`From`] is the reciprocal of [`Into`]. + fn from(p: Pin) -> Self { + p.erase() + } +} - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - } +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 + 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() + } +} - impl embedded_hal_old::digital::v2::StatefulOutputPin for $PXx> { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } +impl Pin> { + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) { + self._set_high() + } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) { + self._set_low() + } - impl embedded_hal::digital::StatefulOutputPin for $PXx> { - fn is_set_high(&mut self) -> Result { - Ok((*self).is_set_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 + } + } - fn is_set_low(&mut self) -> Result { - Ok((*self).is_set_low()) - } - } + /// 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(), + } + } - impl $PXx> { - pub fn is_high(&self) -> bool { - !self.is_low() - } + /// Is the pin in drive high mode? + #[inline(always)] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } - pub fn is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).idr().read().idr(self.i).is_low() } - } - } + /// Is the pin in drive low mode? + #[inline(always)] + pub fn is_set_low(&self) -> bool { + self._is_set_low() + } - impl embedded_hal_old::digital::v2::toggleable::Default for $PXx> {} + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } +} - impl embedded_hal_old::digital::v2::InputPin for $PXx> { - type Error = (); +pub trait ReadPin { + #[inline(always)] + fn is_high(&self) -> bool { + !self.is_low() + } + fn is_low(&self) -> bool; +} - fn is_high(&self) -> Result { - Ok(self.is_high()) - } +impl ReadPin for Pin +where + MODE: marker::Readable, +{ + #[inline(always)] + fn is_low(&self) -> bool { + self.is_low() + } +} - fn is_low(&self) -> Result { - Ok(self.is_low()) - } - } +impl Pin +where + MODE: marker::Readable, +{ + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> bool { + !self.is_low() + } - impl embedded_hal::digital::InputPin for $PXx> { - fn is_high(&mut self) -> Result { - Ok((*self).is_high()) - } + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&self) -> bool { + self._is_low() + } +} - fn is_low(&mut self) -> Result { - Ok((*self).is_low()) - } - } +impl crate::stasis::Freeze for Pin {} - impl $PXx> { - pub fn is_high(&self) -> bool { - !self.is_low() - } +macro_rules! gpio { + ($GPIOX:ident, $gpiox:ident, $PEPin:ident, $port_id:expr, $PXn:ident, [ + $($PXi:ident: ($pxi:ident, $i:expr, [$($A:literal),*] $(, $MODE:ty)?),)+ + ]) => { + /// GPIO + pub mod $gpiox { + use crate::pac::$GPIOX; + use crate::rcc::{Rcc, Enable, Reset}; - pub fn is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).idr().read().idr(self.i).is_low() } - } + /// GPIO parts + pub struct Parts { + $( + /// Pin + pub $pxi: $PXi $(<$MODE>)?, + )+ } - impl embedded_hal_old::digital::v2::InputPin for $PXx> { - type Error = (); - - fn is_high(&self) -> Result { - Ok(self.is_high()) - } - - fn is_low(&self) -> Result { - Ok(self.is_low()) - } - } + impl super::GpioExt for $GPIOX { + type Parts = Parts; - impl embedded_hal::digital::InputPin for $PXx> { - fn is_high(&mut self) -> Result { - Ok((*self).is_high()) - } + fn split(self, rcc: &mut Rcc) -> Parts { + Self::enable(&rcc.rb); + Self::reset(&rcc.rb); - fn is_low(&mut self) -> Result { - Ok((*self).is_low()) + Parts { + $( + $pxi: $PXi::new(), + )+ + } } } - exti_erased!($PXx>, $Pxn); - exti_erased!($PXx>, $Pxn); + #[doc="Common type for "] + #[doc=stringify!($GPIOX)] + #[doc=" related pins"] + pub type $PXn = super::PartiallyErasedPin<$port_id, MODE>; $( - pub struct $PXi { - _mode: PhantomData, - } - - impl crate::Sealed for $PXi {} - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_pull_down_input() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_pull_up_input() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi> for $PXi { - fn into(self) -> $PXi { - self.into_analog() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_open_drain_output() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_push_pull_output() - } - } - - impl crate::stasis::Freeze for $PXi { } - - impl $PXi { - /// Configures the pin to operate as a floating input pin - pub fn into_floating_input(self) -> $PXi> { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).floating() - }); - gpio.moder().modify(|_, w| { - w.moder($i).input() - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a pulled down input pin - pub fn into_pull_down_input(self) -> $PXi> { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).pull_down() - }); - gpio.moder().modify(|_, w| { - w.moder($i).input() - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a pulled up input pin - pub fn into_pull_up_input(self) -> $PXi> { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).pull_up() - }); - gpio.moder().modify(|_, w| { - w.moder($i).input() - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an analog pin - pub fn into_analog(self) -> $PXi { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).floating() - }); - gpio.moder().modify(|_, w| { - w.moder($i).analog() - }); - } - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an open drain output pin - pub fn into_open_drain_output(self) -> $PXi> { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).floating() - }); - gpio.otyper().modify(|_, w| { - w.ot($i).open_drain() - }); - gpio.moder().modify(|_, w| { - w.moder($i).output() - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an push pull output pin - pub fn into_push_pull_output(self) -> $PXi> { - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr().modify(|_, w| { - w.pupdr($i).floating() - }); - gpio.otyper().modify(|_, w| { - w.ot($i).push_pull() - }); - gpio.moder().modify(|_, w| { - w.moder($i).output() - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin as external trigger - pub fn listen(self, edge: SignalEdge, exti: &mut EXTI) -> $PXi { - exti.listen(Event::from_code($i), edge); - $PXi { _mode: PhantomData } - } - - /// Set pin speed - pub fn set_speed(&mut self, speed: Speed) { - unsafe { - (*$GPIOX::ptr()).ospeedr() - .modify(|_, w| w.ospeedr($i).variant(speed.into())); - } - } - - /// Set pin speed - pub fn speed(mut self, speed: Speed) -> Self { - self.set_speed(speed); - self - } - - pub fn into_alternate(self) -> $PXi> { - let mode = A as u8; - unsafe { - let gpio = &(*$GPIOX::ptr()); - if $i < 8 { - gpio.afrl().modify(|_, w| { - w.afr($i).set(mode) - }); - } else { - let offset = $i - 8; - gpio.afrh().modify(|_, w| { - w.afr(offset).set(mode) - }); - } - gpio.moder().modify(|_, w| { - w.moder($i).alternate() - }); - gpio.otyper().modify(|_, w| { - w.ot($i).push_pull() - }); - } - $PXi { _mode: PhantomData } - } - - pub fn into_alternate_open_drain(self) -> $PXi> { - let mode = A as u8; - unsafe { - let gpio = &(*$GPIOX::ptr()); - if $i < 8 { - gpio.afrl().modify(|_, w| { - w.afr($i).set(mode) - }); - } else { - let offset = $i - 8; - gpio.afrh().modify(|_, w| { - w.afr(offset).set(mode) - }); - } - gpio.otyper().modify(|_, w| { - w.ot($i).open_drain() - }); - gpio.moder().modify(|_, w| { - w.moder($i).alternate() - }); - } - $PXi { _mode: PhantomData } - } - } + #[doc=stringify!($PXi)] + #[doc=" pin"] + pub type $PXi = super::Pin<$port_id, $i, MODE>; - impl $PXi> { - /// Erases the pin number from the type - /// - /// 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 downgrade(self) -> $PXx> { - $PXx { i: $i, _mode: self._mode } - } - - pub fn set_high(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr().write(|w| w.bs($i).set_bit()) }; - } - - pub fn set_low(&mut self) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr().write(|w| w.br($i).set_bit()) }; - } - - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } - - pub fn is_set_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).odr().read().odr($i).is_low()} - } - - pub fn toggle(&mut self) { - if self.is_set_low() { - self.set_high() - } else { - self.set_low() - } - } - } - - impl embedded_hal_old::digital::v2::OutputPin for $PXi> { - type Error = (); - - fn set_high(&mut self) -> Result<(), ()> { - self.set_high(); - Ok(()) - } - - fn set_low(&mut self) -> Result<(), ()>{ - self.set_low(); - Ok(()) - } - } - - impl embedded_hal::digital::ErrorType for $PXi { - type Error = core::convert::Infallible; - } - - impl embedded_hal::digital::OutputPin for $PXi> { - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Self::Error>{ - self.set_low(); - Ok(()) - } - } - - impl embedded_hal_old::digital::v2::StatefulOutputPin for $PXi> { - 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::StatefulOutputPin for $PXi> { - fn is_set_high(&mut self) -> Result { - Ok((*self).is_set_high()) - } - - fn is_set_low(&mut self) -> Result { - Ok((*self).is_set_low()) - } - } - - impl embedded_hal_old::digital::v2::toggleable::Default for $PXi> {} - - impl $PXi> { - pub fn is_high(&self) -> bool { - !self.is_low() - } - - pub fn is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).idr().read().idr($i).is_low() } - } - } - - impl embedded_hal_old::digital::v2::InputPin for $PXi> { - type Error = (); - - fn is_high(&self) -> Result { - Ok(self.is_high()) - } - - fn is_low(&self) -> Result { - Ok(self.is_low()) - } - } - - impl embedded_hal::digital::InputPin for $PXi> { - fn is_high(&mut self) -> Result { - Ok((*self).is_high()) - } - - fn is_low(&mut self) -> Result { - Ok((*self).is_low()) - } - } - - impl $PXi> { - /// Erases the pin number from the type - /// - /// 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 downgrade(self) -> $PXx> { - $PXx { i: $i, _mode: self._mode } - } - - pub fn is_high(&self) -> bool { - !self.is_low() - } - - pub fn is_low(&self) -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*$GPIOX::ptr()).idr().read().idr($i).is_low() } - } - } - - impl embedded_hal::digital::InputPin for $PXi> { - fn is_high(&mut self) -> Result { - Ok((*self).is_high()) - } - - fn is_low(&mut self) -> Result { - Ok((*self).is_low()) - } - } - - impl embedded_hal_old::digital::v2::InputPin for $PXi> { - type Error = (); - - fn is_high(&self) -> Result { - Ok(self.is_high()) - } - - fn is_low(&self) -> Result { - Ok(self.is_low()) - } - } - - exti!($PXi>, $Pxn, $i, $exticri); - exti!($PXi>, $Pxn, $i, $exticri); + $( + impl super::marker::IntoAf<$A> for $PXi { } + )* )+ - impl $PXx { - pub fn get_id (&self) -> u8 { - self.i - } - } } pub use $gpiox::{ $($PXi,)+ }; } } - -gpio!(GPIOA, gpioa, gpioaen, PA, 0, [ - PA0: (pa0, 0, exticr1), - PA1: (pa1, 1, exticr1), - PA2: (pa2, 2, exticr1), - PA3: (pa3, 3, exticr1), - PA4: (pa4, 4, exticr2), - PA5: (pa5, 5, exticr2), - PA6: (pa6, 6, exticr2), - PA7: (pa7, 7, exticr2), - PA8: (pa8, 8, exticr3), - PA9: (pa9, 9, exticr3), - PA10: (pa10, 10, exticr3), - PA11: (pa11, 11, exticr3), - PA12: (pa12, 12, exticr4), - PA13: (pa13, 13, exticr4), - PA14: (pa14, 14, exticr4), - PA15: (pa15, 15, exticr4), -]); - -gpio!(GPIOB, gpiob, gpioben, PB, 1, [ - PB0: (pb0, 0, exticr1), - PB1: (pb1, 1, exticr1), - PB2: (pb2, 2, exticr1), - PB3: (pb3, 3, exticr1), - PB4: (pb4, 4, exticr2), - PB5: (pb5, 5, exticr2), - PB6: (pb6, 6, exticr2), - PB7: (pb7, 7, exticr2), - PB8: (pb8, 8, exticr3), - PB9: (pb9, 9, exticr3), - PB10: (pb10, 10, exticr3), - PB11: (pb11, 11, exticr3), - PB12: (pb12, 12, exticr4), - PB13: (pb13, 13, exticr4), - PB14: (pb14, 14, exticr4), - PB15: (pb15, 15, exticr4), -]); - -gpio!(GPIOC, gpioc, gpiocen, PC, 2, [ - PC0: (pc0, 0, exticr1), - PC1: (pc1, 1, exticr1), - PC2: (pc2, 2, exticr1), - PC3: (pc3, 3, exticr1), - PC4: (pc4, 4, exticr2), - PC5: (pc5, 5, exticr2), - PC6: (pc6, 6, exticr2), - PC7: (pc7, 7, exticr2), - PC8: (pc8, 8, exticr3), - PC9: (pc9, 9, exticr3), - PC10: (pc10, 10, exticr3), - PC11: (pc11, 11, exticr3), - PC12: (pc12, 12, exticr4), - PC13: (pc13, 13, exticr4), - PC14: (pc14, 14, exticr4), - PC15: (pc15, 15, exticr4), -]); - -gpio!(GPIOD, gpiod, gpioden, PD, 3, [ - PD0: (pd0, 0, exticr1), - PD1: (pd1, 1, exticr1), - PD2: (pd2, 2, exticr1), - PD3: (pd3, 3, exticr1), - PD4: (pd4, 4, exticr2), - PD5: (pd5, 5, exticr2), - PD6: (pd6, 6, exticr2), - PD7: (pd7, 7, exticr2), - PD8: (pd8, 8, exticr3), - PD9: (pd9, 9, exticr3), - PD10: (pd10, 10, exticr3), - PD11: (pd11, 11, exticr3), - PD12: (pd12, 12, exticr4), - PD13: (pd13, 13, exticr4), - PD14: (pd14, 14, exticr4), - PD15: (pd15, 15, exticr4), -]); - -gpio!(GPIOE, gpioe, gpioeen, PE, 4, [ - PE0: (pe0, 0, exticr1), - PE1: (pe1, 1, exticr1), - PE2: (pe2, 2, exticr1), - PE3: (pe3, 3, exticr1), - PE4: (pe4, 4, exticr2), - PE5: (pe5, 5, exticr2), - PE6: (pe6, 6, exticr2), - PE7: (pe7, 7, exticr2), - PE8: (pe8, 8, exticr3), - PE9: (pe9, 9, exticr3), - PE10: (pe10, 10, exticr3), - PE11: (pe11, 11, exticr3), - PE12: (pe12, 12, exticr4), - PE13: (pe13, 13, exticr4), - PE14: (pe14, 14, exticr4), - PE15: (pe15, 15, exticr4), -]); - -gpio!(GPIOF, gpiof, gpiofen, PF, 5, [ - PF0: (pf0, 0, exticr1), - PF1: (pf1, 1, exticr1), - PF2: (pf2, 2, exticr1), - PF3: (pf3, 3, exticr1), - PF4: (pf4, 4, exticr2), - PF5: (pf5, 5, exticr2), - PF6: (pf6, 6, exticr2), - PF7: (pf7, 7, exticr2), - PF8: (pf8, 8, exticr3), - PF9: (pf9, 9, exticr3), - PF10: (pf10, 10, exticr3), - PF11: (pf11, 11, exticr3), - PF12: (pf12, 12, exticr4), - PF13: (pf13, 13, exticr4), - PF14: (pf14, 14, exticr4), - PF15: (pf15, 15, exticr4), -]); - -gpio!(GPIOG, gpiog, gpiogen, PG, 6, [ - PG0: (pg0, 0, exticr1), - PG1: (pg1, 1, exticr1), - PG2: (pg2, 2, exticr1), - PG3: (pg3, 3, exticr1), - PG4: (pg4, 4, exticr2), - PG5: (pg5, 5, exticr2), - PG6: (pg6, 6, exticr2), - PG7: (pg7, 7, exticr2), - PG8: (pg8, 8, exticr3), - PG9: (pg9, 9, exticr3), - PG10: (pg10, 10, exticr3), - PG11: (pg11, 11, exticr3), - PG12: (pg12, 12, exticr4), - PG13: (pg13, 13, exticr4), - PG14: (pg14, 14, exticr4), - PG15: (pg15, 15, exticr4), -]); +use gpio; + +mod g4; +pub use g4::*; + +const fn gpiox() -> *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 _, + _ => panic!("Unknown GPIO port"), + } +} diff --git a/src/gpio/convert.rs b/src/gpio/convert.rs new file mode 100644 index 00000000..2ac350c0 --- /dev/null +++ b/src/gpio/convert.rs @@ -0,0 +1,362 @@ +use super::*; +use crate::pac::gpioa::{moder::MODE as Mode, otyper::OUTPUT_TYPE as OutputType}; + +impl Input { + pub fn new( + pin: Pin, + pull: Pull, + ) -> Pin { + pin.into_mode().internal_resistor(pull) + } +} + +impl Output { + pub fn new( + mut pin: Pin, + state: PinState, + ) -> Pin + where + Self: PinMode, + { + pin._set_state(state); + pin.into_mode() + } +} + +impl Analog { + pub fn new(pin: Pin) -> Pin { + pin.into_mode() + } +} + +impl Pin> { + /// Turns pin alternate configuration pin into open drain + pub fn set_open_drain(self) -> Pin> { + self.into_mode() + } +} + +impl Pin { + /// Configures the pin to operate alternate mode + pub fn into_alternate(self) -> Pin> + where + Self: marker::IntoAf, + { + self.into_mode() + } + + /// Configures the pin to operate in alternate open drain mode + #[allow(path_statements)] + pub fn into_alternate_open_drain(self) -> Pin> + where + Self: marker::IntoAf, + { + self.into_mode() + } + + /// Configures the pin to operate as a input pin + pub fn into_input(self) -> Pin { + self.into_mode() + } + + /// Configures the pin to operate as a floating input pin + pub fn into_floating_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::None) + } + + /// Configures the pin to operate as a pulled down input pin + pub fn into_pull_down_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::Down) + } + + /// Configures the pin to operate as a pulled up input pin + pub fn into_pull_up_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::Up) + } + + /// Configures the pin to operate as an open drain output pin + /// Initial state will be low. + pub fn into_open_drain_output(self) -> Pin> { + self.into_mode() + } + + /// Configures the pin to operate as an open-drain output pin. + /// `initial_state` specifies whether the pin should be initially high or low. + pub fn into_open_drain_output_in_state( + mut self, + initial_state: PinState, + ) -> Pin> { + self._set_state(initial_state); + self.into_mode() + } + + /// Configures the pin to operate as an push pull output pin + /// Initial state will be low. + pub fn into_push_pull_output(mut self) -> Pin> { + self._set_low(); + self.into_mode() + } + + /// Configures the pin to operate as an push-pull output pin. + /// `initial_state` specifies whether the pin should be initially high or low. + pub fn into_push_pull_output_in_state( + mut self, + initial_state: PinState, + ) -> Pin> { + self._set_state(initial_state); + self.into_mode() + } + + /// Configures the pin to operate as an analog input pin + pub fn into_analog(self) -> 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 + /// ensure they use this properly. + #[inline(always)] + pub(super) fn mode(&mut self) { + change_mode!((*gpiox::

()), N); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> Pin { + self.mode::(); + Pin::new() + } +} + +macro_rules! change_mode { + ($block:expr, $N:ident) => { + unsafe { + if MODE::OTYPER != M::OTYPER { + if let Some(otyper) = M::OTYPER { + $block.otyper().modify(|_, w| w.ot($N).variant(otyper)); + } + } + + if MODE::AFR != M::AFR { + if let Some(afr) = M::AFR { + if $N < 8 { + $block.afrl().modify(|_, w| w.afr($N).bits(afr)); + } else { + $block.afrh().modify(|_, w| w.afr($N - 8).bits(afr)); + } + } + } + + if MODE::MODER != M::MODER { + if let Some(mode) = M::MODER { + $block.moder().modify(|_, w| w.moder($N).variant(mode)); + } + } + } + }; +} +use change_mode; + +use super::ErasedPin; +impl ErasedPin { + #[inline(always)] + pub(super) fn mode(&mut self) { + let n = self.pin_id(); + change_mode!(self.block(), n); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> ErasedPin { + self.mode::(); + ErasedPin::from_pin_port(self.into_pin_port()) + } +} + +use super::PartiallyErasedPin; +impl PartiallyErasedPin { + #[inline(always)] + pub(super) fn mode(&mut self) { + let n = self.pin_id(); + change_mode!((*gpiox::

()), n); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> PartiallyErasedPin { + self.mode::(); + PartiallyErasedPin::new(self.i) + } +} + +impl Pin +where + MODE: PinMode, +{ + fn with_mode(&mut self, f: F) -> R + where + M: PinMode, + F: FnOnce(&mut Pin) -> R, + { + self.mode::(); // change physical mode, without changing typestate + + // This will reset the pin back to the original mode when dropped. + // (so either when `with_mode` returns or when `f` unwinds) + let mut resetti = ResetMode::::new(); + + f(&mut resetti.pin) + } + + /// Temporarily configures this pin as a input. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_input(&mut self, f: impl FnOnce(&mut Pin) -> R) -> R { + self.with_mode(f) + } + + /// 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 Pin) -> R) -> R { + self.with_mode(f) + } + + /// 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. + /// The value of the pin after conversion is undefined. If you + /// want to control it, use `with_open_drain_output_in_state` + pub fn with_open_drain_output( + &mut self, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self.with_mode(f) + } + + /// 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. + /// Note that the new state is set slightly before conversion + /// happens. This can cause a short output glitch if switching + /// between output modes + pub fn with_open_drain_output_in_state( + &mut self, + state: PinState, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self._set_state(state); + self.with_mode(f) + } + + /// 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. + /// The value of the pin after conversion is undefined. If you + /// want to control it, use `with_push_pull_output_in_state` + pub fn with_push_pull_output( + &mut self, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self.with_mode(f) + } + + /// 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. + /// Note that the new state is set slightly before conversion + /// happens. This can cause a short output glitch if switching + /// between output modes + pub fn with_push_pull_output_in_state( + &mut self, + state: PinState, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self._set_state(state); + self.with_mode(f) + } +} + +/// Wrapper around a pin that transitions the pin to mode ORIG when dropped +struct ResetMode { + pub pin: Pin, + _mode: PhantomData, +} +impl ResetMode { + fn new() -> Self { + Self { + pin: Pin::new(), + _mode: PhantomData, + } + } +} +impl Drop + for ResetMode +{ + fn drop(&mut self) { + self.pin.mode::(); + } +} + +/// Marker trait for valid pin modes (type state). +/// +/// It can not be implemented by outside types. +pub trait PinMode: crate::Sealed { + // These constants are used to implement the pin configuration code. + // They are not part of public API. + + #[doc(hidden)] + const MODER: Option = None; + #[doc(hidden)] + const OTYPER: Option = None; + #[doc(hidden)] + const AFR: Option = None; +} + +impl crate::Sealed for Input {} +impl PinMode for Input { + const MODER: Option = Some(Mode::Input); +} + +impl crate::Sealed for Analog {} +impl PinMode for Analog { + const MODER: Option = Some(Mode::Analog); +} + +impl crate::Sealed for Output {} +impl PinMode for Output { + const MODER: Option = Some(Mode::Output); + const OTYPER: Option = Some(OutputType::OpenDrain); +} + +impl PinMode for Output { + const MODER: Option = Some(Mode::Output); + const OTYPER: Option = Some(OutputType::PushPull); +} + +impl crate::Sealed for Alternate {} +impl PinMode for Alternate { + const MODER: Option = Some(Mode::Alternate); + const OTYPER: Option = Some(OutputType::OpenDrain); + const AFR: Option = Some(A); +} + +impl PinMode for Alternate { + const MODER: Option = Some(Mode::Alternate); + const OTYPER: Option = Some(OutputType::PushPull); + const AFR: Option = Some(A); +} diff --git a/src/gpio/dynamic.rs b/src/gpio/dynamic.rs new file mode 100644 index 00000000..fbd33177 --- /dev/null +++ b/src/gpio/dynamic.rs @@ -0,0 +1,154 @@ +use super::*; +use embedded_hal::digital::ErrorKind; + +/// 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 { + /// 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, + /// Push-pull output mode + OutputPushPull, + /// Open-drain output mode + OutputOpenDrain, +} + +/// Error for [DynamicPin] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PinModeError { + /// For operations unsupported in current mode + IncorrectMode, +} + +impl embedded_hal::digital::Error for PinModeError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} + +impl Dynamic { + /// Is pin in readable mode + pub fn is_input(&self) -> bool { + use Dynamic::*; + match self { + InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true, + OutputPushPull => false, + } + } + + /// Is pin in writable mode + pub fn is_output(&self) -> bool { + use Dynamic::*; + match self { + InputFloating | InputPullUp | InputPullDown => false, + OutputPushPull | OutputOpenDrain => true, + } + } +} + +// For conversion simplify +struct Unknown; + +impl crate::Sealed for Unknown {} +impl PinMode for Unknown {} + +impl DynamicPin { + pub(super) const fn new(mode: Dynamic) -> Self { + Self { 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; + } + /// 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; + } + /// 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; + } + + /// 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) + } + } + + /// 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) + } + } + + /// Is the input pin high? + 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(Pin::::new()._is_low()) + } else { + Err(PinModeError::IncorrectMode) + } + } +} diff --git a/src/gpio/erased.rs b/src/gpio/erased.rs new file mode 100644 index 00000000..363d6d34 --- /dev/null +++ b/src/gpio/erased.rs @@ -0,0 +1,172 @@ +use super::*; + +pub use ErasedPin as AnyPin; + +/// Fully erased pin +/// +/// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +pub struct ErasedPin { + // Bits 0-3: Pin, Bits 4-7: Port + pin_port: u8, + _mode: PhantomData, +} + +impl fmt::Debug for ErasedPin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P({}{})<{}>", + self.port_id(), + self.pin_id(), + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ErasedPin { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "P({}{})<{}>", + self.port_id(), + self.pin_id(), + crate::stripped_type_name::() + ); + } +} + +impl PinExt for ErasedPin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + self.pin_port & 0x0f + } + #[inline(always)] + fn port_id(&self) -> u8 { + self.pin_port >> 4 + } +} + +impl ErasedPin { + pub(crate) fn from_pin_port(pin_port: u8) -> Self { + Self { + pin_port, + _mode: PhantomData, + } + } + pub(crate) fn into_pin_port(self) -> u8 { + self.pin_port + } + pub(crate) fn new(port: u8, pin: u8) -> Self { + Self { + pin_port: (port << 4) | pin, + _mode: PhantomData, + } + } + + /// Convert type erased pin to `Pin` with fixed type + pub fn restore(self) -> Pin { + assert_eq!(self.port_id(), P as u8 - b'A'); + assert_eq!(self.pin_id(), N); + Pin::new() + } + + #[inline] + pub(crate) fn block(&self) -> &crate::pac::gpioa::RegisterBlock { + // This function uses pointer arithmetic instead of branching to be more efficient + + // The logic relies on the following assumptions: + // - GPIOA register is available on all chips + // - all gpio register blocks have the same layout + // - consecutive gpio register blocks have the same offset between them, namely 0x0400 + // - ErasedPin::new was called with a valid port + + // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910 + 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 } + } +} + +impl ErasedPin> { + /// 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())) }; + } + + /// 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))) + }; + } + + /// 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().bits() & (1 << self.pin_id()) == 0 + } + + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } +} + +impl ErasedPin +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.block().idr().read().bits() & (1 << self.pin_id()) == 0 + } +} diff --git a/src/gpio/exti.rs b/src/gpio/exti.rs new file mode 100644 index 00000000..c63e93cb --- /dev/null +++ b/src/gpio/exti.rs @@ -0,0 +1,116 @@ +use super::{marker, Pin, PinExt, SignalEdge}; +use crate::exti::{Event, ExtiExt}; +use crate::pac::EXTI; +use crate::syscfg::SysCfg; + +/// External Interrupt Pin +pub trait ExtiPin { + /// Make corresponding EXTI line sensitive to this pin + fn make_interrupt_source(&mut self, syscfg: &mut SysCfg); + + /// Generate interrupt on rising edge, falling edge or both + fn trigger_on_edge(&mut self, exti: &mut EXTI, level: SignalEdge); + + /// Enable external interrupts from this pin. + fn enable_interrupt(&mut self, exti: &mut EXTI); + + /// Disable external interrupts from this pin + fn disable_interrupt(&mut self, exti: &mut EXTI); + + /// Clear the interrupt pending bit for this pin + fn clear_interrupt_pending_bit(&mut self); + + /// Reads the interrupt pending bit for this pin + fn check_interrupt(&self) -> bool; +} + +impl Pin { + /// Configures the pin as external trigger + pub fn listen(self, edge: SignalEdge, exti: &mut EXTI) -> Self { + exti.listen(Event::from_code(self.pin_id()), edge); + self + } +} + +impl ExtiPin for PIN +where + PIN: PinExt, + PIN::Mode: marker::Interruptible, +{ + #[inline(always)] + fn make_interrupt_source(&mut self, syscfg: &mut SysCfg) { + let i = self.pin_id(); + let port = self.port_id() as u32; + let offset = 4 * (i % 4); + match i { + 0..=3 => { + syscfg.exticr1().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | (port << offset)) + }); + } + 4..=7 => { + syscfg.exticr2().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | (port << offset)) + }); + } + 8..=11 => { + syscfg.exticr3().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | (port << offset)) + }); + } + 12..=15 => { + syscfg.exticr4().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | (port << offset)) + }); + } + _ => unreachable!(), + } + } + + #[inline(always)] + fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: SignalEdge) { + let i = self.pin_id(); + match edge { + SignalEdge::Rising => { + exti.rtsr1() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) }); + exti.ftsr1() + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) }); + } + SignalEdge::Falling => { + exti.ftsr1() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) }); + exti.rtsr1() + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) }); + } + SignalEdge::RisingFalling => { + exti.rtsr1() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) }); + exti.ftsr1() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) }); + } + } + } + + #[inline(always)] + fn enable_interrupt(&mut self, exti: &mut EXTI) { + exti.imr1() + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin_id())) }); + } + + #[inline(always)] + fn disable_interrupt(&mut self, exti: &mut EXTI) { + exti.imr1() + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id())) }); + } + + #[inline(always)] + fn clear_interrupt_pending_bit(&mut self) { + unsafe { (*EXTI::ptr()).pr1().write(|w| w.bits(1 << self.pin_id())) }; + } + + #[inline(always)] + fn check_interrupt(&self) -> bool { + unsafe { ((*EXTI::ptr()).pr1().read().bits() & (1 << self.pin_id())) != 0 } + } +} diff --git a/src/gpio/g4.rs b/src/gpio/g4.rs new file mode 100644 index 00000000..6ddc3366 --- /dev/null +++ b/src/gpio/g4.rs @@ -0,0 +1,369 @@ +use super::*; + +pub use super::Analog as DefaultMode; + +// auto-generated using codegen +// STM32CubeMX DB release: DB.6.0.130 + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOA, gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [1, 7, 8, 9, 10, 14, 15]), + PA1: (pa1, 1, [0, 1, 7, 9, 15]), + PA2: (pa2, 2, [1, 7, 8, 9, 12, 14, 15]), + PA3: (pa3, 3, [1, 3, 7, 9, 12, 13, 15]), + PA4: (pa4, 4, [2, 5, 6, 7, 13, 15]), + PA5: (pa5, 5, [1, 2, 5, 14, 15]), + PA6: (pa6, 6, [1, 2, 4, 5, 6, 8, 12, 15]), + PA7: (pa7, 7, [1, 2, 4, 5, 6, 8, 14, 15]), + PA8: (pa8, 8, [0, 2, 4, 5, 6, 7, 10, 12, 14, 15]), + PA9: (pa9, 9, [2, 4, 5, 6, 7, 9, 10, 14, 15]), + PA10: (pa10, 10, [1, 3, 4, 5, 6, 7, 10, 11, 12, 14, 15]), + PA11: (pa11, 11, [5, 6, 7, 8, 9, 10, 11, 12, 14, 15]), + PA12: (pa12, 12, [1, 5, 6, 7, 8, 9, 10, 11, 14, 15]), + PA13: (pa13, 13, [0, 1, 4, 5, 7, 10, 13, 15], super::Debugger), + PA14: (pa14, 14, [0, 1, 4, 5, 6, 7, 13, 15], super::Debugger), + PA15: (pa15, 15, [0, 1, 2, 4, 5, 6, 7, 8, 9, 14, 15], super::Debugger), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOB, gpiob, PB, 'B', PBn, [ + PB0: (pb0, 0, [2, 4, 6, 14, 15]), + PB1: (pb1, 1, [2, 4, 6, 8, 12, 15]), + PB2: (pb2, 2, [0, 1, 4, 15]), + PB3: (pb3, 3, [0, 1, 2, 3, 4, 5, 6, 7, 10, 14, 15], super::Debugger), + PB4: (pb4, 4, [0, 1, 2, 4, 5, 6, 7, 10, 14, 15], super::Debugger), + PB5: (pb5, 5, [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15]), + PB6: (pb6, 6, [1, 2, 5, 6, 7, 8, 10, 11, 14, 15]), + PB7: (pb7, 7, [1, 2, 4, 5, 7, 8, 10, 11, 14, 15]), + PB8: (pb8, 8, [1, 2, 3, 4, 7, 8, 9, 10, 12, 14, 15]), + PB9: (pb9, 9, [1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 14, 15]), + PB10: (pb10, 10, [1, 7, 8, 12, 14, 15]), + PB11: (pb11, 11, [1, 7, 8, 15]), + PB12: (pb12, 12, [4, 5, 6, 7, 8, 15]), + PB13: (pb13, 13, [5, 6, 7, 8, 15]), + PB14: (pb14, 14, [1, 5, 6, 7, 8, 15]), + PB15: (pb15, 15, [0, 1, 2, 3, 4, 5, 15]), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOC, gpioc, PC, 'C', PCn, [ + PC0: (pc0, 0, [1, 2, 8, 15]), + PC1: (pc1, 1, [1, 2, 8, 13, 15]), + PC2: (pc2, 2, [1, 2, 3, 15]), + PC3: (pc3, 3, [1, 2, 3, 6, 13, 15]), + PC4: (pc4, 4, [2, 4, 7, 15]), + PC5: (pc5, 5, [2, 3, 6, 7, 15]), + PC6: (pc6, 6, [2, 4, 6, 15]), + PC7: (pc7, 7, [2, 4, 6, 15]), + PC8: (pc8, 8, [2, 4, 8, 15]), + PC9: (pc9, 9, [2, 4, 5, 6, 8, 15]), + PC10: (pc10, 10, [4, 5, 6, 7, 15]), + PC11: (pc11, 11, [4, 5, 6, 7, 8, 15]), + PC12: (pc12, 12, [4, 6, 7, 14, 15]), + PC13: (pc13, 13, [2, 4, 6, 15]), + PC14: (pc14, 14, [15]), + PC15: (pc15, 15, [15]), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOD, gpiod, PD, 'D', PDn, [ + PD0: (pd0, 0, [6, 9, 15]), + PD1: (pd1, 1, [4, 6, 9, 15]), + PD2: (pd2, 2, [2, 4, 15]), + PD3: (pd3, 3, [2, 7, 15]), + PD4: (pd4, 4, [2, 7, 15]), + PD5: (pd5, 5, [7, 15]), + PD6: (pd6, 6, [2, 3, 7, 13, 15]), + PD7: (pd7, 7, [2, 7, 15]), + PD8: (pd8, 8, [7, 15]), + PD9: (pd9, 9, [7, 15]), + PD10: (pd10, 10, [7, 15]), + PD11: (pd11, 11, [7, 15]), + PD12: (pd12, 12, [2, 7, 15]), + PD13: (pd13, 13, [2, 15]), + PD14: (pd14, 14, [2, 15]), + PD15: (pd15, 15, [2, 6, 15]), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOE, gpioe, PE, 'E', PEn, [ + PE0: (pe0, 0, [2, 4, 7, 15]), + PE1: (pe1, 1, [4, 7, 15]), + PE2: (pe2, 2, [0, 2, 3, 13, 15]), + PE3: (pe3, 3, [0, 2, 13, 15]), + PE4: (pe4, 4, [0, 2, 3, 13, 15]), + PE5: (pe5, 5, [0, 2, 3, 13, 15]), + PE6: (pe6, 6, [0, 3, 13, 15]), + PE7: (pe7, 7, [2, 13, 15]), + PE8: (pe8, 8, [2, 13, 15]), + PE9: (pe9, 9, [2, 13, 15]), + PE10: (pe10, 10, [2, 13, 15]), + PE11: (pe11, 11, [2, 15]), + PE12: (pe12, 12, [2, 15]), + PE13: (pe13, 13, [2, 15]), + PE14: (pe14, 14, [2, 6, 15]), + PE15: (pe15, 15, [2, 6, 7, 15]), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOF, gpiof, PF, 'F', PFn, [ + PF0: (pf0, 0, [4, 5, 6, 15]), + PF1: (pf1, 1, [5, 15]), + PF2: (pf2, 2, [4, 15]), + PF9: (pf9, 9, [3, 5, 13, 15]), + PF10: (pf10, 10, [3, 5, 13, 15]), +]); + +#[cfg(feature = "gpio-g43x")] +gpio!(GPIOG, gpiog, PG, 'G', PGn, [ + PG10: (pg10, 10, [0, 15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOA, gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [1, 2, 7, 8, 9, 10, 14, 15]), + PA1: (pa1, 1, [0, 1, 2, 7, 9, 15]), + PA2: (pa2, 2, [1, 2, 7, 8, 9, 10, 12, 14, 15]), + PA3: (pa3, 3, [1, 2, 3, 7, 9, 10, 12, 13, 15]), + PA4: (pa4, 4, [2, 5, 6, 7, 13, 15]), + PA5: (pa5, 5, [1, 2, 5, 14, 15]), + PA6: (pa6, 6, [1, 2, 4, 5, 6, 8, 10, 12, 15]), + PA7: (pa7, 7, [1, 2, 4, 5, 6, 8, 10, 14, 15]), + PA8: (pa8, 8, [0, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]), + PA9: (pa9, 9, [2, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15]), + PA10: (pa10, 10, [1, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]), + PA11: (pa11, 11, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PA12: (pa12, 12, [1, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15]), + PA13: (pa13, 13, [0, 1, 3, 4, 5, 7, 10, 13, 15], super::Debugger), + PA14: (pa14, 14, [0, 1, 3, 4, 5, 6, 7, 13, 15], super::Debugger), + PA15: (pa15, 15, [0, 1, 2, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15], super::Debugger), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOB, gpiob, PB, 'B', PBn, [ + PB0: (pb0, 0, [2, 4, 6, 10, 13, 14, 15]), + PB1: (pb1, 1, [2, 4, 6, 8, 10, 12, 13, 15]), + PB2: (pb2, 2, [0, 1, 2, 3, 4, 10, 13, 15]), + PB3: (pb3, 3, [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15], super::Debugger), + PB4: (pb4, 4, [0, 1, 2, 4, 5, 6, 7, 8, 10, 11, 13, 14, 15], super::Debugger), + PB5: (pb5, 5, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PB6: (pb6, 6, [1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PB7: (pb7, 7, [1, 2, 3, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15]), + PB8: (pb8, 8, [1, 2, 3, 4, 7, 8, 9, 10, 12, 13, 14, 15]), + PB9: (pb9, 9, [1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 13, 14, 15]), + PB10: (pb10, 10, [1, 7, 8, 10, 12, 13, 14, 15]), + PB11: (pb11, 11, [1, 7, 8, 10, 13, 15]), + PB12: (pb12, 12, [2, 4, 5, 6, 7, 8, 9, 13, 15]), + PB13: (pb13, 13, [5, 6, 7, 8, 9, 13, 15]), + PB14: (pb14, 14, [1, 5, 6, 7, 8, 13, 15]), + PB15: (pb15, 15, [0, 1, 2, 3, 4, 5, 13, 15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOC, gpioc, PC, 'C', PCn, [ + PC0: (pc0, 0, [1, 2, 8, 15]), + PC1: (pc1, 1, [1, 2, 8, 10, 13, 15]), + PC2: (pc2, 2, [1, 2, 3, 6, 10, 15]), + PC3: (pc3, 3, [0, 1, 2, 3, 6, 10, 13, 15]), + PC4: (pc4, 4, [2, 4, 7, 10, 15]), + PC5: (pc5, 5, [2, 3, 6, 7, 13, 15]), + PC6: (pc6, 6, [2, 3, 4, 6, 7, 8, 13, 15]), + PC7: (pc7, 7, [2, 3, 4, 6, 7, 8, 13, 15]), + PC8: (pc8, 8, [2, 3, 4, 6, 7, 8, 15]), + PC9: (pc9, 9, [2, 3, 4, 5, 6, 8, 15]), + PC10: (pc10, 10, [4, 5, 6, 7, 13, 15]), + PC11: (pc11, 11, [3, 4, 5, 6, 7, 8, 15]), + PC12: (pc12, 12, [1, 3, 4, 5, 6, 7, 14, 15]), + PC13: (pc13, 13, [2, 4, 6, 15]), + PC14: (pc14, 14, [15]), + PC15: (pc15, 15, [15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOD, gpiod, PD, 'D', PDn, [ + PD0: (pd0, 0, [6, 9, 12, 15]), + PD1: (pd1, 1, [4, 6, 9, 12, 15]), + PD2: (pd2, 2, [2, 4, 5, 15]), + PD3: (pd3, 3, [2, 7, 10, 12, 15]), + PD4: (pd4, 4, [2, 7, 10, 12, 15]), + PD5: (pd5, 5, [7, 10, 12, 15]), + PD6: (pd6, 6, [2, 3, 7, 10, 12, 13, 15]), + PD7: (pd7, 7, [2, 7, 10, 12, 15]), + PD8: (pd8, 8, [7, 12, 15]), + PD9: (pd9, 9, [7, 12, 15]), + PD10: (pd10, 10, [7, 12, 15]), + PD11: (pd11, 11, [1, 4, 7, 12, 15]), + PD12: (pd12, 12, [2, 7, 12, 15]), + PD13: (pd13, 13, [2, 12, 15]), + PD14: (pd14, 14, [2, 12, 15]), + PD15: (pd15, 15, [2, 6, 12, 15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOE, gpioe, PE, 'E', PEn, [ + PE0: (pe0, 0, [2, 3, 4, 6, 7, 12, 15]), + PE1: (pe1, 1, [4, 6, 7, 12, 15]), + PE2: (pe2, 2, [0, 2, 3, 5, 6, 12, 13, 15]), + PE3: (pe3, 3, [0, 2, 5, 6, 12, 13, 15]), + PE4: (pe4, 4, [0, 2, 3, 5, 6, 12, 13, 15]), + PE5: (pe5, 5, [0, 2, 3, 5, 6, 12, 13, 15]), + PE6: (pe6, 6, [0, 3, 5, 6, 12, 13, 15]), + PE7: (pe7, 7, [2, 12, 13, 15]), + PE8: (pe8, 8, [1, 2, 12, 13, 15]), + PE9: (pe9, 9, [1, 2, 12, 13, 15]), + PE10: (pe10, 10, [2, 10, 12, 13, 15]), + PE11: (pe11, 11, [2, 5, 10, 12, 15]), + PE12: (pe12, 12, [2, 5, 10, 12, 15]), + PE13: (pe13, 13, [2, 5, 10, 12, 15]), + PE14: (pe14, 14, [2, 5, 6, 10, 12, 15]), + PE15: (pe15, 15, [2, 6, 7, 10, 12, 15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOF, gpiof, PF, 'F', PFn, [ + PF0: (pf0, 0, [4, 5, 6, 15]), + PF1: (pf1, 1, [5, 15]), + PF2: (pf2, 2, [2, 4, 12, 15]), + PF3: (pf3, 3, [2, 4, 12, 15]), + PF4: (pf4, 4, [2, 3, 4, 12, 15]), + PF5: (pf5, 5, [2, 12, 15]), + PF6: (pf6, 6, [1, 2, 3, 4, 6, 7, 10, 15]), + PF7: (pf7, 7, [2, 6, 10, 12, 13, 15]), + PF8: (pf8, 8, [2, 6, 10, 12, 13, 15]), + PF9: (pf9, 9, [2, 3, 5, 6, 10, 12, 13, 15]), + PF10: (pf10, 10, [2, 3, 5, 10, 12, 13, 15]), + PF11: (pf11, 11, [2, 12, 15]), + PF12: (pf12, 12, [2, 12, 15]), + PF13: (pf13, 13, [2, 4, 12, 15]), + PF14: (pf14, 14, [2, 4, 12, 15]), + PF15: (pf15, 15, [2, 4, 12, 15]), +]); + +#[cfg(feature = "gpio-g47x")] +gpio!(GPIOG, gpiog, PG, 'G', PGn, [ + PG0: (pg0, 0, [2, 12, 15]), + PG1: (pg1, 1, [2, 12, 15]), + PG2: (pg2, 2, [2, 5, 12, 15]), + PG3: (pg3, 3, [2, 4, 5, 6, 12, 15]), + PG4: (pg4, 4, [2, 4, 5, 12, 15]), + PG5: (pg5, 5, [2, 5, 8, 12, 15]), + PG6: (pg6, 6, [2, 4, 8, 12, 15]), + PG7: (pg7, 7, [3, 4, 8, 12, 13, 15]), + PG8: (pg8, 8, [4, 8, 12, 15]), + PG9: (pg9, 9, [6, 7, 12, 14, 15]), + PG10: (pg10, 10, [0, 15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOA, gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [1, 7, 8, 9, 10, 14, 15]), + PA1: (pa1, 1, [0, 1, 7, 9, 15]), + PA2: (pa2, 2, [1, 7, 8, 9, 10, 12, 14, 15]), + PA3: (pa3, 3, [1, 3, 7, 9, 10, 12, 13, 15]), + PA4: (pa4, 4, [2, 5, 6, 7, 13, 15]), + PA5: (pa5, 5, [1, 2, 5, 14, 15]), + PA6: (pa6, 6, [1, 2, 4, 5, 6, 8, 10, 12, 15]), + PA7: (pa7, 7, [1, 2, 4, 5, 6, 8, 10, 14, 15]), + PA8: (pa8, 8, [0, 2, 4, 5, 6, 7, 10, 12, 14, 15]), + PA9: (pa9, 9, [2, 4, 5, 6, 7, 9, 10, 14, 15]), + PA10: (pa10, 10, [1, 3, 4, 5, 6, 7, 10, 11, 12, 14, 15]), + PA11: (pa11, 11, [5, 6, 7, 8, 9, 10, 11, 12, 14, 15]), + PA12: (pa12, 12, [1, 5, 6, 7, 8, 9, 10, 11, 14, 15]), + PA13: (pa13, 13, [0, 1, 4, 5, 7, 10, 13, 15], super::Debugger), + PA14: (pa14, 14, [0, 1, 4, 5, 6, 7, 13, 15], super::Debugger), + PA15: (pa15, 15, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15], super::Debugger), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOB, gpiob, PB, 'B', PBn, [ + PB0: (pb0, 0, [2, 4, 6, 10, 14, 15]), + PB1: (pb1, 1, [2, 4, 6, 8, 10, 12, 15]), + PB2: (pb2, 2, [0, 1, 3, 4, 10, 15]), + PB3: (pb3, 3, [0, 1, 2, 3, 4, 5, 6, 7, 10, 14, 15], super::Debugger), + PB4: (pb4, 4, [0, 1, 2, 4, 5, 6, 7, 8, 10, 14, 15], super::Debugger), + PB5: (pb5, 5, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15]), + PB6: (pb6, 6, [1, 2, 5, 6, 7, 8, 9, 10, 11, 14, 15]), + PB7: (pb7, 7, [1, 2, 4, 5, 7, 8, 10, 11, 14, 15]), + PB8: (pb8, 8, [1, 2, 3, 4, 7, 8, 9, 10, 12, 14, 15]), + PB9: (pb9, 9, [1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 14, 15]), + PB10: (pb10, 10, [1, 7, 8, 10, 12, 14, 15]), + PB11: (pb11, 11, [1, 7, 8, 10, 15]), + PB12: (pb12, 12, [4, 5, 6, 7, 8, 9, 15]), + PB13: (pb13, 13, [5, 6, 7, 8, 9, 15]), + PB14: (pb14, 14, [1, 5, 6, 7, 8, 15]), + PB15: (pb15, 15, [0, 1, 2, 3, 4, 5, 15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOC, gpioc, PC, 'C', PCn, [ + PC0: (pc0, 0, [1, 2, 8, 15]), + PC1: (pc1, 1, [1, 2, 8, 10, 13, 15]), + PC2: (pc2, 2, [1, 2, 3, 6, 10, 15]), + PC3: (pc3, 3, [1, 2, 3, 6, 10, 13, 15]), + PC4: (pc4, 4, [2, 4, 7, 10, 15]), + PC5: (pc5, 5, [2, 3, 6, 7, 15]), + PC6: (pc6, 6, [2, 4, 6, 15]), + PC7: (pc7, 7, [2, 4, 6, 15]), + PC8: (pc8, 8, [2, 4, 6, 8, 15]), + PC9: (pc9, 9, [2, 4, 5, 6, 8, 15]), + PC10: (pc10, 10, [4, 5, 6, 7, 15]), + PC11: (pc11, 11, [4, 5, 6, 7, 8, 15]), + PC12: (pc12, 12, [4, 5, 6, 7, 14, 15]), + PC13: (pc13, 13, [2, 4, 6, 15]), + PC14: (pc14, 14, [15]), + PC15: (pc15, 15, [15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOD, gpiod, PD, 'D', PDn, [ + PD0: (pd0, 0, [6, 9, 15]), + PD1: (pd1, 1, [4, 6, 9, 15]), + PD2: (pd2, 2, [2, 4, 5, 15]), + PD3: (pd3, 3, [2, 7, 10, 15]), + PD4: (pd4, 4, [2, 7, 10, 15]), + PD5: (pd5, 5, [7, 10, 15]), + PD6: (pd6, 6, [2, 3, 7, 10, 13, 15]), + PD7: (pd7, 7, [2, 7, 10, 15]), + PD8: (pd8, 8, [7, 15]), + PD9: (pd9, 9, [7, 15]), + PD10: (pd10, 10, [7, 15]), + PD11: (pd11, 11, [7, 15]), + PD12: (pd12, 12, [2, 7, 15]), + PD13: (pd13, 13, [2, 15]), + PD14: (pd14, 14, [2, 15]), + PD15: (pd15, 15, [2, 6, 15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOE, gpioe, PE, 'E', PEn, [ + PE0: (pe0, 0, [2, 3, 4, 6, 7, 15]), + PE1: (pe1, 1, [4, 6, 7, 15]), + PE2: (pe2, 2, [0, 2, 3, 6, 13, 15]), + PE3: (pe3, 3, [0, 2, 6, 13, 15]), + PE4: (pe4, 4, [0, 2, 3, 6, 13, 15]), + PE5: (pe5, 5, [0, 2, 3, 6, 13, 15]), + PE6: (pe6, 6, [0, 3, 6, 13, 15]), + PE7: (pe7, 7, [2, 13, 15]), + PE8: (pe8, 8, [2, 13, 15]), + PE9: (pe9, 9, [2, 13, 15]), + PE10: (pe10, 10, [2, 10, 13, 15]), + PE11: (pe11, 11, [2, 10, 15]), + PE12: (pe12, 12, [2, 10, 15]), + PE13: (pe13, 13, [2, 10, 15]), + PE14: (pe14, 14, [2, 6, 10, 15]), + PE15: (pe15, 15, [2, 6, 7, 10, 15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOF, gpiof, PF, 'F', PFn, [ + PF0: (pf0, 0, [4, 5, 6, 15]), + PF1: (pf1, 1, [5, 15]), + PF2: (pf2, 2, [2, 4, 15]), + PF9: (pf9, 9, [2, 3, 5, 10, 13, 15]), + PF10: (pf10, 10, [2, 3, 5, 10, 13, 15]), +]); + +#[cfg(feature = "gpio-g49x")] +gpio!(GPIOG, gpiog, PG, 'G', PGn, [ + PG10: (pg10, 10, [0, 15]), +]); diff --git a/src/gpio/hal_02.rs b/src/gpio/hal_02.rs new file mode 100644 index 00000000..25026fa5 --- /dev/null +++ b/src/gpio/hal_02.rs @@ -0,0 +1,242 @@ +use core::convert::Infallible; + +use super::{ + dynamic::PinModeError, marker, DynamicPin, ErasedPin, Input, OpenDrain, Output, + PartiallyErasedPin, Pin, PinMode, PinState, +}; + +use embedded_hal_old::digital::v2::{ + InputPin, IoPin, OutputPin, StatefulOutputPin, ToggleableOutputPin, +}; + +// Implementations for `Pin` + +impl OutputPin for Pin> { + type Error = Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for Pin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for Pin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for Pin +where + MODE: marker::Readable, +{ + type Error = Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +impl IoPin for Pin> { + type Error = Infallible; + fn into_input_pin(self) -> Result { + Ok(self) + } + fn into_output_pin(mut self, state: PinState) -> Result { + self.set_state(state); + Ok(self) + } +} + +impl IoPin, Self> for Pin> +where + Output: PinMode, +{ + type Error = Infallible; + fn into_input_pin(self) -> Result, Self::Error> { + Ok(self.into_input()) + } + fn into_output_pin(mut self, state: PinState) -> Result { + self.set_state(state); + Ok(self) + } +} + +impl IoPin>> for Pin +where + Output: PinMode, +{ + type Error = Infallible; + fn into_input_pin(self) -> Result { + Ok(self) + } + fn into_output_pin(mut self, state: PinState) -> Result>, Self::Error> { + self._set_state(state); + Ok(self.into_mode()) + } +} + +// Implementations for `ErasedPin` + +impl OutputPin for ErasedPin> { + type Error = core::convert::Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for ErasedPin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for ErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for ErasedPin +where + MODE: marker::Readable, +{ + type Error = core::convert::Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +// Implementations for `PartiallyErasedPin` + +impl OutputPin for PartiallyErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for PartiallyErasedPin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for PartiallyErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for PartiallyErasedPin +where + MODE: marker::Readable, +{ + type Error = Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +// Implementations for `DynamicPin` + +impl OutputPin for DynamicPin { + type Error = PinModeError; + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high() + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low() + } +} + +impl InputPin for DynamicPin { + type Error = PinModeError; + fn is_high(&self) -> Result { + self.is_high() + } + fn is_low(&self) -> Result { + self.is_low() + } +} diff --git a/src/gpio/hal_1.rs b/src/gpio/hal_1.rs new file mode 100644 index 00000000..11db87cb --- /dev/null +++ b/src/gpio/hal_1.rs @@ -0,0 +1,168 @@ +use core::convert::Infallible; + +use super::{ + dynamic::PinModeError, marker, DynamicPin, ErasedPin, Output, PartiallyErasedPin, Pin, +}; + +use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; + +// Implementations for `Pin` +impl ErrorType for Pin { + type Error = Infallible; +} + +impl OutputPin for Pin> { + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for Pin> { + #[inline(always)] + fn is_set_high(&mut self) -> Result { + Ok(Self::is_set_high(self)) + } + + #[inline(always)] + fn is_set_low(&mut self) -> Result { + Ok(Self::is_set_low(self)) + } +} + +impl InputPin for Pin +where + MODE: marker::Readable, +{ + #[inline(always)] + fn is_high(&mut self) -> Result { + Ok(Self::is_high(self)) + } + + #[inline(always)] + fn is_low(&mut self) -> Result { + Ok(Self::is_low(self)) + } +} + +// Implementations for `ErasedPin` +impl ErrorType for ErasedPin { + type Error = core::convert::Infallible; +} + +impl OutputPin for ErasedPin> { + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for ErasedPin> { + #[inline(always)] + fn is_set_high(&mut self) -> Result { + Ok(Self::is_set_high(self)) + } + + #[inline(always)] + fn is_set_low(&mut self) -> Result { + Ok(Self::is_set_low(self)) + } +} + +impl InputPin for ErasedPin +where + MODE: marker::Readable, +{ + #[inline(always)] + fn is_high(&mut self) -> Result { + Ok(Self::is_high(self)) + } + + #[inline(always)] + fn is_low(&mut self) -> Result { + Ok(Self::is_low(self)) + } +} + +// Implementations for `PartiallyErasedPin` +impl ErrorType for PartiallyErasedPin { + type Error = Infallible; +} + +impl OutputPin for PartiallyErasedPin> { + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for PartiallyErasedPin> { + #[inline(always)] + fn is_set_high(&mut self) -> Result { + Ok(Self::is_set_high(self)) + } + + #[inline(always)] + fn is_set_low(&mut self) -> Result { + Ok(Self::is_set_low(self)) + } +} + +impl InputPin for PartiallyErasedPin +where + MODE: marker::Readable, +{ + #[inline(always)] + fn is_high(&mut self) -> Result { + Ok(Self::is_high(self)) + } + + #[inline(always)] + fn is_low(&mut self) -> Result { + Ok(Self::is_low(self)) + } +} + +// Implementations for `DynamicPin +impl ErrorType for DynamicPin { + type Error = PinModeError; +} + +impl OutputPin for DynamicPin { + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high() + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low() + } +} + +impl InputPin for DynamicPin { + fn is_high(&mut self) -> Result { + Self::is_high(self) + } + fn is_low(&mut self) -> Result { + Self::is_low(self) + } +} diff --git a/src/gpio/outport.rs b/src/gpio/outport.rs new file mode 100644 index 00000000..7373b0db --- /dev/null +++ b/src/gpio/outport.rs @@ -0,0 +1,108 @@ +use super::*; + +/// Convert tuple or array of pins to output port +pub trait OutPort { + type Target; + fn outport(self) -> Self::Target; +} + +macro_rules! out_port { + ( $name:ident => $n:literal, ( $($i:tt),+ ), ( $($N:ident),+ )) => { + pub struct $name ( + $(pub Pin>,)+ + ); + + impl OutPort for ($(Pin>),+) { + type Target = $name

; + fn outport(self) -> Self::Target { + $name($(self.$i),+) + } + } + + /// Wrapper for tuple of `Pin`s + impl $name

{ + const fn mask() -> u32 { + 0 $( | (1 << { $N }))+ + } + const fn value_for_write_bsrr(val: u32) -> u32 { + 0 $( | (1 << (if val & (1 << $i) != 0 { $N } else { $N + 16 })))+ + } + + #[doc=concat!("Set/reset pins according to `", $n, "` lower bits")] + #[inline(never)] + pub fn write(&mut self, word: u32) { + unsafe { (*gpiox::

()).bsrr().write(|w| w.bits(Self::value_for_write_bsrr(word))); } + } + + /// Set all pins to `PinState::High` + pub fn all_high(&mut self) { + unsafe { (*gpiox::

()).bsrr().write(|w| w.bits(Self::mask())); } + } + + /// Reset all pins to `PinState::Low` + pub fn all_low(&mut self) { + unsafe { (*gpiox::

()).bsrr().write(|w| w.bits(Self::mask() << 16)); } + } + } + } +} + +out_port!(OutPort2 => 2, (0, 1), (N0, N1)); +out_port!(OutPort3 => 3, (0, 1, 2), (N0, N1, N2)); +out_port!(OutPort4 => 4, (0, 1, 2, 3), (N0, N1, N2, N3)); +out_port!(OutPort5 => 5, (0, 1, 2, 3, 4), (N0, N1, N2, N3, N4)); +out_port!(OutPort6 => 6, (0, 1, 2, 3, 4, 5), (N0, N1, N2, N3, N4, N5)); +out_port!(OutPort7 => 7, (0, 1, 2, 3, 4, 5, 6), (N0, N1, N2, N3, N4, N5, N6)); +out_port!(OutPort8 => 8, (0, 1, 2, 3, 4, 5, 6, 7), (N0, N1, N2, N3, N4, N5, N6, N7)); + +/// Wrapper for array of `PartiallyErasedPin`s +pub struct OutPortArray(pub [PEPin>; SIZE]); + +impl OutPort for [PEPin>; SIZE] { + type Target = OutPortArray; + fn outport(self) -> Self::Target { + OutPortArray(self) + } +} + +impl OutPortArray { + fn mask(&self) -> u32 { + let mut msk = 0; + for pin in self.0.iter() { + msk |= 1 << pin.i; + } + msk + } + fn value_for_write_bsrr(&self, val: u32) -> u32 { + let mut msk = 0; + for (idx, pin) in self.0.iter().enumerate() { + let n = pin.i; + msk |= 1 << (if val & (1 << idx) != 0 { n } else { n + 16 }); + } + msk + } + + /// Set/reset pins according to `SIZE` lower bits + #[inline(never)] + pub fn write(&mut self, word: u32) { + unsafe { + (*gpiox::

()) + .bsrr() + .write(|w| w.bits(self.value_for_write_bsrr(word))); + } + } + + /// Set all pins to `PinState::High` + pub fn all_high(&mut self) { + unsafe { + (*gpiox::

()).bsrr().write(|w| w.bits(self.mask())); + } + } + + /// Reset all pins to `PinState::Low` + pub fn all_low(&mut self) { + unsafe { + (*gpiox::

()).bsrr().write(|w| w.bits(self.mask() << 16)); + } + } +} diff --git a/src/gpio/partially_erased.rs b/src/gpio/partially_erased.rs new file mode 100644 index 00000000..812ea2c0 --- /dev/null +++ b/src/gpio/partially_erased.rs @@ -0,0 +1,153 @@ +use super::*; + +pub use PartiallyErasedPin as PEPin; + +/// Partially erased pin +/// +/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. +pub struct PartiallyErasedPin { + pub(crate) i: u8, + _mode: PhantomData, +} + +impl PartiallyErasedPin { + pub(crate) fn new(i: u8) -> Self { + Self { + i, + _mode: PhantomData, + } + } + + /// Convert partially type erased pin to `Pin` with fixed type + pub fn restore(self) -> Pin { + assert_eq!(self.i, N); + Pin::new() + } +} + +impl fmt::Debug for PartiallyErasedPin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P{}({})<{}>", + P, + self.i, + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for PartiallyErasedPin { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "P{}({})<{}>", + P, + self.i, + crate::stripped_type_name::() + ); + } +} + +impl PinExt for PartiallyErasedPin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + self.i + } + #[inline(always)] + fn port_id(&self) -> u8 { + P as u8 - b'A' + } +} + +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.bits(1 << self.i)); + } + } + + /// 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))); + } + } + + /// 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 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*gpiox::

()).odr().read().bits() & (1 << self.i) == 0 } + } + + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } +} + +impl PartiallyErasedPin +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 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*gpiox::

()).idr().read().bits() & (1 << self.i) == 0 } + } +} + +impl From> for ErasedPin { + /// 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) + } +} diff --git a/src/lib.rs b/src/lib.rs index 470aa878..241cabf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,3 +117,9 @@ impl Ptr for Periph { Self::ptr() } } + +fn stripped_type_name() -> &'static str { + let s = core::any::type_name::(); + let p = s.split("::"); + p.last().unwrap() +} diff --git a/src/rcc/clockout.rs b/src/rcc/clockout.rs index 1be1e390..3cd85cf1 100644 --- a/src/rcc/clockout.rs +++ b/src/rcc/clockout.rs @@ -2,7 +2,7 @@ use crate::gpio::*; use crate::rcc::*; use crate::stm32::RCC; -pub type LscoPin = gpioa::PA2; +pub type LscoPin = gpioa::PA2; pub struct Lsco { pin: gpioa::PA2, @@ -20,7 +20,7 @@ impl Lsco { } pub fn release(self) -> LscoPin { - self.pin.into_floating_input() + self.pin.into_analog() } } @@ -43,7 +43,7 @@ impl LSCOExt for LscoPin { }; rcc.rb.bdcr().modify(|_, w| w.lscosel().bit(src_select_bit)); Lsco { - pin: self.into_alternate(), + pin: self.into_mode(), } } } From e3081033d0201bfc13c4d4fe2559028d8ca0bd13 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Wed, 28 May 2025 19:22:03 +0300 Subject: [PATCH 2/4] fix DAC --- src/dac.rs | 14 +++++++------- tests/nucleo-g474.rs | 2 +- tests/nucleo-g474_w_jumpers.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dac.rs b/src/dac.rs index 9571cd3a..d253b24d 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -9,7 +9,7 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::Deref; -use crate::gpio::{DefaultMode, PA4, PA5, PA6}; +use crate::gpio::{Analog, PA4, PA5, PA6}; use crate::pac; use crate::rcc::{self, *}; use crate::stm32::dac1::mcr::HFSEL; @@ -223,27 +223,27 @@ macro_rules! impl_dac1_ch2_combos { $(impl_pin_for_dac!(DAC1: $pin_ch1, // ch2: Not used $output_ch1 );)* - impl_pin_for_dac!(DAC1: ($($pin_ch1,)* PA5), // ch2: Ext pin + impl_pin_for_dac!(DAC1: ($($pin_ch1,)* PA5), // ch2: Ext pin ($($output_ch1,)* Dac1Ch2) ); impl_pin_for_dac!(DAC1: ($($pin_ch1,)* Dac1IntSig2), // ch2: Internal ($($output_ch1,)* Dac1Ch2) ); - impl_pin_for_dac!(DAC1: ($($pin_ch1,)* (PA5, Dac1IntSig2)),// ch2: Mixed + impl_pin_for_dac!(DAC1: ($($pin_ch1,)* (PA5, Dac1IntSig2)),// ch2: Mixed ($($output_ch1,)* Dac1Ch2) ); }; } impl_dac1_ch2_combos!(); // ch1: Not used -impl_dac1_ch2_combos!(PA4, Dac1Ch1); // ch1: Ext pin +impl_dac1_ch2_combos!(PA4, Dac1Ch1); // ch1: Ext pin impl_dac1_ch2_combos!(Dac1IntSig1, Dac1Ch1); // ch1: Internal -impl_dac1_ch2_combos!((PA4, Dac1IntSig1), Dac1Ch1); // ch1: Mixed +impl_dac1_ch2_combos!((PA4, Dac1IntSig1), Dac1Ch1); // ch1: Mixed // DAC2 -impl_pin_for_dac!(DAC2: PA6, Dac2Ch1); // ch1: Ext pin +impl_pin_for_dac!(DAC2: PA6, Dac2Ch1); // ch1: Ext pin impl_pin_for_dac!(DAC2: Dac2IntSig1, Dac2Ch1); // ch1: Internal -impl_pin_for_dac!(DAC2: (PA6, Dac2IntSig1), Dac2Ch1); // ch1: Mixed +impl_pin_for_dac!(DAC2: (PA6, Dac2IntSig1), Dac2Ch1); // ch1: Mixed // DAC3 int impl_pin_for_dac!(DAC3: Dac3IntSig1, Dac3Ch1); diff --git a/tests/nucleo-g474.rs b/tests/nucleo-g474.rs index 40aac3d2..cc001251 100644 --- a/tests/nucleo-g474.rs +++ b/tests/nucleo-g474.rs @@ -341,7 +341,7 @@ mod tests { let gpioa = dp.GPIOA.split(&mut rcc); let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input(); - let pa4 = gpioa.pa4.into_floating_input(); + let pa4 = gpioa.pa4.into_analog(); let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc); let gpioa = unsafe { &*GPIOA::PTR }; diff --git a/tests/nucleo-g474_w_jumpers.rs b/tests/nucleo-g474_w_jumpers.rs index f5eec361..2f40f420 100644 --- a/tests/nucleo-g474_w_jumpers.rs +++ b/tests/nucleo-g474_w_jumpers.rs @@ -288,7 +288,7 @@ fn setup_opamp_comp_dac() -> Peripherals { let gpioa = dp.GPIOA.split(&mut rcc); let pa1 = gpioa.pa1.into_analog(); let pa2 = gpioa.pa2.into_analog(); - let pa4 = gpioa.pa4.into_floating_input(); + let pa4 = gpioa.pa4.into_analog(); let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc); let dac3ch1 = dp.DAC3.constrain(dac::Dac3IntSig1, &mut rcc); From e2791a6aab769d37e18f223bbbcb6af48dbbaaad Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 28 May 2025 19:38:39 +0200 Subject: [PATCH 3/4] Move dac test so it can check using other pin --- tests/nucleo-g474.rs | 29 ----------------------------- tests/nucleo-g474_w_jumpers.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/tests/nucleo-g474.rs b/tests/nucleo-g474.rs index cc001251..18eccf5b 100644 --- a/tests/nucleo-g474.rs +++ b/tests/nucleo-g474.rs @@ -328,35 +328,6 @@ mod tests { debug!("temp: {}°C", temp); assert!((20.0..35.0).contains(&temp), "20.0 < {} < 35.0", temp); } - - #[test] - fn dac() { - use super::*; - - // TODO: Is it ok to steal these? - let cp = unsafe { stm32::CorePeripherals::steal() }; - let dp = unsafe { stm32::Peripherals::steal() }; - let mut rcc = dp.RCC.constrain(); - let mut delay = cp.SYST.delay(&rcc.clocks); - - let gpioa = dp.GPIOA.split(&mut rcc); - let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input(); - let pa4 = gpioa.pa4.into_analog(); - let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc); - - let gpioa = unsafe { &*GPIOA::PTR }; - - // dac_manual will have its value set manually - let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc); - - dac.set_value(0); - delay.delay_ms(1); - assert!(is_pax_low(gpioa, 4)); - - dac.set_value(4095); - delay.delay_ms(1); - assert!(!is_pax_low(gpioa, 4)); - } } fn is_pax_low(gpioa: &stm32::gpioa::RegisterBlock, x: u8) -> bool { diff --git a/tests/nucleo-g474_w_jumpers.rs b/tests/nucleo-g474_w_jumpers.rs index 2f40f420..b43455f3 100644 --- a/tests/nucleo-g474_w_jumpers.rs +++ b/tests/nucleo-g474_w_jumpers.rs @@ -30,6 +30,35 @@ mod tests { use crate::VREF_ADC_BITS; + #[test] + fn dac() { + use super::*; + + // TODO: Is it ok to steal these? + let cp = unsafe { stm32::CorePeripherals::steal() }; + let dp = unsafe { stm32::Peripherals::steal() }; + let mut rcc = dp.RCC.constrain(); + let mut delay = cp.SYST.delay(&rcc.clocks); + + let gpioa = dp.GPIOA.split(&mut rcc); + let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input(); + let pa4 = gpioa.pa4.into_analog(); + let dac1ch1 = dp.DAC1.constrain(pa4, &mut rcc); + + let gpioa = unsafe { &*stm32::GPIOA::PTR }; + + // dac_manual will have its value set manually + let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc); + + dac.set_value(0); + delay.delay_ms(1); + assert!(is_pax_low(gpioa, 1)); + + dac.set_value(4095); + delay.delay_ms(1); + assert!(!is_pax_low(gpioa, 1)); + } + #[test] fn opamp_follower_dac_adc() { let super::Peripherals { @@ -255,6 +284,10 @@ mod tests { } } +fn is_pax_low(gpioa: &stm32::gpioa::RegisterBlock, x: u8) -> bool { + gpioa.idr().read().idr(x).is_low() +} + /// Vrefint = 1.212V typical /// Vref+ = 3.3V for nucleo /// 1.212V/3.3V*4095 = 1504 adc value From 04ad2da6b21df9f2a6294b047e2b9fc4049869e2 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Thu, 29 May 2025 11:28:58 +0300 Subject: [PATCH 4/4] erasedpin -> anypin --- src/gpio.rs | 14 ++++----- src/gpio/{erased.rs => anypin.rs} | 49 +++++++++++++++++++------------ src/gpio/convert.rs | 10 +++---- src/gpio/hal_02.rs | 10 +++---- src/gpio/hal_1.rs | 12 ++++---- src/gpio/partially_erased.rs | 12 ++++---- tests/nucleo-g474.rs | 3 +- 7 files changed, 59 insertions(+), 51 deletions(-) rename src/gpio/{erased.rs => anypin.rs} (79%) diff --git a/src/gpio.rs b/src/gpio.rs index 0ffe1047..7fd05d5f 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -6,11 +6,11 @@ //! ```rust //! // Acquire the GPIOC peripheral //! // NOTE: `dp` is the device peripherals from the `PAC` crate -//! let mut gpioa = dp.GPIOA.split(); +//! let mut gpioa = dp.GPIOA.split(&mut rcc); //! ``` //! //! This gives you a struct containing all the pins `px0..px15`. -//! By default pins are in floating input mode. You can change their modes. +//! By default pins are in `Analog` mode. You can change their modes. //! For example, to set `pa5` high, you would call //! //! ```rust @@ -62,8 +62,8 @@ mod convert; pub use convert::PinMode; mod partially_erased; pub use partially_erased::{PEPin, PartiallyErasedPin}; -mod erased; -pub use erased::{AnyPin, ErasedPin}; +mod anypin; +pub use anypin::AnyPin; mod exti; pub use exti::ExtiPin; mod dynamic; @@ -408,8 +408,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) } } @@ -422,7 +422,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/erased.rs b/src/gpio/anypin.rs similarity index 79% rename from src/gpio/erased.rs rename to src/gpio/anypin.rs index 363d6d34..adf8d4cd 100644 --- a/src/gpio/erased.rs +++ b/src/gpio/anypin.rs @@ -1,17 +1,15 @@ use super::*; -pub use ErasedPin as AnyPin; - /// 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 +21,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 +33,7 @@ impl defmt::Format for ErasedPin { } } -impl PinExt for ErasedPin { +impl PinExt for AnyPin { type Mode = MODE; #[inline(always)] @@ -48,7 +46,7 @@ impl PinExt for ErasedPin { } } -impl ErasedPin { +impl AnyPin { pub(crate) fn from_pin_port(pin_port: u8) -> Self { Self { pin_port, @@ -73,7 +71,7 @@ impl ErasedPin { } #[inline] - pub(crate) fn block(&self) -> &crate::pac::gpioa::RegisterBlock { + pub(crate) unsafe 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,19 +84,20 @@ impl ErasedPin { 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 } } -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())) }; + unsafe { + (*self.block()) + .bsrr() + .write(|w| w.bs(self.pin_id()).set_bit()) + }; } /// Drives the pin low @@ -106,9 +105,9 @@ impl ErasedPin> { pub fn set_low(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { - self.block() + (*self.block()) .bsrr() - .write(|w| w.bits(1 << (self.pin_id() + 16))) + .write(|w| w.br(self.pin_id()).set_bit()) }; } @@ -140,7 +139,13 @@ 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 + unsafe { + (*self.block()) + .odr() + .read() + .odr(self.pin_id()) + .bit_is_clear() + } } /// Toggle pin output @@ -154,7 +159,7 @@ impl ErasedPin> { } } -impl ErasedPin +impl AnyPin where MODE: marker::Readable, { @@ -167,6 +172,12 @@ where /// Is the input pin low? #[inline(always)] pub fn is_low(&self) -> bool { - self.block().idr().read().bits() & (1 << self.pin_id()) == 0 + unsafe { + (*self.block()) + .idr() + .read() + .idr(self.pin_id()) + .bit_is_clear() + } } } diff --git a/src/gpio/convert.rs b/src/gpio/convert.rs index 2ac350c0..c1e8c4e6 100644 --- a/src/gpio/convert.rs +++ b/src/gpio/convert.rs @@ -166,19 +166,19 @@ 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(); - change_mode!(self.block(), n); + change_mode!((*self.block()), n); } #[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/hal_02.rs b/src/gpio/hal_02.rs index 25026fa5..1e6decfa 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..1dda51e9 100644 --- a/src/gpio/partially_erased.rs +++ b/src/gpio/partially_erased.rs @@ -69,7 +69,7 @@ impl PartiallyErasedPin> { pub fn set_high(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { - (*gpiox::

()).bsrr().write(|w| w.bits(1 << self.i)); + (*gpiox::

()).bsrr().write(|w| w.bs(self.i).set_bit()); } } @@ -78,7 +78,7 @@ impl PartiallyErasedPin> { 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))); + (*gpiox::

()).bsrr().write(|w| w.br(self.i).set_bit()); } } @@ -111,7 +111,7 @@ 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 +139,15 @@ 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) } } diff --git a/tests/nucleo-g474.rs b/tests/nucleo-g474.rs index 18eccf5b..9d153434 100644 --- a/tests/nucleo-g474.rs +++ b/tests/nucleo-g474.rs @@ -27,7 +27,7 @@ pub fn now() -> MicrosDurationU32 { #[defmt_test::tests] mod tests { - use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle}; + use embedded_hal::pwm::SetDutyCycle; use fixed::types::I1F15; use fugit::RateExtU32; use stm32g4xx_hal::{ @@ -39,7 +39,6 @@ mod tests { types::{Q15, Q31}, Ext, }, - dac::{DacExt, DacOut}, delay::SYSTDelayExt, gpio::{GpioExt, AF6, PA8}, pwm::PwmExt,