stm32f4xx_hal/
gpio.rs

1//! General Purpose Input / Output
2//!
3//! The GPIO pins are organised into groups of 16 pins which can be accessed through the
4//! `gpioa`, `gpiob`... modules. To get access to the pins, you first need to convert them into a
5//! HAL designed struct from the `pac` struct using the [split](trait.GpioExt.html#tymethod.split) function.
6//! ```rust
7//! // Acquire the GPIOC peripheral
8//! // NOTE: `dp` is the device peripherals from the `PAC` crate
9//! let mut gpioa = dp.GPIOA.split();
10//! ```
11//!
12//! This gives you a struct containing all the pins `px0..px15`.
13//! By default pins are in floating input mode. You can change their modes.
14//! For example, to set `pa5` high, you would call
15//!
16//! ```rust
17//! let output = gpioa.pa5.into_push_pull_output();
18//! output.set_high();
19//! ```
20//!
21//! ## Modes
22//!
23//! Each GPIO pin can be set to various modes:
24//!
25//! - **Alternate**: Pin mode required when the pin is driven by other peripherals
26//! - **Analog**: Analog input to be used with ADC.
27//! - **Dynamic**: Pin mode is selected at runtime. See changing configurations for more details
28//! - Input
29//!     - **PullUp**: Input connected to high with a weak pull up resistor. Will be high when nothing
30//!       is connected
31//!     - **PullDown**: Input connected to high with a weak pull up resistor. Will be low when nothing
32//!       is connected
33//!     - **Floating**: Input not pulled to high or low. Will be undefined when nothing is connected
34//! - Output
35//!     - **PushPull**: Output which either drives the pin high or low
36//!     - **OpenDrain**: Output which leaves the gate floating, or pulls it do ground in drain
37//!       mode. Can be used as an input in the `open` configuration
38//!
39//! ## Changing modes
40//! The simplest way to change the pin mode is to use the `into_<mode>` functions. These return a
41//! new struct with the correct mode that you can use the input or output functions on.
42//!
43//! If you need a more temporary mode change, and can not use the `into_<mode>` functions for
44//! ownership reasons, you can use the closure based `with_<mode>` functions to temporarily change the pin type, do
45//! some output or input, and then have it change back once done.
46//!
47//! ### Dynamic Mode Change
48//! The above mode change methods guarantee that you can only call input functions when the pin is
49//! in input mode, and output when in output modes, but can lead to some issues. Therefore, there
50//! is also a mode where the state is kept track of at runtime, allowing you to change the mode
51//! often, and without problems with ownership, or references, at the cost of some performance and
52//! the risk of runtime errors.
53//!
54//! To make a pin dynamic, use the `into_dynamic` function, and then use the `make_<mode>` functions to
55//! change the mode
56
57use core::marker::PhantomData;
58
59use crate::pac::{self, RCC};
60pub mod alt;
61mod convert;
62pub use convert::PinMode;
63mod partially_erased;
64pub use partially_erased::{PEPin, PartiallyErasedPin};
65mod erased;
66pub use erased::{AnyPin, ErasedPin};
67mod exti;
68pub use exti::ExtiPin;
69mod dynamic;
70pub use dynamic::{Dynamic, DynamicPin};
71mod hal_02;
72mod hal_1;
73pub mod outport;
74
75pub use embedded_hal_02::digital::v2::PinState;
76
77use core::fmt;
78
79/// Extension trait to split a GPIO peripheral in independent pins and registers
80pub trait GpioExt {
81    /// The parts to split the GPIO into
82    type Parts;
83
84    /// Splits the GPIO block into independent pins and registers
85    fn split(self, rcc: &mut RCC) -> Self::Parts;
86}
87
88/// Id, port and mode for any pin
89pub trait PinExt {
90    /// Current pin mode
91    type Mode;
92    /// Pin number
93    fn pin_id(&self) -> u8;
94    /// Port number starting from 0
95    fn port_id(&self) -> u8;
96}
97
98/// Some alternate mode (type state)
99#[derive(Debug, Default)]
100#[cfg_attr(feature = "defmt", derive(defmt::Format))]
101pub struct Alternate<const A: u8, Otype = PushPull>(PhantomData<Otype>);
102
103/// Input mode (type state)
104#[derive(Debug, Default)]
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106pub struct Input;
107
108/// Pull setting for an input.
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110#[cfg_attr(feature = "defmt", derive(defmt::Format))]
111pub enum Pull {
112    /// Floating
113    None = 0,
114    /// Pulled up
115    Up = 1,
116    /// Pulled down
117    Down = 2,
118}
119
120impl From<Pull> for pac::gpioa::pupdr::PULL {
121    fn from(value: Pull) -> Self {
122        match value {
123            Pull::Down => Self::PullDown,
124            Pull::Up => Self::PullUp,
125            Pull::None => Self::Floating,
126        }
127    }
128}
129
130/// Open drain input or output (type state)
131#[derive(Debug, Default)]
132#[cfg_attr(feature = "defmt", derive(defmt::Format))]
133pub struct OpenDrain;
134
135/// Output mode (type state)
136#[derive(Debug, Default)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub struct Output<Otype = PushPull> {
139    _mode: PhantomData<Otype>,
140}
141
142/// Push pull output (type state)
143#[derive(Debug, Default)]
144#[cfg_attr(feature = "defmt", derive(defmt::Format))]
145pub struct PushPull;
146
147/// Analog mode (type state)
148#[derive(Debug, Default)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150pub struct Analog;
151
152/// JTAG/SWD mote (type state)
153pub type Debugger = Alternate<0, PushPull>;
154
155pub(crate) mod marker {
156    /// Marker trait that show if `ExtiPin` can be implemented
157    pub trait Interruptible {}
158    /// Marker trait for readable pin modes
159    pub trait Readable {}
160    /// Marker trait for slew rate configurable pin modes
161    pub trait OutputSpeed {}
162    /// Marker trait for active pin modes
163    pub trait Active {}
164    /// Marker trait for all pin modes except alternate
165    pub trait NotAlt {}
166    /// Marker trait for pins with alternate function `A` mapping
167    pub trait IntoAf<const A: u8> {}
168}
169
170impl<MODE> marker::Interruptible for Output<MODE> {}
171impl marker::Interruptible for Input {}
172impl marker::Readable for Input {}
173impl marker::Readable for Output<OpenDrain> {}
174impl<const A: u8, Otype> marker::Interruptible for Alternate<A, Otype> {}
175impl<const A: u8, Otype> marker::Readable for Alternate<A, Otype> {}
176impl marker::Active for Input {}
177impl<Otype> marker::OutputSpeed for Output<Otype> {}
178impl<const A: u8, Otype> marker::OutputSpeed for Alternate<A, Otype> {}
179impl<Otype> marker::Active for Output<Otype> {}
180impl<const A: u8, Otype> marker::Active for Alternate<A, Otype> {}
181impl marker::NotAlt for Input {}
182impl<Otype> marker::NotAlt for Output<Otype> {}
183impl marker::NotAlt for Analog {}
184
185/// GPIO Pin speed selection
186#[cfg_attr(feature = "defmt", derive(defmt::Format))]
187#[derive(Debug, PartialEq, Eq, Clone, Copy)]
188pub enum Speed {
189    /// Low speed
190    Low = 0,
191    /// Medium speed
192    Medium = 1,
193    /// High speed
194    High = 2,
195    /// Very high speed
196    VeryHigh = 3,
197}
198
199impl From<Speed> for pac::gpioa::ospeedr::OUTPUT_SPEED {
200    fn from(value: Speed) -> Self {
201        match value {
202            Speed::Low => Self::LowSpeed,
203            Speed::Medium => Self::MediumSpeed,
204            Speed::High => Self::HighSpeed,
205            Speed::VeryHigh => Self::VeryHighSpeed,
206        }
207    }
208}
209
210/// GPIO interrupt trigger edge selection
211#[cfg_attr(feature = "defmt", derive(defmt::Format))]
212#[derive(Debug, PartialEq, Eq, Clone, Copy)]
213pub enum Edge {
214    /// Rising edge of voltage
215    Rising,
216    /// Falling edge of voltage
217    Falling,
218    /// Rising and falling edge of voltage
219    RisingFalling,
220}
221
222macro_rules! af {
223    ($($i:literal: $AFi:ident),+) => {
224        $(
225            #[doc = concat!("Alternate function ", $i, " (type state)" )]
226            pub type $AFi<Otype = PushPull> = Alternate<$i, Otype>;
227        )+
228    };
229}
230
231af!(
232    0: AF0,
233    1: AF1,
234    2: AF2,
235    3: AF3,
236    4: AF4,
237    5: AF5,
238    6: AF6,
239    7: AF7,
240    8: AF8,
241    9: AF9,
242    10: AF10,
243    11: AF11,
244    12: AF12,
245    13: AF13,
246    14: AF14,
247    15: AF15
248);
249
250/// Generic pin type
251///
252/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section).
253/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
254/// - `N` is pin number: from `0` to `15`.
255pub struct Pin<const P: char, const N: u8, MODE = DefaultMode> {
256    _mode: PhantomData<MODE>,
257}
258impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
259    const fn new() -> Self {
260        Self { _mode: PhantomData }
261    }
262}
263
264impl<const P: char, const N: u8, MODE> fmt::Debug for Pin<P, N, MODE> {
265    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
266        formatter.write_fmt(format_args!(
267            "P{}{}<{}>",
268            P,
269            N,
270            crate::stripped_type_name::<MODE>()
271        ))
272    }
273}
274
275#[cfg(feature = "defmt")]
276impl<const P: char, const N: u8, MODE> defmt::Format for Pin<P, N, MODE> {
277    fn format(&self, f: defmt::Formatter) {
278        defmt::write!(f, "P{}{}<{}>", P, N, crate::stripped_type_name::<MODE>());
279    }
280}
281
282impl<const P: char, const N: u8, MODE> PinExt for Pin<P, N, MODE> {
283    type Mode = MODE;
284
285    #[inline(always)]
286    fn pin_id(&self) -> u8 {
287        N
288    }
289    #[inline(always)]
290    fn port_id(&self) -> u8 {
291        P as u8 - b'A'
292    }
293}
294
295pub trait PinSpeed: Sized {
296    /// Set pin speed
297    fn set_speed(&mut self, speed: Speed);
298
299    #[inline(always)]
300    fn speed(mut self, speed: Speed) -> Self {
301        self.set_speed(speed);
302        self
303    }
304}
305
306pub trait PinPull: Sized {
307    /// Set the internal pull-up and pull-down resistor
308    fn set_internal_resistor(&mut self, resistor: Pull);
309
310    #[inline(always)]
311    fn internal_resistor(mut self, resistor: Pull) -> Self {
312        self.set_internal_resistor(resistor);
313        self
314    }
315}
316
317impl<const P: char, const N: u8, MODE> PinSpeed for Pin<P, N, MODE>
318where
319    MODE: marker::OutputSpeed,
320{
321    #[inline(always)]
322    fn set_speed(&mut self, speed: Speed) {
323        self.set_speed(speed)
324    }
325}
326
327impl<const P: char, const N: u8, MODE> Pin<P, N, MODE>
328where
329    MODE: marker::OutputSpeed,
330{
331    /// Set pin speed
332    pub fn set_speed(&mut self, speed: Speed) {
333        unsafe { &(*gpiox::<P>()) }
334            .ospeedr()
335            .modify(|_, w| w.ospeedr(N).variant(speed.into()));
336    }
337
338    /// Set pin speed
339    pub fn speed(mut self, speed: Speed) -> Self {
340        self.set_speed(speed);
341        self
342    }
343}
344
345impl<const P: char, const N: u8, MODE> PinPull for Pin<P, N, MODE>
346where
347    MODE: marker::Active,
348{
349    #[inline(always)]
350    fn set_internal_resistor(&mut self, resistor: Pull) {
351        self.set_internal_resistor(resistor)
352    }
353}
354
355impl<const P: char, const N: u8, MODE> Pin<P, N, MODE>
356where
357    MODE: marker::Active,
358{
359    /// Set the internal pull-up and pull-down resistor
360    pub fn set_internal_resistor(&mut self, resistor: Pull) {
361        unsafe { &(*gpiox::<P>()) }
362            .pupdr()
363            .modify(|_, w| w.pupdr(N).variant(resistor.into()));
364    }
365
366    /// Set the internal pull-up and pull-down resistor
367    pub fn internal_resistor(mut self, resistor: Pull) -> Self {
368        self.set_internal_resistor(resistor);
369        self
370    }
371
372    /// Enables / disables the internal pull up
373    pub fn internal_pull_up(self, on: bool) -> Self {
374        if on {
375            self.internal_resistor(Pull::Up)
376        } else {
377            self.internal_resistor(Pull::None)
378        }
379    }
380
381    /// Enables / disables the internal pull down
382    pub fn internal_pull_down(self, on: bool) -> Self {
383        if on {
384            self.internal_resistor(Pull::Down)
385        } else {
386            self.internal_resistor(Pull::None)
387        }
388    }
389}
390
391impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
392    /// Erases the pin number from the type
393    ///
394    /// This is useful when you want to collect the pins into an array where you
395    /// need all the elements to have the same type
396    pub fn erase_number(self) -> PartiallyErasedPin<P, MODE> {
397        PartiallyErasedPin::new(N)
398    }
399
400    /// Erases the pin number and the port from the type
401    ///
402    /// This is useful when you want to collect the pins into an array where you
403    /// need all the elements to have the same type
404    pub fn erase(self) -> AnyPin<MODE> {
405        AnyPin::new(P as u8 - b'A', N)
406    }
407}
408
409impl<const P: char, const N: u8, MODE> From<Pin<P, N, MODE>> for PartiallyErasedPin<P, MODE> {
410    /// Pin-to-partially erased pin conversion using the [`From`] trait.
411    ///
412    /// Note that [`From`] is the reciprocal of [`Into`].
413    fn from(p: Pin<P, N, MODE>) -> Self {
414        p.erase_number()
415    }
416}
417
418impl<const P: char, const N: u8, MODE> From<Pin<P, N, MODE>> for AnyPin<MODE> {
419    /// Pin-to-erased pin conversion using the [`From`] trait.
420    ///
421    /// Note that [`From`] is the reciprocal of [`Into`].
422    fn from(p: Pin<P, N, MODE>) -> Self {
423        p.erase()
424    }
425}
426
427impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
428    /// Set the output of the pin regardless of its mode.
429    /// Primarily used to set the output value of the pin
430    /// before changing its mode to an output to avoid
431    /// a short spike of an incorrect value
432    #[inline(always)]
433    fn _set_state(&mut self, state: PinState) {
434        match state {
435            PinState::High => self._set_high(),
436            PinState::Low => self._set_low(),
437        }
438    }
439    #[inline(always)]
440    fn _set_high(&mut self) {
441        // NOTE(unsafe) atomic write to a stateless register
442        let gpio = unsafe { &(*gpiox::<P>()) };
443        gpio.bsrr().write(|w| w.bs(N).set_bit());
444    }
445    #[inline(always)]
446    fn _set_low(&mut self) {
447        // NOTE(unsafe) atomic write to a stateless register
448        let gpio = unsafe { &(*gpiox::<P>()) };
449        gpio.bsrr().write(|w| w.br(N).set_bit());
450    }
451    #[inline(always)]
452    fn _is_set_low(&self) -> bool {
453        // NOTE(unsafe) atomic read with no side effects
454        let gpio = unsafe { &(*gpiox::<P>()) };
455        gpio.odr().read().odr(N).bit_is_clear()
456    }
457    #[inline(always)]
458    fn _is_low(&self) -> bool {
459        // NOTE(unsafe) atomic read with no side effects
460        let gpio = unsafe { &(*gpiox::<P>()) };
461        gpio.idr().read().idr(N).bit_is_clear()
462    }
463}
464
465impl<const P: char, const N: u8, MODE> Pin<P, N, Output<MODE>> {
466    /// Drives the pin high
467    #[inline(always)]
468    pub fn set_high(&mut self) {
469        self._set_high()
470    }
471
472    /// Drives the pin low
473    #[inline(always)]
474    pub fn set_low(&mut self) {
475        self._set_low()
476    }
477
478    /// Is the pin in drive high or low mode?
479    #[inline(always)]
480    pub fn get_state(&self) -> PinState {
481        if self.is_set_low() {
482            PinState::Low
483        } else {
484            PinState::High
485        }
486    }
487
488    /// Drives the pin high or low depending on the provided value
489    #[inline(always)]
490    pub fn set_state(&mut self, state: PinState) {
491        match state {
492            PinState::Low => self.set_low(),
493            PinState::High => self.set_high(),
494        }
495    }
496
497    /// Is the pin in drive high mode?
498    #[inline(always)]
499    pub fn is_set_high(&self) -> bool {
500        !self.is_set_low()
501    }
502
503    /// Is the pin in drive low mode?
504    #[inline(always)]
505    pub fn is_set_low(&self) -> bool {
506        self._is_set_low()
507    }
508
509    /// Toggle pin output
510    #[inline(always)]
511    pub fn toggle(&mut self) {
512        if self.is_set_low() {
513            self.set_high()
514        } else {
515            self.set_low()
516        }
517    }
518}
519
520pub trait ReadPin {
521    #[inline(always)]
522    fn is_high(&self) -> bool {
523        !self.is_low()
524    }
525    fn is_low(&self) -> bool;
526}
527
528impl<const P: char, const N: u8, MODE> ReadPin for Pin<P, N, MODE>
529where
530    MODE: marker::Readable,
531{
532    #[inline(always)]
533    fn is_low(&self) -> bool {
534        self.is_low()
535    }
536}
537
538impl<const P: char, const N: u8, MODE> Pin<P, N, MODE>
539where
540    MODE: marker::Readable,
541{
542    /// Is the input pin high?
543    #[inline(always)]
544    pub fn is_high(&self) -> bool {
545        !self.is_low()
546    }
547
548    /// Is the input pin low?
549    #[inline(always)]
550    pub fn is_low(&self) -> bool {
551        self._is_low()
552    }
553}
554
555macro_rules! gpio {
556    ($GPIOX:ident, $gpiox:ident, $PEPin:ident, $port_id:expr, $PXn:ident, [
557        $($PXi:ident: ($pxi:ident, $i:expr, [$($A:literal),*] $(, $MODE:ty)?),)+
558    ]) => {
559        /// GPIO
560        pub mod $gpiox {
561            use crate::pac::{$GPIOX, RCC};
562            use crate::rcc::{Enable, Reset};
563
564            /// GPIO parts
565            pub struct Parts {
566                $(
567                    /// Pin
568                    pub $pxi: $PXi $(<$MODE>)?,
569                )+
570            }
571
572            impl super::GpioExt for $GPIOX {
573                type Parts = Parts;
574
575                fn split(self, rcc: &mut RCC) -> Parts {
576                    // Enable clock.
577                    $GPIOX::enable(rcc);
578                    $GPIOX::reset(rcc);
579                    Parts {
580                        $(
581                            $pxi: $PXi::new(),
582                        )+
583                    }
584                }
585            }
586
587            #[doc="Common type for "]
588            #[doc=stringify!($GPIOX)]
589            #[doc=" related pins"]
590            pub type $PXn<MODE> = super::PartiallyErasedPin<$port_id, MODE>;
591
592            $(
593                #[doc=stringify!($PXi)]
594                #[doc=" pin"]
595                pub type $PXi<MODE = super::DefaultMode> = super::Pin<$port_id, $i, MODE>;
596
597                $(
598                    impl<MODE> super::marker::IntoAf<$A> for $PXi<MODE> { }
599                )*
600            )+
601
602        }
603
604        pub use $gpiox::{ $($PXi,)+ };
605    }
606}
607use gpio;
608
609mod f4;
610pub use f4::*;
611
612const fn gpiox<const P: char>() -> *const crate::pac::gpioa::RegisterBlock {
613    match P {
614        'A' => crate::pac::GPIOA::ptr(),
615        'B' => crate::pac::GPIOB::ptr() as _,
616        'C' => crate::pac::GPIOC::ptr() as _,
617        #[cfg(feature = "gpiod")]
618        'D' => crate::pac::GPIOD::ptr() as _,
619        #[cfg(feature = "gpioe")]
620        'E' => crate::pac::GPIOE::ptr() as _,
621        #[cfg(feature = "gpiof")]
622        'F' => crate::pac::GPIOF::ptr() as _,
623        #[cfg(feature = "gpiog")]
624        'G' => crate::pac::GPIOG::ptr() as _,
625        'H' => crate::pac::GPIOH::ptr() as _,
626        #[cfg(feature = "gpioi")]
627        'I' => crate::pac::GPIOI::ptr() as _,
628        #[cfg(feature = "gpioj")]
629        'J' => crate::pac::GPIOJ::ptr() as _,
630        #[cfg(feature = "gpiok")]
631        'K' => crate::pac::GPIOK::ptr() as _,
632        _ => panic!("Unknown GPIO port"),
633    }
634}