stm32_hal2/
gpio.rs

1//! This module provides functionality for General Purpose Input and Output (GPIO) pins,
2//! including all GPIOx register functions. It also configures GPIO interrupts using SYSCFG and EXTI
3//! registers as appropriate. It allows pin mode configuration, interrupts, and DMA.
4//!
5//! The primary API uses a `Pin` struct, with its methods. There are also standalone functions
6//! available to set and read pin state, and clear interrupts, without access to a `Pin`.
7
8// todo: WL is missing port C here due to some pins being missing, and this being tough
9// todo to change with our current model. Note sure if PAC, or MCU limitation
10// todo: WL is also missing interrupt support.
11
12#[cfg(feature = "embedded_hal")]
13use core::convert::Infallible;
14
15#[cfg(feature = "embedded_hal")]
16use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
17
18use crate::pac::{self, EXTI, RCC};
19#[cfg(not(feature = "h7"))]
20use crate::util::rcc_en_reset;
21
22// #[cfg(not(any(
23//     // feature = "g0",
24//     feature = "f4",
25//     // feature = "l5",
26//     feature = "f3",
27//     feature = "l4"
28// )))]
29// use core::ops::Deref;
30
31cfg_if! {
32    if #[cfg(all(feature = "g0", not(any(feature = "g0b1", feature = "g0c1"))))] {
33        use crate::pac::DMA as DMA1;
34    } else if #[cfg(any(feature = "f4", feature = "h5"))] {} else {
35        use crate::pac::DMA1;
36    }
37}
38
39use cfg_if::cfg_if;
40use paste::paste;
41
42#[cfg(not(any(feature = "f4", feature = "l552", feature = "h5")))]
43use crate::dma::{self, ChannelCfg, DmaChannel};
44
45#[derive(Copy, Clone)]
46#[repr(u8)]
47/// Values for `GPIOx_MODER`. Sets pin to input, output, and other functionality.
48pub enum PinMode {
49    /// An input pin; read by firmware; set by something connected to the pin.
50    Input,
51    /// An output pin; set by firmware; read by something connected to the pin.
52    Output,
53    /// An alternate function, as defined in the MCU's user manual. Used for various
54    /// onboard peripherals like buses, timers etc.
55    Alt(u8),
56    /// For use with the onboard ADC and DAC. Prevent parasitic power loss on the pin
57    // if using it for one of these functionalities.
58    Analog,
59}
60
61impl PinMode {
62    /// We use this function to find the value bits due to being unable to repr(u8) with
63    /// the wrapped `AltFn` value.
64    fn val(&self) -> u8 {
65        match self {
66            Self::Input => 0b00,
67            Self::Output => 0b01,
68            Self::Alt(_) => 0b10,
69            Self::Analog => 0b11,
70        }
71    }
72}
73
74#[derive(Copy, Clone)]
75#[repr(u8)]
76/// Values for `GPIOx_OTYPER`.
77pub enum OutputType {
78    PushPull = 0,
79    OpenDrain = 1,
80}
81
82#[derive(Copy, Clone)]
83#[repr(u8)]
84/// Values for `GPIOx_OSPEEDR`. This configures I/O output speed. See the user manual
85/// for your MCU for what speeds these are. Note that Fast speed (0b10) is not
86/// available on all STM32 families.
87pub enum OutputSpeed {
88    Low = 0b00,
89    Medium = 0b01,
90    #[cfg(not(feature = "f3"))]
91    High = 0b10, // Called "Fast" on some families.
92    VeryHigh = 0b11, // Called "High" on some families.
93}
94
95#[derive(Copy, Clone)]
96#[repr(u8)]
97/// Values for `GPIOx_PUPDR`. Sets if the pin uses the internal pull-up or pull-down
98// resistor.
99pub enum Pull {
100    Floating = 0b00,
101    Up = 0b01,
102    Dn = 0b10,
103}
104
105#[derive(Copy, Clone)]
106#[repr(u8)]
107/// Values for `GPIOx_IDR` and `GPIOx_ODR`.
108pub enum PinState {
109    High = 1,
110    Low = 0,
111}
112
113#[derive(Copy, Clone)]
114#[repr(u8)]
115/// Values for `GPIOx_LCKR`.
116pub enum CfgLock {
117    NotLocked = 0,
118    Locked = 1,
119}
120
121#[derive(Copy, Clone)]
122#[repr(u8)]
123/// Values for `GPIOx_BRR`.
124pub enum ResetState {
125    NoAction = 0,
126    Reset = 1,
127}
128
129// todo: If you get rid of Port struct, rename this enum Port
130#[derive(Copy, Clone, PartialEq)]
131/// GPIO port letter
132pub enum Port {
133    A,
134    B,
135    #[cfg(not(feature = "wl"))]
136    C,
137    #[cfg(not(any(feature = "f410", feature = "wl")))]
138    D,
139    #[cfg(not(any(
140        feature = "f301",
141        feature = "f3x4",
142        feature = "f410",
143        feature = "g0",
144        feature = "wb",
145        feature = "wl"
146    )))]
147    E,
148    #[cfg(not(any(
149        feature = "f401",
150        feature = "f410",
151        feature = "f411",
152        feature = "l4x1",
153        feature = "l4x2",
154        feature = "l412",
155        feature = "l4x3",
156        feature = "wb",
157        feature = "wl"
158    )))]
159    F,
160    #[cfg(not(any(
161        feature = "f373",
162        feature = "f301",
163        feature = "f3x4",
164        feature = "f401",
165        feature = "f410",
166        feature = "f411",
167        feature = "l4x1",
168        feature = "l4x2",
169        feature = "l412",
170        feature = "l4x3",
171        feature = "g0",
172        feature = "wb",
173        feature = "wl"
174    )))]
175    G,
176    #[cfg(not(any(
177        feature = "f373",
178        feature = "f301",
179        feature = "f3x4",
180        feature = "f410",
181        feature = "l4x1",
182        feature = "l4x2",
183        feature = "l412",
184        feature = "l4x3",
185        feature = "g0",
186        feature = "g4",
187        feature = "wb",
188        feature = "wl"
189    )))]
190    H,
191    #[cfg(any(feature = "h747cm4", feature = "h747cm7", feature = "l4x6",))]
192    I,
193}
194
195impl Port {
196    /// See F303 RM section 12.1.3: each reg has an associated value
197    fn cr_val(&self) -> u8 {
198        match self {
199            Self::A => 0,
200            Self::B => 1,
201            #[cfg(not(feature = "wl"))]
202            Self::C => 2,
203            #[cfg(not(any(feature = "f410", feature = "wl")))]
204            Self::D => 3,
205            #[cfg(not(any(
206                feature = "f301",
207                feature = "f3x4",
208                feature = "f410",
209                feature = "g0",
210                feature = "wb",
211                feature = "wl"
212            )))]
213            Self::E => 4,
214            #[cfg(not(any(
215                feature = "f401",
216                feature = "f410",
217                feature = "f411",
218                feature = "l4x1",
219                feature = "l4x2",
220                feature = "l412",
221                feature = "l4x3",
222                feature = "wb",
223                feature = "wl"
224            )))]
225            Self::F => 5,
226            #[cfg(not(any(
227                feature = "f373",
228                feature = "f301",
229                feature = "f3x4",
230                feature = "f401",
231                feature = "f410",
232                feature = "f411",
233                feature = "l4x1",
234                feature = "l4x2",
235                feature = "l412",
236                feature = "l4x3",
237                feature = "g0",
238                // feature = "g4",
239                feature = "wb",
240                feature = "wl"
241            )))]
242            Self::G => 6,
243            #[cfg(not(any(
244                feature = "f373",
245                feature = "f301",
246                feature = "f3x4",
247                feature = "f410",
248                feature = "l4x1",
249                feature = "l4x2",
250                feature = "l412",
251                feature = "l4x3",
252                feature = "g0",
253                feature = "g4",
254                feature = "wb",
255                feature = "wl"
256            )))]
257            Self::H => 7,
258            #[cfg(any(feature = "h747cm4", feature = "h747cm7", feature = "l4x6",))]
259            Self::I => 8,
260        }
261    }
262}
263
264#[derive(Copy, Clone, Debug)]
265/// The pulse edge used to trigger interrupts. Either rising, falling, or either.
266pub enum Edge {
267    /// Interrupts trigger on rising pin edge.
268    Rising,
269    /// Interrupts trigger on falling pin edge.
270    Falling,
271    /// Interrupts trigger on either rising or falling pin edges.
272    Either,
273}
274
275// These macros are used to interate over pin number, for use with PAC fields.
276macro_rules! set_field {
277    ($regs:expr, $pin:expr, $reg:ident,$field:ident, $bit:ident, $val:expr, [$($num:expr),+]) => {
278        paste! {
279            unsafe {
280                match $pin {
281                    $(
282                        $num => (*$regs).$reg.modify(|_, w| w.[<$field $num>]().$bit($val)),
283                    )+
284                    _ => panic!("GPIO pins must be 0 - 15."),
285                }
286            }
287        }
288    }
289}
290
291macro_rules! set_alt {
292    ($regs: expr, $pin:expr, $field_af:ident, $val:expr, [$(($num:expr, $lh:ident)),+]) => {
293        paste! {
294            unsafe {
295                match $pin {
296                    $(
297                        $num => {
298                            #[cfg(feature = "h5")]
299                            (*$regs).moder.modify(|_, w| w.[<mode $num>]().bits(PinMode::Alt(0).val()));
300                            #[cfg(not(feature = "h5"))]
301                            (*$regs).moder.modify(|_, w| w.[<moder $num>]().bits(PinMode::Alt(0).val()));
302                            #[cfg(any(feature = "l5", feature = "g0", feature = "h5", feature = "h7", feature = "wb"))]
303                            (*$regs).[<afr $lh>].modify(|_, w| w.[<$field_af $num>]().bits($val));
304                            #[cfg(not(any(feature = "l5", feature = "g0", feature = "h5", feature = "h7", feature = "wb")))]
305                            (*$regs).[<afr $lh>].modify(|_, w| w.[<$field_af $lh $num>]().bits($val));
306                        }
307                    )+
308                    _ => panic!("GPIO pins must be 0 - 15."),
309                }
310            }
311        }
312    }
313}
314
315macro_rules! get_input_data {
316    ($regs: expr, $pin:expr, [$($num:expr),+]) => {
317        paste! {
318            unsafe {
319                match $pin {
320                    $(
321                        #[cfg(feature = "h5")]
322                        $num => (*$regs).idr.read().[<id $num>]().bit_is_set(),
323                        #[cfg(not(feature = "h5"))]
324                        $num => (*$regs).idr.read().[<idr $num>]().bit_is_set(),
325                    )+
326                    _ => panic!("GPIO pins must be 0 - 15."),
327                }
328            }
329        }
330    }
331}
332
333macro_rules! set_state {
334    ($regs: expr, $pin:expr, $offset: expr, [$($num:expr),+]) => {
335        paste! {
336            unsafe {
337                match $pin {
338                    $(
339                        $num => (*$regs).bsrr.write(|w| w.bits(1 << ($offset + $num))),
340                    )+
341                    _ => panic!("GPIO pins must be 0 - 15."),
342                }
343            }
344        }
345    }
346}
347
348// todo: Consolidate these exti macros
349
350// Reduce DRY for setting up interrupts.
351macro_rules! set_exti {
352    ($pin:expr, $rising:expr, $falling:expr, $val:expr, [$(($num:expr, $crnum:expr)),+]) => {
353        let exti = unsafe { &(*pac::EXTI::ptr()) };
354        let syscfg  = unsafe { &(*pac::SYSCFG::ptr()) };
355
356        paste! {
357            match $pin {
358                $(
359                    $num => {
360                    // todo: Core 2 interrupts for wb. (?)
361                        cfg_if! {
362                            if #[cfg(all(feature = "h7", not(any(feature = "h747cm4", feature = "h747cm7"))))] {
363                                exti.cpuimr1.modify(|_, w| w.[<mr $num>]().set_bit());
364                            } else if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
365                                exti.c1imr1.modify(|_, w| w.[<mr $num>]().set_bit());
366                            } else if #[cfg(any(feature = "g4", feature = "wb", feature = "wl"))] {
367                                exti.imr1.modify(|_, w| w.[<im $num>]().set_bit());
368                            } else {
369                                exti.imr1.modify(|_, w| w.[<mr $num>]().set_bit());
370                            }
371                        }
372
373                        cfg_if! {
374                            if #[cfg(any(feature = "g4", feature = "wb", feature = "wl"))] {
375                                exti.rtsr1.modify(|_, w| w.[<rt $num>]().bit($rising));
376                                exti.ftsr1.modify(|_, w| w.[<ft $num>]().bit($falling));
377                            // } else if #[cfg(any(feature = "wb", feature = "wl"))] {
378                            //     // todo: Missing in PAC, so we read+write. https://github.com/stm32-rs/stm32-rs/issues/570
379                            //     let val_r =  $exti.rtsr1.read().bits();
380                            //     $exti.rtsr1.write(|w| unsafe { w.bits(val_r | (1 << $num)) });
381                            //     let val_f =  $exti.ftsr1.read().bits();
382                            //     $exti.ftsr1.write(|w| unsafe { w.bits(val_f | (1 << $num)) });
383                            //     // todo: Core 2 interrupts.
384                            } else {
385                                exti.rtsr1.modify(|_, w| w.[<tr $num>]().bit($rising));
386                                exti.ftsr1.modify(|_, w| w.[<tr $num>]().bit($falling));
387                            }
388                        }
389                        syscfg
390                            .[<exticr $crnum>]
391                            .modify(|_, w| unsafe { w.[<exti $num>]().bits($val) });
392                    }
393                )+
394                _ => panic!("GPIO pins must be 0 - 15."),
395            }
396        }
397    }
398}
399
400#[cfg(feature = "f4")]
401// Similar to `set_exti`, but with reg names sans `1`.
402macro_rules! set_exti_f4 {
403    ($pin:expr, $rising:expr, $falling:expr, $val:expr, [$(($num:expr, $crnum:expr)),+]) => {
404        let exti = unsafe { &(*pac::EXTI::ptr()) };
405        let syscfg  = unsafe { &(*pac::SYSCFG::ptr()) };
406
407        paste! {
408            match $pin {
409                $(
410                    $num => {
411                        exti.imr.modify(|_, w| w.[<mr $num>]().unmasked());
412                        exti.rtsr.modify(|_, w| w.[<tr $num>]().bit($rising));
413                        exti.ftsr.modify(|_, w| w.[<tr $num>]().bit($falling));
414                        syscfg
415                            .[<exticr $crnum>]
416                            .modify(|_, w| unsafe { w.[<exti $num>]().bits($val) });
417                    }
418                )+
419                _ => panic!("GPIO pins must be 0 - 15."),
420            }
421        }
422    }
423}
424
425#[cfg(any(feature = "l5", feature = "h5"))]
426// For L5 See `set_exti!`. Different method naming pattern for exticr.
427macro_rules! set_exti_l5 {
428    ($pin:expr, $rising:expr, $falling:expr, $val:expr, [$(($num:expr, $crnum:expr, $num2:expr)),+]) => {
429        let exti = unsafe { &(*pac::EXTI::ptr()) };
430
431        paste! {
432            match $pin {
433                $(
434                    $num => {
435                        exti.imr1.modify(|_, w| w.[<im $num>]().set_bit());  // unmask
436                        exti.rtsr1.modify(|_, w| w.[<rt $num>]().bit($rising));  // Rising trigger
437                        exti.ftsr1.modify(|_, w| w.[<ft $num>]().bit($falling));   // Falling trigger
438
439                        #[cfg(feature = "l5")]
440                        exti
441                            .[<exticr $crnum>]
442                            .modify(|_, w| unsafe { w.[<exti $num2>]().bits($val) });
443
444                        #[cfg(feature = "h5")]
445                        exti
446                            .[<exticr $crnum>]
447                            .modify(|_, w| unsafe { w.[<exti $num>]().bits($val) });
448                    }
449                )+
450                _ => panic!("GPIO pins must be 0 - 15."),
451            }
452        }
453    }
454}
455
456#[cfg(feature = "g0")]
457// For G0. See `set_exti!`. Todo? Reduce DRY.
458macro_rules! set_exti_g0 {
459    ($pin:expr, $rising:expr, $falling:expr, $val:expr, [$(($num:expr, $crnum:expr, $num2:expr)),+]) => {
460        let exti = unsafe { &(*pac::EXTI::ptr()) };
461
462        paste! {
463            match $pin {
464                $(
465                    $num => {
466                        exti.imr1.modify(|_, w| w.[<im $num>]().set_bit());  // unmask
467                        exti.rtsr1.modify(|_, w| w.[<tr $num>]().bit($rising));  // Rising trigger
468                        // This field name is probably a PAC error.
469                        exti.ftsr1.modify(|_, w| w.[<tr $num>]().bit($falling));   // Falling trigger
470                        exti
471                            .[<exticr $crnum>]
472                            .modify(|_, w| unsafe { w.[<exti $num2>]().bits($val) });
473                    }
474                )+
475                _ => panic!("GPIO pins must be 0 - 15."),
476            }
477        }
478    }
479}
480
481#[derive(Clone)]
482/// Represents a single GPIO pin. Allows configuration, and reading/setting state.
483pub struct Pin {
484    /// The GPIO Port letter. Eg A, B, C.
485    pub port: Port,
486    /// The pin number: 1 - 15.
487    pub pin: u8,
488}
489
490impl Pin {
491    /// Internal function to get the appropriate GPIO block pointer.
492    const fn regs(&self) -> *const pac::gpioa::RegisterBlock {
493        // Note that we use this `const` fn and pointer casting since not all ports actually
494        // deref to GPIOA in PAC.
495        regs(self.port)
496    }
497
498    /// Create a new pin, with a specific mode. Enables the RCC peripheral clock to the port,
499    /// if not already enabled. Example: `let pa1 = Pin::new(Port::A, 1, PinMode::Output);` Leaves settings
500    /// other than mode and alternate function (if applicable) at their hardware defaults.
501    pub fn new(port: Port, pin: u8, mode: PinMode) -> Self {
502        assert!(pin <= 15, "Pin must be 0 - 15.");
503
504        let rcc = unsafe { &(*RCC::ptr()) };
505
506        match port {
507            Port::A => {
508                cfg_if! {
509                    if #[cfg(feature = "f3")] {
510                        if rcc.ahbenr.read().iopaen().bit_is_clear() {
511                            rcc_en_reset!(ahb1, iopa, rcc);
512                        }
513                    } else if #[cfg(feature = "h7")] {
514                        if rcc.ahb4enr.read().gpioaen().bit_is_clear() {
515                            rcc.ahb4enr.modify(|_, w| w.gpioaen().set_bit());
516                            rcc.ahb4rstr.modify(|_, w| w.gpioarst().set_bit());
517                            rcc.ahb4rstr.modify(|_, w| w.gpioarst().clear_bit());
518                        }
519                    } else if #[cfg(feature = "f4")] {
520                        if rcc.ahb1enr.read().gpioaen().bit_is_clear() {
521                            rcc_en_reset!(ahb1, gpioa, rcc);
522                        }
523                    } else if #[cfg(feature = "g0")] {
524                        if rcc.iopenr.read().iopaen().bit_is_clear() {
525                            rcc.iopenr.modify(|_, w| w.iopaen().set_bit());
526                            rcc.ioprstr.modify(|_, w| w.ioparst().set_bit());
527                            rcc.ioprstr.modify(|_, w| w.ioparst().clear_bit());
528                        }
529                    } else { // L4, L5, G4
530                        if rcc.ahb2enr.read().gpioaen().bit_is_clear() {
531                            rcc_en_reset!(ahb2, gpioa, rcc);
532                        }
533                    }
534                }
535            }
536            Port::B => {
537                cfg_if! {
538                    if #[cfg(feature = "f3")] {
539                        if rcc.ahbenr.read().iopben().bit_is_clear() {
540                            rcc_en_reset!(ahb1, iopb, rcc);
541                        }
542                    } else if #[cfg(feature = "h7")] {
543                        if rcc.ahb4enr.read().gpioben().bit_is_clear() {
544                            rcc.ahb4enr.modify(|_, w| w.gpioben().set_bit());
545                            rcc.ahb4rstr.modify(|_, w| w.gpiobrst().set_bit());
546                            rcc.ahb4rstr.modify(|_, w| w.gpiobrst().clear_bit());
547                        }
548                    } else if #[cfg(feature = "f4")] {
549                        if rcc.ahb1enr.read().gpioben().bit_is_clear() {
550                            rcc_en_reset!(ahb1, gpiob, rcc);
551                        }
552                    } else if #[cfg(feature = "g0")] {
553                        if rcc.iopenr.read().iopben().bit_is_clear() {
554                            rcc.iopenr.modify(|_, w| w.iopben().set_bit());
555                            rcc.ioprstr.modify(|_, w| w.iopbrst().set_bit());
556                            rcc.ioprstr.modify(|_, w| w.iopbrst().clear_bit());
557                        }
558                    } else { // L4, L5, G4
559                        if rcc.ahb2enr.read().gpioben().bit_is_clear() {
560                            rcc_en_reset!(ahb2, gpiob, rcc);
561                        }
562                    }
563                }
564            }
565            #[cfg(not(feature = "wl"))]
566            Port::C => {
567                cfg_if! {
568                    if #[cfg(feature = "f3")] {
569                        if rcc.ahbenr.read().iopcen().bit_is_clear() {
570                            rcc_en_reset!(ahb1, iopc, rcc);
571                        }
572                    } else if #[cfg(feature = "h7")] {
573                        if rcc.ahb4enr.read().gpiocen().bit_is_clear() {
574                            rcc.ahb4enr.modify(|_, w| w.gpiocen().set_bit());
575                            rcc.ahb4rstr.modify(|_, w| w.gpiocrst().set_bit());
576                            rcc.ahb4rstr.modify(|_, w| w.gpiocrst().clear_bit());
577                        }
578                    } else if #[cfg(feature = "f4")] {
579                        if rcc.ahb1enr.read().gpiocen().bit_is_clear() {
580                            rcc_en_reset!(ahb1, gpioc, rcc);
581                        }
582                    } else if #[cfg(feature = "g0")] {
583                        if rcc.iopenr.read().iopcen().bit_is_clear() {
584                            rcc.iopenr.modify(|_, w| w.iopcen().set_bit());
585                            rcc.ioprstr.modify(|_, w| w.iopcrst().set_bit());
586                            rcc.ioprstr.modify(|_, w| w.iopcrst().clear_bit());
587                        }
588                    } else { // L4, L5, G4
589                        if rcc.ahb2enr.read().gpiocen().bit_is_clear() {
590                            rcc_en_reset!(ahb2, gpioc, rcc);
591                        }
592                    }
593                }
594            }
595            #[cfg(not(any(feature = "f410", feature = "wl")))]
596            Port::D => {
597                cfg_if! {
598                    if #[cfg(feature = "f3")] {
599                        if rcc.ahbenr.read().iopden().bit_is_clear() {
600                            rcc_en_reset!(ahb1, iopd, rcc);
601                        }
602                    } else if #[cfg(feature = "h7")] {
603                        if rcc.ahb4enr.read().gpioden().bit_is_clear() {
604                            rcc.ahb4enr.modify(|_, w| w.gpioden().set_bit());
605                            rcc.ahb4rstr.modify(|_, w| w.gpiodrst().set_bit());
606                            rcc.ahb4rstr.modify(|_, w| w.gpiodrst().clear_bit());
607                        }
608                    } else if #[cfg(feature = "f4")] {
609                        if rcc.ahb1enr.read().gpioden().bit_is_clear() {
610                            rcc_en_reset!(ahb1, gpiod, rcc);
611                        }
612                    } else if #[cfg(feature = "g0")] {
613                        if rcc.iopenr.read().iopden().bit_is_clear() {
614                            rcc.iopenr.modify(|_, w| w.iopden().set_bit());
615                            rcc.ioprstr.modify(|_, w| w.iopdrst().set_bit());
616                            rcc.ioprstr.modify(|_, w| w.iopdrst().clear_bit());
617                        }
618                    } else { // L4, L5, G4
619                        if rcc.ahb2enr.read().gpioden().bit_is_clear() {
620                            rcc_en_reset!(ahb2, gpiod, rcc);
621                        }
622                    }
623                }
624            }
625            #[cfg(not(any(
626                feature = "f301",
627                feature = "f3x4",
628                feature = "f410",
629                feature = "g0",
630                feature = "wb",
631                feature = "wl"
632            )))]
633            Port::E => {
634                cfg_if! {
635                    if #[cfg(feature = "f3")] {
636                        if rcc.ahbenr.read().iopeen().bit_is_clear() {
637                            rcc_en_reset!(ahb1, iope, rcc);
638                        }
639                    } else if #[cfg(feature = "h7")] {
640                        if rcc.ahb4enr.read().gpioeen().bit_is_clear() {
641                            rcc.ahb4enr.modify(|_, w| w.gpioeen().set_bit());
642                            rcc.ahb4rstr.modify(|_, w| w.gpioerst().set_bit());
643                            rcc.ahb4rstr.modify(|_, w| w.gpioerst().clear_bit());
644                        }
645                    } else if #[cfg(feature = "f4")] {
646                        if rcc.ahb1enr.read().gpioeen().bit_is_clear() {
647                            rcc_en_reset!(ahb1, gpioe, rcc);
648                        }
649                    } else if #[cfg(feature = "g0")] {
650                        if rcc.iopenr.read().iopeen().bit_is_clear() {
651                            rcc.iopenr.modify(|_, w| w.iopeen().set_bit());
652                            rcc.ioprstr.modify(|_, w| w.ioperst().set_bit());
653                            rcc.ioprstr.modify(|_, w| w.ioperst().clear_bit());
654                        }
655                    } else { // L4, L5, G4
656                        if rcc.ahb2enr.read().gpioeen().bit_is_clear() {
657                            rcc_en_reset!(ahb2, gpioe, rcc);
658                        }
659                    }
660                }
661            }
662            #[cfg(not(any(
663                feature = "f401",
664                feature = "f410",
665                feature = "f411",
666                feature = "l4x1",
667                feature = "l4x2",
668                feature = "l412",
669                feature = "l4x3",
670                feature = "wb",
671                feature = "wl"
672            )))]
673            Port::F => {
674                cfg_if! {
675                    if #[cfg(feature = "f3")] {
676                        if rcc.ahbenr.read().iopfen().bit_is_clear() {
677                            rcc_en_reset!(ahb1, iopf, rcc);
678                        }
679                    } else if #[cfg(feature = "h7")] {
680                        if rcc.ahb4enr.read().gpiofen().bit_is_clear() {
681                            rcc.ahb4enr.modify(|_, w| w.gpiofen().set_bit());
682                            rcc.ahb4rstr.modify(|_, w| w.gpiofrst().set_bit());
683                            rcc.ahb4rstr.modify(|_, w| w.gpiofrst().clear_bit());
684                        }
685                    } else if #[cfg(feature = "f4")] {
686                        if rcc.ahb1enr.read().gpiofen().bit_is_clear() {
687                            rcc_en_reset!(ahb1, gpiof, rcc);
688                        }
689                    } else if #[cfg(feature = "g0")] {
690                        if rcc.iopenr.read().iopfen().bit_is_clear() {
691                            rcc.iopenr.modify(|_, w| w.iopfen().set_bit());
692                            rcc.ioprstr.modify(|_, w| w.iopfrst().set_bit());
693                            rcc.ioprstr.modify(|_, w| w.iopfrst().clear_bit());
694                        }
695                    } else { // L4, L5, G4
696                        if rcc.ahb2enr.read().gpiofen().bit_is_clear() {
697                            rcc_en_reset!(ahb2, gpiof, rcc);
698                        }
699                    }
700                }
701            }
702            #[cfg(not(any(
703                feature = "f373",
704                feature = "f301",
705                feature = "f3x4",
706                feature = "f401",
707                feature = "f410",
708                feature = "f411",
709                feature = "l4x1",
710                feature = "l4x2",
711                feature = "l412",
712                feature = "l4x3",
713                feature = "g0",
714                feature = "wb",
715                feature = "wl"
716            )))]
717            Port::G => {
718                cfg_if! {
719                    if #[cfg(feature = "f3")] {
720                        if rcc.ahbenr.read().iopgen().bit_is_clear() {
721                            rcc_en_reset!(ahb1, iopg, rcc);
722                        }
723                    } else if #[cfg(feature = "h7")] {
724                        if rcc.ahb4enr.read().gpiogen().bit_is_clear() {
725                            rcc.ahb4enr.modify(|_, w| w.gpiogen().set_bit());
726                            rcc.ahb4rstr.modify(|_, w| w.gpiogrst().set_bit());
727                            rcc.ahb4rstr.modify(|_, w| w.gpiogrst().clear_bit());
728                        }
729                    } else if #[cfg(feature = "f4")] {
730                        if rcc.ahb1enr.read().gpiogen().bit_is_clear() {
731                            rcc_en_reset!(ahb1, gpiog, rcc);
732                        }
733                    } else if #[cfg(feature = "g0")] {
734                        if rcc.iopenr.read().iopgen().bit_is_clear() {
735                            rcc.iopenr.modify(|_, w| w.iopgen().set_bit());
736                            rcc.ioprstr.modify(|_, w| w.iopgrst().set_bit());
737                            rcc.ioprstr.modify(|_, w| w.iopgrst().clear_bit());
738                        }
739                    } else { // L4, L5, G4
740                        if rcc.ahb2enr.read().gpiogen().bit_is_clear() {
741                            rcc_en_reset!(ahb2, gpiog, rcc);
742
743                            #[cfg(feature = "l4x6")]
744                            {
745                                let pwr = unsafe { &(*pac::PWR::ptr()) };
746                                // RM0351: Setting this bit (IOSV) is mandatory to use PG[15:2].
747                                rcc.apb1enr1.modify(|_, w| w.pwren().set_bit());
748                                pwr.cr2.modify(|_, w| w.iosv().set_bit());
749                            }
750                        }
751                    }
752                }
753                #[cfg(feature = "l5")]
754                // also for RM0351 L4 variants, which we don't currently support
755                // L5 RM: "[The IOSV bit] is used to validate the VDDIO2 supply for electrical and logical isolation purpose.
756                // Setting this bit is mandatory to use PG[15:2]."
757                {
758                    unsafe {
759                        (*crate::pac::PWR::ptr())
760                            .cr2
761                            .modify(|_, w| w.iosv().set_bit());
762                    }
763                }
764            }
765            #[cfg(not(any(
766                feature = "f373",
767                feature = "f301",
768                feature = "f3x4",
769                feature = "f410",
770                feature = "l4x1",
771                feature = "l4x2",
772                feature = "l412",
773                feature = "l4x3",
774                feature = "g0",
775                feature = "g4",
776                feature = "wb",
777                feature = "wl"
778            )))]
779            Port::H => {
780                cfg_if! {
781                    if #[cfg(feature = "f3")] {
782                        if rcc.ahbenr.read().iophen().bit_is_clear() {
783                            rcc_en_reset!(ahb1, ioph, rcc);
784                        }
785                    } else if #[cfg(feature = "h7")] {
786                        if rcc.ahb4enr.read().gpiohen().bit_is_clear() {
787                            rcc.ahb4enr.modify(|_, w| w.gpiohen().set_bit());
788                            rcc.ahb4rstr.modify(|_, w| w.gpiohrst().set_bit());
789                            rcc.ahb4rstr.modify(|_, w| w.gpiohrst().clear_bit());
790                        }
791                    } else if #[cfg(feature = "f4")] {
792                        if rcc.ahb1enr.read().gpiohen().bit_is_clear() {
793                            rcc_en_reset!(ahb1, gpioh, rcc);
794                        }
795                    } else if #[cfg(feature = "g0")] {
796                        if rcc.iopenr.read().iophen().bit_is_clear() {
797                            rcc.iopenr.modify(|_, w| w.iophen().set_bit());
798                            rcc.ioprstr.modify(|_, w| w.iophrst().set_bit());
799                            rcc.ioprstr.modify(|_, w| w.iophrst().clear_bit());
800                        }
801                    } else { // L4, L5, G4
802                        if rcc.ahb2enr.read().gpiohen().bit_is_clear() {
803                            rcc_en_reset!(ahb2, gpioh, rcc);
804                        }
805                    }
806                }
807            }
808            #[cfg(any(feature = "l4x6", feature = "h747cm4", feature = "h747cm7"))]
809            Port::I => {
810                cfg_if! {
811                    if #[cfg(feature = "h7")] {
812                        if rcc.ahb4enr.read().gpioien().bit_is_clear() {
813                            rcc.ahb4enr.modify(|_, w| w.gpioien().set_bit());
814                            rcc.ahb4rstr.modify(|_, w| w.gpioirst().set_bit());
815                            rcc.ahb4rstr.modify(|_, w| w.gpioirst().clear_bit());
816                        }
817                    } else if #[cfg(feature = "l4")] {
818                        if rcc.ahb2enr.read().gpioien().bit_is_clear() {
819                            rcc.ahb2enr.modify(|_,w| w.gpioien().set_bit());
820                            rcc.ahb2rstr.modify(|_, w| w.gpioirst().set_bit());
821                            rcc.ahb2rstr.modify(|_, w| w.gpioirst().clear_bit());
822                        }
823                    }
824                }
825            }
826        }
827
828        let mut result = Self { port, pin };
829        result.mode(mode);
830
831        result
832    }
833
834    /// Set pin mode. Eg, Output, Input, Analog, or Alt. Sets the `MODER` register.
835    pub fn mode(&mut self, value: PinMode) {
836        #[cfg(feature = "h5")] // todo: Probably needs a PAC fix for H5.
837        set_field!(
838            self.regs(),
839            self.pin,
840            moder,
841            mode,
842            bits,
843            value.val(),
844            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
845        );
846
847        #[cfg(not(feature = "h5"))] // todo: Probably needs a PAC fix for H5.
848        set_field!(
849            self.regs(),
850            self.pin,
851            moder,
852            moder,
853            bits,
854            value.val(),
855            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
856        );
857
858        if let PinMode::Alt(alt) = value {
859            self.alt_fn(alt);
860        }
861    }
862
863    /// Set output type. Sets the `OTYPER` register.
864    pub fn output_type(&mut self, value: OutputType) {
865        set_field!(
866            self.regs(),
867            self.pin,
868            otyper,
869            ot,
870            bit,
871            value as u8 != 0,
872            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
873        );
874    }
875
876    /// Set output speed to Low, Medium, or High. Sets the `OSPEEDR` register.
877    pub fn output_speed(&mut self, value: OutputSpeed) {
878        #[cfg(not(feature = "h5"))] // todo: Probably needs a PAC fix for H5.
879        set_field!(
880            self.regs(),
881            self.pin,
882            ospeedr,
883            ospeedr,
884            bits,
885            value as u8,
886            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
887        );
888    }
889
890    /// Set internal pull resistor: Pull up, pull down, or floating. Sets the `PUPDR` register.
891    pub fn pull(&mut self, value: Pull) {
892        #[cfg(feature = "h5")] // todo: Probably needs a PAC fix for H5.
893        set_field!(
894            self.regs(),
895            self.pin,
896            pupdr,
897            pupd,
898            bits,
899            value as u8,
900            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
901        );
902
903        #[cfg(not(feature = "h5"))] // todo: Probably needs a PAC fix for H5.
904        set_field!(
905            self.regs(),
906            self.pin,
907            pupdr,
908            pupdr,
909            bits,
910            value as u8,
911            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
912        );
913    }
914
915    // TODO: F373 doesn't have LOCKR on ports C, E, F. You can impl for others
916    #[cfg(not(feature = "f373"))]
917    /// Lock or unlock a port configuration. Sets the `LCKR` register.
918    pub fn cfg_lock(&mut self, value: CfgLock) {
919        set_field!(
920            self.regs(),
921            self.pin,
922            lckr,
923            lck,
924            bit,
925            value as u8 != 0,
926            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
927        );
928    }
929
930    /// Read the input data register. Eg determine if the pin is high or low. See also `is_high()`
931    /// and `is_low()`. Reads from the `IDR` register.
932    pub fn get_state(&mut self) -> PinState {
933        let val = get_input_data!(
934            self.regs(),
935            self.pin,
936            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
937        );
938        if val { PinState::High } else { PinState::Low }
939    }
940
941    /// Set a pin state (ie set high or low output voltage level). See also `set_high()` and
942    /// `set_low()`. Sets the `BSRR` register. Atomic.
943    pub fn set_state(&mut self, value: PinState) {
944        let offset = match value {
945            PinState::Low => 16,
946            PinState::High => 0,
947        };
948
949        set_state!(
950            self.regs(),
951            self.pin,
952            offset,
953            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
954        );
955    }
956
957    /// Set up a pin's alternate function. We set this up initially using `mode()`.
958    fn alt_fn(&mut self, value: u8) {
959        assert!(value <= 15, "Alt function must be 0 to 15.");
960
961        cfg_if! {
962            if #[cfg(any(feature = "l5", feature = "g0", feature = "wb", feature = "h5"))] {
963                set_alt!(self.regs(), self.pin, afsel, value, [(0, l), (1, l), (2, l),
964                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
965                    (13, h), (14, h), (15, h)])
966            } else if #[cfg(feature = "h7")] {
967                set_alt!(self.regs(), self.pin, afr, value, [(0, l), (1, l), (2, l),
968                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
969                    (13, h), (14, h), (15, h)])
970            } else {  // f3, f4, l4, g4, wl(?)
971                set_alt!(self.regs(), self.pin, afr, value, [(0, l), (1, l), (2, l),
972                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
973                    (13, h), (14, h), (15, h)])
974            }
975        }
976    }
977
978    #[cfg(not(any(feature = "f373", feature = "wl")))]
979    /// Configure this pin as an interrupt source. Set the edge as Rising or Falling.
980    pub fn enable_interrupt(&mut self, edge: Edge) {
981        let rising = match edge {
982            Edge::Falling => false,
983            _ => true, // rising or either.
984        };
985
986        let falling = match edge {
987            Edge::Rising => false,
988            _ => true, // falling or either.
989        };
990
991        cfg_if! {
992            if #[cfg(feature = "g0")] {
993                set_exti_g0!(self.pin, rising, falling, self.port.cr_val(), [(0, 1, 0_7), (1, 1, 0_7), (2, 1, 0_7),
994                    (3, 1, 0_7), (4, 2, 0_7), (5, 2, 0_7), (6, 2, 0_7), (7, 2, 0_7), (8, 3, 8_15),
995                    (9, 3, 8_15), (10, 3, 8_15), (11, 3, 8_15), (12, 4, 8_15),
996                    (13, 4, 8_15), (14, 4, 8_15), (15, 4, 8_15)]
997                );
998            } else if #[cfg(any(feature = "l5", feature = "h5"))] {
999                set_exti_l5!(self.pin, rising, falling, self.port.cr_val(), [(0, 1, 0_7), (1, 1, 0_7), (2, 1, 0_7),
1000                    (3, 1, 0_7), (4, 2, 0_7), (5, 2, 0_7), (6, 2, 0_7), (7, 2, 0_7), (8, 3, 8_15),
1001                    (9, 3, 8_15), (10, 3, 8_15), (11, 3, 8_15), (12, 4, 8_15),
1002                    (13, 4, 8_15), (14, 4, 8_15), (15, 4, 8_15)]
1003                );
1004            } else if #[cfg(feature = "f4")] {
1005                set_exti_f4!(self.pin, rising, falling, self.port.cr_val(), [(0, 1), (1, 1), (2, 1),
1006                        (3, 1), (4, 2), (5, 2), (6, 2), (7, 2), (8, 3), (9, 3), (10, 3), (11, 3), (12, 4),
1007                        (13, 4), (14, 4), (15, 4)]
1008                );
1009            } else {
1010                set_exti!(self.pin, rising, falling, self.port.cr_val(), [(0, 1), (1, 1), (2, 1),
1011                    (3, 1), (4, 2), (5, 2), (6, 2), (7, 2), (8, 3), (9, 3), (10, 3), (11, 3), (12, 4),
1012                    (13, 4), (14, 4), (15, 4)]
1013                );
1014            }
1015        }
1016    }
1017
1018    #[cfg(feature = "l4x6")]
1019    /// For the ADC, DAC, OPAMP and COMP, configure the desired I/O in analog mode
1020    /// in the GPIOx_MODER register and configure the required function in the ADC,
1021    /// DAC, OPAMP, and COMP registers. For the ADC, it is necessary to configure the
1022    /// GPIOx_ASCR register (only for STM32L47x/L48x). Note that our `l4x6` feature
1023    /// gate is good enough here, since the most popular variants affected are L476 and L486.
1024    /// todo: Disconnect method?
1025    pub fn connect_to_adc(&mut self) {
1026        set_field!(
1027            self.regs(),
1028            self.pin,
1029            ascr,
1030            asc,
1031            bit,
1032            true,
1033            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1034        );
1035    }
1036
1037    /// Check if the pin's input voltage is high. Reads from the `IDR` register.
1038    pub fn is_high(&self) -> bool {
1039        get_input_data!(
1040            self.regs(),
1041            self.pin,
1042            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1043        )
1044    }
1045
1046    /// Check if the pin's input voltage is low. Reads from the `IDR` register.
1047    pub fn is_low(&self) -> bool {
1048        !self.is_high()
1049    }
1050
1051    /// Set the pin's output voltage to high. Sets the `BSRR` register. Atomic.
1052    pub fn set_high(&mut self) {
1053        self.set_state(PinState::High);
1054    }
1055
1056    /// Set the pin's output voltage to low. Sets the `BSRR` register. Atomic.
1057    pub fn set_low(&mut self) {
1058        self.set_state(PinState::Low);
1059    }
1060
1061    /// Toggle output voltage between low and high. Sets the `BSRR` register. Atomic.
1062    pub fn toggle(&mut self) {
1063        // if self.is_high() {
1064        if Pin::is_high(self) {
1065            Pin::set_low(self);
1066            // self.set_low();
1067        } else {
1068            // self.set_high();
1069            Pin::set_high(self);
1070        }
1071    }
1072}
1073
1074#[cfg(feature = "embedded_hal")]
1075impl ErrorType for Pin {
1076    type Error = Infallible;
1077}
1078
1079#[cfg(feature = "embedded_hal")]
1080impl InputPin for Pin {
1081    fn is_high(&mut self) -> Result<bool, Self::Error> {
1082        Ok(Pin::is_high(self))
1083    }
1084
1085    fn is_low(&mut self) -> Result<bool, Self::Error> {
1086        Ok(Pin::is_low(self))
1087    }
1088}
1089
1090#[cfg(feature = "embedded_hal")]
1091impl OutputPin for Pin {
1092    fn set_low(&mut self) -> Result<(), Self::Error> {
1093        Pin::set_low(self);
1094        Ok(())
1095    }
1096
1097    fn set_high(&mut self) -> Result<(), Self::Error> {
1098        Pin::set_high(self);
1099        Ok(())
1100    }
1101}
1102
1103#[cfg(feature = "embedded_hal")]
1104impl StatefulOutputPin for Pin {
1105    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
1106        Ok(Pin::is_high(self))
1107    }
1108
1109    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
1110        Ok(Pin::is_low(self))
1111    }
1112
1113    fn toggle(&mut self) -> Result<(), Self::Error> {
1114        Pin::toggle(self);
1115        Ok(())
1116    }
1117}
1118
1119/// Check if a pin's input voltage is high. Reads from the `IDR` register.
1120/// Does not require a `Pin` struct.
1121pub fn is_high(port: Port, pin: u8) -> bool {
1122    get_input_data!(
1123        regs(port),
1124        pin,
1125        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1126    )
1127}
1128
1129/// Check if a pin's input voltage is low. Reads from the `IDR` register.
1130/// Does not require a `Pin` struct.
1131pub fn is_low(port: Port, pin: u8) -> bool {
1132    !is_high(port, pin)
1133}
1134
1135/// Set a pin's output voltage to high. Sets the `BSRR` register. Atomic.
1136/// Does not require a `Pin` struct.
1137pub fn set_high(port: Port, pin: u8) {
1138    set_state(port, pin, PinState::High);
1139}
1140
1141/// Set a pin's output voltage to low. Sets the `BSRR` register. Atomic.
1142/// Does not require a `Pin` struct.
1143pub fn set_low(port: Port, pin: u8) {
1144    set_state(port, pin, PinState::Low);
1145}
1146
1147/// Set a pin state (ie set high or low output voltage level). See also `set_high()` and
1148/// `set_low()`. Sets the `BSRR` register. Atomic.
1149/// Does not require a `Pin` struct.
1150pub fn set_state(port: Port, pin: u8, value: PinState) {
1151    let offset = match value {
1152        PinState::Low => 16,
1153        PinState::High => 0,
1154    };
1155
1156    set_state!(
1157        regs(port),
1158        pin,
1159        offset,
1160        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1161    );
1162}
1163
1164/// Clear an EXTI interrupt, lines 0 - 15. Note that this function currently doesn't support
1165/// higher extis, but will work for all GPIO interrupts.
1166pub fn clear_exti_interrupt(line: u8) {
1167    // todo: Macro to avoid DRY?
1168    unsafe {
1169        cfg_if! {
1170            if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
1171                (*EXTI::ptr()).c1pr1.modify(|_, w| {
1172                    match line {
1173                        0 => w.pr0().set_bit(),
1174                        1 => w.pr1().set_bit(),
1175                        2 => w.pr2().set_bit(),
1176                        3 => w.pr3().set_bit(),
1177                        4 => w.pr4().set_bit(),
1178                        5 => w.pr5().set_bit(),
1179                        6 => w.pr6().set_bit(),
1180                        7 => w.pr7().set_bit(),
1181                        8 => w.pr8().set_bit(),
1182                        9 => w.pr9().set_bit(),
1183                        10 => w.pr10().set_bit(),
1184                        11 => w.pr11().set_bit(),
1185                        12 => w.pr12().set_bit(),
1186                        13 => w.pr13().set_bit(),
1187                        14 => w.pr14().set_bit(),
1188                        15 => w.pr15().set_bit(),
1189                        _ => panic!(),
1190                    }
1191                });
1192            } else if #[cfg(feature = "h7")] {
1193                (*EXTI::ptr()).cpupr1.modify(|_, w| {
1194                    match line {
1195                        0 => w.pr0().set_bit(),
1196                        1 => w.pr1().set_bit(),
1197                        2 => w.pr2().set_bit(),
1198                        3 => w.pr3().set_bit(),
1199                        4 => w.pr4().set_bit(),
1200                        5 => w.pr5().set_bit(),
1201                        6 => w.pr6().set_bit(),
1202                        7 => w.pr7().set_bit(),
1203                        8 => w.pr8().set_bit(),
1204                        9 => w.pr9().set_bit(),
1205                        10 => w.pr10().set_bit(),
1206                        11 => w.pr11().set_bit(),
1207                        12 => w.pr12().set_bit(),
1208                        13 => w.pr13().set_bit(),
1209                        14 => w.pr14().set_bit(),
1210                        15 => w.pr15().set_bit(),
1211                        _ => panic!(),
1212                    }
1213                });
1214            } else if #[cfg(any(feature = "l5", feature = "g0"))] {
1215                (*EXTI::ptr()).rpr1.modify(|_, w| {
1216                    match line {
1217                        0 => w.rpif0().set_bit(),
1218                        1 => w.rpif1().set_bit(),
1219                        2 => w.rpif2().set_bit(),
1220                        3 => w.rpif3().set_bit(),
1221                        4 => w.rpif4().set_bit(),
1222                        5 => w.rpif5().set_bit(),
1223                        6 => w.rpif6().set_bit(),
1224                        7 => w.rpif7().set_bit(),
1225                        8 => w.rpif8().set_bit(),
1226                        9 => w.rpif9().set_bit(),
1227                        10 => w.rpif10().set_bit(),
1228                        11 => w.rpif11().set_bit(),
1229                        12 => w.rpif12().set_bit(),
1230                        13 => w.rpif13().set_bit(),
1231                        14 => w.rpif14().set_bit(),
1232                        15 => w.rpif15().set_bit(),
1233                        _ => panic!(),
1234                    }
1235                });
1236            } else if #[cfg(any(feature = "f373", feature = "f4"))] {
1237                (*EXTI::ptr()).pr.modify(|_, w| {
1238                    match line {
1239                        0 => w.pr0().set_bit(),
1240                        1 => w.pr1().set_bit(),
1241                        2 => w.pr2().set_bit(),
1242                        3 => w.pr3().set_bit(),
1243                        4 => w.pr4().set_bit(),
1244                        5 => w.pr5().set_bit(),
1245                        6 => w.pr6().set_bit(),
1246                        7 => w.pr7().set_bit(),
1247                        8 => w.pr8().set_bit(),
1248                        9 => w.pr9().set_bit(),
1249                        10 => w.pr10().set_bit(),
1250                        11 => w.pr11().set_bit(),
1251                        12 => w.pr12().set_bit(),
1252                        13 => w.pr13().set_bit(),
1253                        14 => w.pr14().set_bit(),
1254                        15 => w.pr15().set_bit(),
1255                        _ => panic!(),
1256                    }
1257                });
1258            } else if #[cfg(any(feature = "f3", feature = "l4"))] {
1259                (*EXTI::ptr()).pr1.modify(|_, w| {
1260                    match line {
1261                        0 => w.pr0().set_bit(),
1262                        1 => w.pr1().set_bit(),
1263                        2 => w.pr2().set_bit(),
1264                        3 => w.pr3().set_bit(),
1265                        4 => w.pr4().set_bit(),
1266                        5 => w.pr5().set_bit(),
1267                        6 => w.pr6().set_bit(),
1268                        7 => w.pr7().set_bit(),
1269                        8 => w.pr8().set_bit(),
1270                        9 => w.pr9().set_bit(),
1271                        10 => w.pr10().set_bit(),
1272                        11 => w.pr11().set_bit(),
1273                        12 => w.pr12().set_bit(),
1274                        13 => w.pr13().set_bit(),
1275                        14 => w.pr14().set_bit(),
1276                        15 => w.pr15().set_bit(),
1277                        _ => panic!(),
1278                    }
1279                });
1280              } else if #[cfg(feature = "h5")] {
1281                (*EXTI::ptr()).rpr1.modify(|_, w| {
1282                    match line {
1283                        0 => w.rpif0().set_bit(),
1284                        1 => w.rpif1().set_bit(),
1285                        2 => w.rpif2().set_bit(),
1286                        3 => w.rpif3().set_bit(),
1287                        4 => w.rpif4().set_bit(),
1288                        5 => w.rpif5().set_bit(),
1289                        6 => w.rpif6().set_bit(),
1290                        7 => w.rpif7().set_bit(),
1291                        8 => w.rpif8().set_bit(),
1292                        9 => w.rpif9().set_bit(),
1293                        10 => w.rpif10().set_bit(),
1294                        11 => w.rpif11().set_bit(),
1295                        12 => w.rpif12().set_bit(),
1296                        13 => w.rpif13().set_bit(),
1297                        14 => w.rpif14().set_bit(),
1298                        15 => w.rpif15().set_bit(),
1299                        _ => panic!(),
1300                    }
1301                });
1302            } else { // eg G4
1303                (*EXTI::ptr()).pr1.modify(|_, w| {
1304                    match line {
1305                        0 => w.pif0().set_bit(),
1306                        1 => w.pif1().set_bit(),
1307                        2 => w.pif2().set_bit(),
1308                        3 => w.pif3().set_bit(),
1309                        4 => w.pif4().set_bit(),
1310                        5 => w.pif5().set_bit(),
1311                        6 => w.pif6().set_bit(),
1312                        7 => w.pif7().set_bit(),
1313                        8 => w.pif8().set_bit(),
1314                        9 => w.pif9().set_bit(),
1315                        10 => w.pif10().set_bit(),
1316                        11 => w.pif11().set_bit(),
1317                        12 => w.pif12().set_bit(),
1318                        13 => w.pif13().set_bit(),
1319                        14 => w.pif14().set_bit(),
1320                        15 => w.pif15().set_bit(),
1321                        _ => panic!(),
1322                    }
1323                });
1324            }
1325        }
1326    }
1327}
1328
1329const fn regs(port: Port) -> *const pac::gpioa::RegisterBlock {
1330    // Note that we use this `const` fn and pointer casting since not all ports actually
1331    // deref to GPIOA in PAC.
1332    match port {
1333        Port::A => crate::pac::GPIOA::ptr(),
1334        Port::B => crate::pac::GPIOB::ptr() as _,
1335        #[cfg(not(feature = "wl"))]
1336        Port::C => crate::pac::GPIOC::ptr() as _,
1337        #[cfg(not(any(feature = "f410", feature = "wl")))]
1338        Port::D => crate::pac::GPIOD::ptr() as _,
1339        #[cfg(not(any(
1340            feature = "f301",
1341            feature = "f3x4",
1342            feature = "f410",
1343            feature = "g0",
1344            feature = "wb",
1345            feature = "wl"
1346        )))]
1347        Port::E => crate::pac::GPIOE::ptr() as _,
1348        #[cfg(not(any(
1349            feature = "f401",
1350            feature = "f410",
1351            feature = "f411",
1352            feature = "l4x1",
1353            feature = "l4x2",
1354            feature = "l412",
1355            feature = "l4x3",
1356            feature = "wb",
1357            feature = "wl"
1358        )))]
1359        Port::F => crate::pac::GPIOF::ptr() as _,
1360        #[cfg(not(any(
1361            feature = "f373",
1362            feature = "f301",
1363            feature = "f3x4",
1364            feature = "f401",
1365            feature = "f410",
1366            feature = "f411",
1367            feature = "l4x1",
1368            feature = "l4x2",
1369            feature = "l412",
1370            feature = "l4x3",
1371            feature = "g0",
1372            feature = "wb",
1373            feature = "wl"
1374        )))]
1375        Port::G => crate::pac::GPIOG::ptr() as _,
1376        #[cfg(not(any(
1377            feature = "f373",
1378            feature = "f301",
1379            feature = "f3x4",
1380            feature = "f410",
1381            feature = "l4x1",
1382            feature = "l4x2",
1383            feature = "l412",
1384            feature = "l4x3",
1385            feature = "g0",
1386            feature = "g4",
1387            feature = "wb",
1388            feature = "wl"
1389        )))]
1390        Port::H => crate::pac::GPIOH::ptr() as _,
1391        #[cfg(any(feature = "h747cm4", feature = "h747cm7", feature = "l4x6"))]
1392        Port::I => crate::pac::GPIOI::ptr() as _,
1393    }
1394}
1395
1396#[cfg(not(any(
1397    feature = "f4",
1398    feature = "l5",
1399    feature = "f3",
1400    feature = "l4",
1401    feature = "h5"
1402)))]
1403/// Write a series of words to the BSRR (atomic output) register. Note that these are direct writes
1404/// to the full, 2-sided register - not a series of low/high values.
1405pub unsafe fn write_dma(
1406    buf: &[u32],
1407    port: Port,
1408    dma_channel: DmaChannel,
1409    channel_cfg: ChannelCfg,
1410    dma_periph: dma::DmaPeriph,
1411) {
1412    let (ptr, len) = (buf.as_ptr(), buf.len());
1413
1414    let periph_addr = &(*(regs(port))).bsrr as *const _ as u32;
1415
1416    #[cfg(feature = "h7")]
1417    let num_data = len as u32;
1418    #[cfg(not(feature = "h7"))]
1419    let num_data = len as u16;
1420
1421    match dma_periph {
1422        dma::DmaPeriph::Dma1 => {
1423            let mut regs = unsafe { &(*DMA1::ptr()) };
1424            dma::cfg_channel(
1425                &mut regs,
1426                dma_channel,
1427                periph_addr,
1428                ptr as u32,
1429                num_data,
1430                dma::Direction::ReadFromMem,
1431                dma::DataSize::S32,
1432                dma::DataSize::S32,
1433                channel_cfg,
1434            );
1435        }
1436        #[cfg(not(any(feature = "g0", feature = "wb")))]
1437        dma::DmaPeriph::Dma2 => {
1438            let mut regs = unsafe { &(*pac::DMA2::ptr()) };
1439            dma::cfg_channel(
1440                &mut regs,
1441                dma_channel,
1442                periph_addr,
1443                ptr as u32,
1444                num_data,
1445                dma::Direction::ReadFromMem,
1446                dma::DataSize::S32,
1447                dma::DataSize::S32,
1448                channel_cfg,
1449            );
1450        }
1451    }
1452}
1453
1454#[cfg(not(any(
1455    feature = "f4",
1456    feature = "l5",
1457    feature = "f3",
1458    feature = "l4",
1459    feature = "h5"
1460)))]
1461/// Read a series of words from the IDR register.
1462pub unsafe fn read_dma(
1463    buf: &[u32],
1464    port: Port,
1465    dma_channel: DmaChannel,
1466    channel_cfg: ChannelCfg,
1467    dma_periph: dma::DmaPeriph,
1468) {
1469    let (ptr, len) = (buf.as_ptr(), buf.len());
1470
1471    let periph_addr = &(*(regs(port))).idr as *const _ as u32;
1472
1473    #[cfg(feature = "h7")]
1474    let num_data = len as u32;
1475    #[cfg(not(feature = "h7"))]
1476    let num_data = len as u16;
1477
1478    match dma_periph {
1479        dma::DmaPeriph::Dma1 => {
1480            let mut regs = unsafe { &(*DMA1::ptr()) };
1481            dma::cfg_channel(
1482                &mut regs,
1483                dma_channel,
1484                periph_addr,
1485                ptr as u32,
1486                num_data,
1487                dma::Direction::ReadFromPeriph,
1488                dma::DataSize::S32,
1489                dma::DataSize::S32,
1490                channel_cfg,
1491            );
1492        }
1493        #[cfg(not(any(feature = "g0", feature = "wb")))]
1494        dma::DmaPeriph::Dma2 => {
1495            let mut regs = unsafe { &(*pac::DMA2::ptr()) };
1496            dma::cfg_channel(
1497                &mut regs,
1498                dma_channel,
1499                periph_addr,
1500                ptr as u32,
1501                num_data,
1502                dma::Direction::ReadFromPeriph,
1503                dma::DataSize::S32,
1504                dma::DataSize::S32,
1505                channel_cfg,
1506            );
1507        }
1508    }
1509}