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