stm32f7xx_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::{EXTI, SYSCFG};
60use crate::rcc::{Enable, APB2};
61
62mod convert;
63mod partially_erased;
64pub use partially_erased::{PEPin, PartiallyErasedPin};
65mod erased;
66pub use erased::{EPin, ErasedPin};
67mod dynamic;
68pub use dynamic::{Dynamic, DynamicPin};
69mod hal_02;
70
71pub use embedded_hal::digital::v2::PinState;
72
73use core::fmt;
74
75/// A filler pin type
76#[derive(Debug)]
77pub struct NoPin;
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) -> Self::Parts;
86}
87
88pub trait PinExt {
89    type Mode;
90    /// Return pin number
91    fn pin_id(&self) -> u8;
92    /// Return port number
93    fn port_id(&self) -> u8;
94}
95
96/// Some alternate mode (type state)
97pub struct Alternate<const A: u8, Otype = PushPull>(PhantomData<Otype>);
98
99/// Input mode (type state)
100pub struct Input<MODE = Floating> {
101    _mode: PhantomData<MODE>,
102}
103
104/// Floating input (type state)
105pub struct Floating;
106
107/// Pulled down input (type state)
108pub struct PullDown;
109
110/// Pulled up input (type state)
111pub struct PullUp;
112
113/// Open drain input or output (type state)
114pub struct OpenDrain;
115
116/// Output mode (type state)
117pub struct Output<MODE = PushPull> {
118    _mode: PhantomData<MODE>,
119}
120
121/// Push pull output (type state)
122pub struct PushPull;
123
124/// Analog mode (type state)
125pub struct Analog;
126
127pub type Debugger = Alternate<0, PushPull>;
128
129/// GPIO Pin speed selection
130#[derive(Debug, PartialEq, Eq, Clone, Copy)]
131pub enum Speed {
132    Low = 0,
133    Medium = 1,
134    High = 2,
135    VeryHigh = 3,
136}
137
138#[derive(Debug, PartialEq, Eq, Clone, Copy)]
139pub enum Edge {
140    Rising,
141    Falling,
142    RisingFalling,
143}
144
145mod sealed {
146    /// Marker trait that show if `ExtiPin` can be implemented
147    pub trait Interruptable {}
148}
149
150use sealed::Interruptable;
151impl<MODE> Interruptable for Output<MODE> {}
152impl<MODE> Interruptable for Input<MODE> {}
153impl<const A: u8, MODE> Interruptable for Alternate<A, MODE> {}
154
155/// External Interrupt Pin
156pub trait ExtiPin {
157    fn make_interrupt_source(&mut self, syscfg: &mut SYSCFG, apb2: &mut APB2);
158    fn trigger_on_edge(&mut self, exti: &mut EXTI, level: Edge);
159    fn enable_interrupt(&mut self, exti: &mut EXTI);
160    fn disable_interrupt(&mut self, exti: &mut EXTI);
161    fn clear_interrupt_pending_bit(&mut self);
162    fn check_interrupt(&self) -> bool;
163}
164
165impl<PIN> ExtiPin for PIN
166where
167    PIN: PinExt,
168    PIN::Mode: Interruptable,
169{
170    /// Make corresponding EXTI line sensitive to this pin
171    #[inline(always)]
172    fn make_interrupt_source(&mut self, syscfg: &mut SYSCFG, apb2: &mut APB2) {
173        // SYSCFG clock must be enabled in order to do register writes
174        SYSCFG::enable(apb2);
175
176        let i = self.pin_id();
177        let port = self.port_id() as u32;
178        let offset = 4 * (i % 4);
179        match i {
180            0..=3 => {
181                syscfg.exticr1.modify(|r, w| unsafe {
182                    w.bits((r.bits() & !(0xf << offset)) | (port << offset))
183                });
184            }
185            4..=7 => {
186                syscfg.exticr2.modify(|r, w| unsafe {
187                    w.bits((r.bits() & !(0xf << offset)) | (port << offset))
188                });
189            }
190            8..=11 => {
191                syscfg.exticr3.modify(|r, w| unsafe {
192                    w.bits((r.bits() & !(0xf << offset)) | (port << offset))
193                });
194            }
195            12..=15 => {
196                syscfg.exticr4.modify(|r, w| unsafe {
197                    w.bits((r.bits() & !(0xf << offset)) | (port << offset))
198                });
199            }
200            _ => unreachable!(),
201        }
202    }
203
204    /// Generate interrupt on rising edge, falling edge or both
205    #[inline(always)]
206    fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: Edge) {
207        let i = self.pin_id();
208        match edge {
209            Edge::Rising => {
210                exti.rtsr
211                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
212                exti.ftsr
213                    .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) });
214            }
215            Edge::Falling => {
216                exti.ftsr
217                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
218                exti.rtsr
219                    .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) });
220            }
221            Edge::RisingFalling => {
222                exti.rtsr
223                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
224                exti.ftsr
225                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
226            }
227        }
228    }
229
230    /// Enable external interrupts from this pin.
231    #[inline(always)]
232    fn enable_interrupt(&mut self, exti: &mut EXTI) {
233        exti.imr
234            .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin_id())) });
235    }
236
237    /// Disable external interrupts from this pin
238    #[inline(always)]
239    fn disable_interrupt(&mut self, exti: &mut EXTI) {
240        exti.imr
241            .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id())) });
242    }
243
244    /// Clear the interrupt pending bit for this pin
245    #[inline(always)]
246    fn clear_interrupt_pending_bit(&mut self) {
247        unsafe { (*EXTI::ptr()).pr.write(|w| w.bits(1 << self.pin_id())) };
248    }
249
250    /// Reads the interrupt pending bit for this pin
251    #[inline(always)]
252    fn check_interrupt(&self) -> bool {
253        unsafe { ((*EXTI::ptr()).pr.read().bits() & (1 << self.pin_id())) != 0 }
254    }
255}
256
257/// Generic pin type
258///
259/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section).
260/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
261/// - `N` is pin number: from `0` to `15`.
262pub struct Pin<const P: char, const N: u8, MODE = Input<Floating>> {
263    _mode: PhantomData<MODE>,
264}
265impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
266    const fn new() -> Self {
267        Self { _mode: PhantomData }
268    }
269}
270
271impl<const P: char, const N: u8, MODE> fmt::Debug for Pin<P, N, MODE> {
272    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
273        formatter.write_fmt(format_args!(
274            "P{}{}<{}>",
275            P,
276            N,
277            crate::stripped_type_name::<MODE>()
278        ))
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
295impl<const P: char, const N: u8, MODE> Pin<P, N, Output<MODE>> {
296    /// Set pin speed
297    pub fn set_speed(self, speed: Speed) -> Self {
298        let offset = 2 * { N };
299
300        unsafe {
301            (*Gpio::<P>::ptr())
302                .ospeedr
303                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | ((speed as u32) << offset)))
304        };
305
306        self
307    }
308}
309
310impl<const P: char, const N: u8> Pin<P, N, Output<OpenDrain>> {
311    /// Enables / disables the internal pull up
312    pub fn internal_pull_up(self, on: bool) -> Self {
313        let offset = 2 * { N };
314        let value = if on { 0b01 } else { 0b00 };
315        unsafe {
316            (*Gpio::<P>::ptr())
317                .pupdr
318                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (value << offset)))
319        };
320
321        self
322    }
323
324    /// Enables / disables the internal pull down
325    pub fn internal_pull_down(self, on: bool) -> Self {
326        let offset = 2 * { N };
327        let value = if on { 0b10 } else { 0b00 };
328        unsafe {
329            (*Gpio::<P>::ptr())
330                .pupdr
331                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (value << offset)))
332        };
333
334        self
335    }
336}
337
338impl<const P: char, const N: u8, const A: u8> Pin<P, N, Alternate<A, PushPull>> {
339    /// Set pin speed
340    pub fn set_speed(self, speed: Speed) -> Self {
341        let offset = 2 * { N };
342
343        unsafe {
344            (*Gpio::<P>::ptr())
345                .ospeedr
346                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | ((speed as u32) << offset)))
347        };
348
349        self
350    }
351
352    /// Enables / disables the internal pull up
353    pub fn internal_pull_up(self, on: bool) -> Self {
354        let offset = 2 * { N };
355        let value = if on { 0b01 } else { 0b00 };
356        unsafe {
357            (*Gpio::<P>::ptr())
358                .pupdr
359                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (value << offset)))
360        };
361
362        self
363    }
364
365    /// Enables / disables the internal pull down
366    pub fn internal_pull_down(self, on: bool) -> Self {
367        let offset = 2 * { N };
368        let value = if on { 0b10 } else { 0b00 };
369        unsafe {
370            (*Gpio::<P>::ptr())
371                .pupdr
372                .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (value << offset)))
373        };
374
375        self
376    }
377}
378
379impl<const P: char, const N: u8, const A: u8> Pin<P, N, Alternate<A, PushPull>> {
380    /// Turns pin alternate configuration pin into open drain
381    pub fn set_open_drain(self) -> Pin<P, N, Alternate<A, OpenDrain>> {
382        let offset = { N };
383        unsafe {
384            (*Gpio::<P>::ptr())
385                .otyper
386                .modify(|r, w| w.bits(r.bits() | (1 << offset)))
387        };
388
389        Pin::new()
390    }
391}
392
393impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
394    /// Erases the pin number from the type
395    ///
396    /// This is useful when you want to collect the pins into an array where you
397    /// need all the elements to have the same type
398    pub fn erase_number(self) -> PEPin<P, MODE> {
399        PEPin::new(N)
400    }
401
402    /// Erases the pin number and the port 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(self) -> EPin<MODE> {
407        EPin::new(P as u8 - b'A', N)
408    }
409}
410
411impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
412    /// Set the output of the pin regardless of its mode.
413    /// Primarily used to set the output value of the pin
414    /// before changing its mode to an output to avoid
415    /// a short spike of an incorrect value
416    #[inline(always)]
417    fn _set_state(&mut self, state: PinState) {
418        match state {
419            PinState::High => self._set_high(),
420            PinState::Low => self._set_low(),
421        }
422    }
423    #[inline(always)]
424    fn _set_high(&mut self) {
425        // NOTE(unsafe) atomic write to a stateless register
426        unsafe { (*Gpio::<P>::ptr()).bsrr.write(|w| w.bits(1 << N)) }
427    }
428    #[inline(always)]
429    fn _set_low(&mut self) {
430        // NOTE(unsafe) atomic write to a stateless register
431        unsafe { (*Gpio::<P>::ptr()).bsrr.write(|w| w.bits(1 << (16 + N))) }
432    }
433    #[inline(always)]
434    fn _is_set_low(&self) -> bool {
435        // NOTE(unsafe) atomic read with no side effects
436        unsafe { (*Gpio::<P>::ptr()).odr.read().bits() & (1 << N) == 0 }
437    }
438    #[inline(always)]
439    fn _is_low(&self) -> bool {
440        // NOTE(unsafe) atomic read with no side effects
441        unsafe { (*Gpio::<P>::ptr()).idr.read().bits() & (1 << N) == 0 }
442    }
443}
444
445impl<const P: char, const N: u8, MODE> Pin<P, N, Output<MODE>> {
446    #[inline(always)]
447    pub fn set_high(&mut self) {
448        self._set_high()
449    }
450
451    #[inline(always)]
452    pub fn set_low(&mut self) {
453        self._set_low()
454    }
455
456    #[inline(always)]
457    pub fn get_state(&self) -> PinState {
458        if self.is_set_low() {
459            PinState::Low
460        } else {
461            PinState::High
462        }
463    }
464
465    #[inline(always)]
466    pub fn set_state(&mut self, state: PinState) {
467        match state {
468            PinState::Low => self.set_low(),
469            PinState::High => self.set_high(),
470        }
471    }
472
473    #[inline(always)]
474    pub fn is_set_high(&self) -> bool {
475        !self.is_set_low()
476    }
477
478    #[inline(always)]
479    pub fn is_set_low(&self) -> bool {
480        self._is_set_low()
481    }
482
483    #[inline(always)]
484    pub fn toggle(&mut self) {
485        if self.is_set_low() {
486            self.set_high()
487        } else {
488            self.set_low()
489        }
490    }
491}
492
493impl<const P: char, const N: u8> Pin<P, N, Output<OpenDrain>> {
494    #[inline(always)]
495    pub fn is_high(&self) -> bool {
496        !self.is_low()
497    }
498
499    #[inline(always)]
500    pub fn is_low(&self) -> bool {
501        self._is_low()
502    }
503}
504
505impl<const P: char, const N: u8, MODE> Pin<P, N, Input<MODE>> {
506    #[inline(always)]
507    pub fn is_high(&self) -> bool {
508        !self.is_low()
509    }
510
511    #[inline(always)]
512    pub fn is_low(&self) -> bool {
513        self._is_low()
514    }
515}
516
517macro_rules! gpio {
518    ($GPIOX:ident, $gpiox:ident, $PEPin:ident, $port_id:expr, $PXn:ident, [
519        $($PXi:ident: ($pxi:ident, $i:expr $(, $MODE:ty)?),)+
520    ]) => {
521        /// GPIO
522        pub mod $gpiox {
523            use crate::pac::$GPIOX;
524            use crate::rcc::{Enable, Reset};
525            use super::{
526                Floating, Input,
527            };
528
529            /// GPIO parts
530            pub struct Parts {
531                $(
532                    /// Pin
533                    pub $pxi: $PXi $(<$MODE>)?,
534                )+
535            }
536
537            impl super::GpioExt for $GPIOX {
538                type Parts = Parts;
539
540                fn split(self) -> Parts {
541                    unsafe {
542                        <$GPIOX>::enable_unchecked();
543                        <$GPIOX>::reset_unchecked();
544                    }
545
546                    Parts {
547                        $(
548                            $pxi: $PXi::new(),
549                        )+
550                    }
551                }
552            }
553
554            pub type $PXn<MODE> = super::PEPin<$port_id, MODE>;
555
556            $(
557                pub type $PXi<MODE = Input<Floating>> = super::Pin<$port_id, $i, MODE>;
558            )+
559
560        }
561
562        pub use $gpiox::{ $($PXi,)+ };
563    }
564}
565
566gpio!(GPIOA, gpioa, PA, 'A', PAn, [
567    PA0: (pa0, 0),
568    PA1: (pa1, 1),
569    PA2: (pa2, 2),
570    PA3: (pa3, 3),
571    PA4: (pa4, 4),
572    PA5: (pa5, 5),
573    PA6: (pa6, 6),
574    PA7: (pa7, 7),
575    PA8: (pa8, 8),
576    PA9: (pa9, 9),
577    PA10: (pa10, 10),
578    PA11: (pa11, 11),
579    PA12: (pa12, 12),
580    PA13: (pa13, 13, super::Debugger), // SWDIO, PullUp VeryHigh speed
581    PA14: (pa14, 14, super::Debugger), // SWCLK, PullDown
582    PA15: (pa15, 15, super::Debugger), // JTDI, PullUp
583]);
584
585gpio!(GPIOB, gpiob, PB, 'B', PBn, [
586    PB0: (pb0, 0),
587    PB1: (pb1, 1),
588    PB2: (pb2, 2),
589    PB3: (pb3, 3, super::Debugger), // SWO, VeryHigh speed
590    PB4: (pb4, 4, super::Debugger), // JTRST, PullUp
591    PB5: (pb5, 5),
592    PB6: (pb6, 6),
593    PB7: (pb7, 7),
594    PB8: (pb8, 8),
595    PB9: (pb9, 9),
596    PB10: (pb10, 10),
597    PB11: (pb11, 11),
598    PB12: (pb12, 12),
599    PB13: (pb13, 13),
600    PB14: (pb14, 14),
601    PB15: (pb15, 15),
602]);
603
604gpio!(GPIOC, gpioc, PC, 'C', PCn, [
605    PC0: (pc0, 0),
606    PC1: (pc1, 1),
607    PC2: (pc2, 2),
608    PC3: (pc3, 3),
609    PC4: (pc4, 4),
610    PC5: (pc5, 5),
611    PC6: (pc6, 6),
612    PC7: (pc7, 7),
613    PC8: (pc8, 8),
614    PC9: (pc9, 9),
615    PC10: (pc10, 10),
616    PC11: (pc11, 11),
617    PC12: (pc12, 12),
618    PC13: (pc13, 13),
619    PC14: (pc14, 14),
620    PC15: (pc15, 15),
621]);
622
623gpio!(GPIOD, gpiod, PD, 'D', PDn, [
624    PD0: (pd0, 0),
625    PD1: (pd1, 1),
626    PD2: (pd2, 2),
627    PD3: (pd3, 3),
628    PD4: (pd4, 4),
629    PD5: (pd5, 5),
630    PD6: (pd6, 6),
631    PD7: (pd7, 7),
632    PD8: (pd8, 8),
633    PD9: (pd9, 9),
634    PD10: (pd10, 10),
635    PD11: (pd11, 11),
636    PD12: (pd12, 12),
637    PD13: (pd13, 13),
638    PD14: (pd14, 14),
639    PD15: (pd15, 15),
640]);
641
642gpio!(GPIOE, gpioe, PE, 'E', PEn, [
643    PE0: (pe0, 0),
644    PE1: (pe1, 1),
645    PE2: (pe2, 2),
646    PE3: (pe3, 3),
647    PE4: (pe4, 4),
648    PE5: (pe5, 5),
649    PE6: (pe6, 6),
650    PE7: (pe7, 7),
651    PE8: (pe8, 8),
652    PE9: (pe9, 9),
653    PE10: (pe10, 10),
654    PE11: (pe11, 11),
655    PE12: (pe12, 12),
656    PE13: (pe13, 13),
657    PE14: (pe14, 14),
658    PE15: (pe15, 15),
659]);
660
661gpio!(GPIOF, gpiof, PF, 'F', PFn, [
662    PF0: (pf0, 0),
663    PF1: (pf1, 1),
664    PF2: (pf2, 2),
665    PF3: (pf3, 3),
666    PF4: (pf4, 4),
667    PF5: (pf5, 5),
668    PF6: (pf6, 6),
669    PF7: (pf7, 7),
670    PF8: (pf8, 8),
671    PF9: (pf9, 9),
672    PF10: (pf10, 10),
673    PF11: (pf11, 11),
674    PF12: (pf12, 12),
675    PF13: (pf13, 13),
676    PF14: (pf14, 14),
677    PF15: (pf15, 15),
678]);
679
680gpio!(GPIOG, gpiog, PG, 'G', PGn, [
681    PG0: (pg0, 0),
682    PG1: (pg1, 1),
683    PG2: (pg2, 2),
684    PG3: (pg3, 3),
685    PG4: (pg4, 4),
686    PG5: (pg5, 5),
687    PG6: (pg6, 6),
688    PG7: (pg7, 7),
689    PG8: (pg8, 8),
690    PG9: (pg9, 9),
691    PG10: (pg10, 10),
692    PG11: (pg11, 11),
693    PG12: (pg12, 12),
694    PG13: (pg13, 13),
695    PG14: (pg14, 14),
696    PG15: (pg15, 15),
697]);
698
699gpio!(GPIOH, gpioh, PH, 'H', PHn, [
700    PH0: (ph0, 0),
701    PH1: (ph1, 1),
702    PH2: (ph2, 2),
703    PH3: (ph3, 3),
704    PH4: (ph4, 4),
705    PH5: (ph5, 5),
706    PH6: (ph6, 6),
707    PH7: (ph7, 7),
708    PH8: (ph8, 8),
709    PH9: (ph9, 9),
710    PH10: (ph10, 10),
711    PH11: (ph11, 11),
712    PH12: (ph12, 12),
713    PH13: (ph13, 13),
714    PH14: (ph14, 14),
715    PH15: (ph15, 15),
716]);
717
718gpio!(GPIOI, gpioi, PI, 'I', PIn, [
719    PI0: (pi0, 0),
720    PI1: (pi1, 1),
721    PI2: (pi2, 2),
722    PI3: (pi3, 3),
723    PI4: (pi4, 4),
724    PI5: (pi5, 5),
725    PI6: (pi6, 6),
726    PI7: (pi7, 7),
727    PI8: (pi8, 8),
728    PI9: (pi9, 9),
729    PI10: (pi10, 10),
730    PI11: (pi11, 11),
731    PI12: (pi12, 12),
732    PI13: (pi13, 13),
733    PI14: (pi14, 14),
734    PI15: (pi15, 15),
735]);
736
737#[cfg(feature = "gpioj")]
738gpio!(GPIOJ, gpioj, PJ, 'J', PJn, [
739    PJ0: (pj0, 0),
740    PJ1: (pj1, 1),
741    PJ2: (pj2, 2),
742    PJ3: (pj3, 3),
743    PJ4: (pj4, 4),
744    PJ5: (pj5, 5),
745    PJ6: (pj6, 6),
746    PJ7: (pj7, 7),
747    PJ8: (pj8, 8),
748    PJ9: (pj9, 9),
749    PJ10: (pj10, 10),
750    PJ11: (pj11, 11),
751    PJ12: (pj12, 12),
752    PJ13: (pj13, 13),
753    PJ14: (pj14, 14),
754    PJ15: (pj15, 15),
755]);
756
757#[cfg(feature = "gpiok")]
758gpio!(GPIOK, gpiok, PK, 'K', PKn, [
759    PK0: (pk0, 0),
760    PK1: (pk1, 1),
761    PK2: (pk2, 2),
762    PK3: (pk3, 3),
763    PK4: (pk4, 4),
764    PK5: (pk5, 5),
765    PK6: (pk6, 6),
766    PK7: (pk7, 7),
767]);
768
769struct Gpio<const P: char>;
770impl<const P: char> Gpio<P> {
771    const fn ptr() -> *const crate::pac::gpioa::RegisterBlock {
772        match P {
773            'A' => crate::pac::GPIOA::ptr(),
774            'B' => crate::pac::GPIOB::ptr() as _,
775            'C' => crate::pac::GPIOC::ptr() as _,
776            'D' => crate::pac::GPIOD::ptr() as _,
777            'E' => crate::pac::GPIOE::ptr() as _,
778            'F' => crate::pac::GPIOF::ptr() as _,
779            'G' => crate::pac::GPIOG::ptr() as _,
780            'H' => crate::pac::GPIOH::ptr() as _,
781            'I' => crate::pac::GPIOI::ptr() as _,
782            #[cfg(feature = "gpioj")]
783            'J' => crate::pac::GPIOJ::ptr() as _,
784            #[cfg(feature = "gpiok")]
785            'K' => crate::pac::GPIOK::ptr() as _,
786            _ => crate::pac::GPIOA::ptr(),
787        }
788    }
789}