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