stm32g0xx_hal/
gpio.rs

1//! General Purpose Input / Output
2use core::marker::PhantomData;
3
4use crate::rcc::Rcc;
5use core::convert::Infallible;
6use embedded_hal::digital::v2::PinState;
7use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin};
8
9/// Default pin mode
10pub type DefaultMode = Analog;
11
12/// Extension trait to split a GPIO peripheral in independent pins and registers
13pub trait GpioExt {
14    /// The parts to split the GPIO into
15    type Parts;
16
17    /// Splits the GPIO block into independent pins and registers
18    fn split(self, rcc: &mut Rcc) -> Self::Parts;
19}
20
21trait GpioRegExt {
22    fn is_low(&self, pos: u8) -> bool;
23    fn is_set_low(&self, pos: u8) -> bool;
24    fn set_high(&self, pos: u8);
25    fn set_low(&self, pos: u8);
26}
27
28/// Input mode (type state)
29pub struct Input<MODE> {
30    _mode: PhantomData<MODE>,
31}
32
33/// Floating input (type state)
34pub struct Floating;
35
36/// Pulled down input (type state)
37pub struct PullDown;
38
39/// Pulled up input (type state)
40pub struct PullUp;
41
42/// Open drain input or output (type state)
43pub struct OpenDrain;
44
45/// Analog mode (type state)
46pub struct Analog;
47
48/// Output mode (type state)
49pub struct Output<MODE> {
50    _mode: PhantomData<MODE>,
51}
52
53/// Push pull output (type state)
54pub struct PushPull;
55
56/// Fully erased pin
57pub struct Pin<MODE> {
58    i: u8,
59    port: *const dyn GpioRegExt,
60    _mode: PhantomData<MODE>,
61}
62
63macro_rules! gpio_trait {
64    ($gpiox:ident) => {
65        impl GpioRegExt for crate::stm32::$gpiox::RegisterBlock {
66            fn is_low(&self, pos: u8) -> bool {
67                // NOTE(unsafe) atomic read with no side effects
68                self.idr.read().bits() & (1 << pos) == 0
69            }
70
71            fn is_set_low(&self, pos: u8) -> bool {
72                // NOTE(unsafe) atomic read with no side effects
73                self.odr.read().bits() & (1 << pos) == 0
74            }
75
76            fn set_high(&self, pos: u8) {
77                // NOTE(unsafe) atomic write to a stateless register
78                unsafe { self.bsrr.write(|w| w.bits(1 << pos)) }
79            }
80
81            fn set_low(&self, pos: u8) {
82                // NOTE(unsafe) atomic write to a stateless register
83                unsafe { self.bsrr.write(|w| w.bits(1 << (pos + 16))) }
84            }
85        }
86    };
87}
88
89gpio_trait!(gpioa);
90gpio_trait!(gpiob);
91
92// NOTE(unsafe) The only write acess is to BSRR, which is thread safe
93unsafe impl<MODE> Sync for Pin<MODE> {}
94// NOTE(unsafe) this only enables read access to the same pin from multiple
95// threads
96unsafe impl<MODE> Send for Pin<MODE> {}
97
98impl<MODE> StatefulOutputPin for Pin<Output<MODE>> {
99    #[inline(always)]
100    fn is_set_high(&self) -> Result<bool, Self::Error> {
101        self.is_set_low().map(|v| !v)
102    }
103
104    #[inline(always)]
105    fn is_set_low(&self) -> Result<bool, Self::Error> {
106        Ok(unsafe { (*self.port).is_set_low(self.i) })
107    }
108}
109
110impl<MODE> OutputPin for Pin<Output<MODE>> {
111    type Error = Infallible;
112
113    #[inline(always)]
114    fn set_high(&mut self) -> Result<(), Self::Error> {
115        unsafe { (*self.port).set_high(self.i) };
116        Ok(())
117    }
118
119    #[inline(always)]
120    fn set_low(&mut self) -> Result<(), Self::Error> {
121        unsafe { (*self.port).set_low(self.i) }
122        Ok(())
123    }
124}
125
126impl<MODE> toggleable::Default for Pin<Output<MODE>> {}
127
128impl InputPin for Pin<Output<OpenDrain>> {
129    type Error = Infallible;
130
131    #[inline(always)]
132    fn is_high(&self) -> Result<bool, Self::Error> {
133        self.is_low().map(|v| !v)
134    }
135
136    #[inline(always)]
137    fn is_low(&self) -> Result<bool, Self::Error> {
138        Ok(unsafe { (*self.port).is_low(self.i) })
139    }
140}
141
142impl<MODE> InputPin for Pin<Input<MODE>> {
143    type Error = Infallible;
144
145    #[inline(always)]
146    fn is_high(&self) -> Result<bool, Self::Error> {
147        self.is_low().map(|v| !v)
148    }
149
150    #[inline(always)]
151    fn is_low(&self) -> Result<bool, Self::Error> {
152        Ok(unsafe { (*self.port).is_low(self.i) })
153    }
154}
155
156/// GPIO Pin speed selection
157#[derive(Debug, PartialEq, Eq, Clone, Copy)]
158pub enum Speed {
159    Low = 0,
160    Medium = 1,
161    High = 2,
162    VeryHigh = 3,
163}
164
165/// Trigger edge
166#[derive(Debug, PartialEq, Eq, Clone, Copy)]
167pub enum SignalEdge {
168    Rising,
169    Falling,
170    All,
171}
172
173#[allow(dead_code)]
174pub(crate) enum AltFunction {
175    AF0 = 0,
176    AF1 = 1,
177    AF2 = 2,
178    AF3 = 3,
179    AF4 = 4,
180    AF5 = 5,
181    AF6 = 6,
182    AF7 = 7,
183}
184
185macro_rules! gpio {
186    ($GPIOX:ident, $gpiox:ident, $PXx:ident, $Pxn:expr, [
187        $($PXi:ident: ($pxi:ident, $i:expr),)+
188    ]) => {
189        /// GPIO
190        pub mod $gpiox {
191            use core::convert::Infallible;
192            use core::marker::PhantomData;
193            use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin};
194            use crate::stm32::{EXTI, $GPIOX};
195            use crate::exti::{ExtiExt, Event};
196            use crate::rcc::{Enable, Rcc};
197            use super::*;
198
199            /// GPIO parts
200            pub struct Parts {
201                $(
202                    pub $pxi: $PXi<DefaultMode>,
203                )+
204            }
205
206            impl GpioExt for $GPIOX {
207                type Parts = Parts;
208
209                fn split(self, rcc: &mut Rcc) -> Parts {
210                    <$GPIOX>::enable(rcc);
211
212                    Parts {
213                        $(
214                            $pxi: $PXi { _mode: PhantomData },
215                        )+
216                    }
217                }
218            }
219
220            /// Partially erased pin
221            pub struct $PXx<MODE> {
222                i: u8,
223                _mode: PhantomData<MODE>,
224            }
225
226            impl<MODE> OutputPin for $PXx<Output<MODE>> {
227                type Error = Infallible;
228
229                fn set_high(&mut self) -> Result<(), Self::Error> {
230                    // NOTE(unsafe) atomic write to a stateless register
231                    unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << self.i)) };
232                    Ok(())
233                }
234
235                fn set_low(&mut self) -> Result<(), Self::Error> {
236                    // NOTE(unsafe) atomic write to a stateless register
237                    unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (self.i + 16))) };
238                    Ok(())
239                }
240            }
241
242            impl<MODE> StatefulOutputPin for $PXx<Output<MODE>> {
243                fn is_set_high(&self) -> Result<bool, Self::Error> {
244                    let is_set_high = !self.is_set_low()?;
245                    Ok(is_set_high)
246                }
247
248                fn is_set_low(&self) -> Result<bool, Self::Error> {
249                    // NOTE(unsafe) atomic read with no side effects
250                    let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << self.i) == 0 };
251                    Ok(is_set_low)
252                }
253            }
254
255            impl<MODE> toggleable::Default for $PXx<Output<MODE>> {
256            }
257
258            impl<MODE> InputPin for $PXx<Output<MODE>> {
259                type Error = Infallible;
260
261                fn is_high(&self) -> Result<bool, Self::Error> {
262                    let is_high = !self.is_low()?;
263                    Ok(is_high)
264                }
265
266                fn is_low(&self) -> Result<bool, Self::Error>  {
267                    // NOTE(unsafe) atomic read with no side effects
268                    let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 };
269                    Ok(is_low)
270                }
271            }
272
273            impl<MODE> InputPin for $PXx<Input<MODE>> {
274                type Error = Infallible;
275
276                fn is_high(&self) -> Result<bool, Self::Error> {
277                    let is_high = !self.is_low()?;
278                    Ok(is_high)
279                }
280
281                fn is_low(&self) -> Result<bool, Self::Error> {
282                    // NOTE(unsafe) atomic read with no side effects
283                    let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 };
284                    Ok(is_low)
285                }
286            }
287
288            $(
289                pub struct $PXi<MODE> {
290                    _mode: PhantomData<MODE>,
291                }
292
293                #[allow(clippy::from_over_into)]
294                impl Into<$PXi<Input<PullDown>>> for $PXi<DefaultMode> {
295                    fn into(self) -> $PXi<Input<PullDown>> {
296                        self.into_pull_down_input()
297                    }
298                }
299
300                #[allow(clippy::from_over_into)]
301                impl Into<$PXi<Input<PullUp>>> for $PXi<DefaultMode> {
302                    fn into(self) -> $PXi<Input<PullUp>> {
303                        self.into_pull_up_input()
304                    }
305                }
306
307                #[allow(clippy::from_over_into)]
308                impl Into<$PXi<Input<Floating>>> for $PXi<DefaultMode> {
309                    fn into(self) -> $PXi<Input<Floating>> {
310                        self.into_floating_input()
311                    }
312                }
313
314                #[allow(clippy::from_over_into)]
315                impl Into<$PXi<Output<OpenDrain>>> for $PXi<DefaultMode> {
316                    fn into(self) -> $PXi<Output<OpenDrain>> {
317                        self.into_open_drain_output()
318                    }
319                }
320
321                #[allow(clippy::from_over_into)]
322                impl Into<$PXi<Output<PushPull>>> for $PXi<DefaultMode> {
323                    fn into(self) -> $PXi<Output<PushPull>> {
324                        self.into_push_pull_output()
325                    }
326                }
327
328                impl<MODE> $PXi<MODE> {
329                    /// Configures the pin to operate as a floating input pin
330                    pub fn into_floating_input(self) -> $PXi<Input<Floating>> {
331                        let offset = 2 * $i;
332                        unsafe {
333                            let gpio = &(*$GPIOX::ptr());
334                            gpio.pupdr.modify(|r, w| {
335                                w.bits(r.bits() & !(0b11 << offset))
336                            });
337                            gpio.moder.modify(|r, w| {
338                                w.bits(r.bits() & !(0b11 << offset))
339                            })
340                        };
341                        $PXi { _mode: PhantomData }
342                    }
343
344                    /// Configures the pin to operate as a pulled down input pin
345                    pub fn into_pull_down_input(self) -> $PXi<Input<PullDown>> {
346                        let offset = 2 * $i;
347                        unsafe {
348                            let gpio = &(*$GPIOX::ptr());
349                            gpio.pupdr.modify(|r, w| {
350                                w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset))
351                            });
352                            gpio.moder.modify(|r, w| {
353                                w.bits(r.bits() & !(0b11 << offset))
354                            })
355                        };
356                        $PXi { _mode: PhantomData }
357                    }
358
359                    /// Configures the pin to operate as a pulled up input pin
360                    pub fn into_pull_up_input(self) -> $PXi<Input<PullUp>> {
361                        let offset = 2 * $i;
362                        unsafe {
363                            let gpio = &(*$GPIOX::ptr());
364                            gpio.pupdr.modify(|r, w| {
365                                w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
366                            });
367                            gpio.moder.modify(|r, w| {
368                                w.bits(r.bits() & !(0b11 << offset))
369                            })
370                        };
371                        $PXi { _mode: PhantomData }
372                    }
373
374                    /// Configures the pin to operate as an analog pin
375                    pub fn into_analog(self) -> $PXi<Analog> {
376                        let offset = 2 * $i;
377                        unsafe {
378                            let gpio = &(*$GPIOX::ptr());
379                            gpio.pupdr.modify(|r, w| {
380                                w.bits(r.bits() & !(0b11 << offset))
381                            });
382                            gpio.moder.modify(|r, w| {
383                                w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset))
384                            });
385                        }
386                        $PXi { _mode: PhantomData }
387                    }
388
389                    /// Configures the pin to operate as an open drain output
390                    /// pin with `initial_state` specifying whether the pin
391                    /// should initially be high or low
392                    pub fn into_open_drain_output_in_state(mut self, initial_state: PinState) -> $PXi<Output<OpenDrain>> {
393                        self.internal_set_state(initial_state);
394                        self.into_open_drain_output()
395                    }
396
397                    /// Configures the pin to operate as an open drain output pin
398                    pub fn into_open_drain_output(self) -> $PXi<Output<OpenDrain>> {
399                        let offset = 2 * $i;
400                        unsafe {
401                            let gpio = &(*$GPIOX::ptr());
402                            gpio.pupdr.modify(|r, w| {
403                                w.bits(r.bits() & !(0b11 << offset))
404                            });
405                            gpio.otyper.modify(|r, w| {
406                                w.bits(r.bits() | (0b1 << $i))
407                            });
408                            gpio.moder.modify(|r, w| {
409                                w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
410                            })
411                        };
412                        $PXi { _mode: PhantomData }
413                    }
414
415                    /// Configures the pin to operate as a push pull output pin
416                    /// with `initial_state` specifying whether the pin should
417                    /// initially be high or low
418                    pub fn into_push_pull_output_in_state(mut self, initial_state: PinState) -> $PXi<Output<PushPull>> {
419                        self.internal_set_state(initial_state);
420                        self.into_push_pull_output()
421                    }
422
423                    /// Configures the pin to operate as a push pull output pin
424                    pub fn into_push_pull_output(self) -> $PXi<Output<PushPull>> {
425                        let offset = 2 * $i;
426                        unsafe {
427                            let gpio = &(*$GPIOX::ptr());
428                            gpio.pupdr.modify(|r, w| {
429                                w.bits(r.bits() & !(0b11 << offset))
430                            });
431                            gpio.otyper.modify(|r, w| {
432                                w.bits(r.bits() & !(0b1 << $i))
433                            });
434                            gpio.moder.modify(|r, w| {
435                                w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
436                            })
437                        };
438                        $PXi { _mode: PhantomData }
439                    }
440
441                    /// Configures the pin as external trigger
442                    pub fn listen(self, edge: SignalEdge, exti: &mut EXTI) -> $PXi<Input<Floating>> {
443                        let offset = 2 * $i;
444                        unsafe {
445                            let _ = &(*$GPIOX::ptr()).pupdr.modify(|r, w| {
446                                w.bits(r.bits() & !(0b11 << offset))
447                            });
448                            &(*$GPIOX::ptr()).moder.modify(|r, w| {
449                                w.bits(r.bits() & !(0b11 << offset))
450                            })
451                        };
452                        let offset = ($i % 4) * 8;
453                        let mask = $Pxn << offset;
454                        let reset = !(0xff << offset);
455                        match $i as u8 {
456                            0..=3   => exti.exticr1.modify(|r, w| unsafe {
457                                w.bits(r.bits() & reset | mask)
458                            }),
459                            4..=7  => exti.exticr2.modify(|r, w| unsafe {
460                                w.bits(r.bits() & reset | mask)
461                            }),
462                            8..=11 => exti.exticr3.modify(|r, w| unsafe {
463                                w.bits(r.bits() & reset | mask)
464                            }),
465                            12..=16 => exti.exticr4.modify(|r, w| unsafe {
466                                w.bits(r.bits() & reset | mask)
467                            }),
468                            _ => unreachable!(),
469                        }
470                        exti.listen(Event::from_code($i), edge);
471                        $PXi { _mode: PhantomData }
472                    }
473
474                    /// Set pin speed
475                    pub fn set_speed(self, speed: Speed) -> Self {
476                        let offset = 2 * $i;
477                        unsafe {
478                            &(*$GPIOX::ptr()).ospeedr.modify(|r, w| {
479                                w.bits((r.bits() & !(0b11 << offset)) | ((speed as u32) << offset))
480                            })
481                        };
482                        self
483                    }
484
485                    #[allow(dead_code)]
486                    pub(crate) fn set_alt_mode(&self, mode: AltFunction) {
487                        let mode = mode as u32;
488                        let offset = 2 * $i;
489                        let offset2 = 4 * $i;
490                        unsafe {
491                            let gpio = &(*$GPIOX::ptr());
492                            if offset2 < 32 {
493                                gpio.afrl.modify(|r, w| {
494                                    w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2))
495                                });
496                            } else {
497                                let offset2 = offset2 - 32;
498                                gpio.afrh.modify(|r, w| {
499                                    w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2))
500                                });
501                            }
502                            gpio.moder.modify(|r, w| {
503                                w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset))
504                            });
505                        }
506                    }
507
508                    fn internal_set_state(&mut self, state: PinState) {
509                        match state {
510                            PinState::High => {
511                                // NOTE(unsafe) atomic write to a stateless register
512                                unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) };
513                            }
514                            PinState::Low => {
515                                // NOTE(unsafe) atomic write to a stateless register
516                                unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << ($i + 16))) };
517                            }
518                        }
519                    }
520                }
521
522                impl<MODE> $PXi<Output<MODE>> {
523                    /// Erases the pin number from the type
524                    ///
525                    /// This is useful when you want to collect the pins into an array where you
526                    /// need all the elements to have the same type
527                    pub fn downgrade(self) -> $PXx<Output<MODE>> {
528                        $PXx { i: $i, _mode: self._mode }
529                    }
530                }
531
532                impl<MODE> OutputPin for $PXi<Output<MODE>> {
533                    type Error = Infallible;
534
535                    fn set_high(&mut self) -> Result<(), Self::Error> {
536                        self.internal_set_state(PinState::High);
537                        Ok(())
538                    }
539
540                    fn set_low(&mut self) -> Result<(), Self::Error>{
541                        self.internal_set_state(PinState::Low);
542                        Ok(())
543                    }
544                }
545
546                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
547                    fn is_set_high(&self) -> Result<bool, Self::Error> {
548                        let is_set_high = !self.is_set_low()?;
549                        Ok(is_set_high)
550                    }
551
552                    fn is_set_low(&self) -> Result<bool, Self::Error> {
553                        // NOTE(unsafe) atomic read with no side effects
554                        let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 };
555                        Ok(is_set_low)
556                    }
557                }
558
559                impl<MODE> toggleable::Default for $PXi<Output<MODE>> {
560                }
561
562                impl<MODE> InputPin for $PXi<Output<MODE>> {
563                    type Error = Infallible;
564
565                    fn is_high(&self) -> Result<bool, Self::Error> {
566                        let is_high = !self.is_low()?;
567                        Ok(is_high)
568                    }
569
570                    fn is_low(&self) -> Result<bool, Self::Error>  {
571                        // NOTE(unsafe) atomic read with no side effects
572                        let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 };
573                        Ok(is_low)
574                    }
575                }
576
577                impl<MODE> $PXi<Input<MODE>> {
578                    /// Erases the pin number from the type
579                    ///
580                    /// This is useful when you want to collect the pins into an array where you
581                    /// need all the elements to have the same type
582                    pub fn downgrade(self) -> $PXx<Input<MODE>> {
583                        $PXx { i: $i, _mode: self._mode }
584                    }
585                }
586
587                impl<MODE> InputPin for $PXi<Input<MODE>> {
588                    type Error = Infallible;
589
590                    fn is_high(&self) -> Result<bool, Self::Error> {
591                        let is_high = !self.is_low()?;
592                        Ok(is_high)
593                    }
594
595                    fn is_low(&self) -> Result<bool, Self::Error> {
596                        // NOTE(unsafe) atomic read with no side effects
597                        let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 };
598                        Ok(is_low)
599                    }
600                }
601            )+
602
603            impl<TYPE> $PXx<TYPE> {
604                pub fn get_id (&self) -> u8 {
605                    self.i
606                }
607            }
608
609            impl<MODE> $PXx<Output<MODE>> {
610                /// Erases the port number from the type
611                ///
612                /// This is useful when you want to collect the pins into an array where you
613                /// need all the elements to have the same type
614                pub fn downgrade(self) -> Pin<Output<MODE>> {
615                    Pin {
616                        i: self.get_id(),
617                        port: $GPIOX::ptr() as *const dyn GpioRegExt,
618                        _mode: self._mode,
619                    }
620                }
621            }
622
623            impl<MODE> $PXx<Input<MODE>> {
624                /// Erases the port number from the type
625                ///
626                /// This is useful when you want to collect the pins into an array where you
627                /// need all the elements to have the same type
628                pub fn downgrade(self) -> Pin<Input<MODE>> {
629                    Pin {
630                        i: self.get_id(),
631                        port: $GPIOX::ptr() as *const dyn GpioRegExt,
632                        _mode: self._mode,
633                    }
634                }
635            }
636        }
637
638        pub use $gpiox::{ $($PXi,)+ };
639    }
640}
641
642gpio!(GPIOA, gpioa, PA, 0, [
643    PA0: (pa0, 0),
644    PA1: (pa1, 1),
645    PA2: (pa2, 2),
646    PA3: (pa3, 3),
647    PA4: (pa4, 4),
648    PA5: (pa5, 5),
649    PA6: (pa6, 6),
650    PA7: (pa7, 7),
651    PA8: (pa8, 8),
652    PA9: (pa9, 9),
653    PA10: (pa10, 10),
654    PA11: (pa11, 11),
655    PA12: (pa12, 12),
656    PA13: (pa13, 13),
657    PA14: (pa14, 14),
658    PA15: (pa15, 15),
659]);
660
661gpio!(GPIOB, gpiob, PB, 1, [
662    PB0: (pb0, 0),
663    PB1: (pb1, 1),
664    PB2: (pb2, 2),
665    PB3: (pb3, 3),
666    PB4: (pb4, 4),
667    PB5: (pb5, 5),
668    PB6: (pb6, 6),
669    PB7: (pb7, 7),
670    PB8: (pb8, 8),
671    PB9: (pb9, 9),
672    PB10: (pb10, 10),
673    PB11: (pb11, 11),
674    PB12: (pb12, 12),
675    PB13: (pb13, 13),
676    PB14: (pb14, 14),
677    PB15: (pb15, 15),
678]);
679
680gpio!(GPIOC, gpioc, PC, 2, [
681    PC0: (pc0, 0),
682    PC1: (pc1, 1),
683    PC2: (pc2, 2),
684    PC3: (pc3, 3),
685    PC4: (pc4, 4),
686    PC5: (pc5, 5),
687    PC6: (pc6, 6),
688    PC7: (pc7, 7),
689    PC8: (pc8, 8),
690    PC9: (pc9, 9),
691    PC10: (pc10, 10),
692    PC11: (pc11, 11),
693    PC12: (pc12, 12),
694    PC13: (pc13, 13),
695    PC14: (pc14, 14),
696    PC15: (pc15, 15),
697]);
698
699gpio!(GPIOD, gpiod, PD, 3, [
700    PD0: (pd0, 0),
701    PD1: (pd1, 1),
702    PD2: (pd2, 2),
703    PD3: (pd3, 3),
704    PD4: (pd4, 4),
705    PD5: (pd5, 5),
706    PD6: (pd6, 6),
707    PD7: (pd7, 7),
708    PD8: (pd8, 8),
709    PD9: (pd9, 9),
710    PD10: (pd10, 10),
711    PD11: (pd11, 11),
712    PD12: (pd12, 12),
713    PD13: (pd13, 13),
714    PD14: (pd14, 14),
715    PD15: (pd15, 15),
716]);
717
718gpio!(GPIOF, gpiof, PF, 5, [
719    PF0: (pf0, 0),
720    PF1: (pf1, 1),
721    PF2: (pf2, 2),
722    PF3: (pf3, 3),
723    PF4: (pf4, 4),
724    PF5: (pf5, 5),
725    PF6: (pf6, 6),
726    PF7: (pf7, 7),
727    PF8: (pf8, 8),
728    PF9: (pf9, 9),
729    PF10: (pf10, 10),
730    PF11: (pf11, 11),
731    PF12: (pf12, 12),
732    PF13: (pf13, 13),
733    PF14: (pf14, 14),
734    PF15: (pf15, 15),
735]);