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                            } else if #[cfg(feature = "l4")]{
367                                exti.imr1().modify(|_, w| w.[<mr $num>]().bit(true));
368                            }
369                        }
370
371                        cfg_if! {
372                            if #[cfg(any(feature = "g4", feature = "wb", feature = "wl", feature = "c0", feature = "l5",
373                                feature = "g030", feature = "g050", feature = "g070", feature = "g0b0", feature = "g0b1", feature = "g0c1"))] {
374                                exti.rtsr1().modify(|_, w| w.[<rt $num>]().bit($rising));
375                                exti.ftsr1().modify(|_, w| w.[<ft $num>]().bit($falling));
376                            // } else if #[cfg(any(feature = "wb", feature = "wl"))] {
377                            //     // todo: Missing in PAC, so we read+write. https://github.com/stm32-rs/stm32-rs/issues/570
378                            //     let val_r =  $exti.rtsr1().read().bits();
379                            //     $exti.rtsr1().write(|w| unsafe { w.bits(val_r | (1 << $num)) });
380                            //     let val_f =  $exti.ftsr1().read().bits();
381                            //     $exti.ftsr1().write(|w| unsafe { w.bits(val_f | (1 << $num)) });
382                            //     // todo: Core 2 interrupts.
383                            } else {
384                                exti.rtsr1().modify(|_, w| w.[<tr $num>]().bit($rising));
385                                exti.ftsr1().modify(|_, w| w.[<tr $num>]().bit($falling));
386                            }
387                        }
388
389                        #[cfg(not(any(feature = "g0", feature = "c0", feature = "l5")))]
390                        syscfg
391                            .[<exticr $crnum>]()
392                            .modify(|_, w| unsafe { w.[<exti $num>]().bits($val) });
393                    }
394                )+
395                _ => panic!("GPIO pins must be 0 - 15."),
396            }
397        }
398    }
399}
400
401#[cfg(feature = "f4")]
402// Similar to `set_exti`, but with reg names sans `1`.
403macro_rules! set_exti_f4 {
404    ($pin:expr, $rising:expr, $falling:expr, $val:expr, [$(($num:expr, $crnum:expr)),+]) => {
405        let exti = unsafe { &(*pac::EXTI::ptr()) };
406        let syscfg  = unsafe { &(*pac::SYSCFG::ptr()) };
407
408        paste! {
409            match $pin {
410                $(
411                    $num => {
412                        exti.imr().modify(|_, w| w.[<mr $num>]().unmasked());
413                        exti.rtsr().modify(|_, w| w.[<tr $num>]().bit($rising));
414                        exti.ftsr().modify(|_, w| w.[<tr $num>]().bit($falling));
415                        syscfg
416                            .[<exticr $crnum>]()
417                            .modify(|_, w| unsafe { w.[<exti $num>]().bits($val) });
418                    }
419                )+
420                _ => panic!("GPIO pins must be 0 - 15."),
421            }
422        }
423    }
424}
425
426#[derive(Clone)]
427/// Represents a single GPIO pin. Allows configuration, and reading/setting state.
428pub struct Pin {
429    /// The GPIO Port letter. Eg A, B, C.
430    pub port: Port,
431    /// The pin number: 1 - 15.
432    pub pin: u8,
433}
434
435impl Pin {
436    /// Internal function to get the appropriate GPIO block pointer.
437    const fn regs(&self) -> *const pac::gpioa::RegisterBlock {
438        // Note that we use this `const` fn and pointer casting since not all ports actually
439        // deref to GPIOA in PAC.
440        regs(self.port)
441    }
442
443    /// Create a new pin, with a specific mode. Enables the RCC peripheral clock to the port,
444    /// if not already enabled. Example: `let pa1 = Pin::new(Port::A, 1, PinMode::Output);` Leaves settings
445    /// other than mode and alternate function (if applicable) at their hardware defaults.
446    pub fn new(port: Port, pin: u8, mode: PinMode) -> Self {
447        assert!(pin <= 15, "Pin must be 0 - 15.");
448
449        let rcc = unsafe { &(*RCC::ptr()) };
450
451        match port {
452            Port::A => {
453                cfg_if! {
454                    if #[cfg(feature = "f3")] {
455                        if rcc.ahbenr().read().iopaen().bit_is_clear() {
456                            rcc_en_reset!(ahb1, iopa, rcc);
457                        }
458                    } else if #[cfg(feature = "h7")] {
459                        if rcc.ahb4enr().read().gpioaen().bit_is_clear() {
460                            rcc.ahb4enr().modify(|_, w| w.gpioaen().bit(true));
461                            rcc.ahb4rstr().modify(|_, w| w.gpioarst().bit(true));
462                            rcc.ahb4rstr().modify(|_, w| w.gpioarst().clear_bit());
463                        }
464                    } else if #[cfg(feature = "f4")] {
465                        if rcc.ahb1enr().read().gpioaen().bit_is_clear() {
466                            rcc_en_reset!(ahb1, gpioa, rcc);
467                        }
468                    } else if #[cfg(any(feature = "g031", feature = "g041", feature = "g051", feature = "g061", feature = "g071", feature = "g081"))] {
469                        if rcc.iopenr().read().iopaen().bit_is_clear() {
470                            rcc.iopenr().modify(|_, w| w.iopaen().bit(true));
471                            rcc.ioprstr().modify(|_, w| w.ioparst().bit(true));
472                            rcc.ioprstr().modify(|_, w| w.ioparst().clear_bit());
473                        }
474                    } else if #[cfg(any(feature = "g0"))] {
475                        if rcc.iopenr().read().gpioaen().bit_is_clear() {
476                            rcc.iopenr().modify(|_, w| w.gpioaen().bit(true));
477                            rcc.ioprstr().modify(|_, w| w.gpioarst().bit(true));
478                            rcc.ioprstr().modify(|_, w| w.gpioarst().clear_bit());
479                        }
480                    } else if #[cfg(feature = "c0")] {
481                         if rcc.iopenr().read().gpioaen().bit_is_clear() {
482                            rcc.iopenr().modify(|_, w| w.gpioaen().bit(true));
483                            rcc.ioprstr().modify(|_, w| w.gpioarst().bit(true));
484                            rcc.ioprstr().modify(|_, w| w.gpioarst().clear_bit());
485                        }
486                    } else { // L4, L5, G4
487                        if rcc.ahb2enr().read().gpioaen().bit_is_clear() {
488                            rcc_en_reset!(ahb2, gpioa, rcc);
489                        }
490                    }
491                }
492            }
493            Port::B => {
494                cfg_if! {
495                    if #[cfg(feature = "f3")] {
496                        if rcc.ahbenr().read().iopben().bit_is_clear() {
497                            rcc_en_reset!(ahb1, iopb, rcc);
498                        }
499                    } else if #[cfg(feature = "h7")] {
500                        if rcc.ahb4enr().read().gpioben().bit_is_clear() {
501                            rcc.ahb4enr().modify(|_, w| w.gpioben().bit(true));
502                            rcc.ahb4rstr().modify(|_, w| w.gpiobrst().bit(true));
503                            rcc.ahb4rstr().modify(|_, w| w.gpiobrst().clear_bit());
504                        }
505                    } else if #[cfg(feature = "f4")] {
506                        if rcc.ahb1enr().read().gpioben().bit_is_clear() {
507                            rcc_en_reset!(ahb1, gpiob, rcc);
508                        }
509                    } else if #[cfg(any(feature = "g031", feature = "g041", feature = "g051", feature = "g061", feature = "g071", feature = "g081"))] {
510                        if rcc.iopenr().read().iopaen().bit_is_clear() {
511                            rcc.iopenr().modify(|_, w| w.iopaen().bit(true));
512                            rcc.ioprstr().modify(|_, w| w.ioparst().bit(true));
513                            rcc.ioprstr().modify(|_, w| w.ioparst().clear_bit());
514                        }
515                    } else if #[cfg(feature = "g0")] {
516                        if rcc.iopenr().read().gpioben().bit_is_clear() {
517                            rcc.iopenr().modify(|_, w| w.gpioben().bit(true));
518                            rcc.ioprstr().modify(|_, w| w.gpiobrst().bit(true));
519                            rcc.ioprstr().modify(|_, w| w.gpiobrst().clear_bit());
520                        }
521                    } else if #[cfg(feature = "c0")] {
522                         if rcc.iopenr().read().gpioben().bit_is_clear() {
523                            rcc.iopenr().modify(|_, w| w.gpioben().bit(true));
524                            rcc.ioprstr().modify(|_, w| w.gpiobrst().bit(true));
525                            rcc.ioprstr().modify(|_, w| w.gpiobrst().clear_bit());
526                        }
527                    } else { // L4, L5, G4
528                        if rcc.ahb2enr().read().gpioben().bit_is_clear() {
529                            rcc_en_reset!(ahb2, gpiob, rcc);
530                        }
531                    }
532                }
533            }
534            #[cfg(not(feature = "wl"))]
535            Port::C => {
536                cfg_if! {
537                    if #[cfg(feature = "f3")] {
538                        if rcc.ahbenr().read().iopcen().bit_is_clear() {
539                            rcc_en_reset!(ahb1, iopc, rcc);
540                        }
541                    } else if #[cfg(feature = "h7")] {
542                        if rcc.ahb4enr().read().gpiocen().bit_is_clear() {
543                            rcc.ahb4enr().modify(|_, w| w.gpiocen().bit(true));
544                            rcc.ahb4rstr().modify(|_, w| w.gpiocrst().bit(true));
545                            rcc.ahb4rstr().modify(|_, w| w.gpiocrst().clear_bit());
546                        }
547                    } else if #[cfg(feature = "f4")] {
548                        if rcc.ahb1enr().read().gpiocen().bit_is_clear() {
549                            rcc_en_reset!(ahb1, gpioc, rcc);
550                        }
551                    } else if #[cfg(any(feature = "g031", feature = "g041", feature = "g051", feature = "g061", feature = "g071", feature = "g081"))] {
552                        if rcc.iopenr().read().iopaen().bit_is_clear() {
553                            rcc.iopenr().modify(|_, w| w.iopaen().bit(true));
554                            rcc.ioprstr().modify(|_, w| w.ioparst().bit(true));
555                            rcc.ioprstr().modify(|_, w| w.ioparst().clear_bit());
556                        }
557                    } else if #[cfg(feature = "g0")] {
558                        if rcc.iopenr().read().gpiocen().bit_is_clear() {
559                            rcc.iopenr().modify(|_, w| w.gpiocen().bit(true));
560                            rcc.ioprstr().modify(|_, w| w.gpiocrst().bit(true));
561                            rcc.ioprstr().modify(|_, w| w.gpiocrst().clear_bit());
562                        }
563                    } else if #[cfg(feature = "c0")] {
564                         if rcc.iopenr().read().gpiocen().bit_is_clear() {
565                            rcc.iopenr().modify(|_, w| w.gpiocen().bit(true));
566                            rcc.ioprstr().modify(|_, w| w.gpiocrst().bit(true));
567                            rcc.ioprstr().modify(|_, w| w.gpiocrst().clear_bit());
568                        }
569                    } else { // L4, L5, G4
570                        if rcc.ahb2enr().read().gpiocen().bit_is_clear() {
571                            rcc_en_reset!(ahb2, gpioc, rcc);
572                        }
573                    }
574                }
575            }
576            #[cfg(not(any(feature = "f410", feature = "wl", feature = "l412")))]
577            Port::D => {
578                cfg_if! {
579                    if #[cfg(feature = "f3")] {
580                        if rcc.ahbenr().read().iopden().bit_is_clear() {
581                            rcc_en_reset!(ahb1, iopd, rcc);
582                        }
583                    } else if #[cfg(feature = "h7")] {
584                        if rcc.ahb4enr().read().gpioden().bit_is_clear() {
585                            rcc.ahb4enr().modify(|_, w| w.gpioden().bit(true));
586                            rcc.ahb4rstr().modify(|_, w| w.gpiodrst().bit(true));
587                            rcc.ahb4rstr().modify(|_, w| w.gpiodrst().clear_bit());
588                        }
589                    } else if #[cfg(feature = "f4")] {
590                        if rcc.ahb1enr().read().gpioden().bit_is_clear() {
591                            rcc_en_reset!(ahb1, gpiod, rcc);
592                        }
593                    } else if #[cfg(any(feature = "g031", feature = "g041", feature = "g051", feature = "g061", feature = "g071", feature = "g081"))] {
594                        if rcc.iopenr().read().iopaen().bit_is_clear() {
595                            rcc.iopenr().modify(|_, w| w.iopaen().bit(true));
596                            rcc.ioprstr().modify(|_, w| w.ioparst().bit(true));
597                            rcc.ioprstr().modify(|_, w| w.ioparst().clear_bit());
598                        }
599                    } else if #[cfg(feature = "g0")] {
600                        if rcc.iopenr().read().gpioden().bit_is_clear() {
601                            rcc.iopenr().modify(|_, w| w.gpioden().bit(true));
602                            rcc.ioprstr().modify(|_, w| w.gpiodrst().bit(true));
603                            rcc.ioprstr().modify(|_, w| w.gpiodrst().clear_bit());
604                        }
605                    } else if #[cfg(feature = "c0")] {
606                         if rcc.iopenr().read().gpioden().bit_is_clear() {
607                            rcc.iopenr().modify(|_, w| w.gpioden().bit(true));
608                            rcc.ioprstr().modify(|_, w| w.gpiodrst().bit(true));
609                            rcc.ioprstr().modify(|_, w| w.gpiodrst().clear_bit());
610                        }
611                    } else { // L4, L5, G4
612                        if rcc.ahb2enr().read().gpioden().bit_is_clear() {
613                            rcc_en_reset!(ahb2, gpiod, rcc);
614                        }
615                    }
616                }
617            }
618            #[cfg(not(any(
619                feature = "f301",
620                feature = "f3x4",
621                feature = "f410",
622                feature = "g0",
623                feature = "c0",
624                feature = "wb",
625                feature = "wl",
626                feature = "l412",
627            )))]
628            Port::E => {
629                cfg_if! {
630                    if #[cfg(feature = "f3")] {
631                        if rcc.ahbenr().read().iopeen().bit_is_clear() {
632                            rcc_en_reset!(ahb1, iope, rcc);
633                        }
634                    } else if #[cfg(feature = "h7")] {
635                        if rcc.ahb4enr().read().gpioeen().bit_is_clear() {
636                            rcc.ahb4enr().modify(|_, w| w.gpioeen().bit(true));
637                            rcc.ahb4rstr().modify(|_, w| w.gpioerst().bit(true));
638                            rcc.ahb4rstr().modify(|_, w| w.gpioerst().clear_bit());
639                        }
640                    } else if #[cfg(feature = "f4")] {
641                        if rcc.ahb1enr().read().gpioeen().bit_is_clear() {
642                            rcc_en_reset!(ahb1, gpioe, rcc);
643                        }
644                    } else { // L4, L5, G4
645                        if rcc.ahb2enr().read().gpioeen().bit_is_clear() {
646                            rcc_en_reset!(ahb2, gpioe, rcc);
647                        }
648                    }
649                }
650            }
651            #[cfg(not(any(
652                feature = "f401",
653                feature = "f410",
654                feature = "f411",
655                feature = "l4x1",
656                feature = "l4x2",
657                feature = "l412",
658                feature = "l4x3",
659                feature = "wb",
660                feature = "wl",
661            )))]
662            Port::F => {
663                cfg_if! {
664                    if #[cfg(feature = "f3")] {
665                        if rcc.ahbenr().read().iopfen().bit_is_clear() {
666                            rcc_en_reset!(ahb1, iopf, rcc);
667                        }
668                    } else if #[cfg(feature = "h7")] {
669                        if rcc.ahb4enr().read().gpiofen().bit_is_clear() {
670                            rcc.ahb4enr().modify(|_, w| w.gpiofen().bit(true));
671                            rcc.ahb4rstr().modify(|_, w| w.gpiofrst().bit(true));
672                            rcc.ahb4rstr().modify(|_, w| w.gpiofrst().clear_bit());
673                        }
674                    } else if #[cfg(feature = "f4")] {
675                        if rcc.ahb1enr().read().gpiofen().bit_is_clear() {
676                            rcc_en_reset!(ahb1, gpiof, rcc);
677                        }
678                    } else if #[cfg(any(feature = "g031", feature = "g041", feature = "g051", feature = "g061", feature = "g071", feature = "g081"))] {
679                        if rcc.iopenr().read().iopaen().bit_is_clear() {
680                            rcc.iopenr().modify(|_, w| w.iopaen().bit(true));
681                            rcc.ioprstr().modify(|_, w| w.ioparst().bit(true));
682                            rcc.ioprstr().modify(|_, w| w.ioparst().clear_bit());
683                        }
684                    } else if #[cfg(feature = "g0")] {
685                        if rcc.iopenr().read().gpiofen().bit_is_clear() {
686                            rcc.iopenr().modify(|_, w| w.gpiofen().bit(true));
687                            rcc.ioprstr().modify(|_, w| w.gpiofrst().bit(true));
688                            rcc.ioprstr().modify(|_, w| w.gpiofrst().clear_bit());
689                        }
690                    } else if #[cfg(feature = "c0")] {
691                         if rcc.iopenr().read().gpiofen().bit_is_clear() {
692                            rcc.iopenr().modify(|_, w| w.gpiofen().bit(true));
693                            rcc.ioprstr().modify(|_, w| w.gpiofrst().bit(true));
694                            rcc.ioprstr().modify(|_, w| w.gpiofrst().clear_bit());
695                        }
696                    } else { // L4, L5, G4
697                        if rcc.ahb2enr().read().gpiofen().bit_is_clear() {
698                            rcc_en_reset!(ahb2, gpiof, rcc);
699                        }
700                    }
701                }
702            }
703            #[cfg(not(any(
704                feature = "f373",
705                feature = "f301",
706                feature = "f3x4",
707                feature = "f401",
708                feature = "f410",
709                feature = "f411",
710                feature = "l4x1",
711                feature = "l4x2",
712                feature = "l412",
713                feature = "l4x3",
714                feature = "g0",
715                feature = "c0",
716                feature = "wb",
717                feature = "wl"
718            )))]
719            Port::G => {
720                cfg_if! {
721                    if #[cfg(feature = "f3")] {
722                        if rcc.ahbenr().read().iopgen().bit_is_clear() {
723                            rcc_en_reset!(ahb1, iopg, rcc);
724                        }
725                    } else if #[cfg(feature = "h7")] {
726                        if rcc.ahb4enr().read().gpiogen().bit_is_clear() {
727                            rcc.ahb4enr().modify(|_, w| w.gpiogen().bit(true));
728                            rcc.ahb4rstr().modify(|_, w| w.gpiogrst().bit(true));
729                            rcc.ahb4rstr().modify(|_, w| w.gpiogrst().clear_bit());
730                        }
731                    } else if #[cfg(feature = "f4")] {
732                        if rcc.ahb1enr().read().gpiogen().bit_is_clear() {
733                            rcc_en_reset!(ahb1, gpiog, rcc);
734                        }
735                    } else { // L4, L5, G4
736                        if rcc.ahb2enr().read().gpiogen().bit_is_clear() {
737                            rcc_en_reset!(ahb2, gpiog, rcc);
738
739                            #[cfg(feature = "l4x6")]
740                            {
741                                let pwr = unsafe { &(*pac::PWR::ptr()) };
742                                // RM0351: Setting this bit (IOSV) is mandatory to use PG[15:2].
743                                rcc.apb1enr1().modify(|_, w| w.pwren().bit(true));
744                                pwr.cr2().modify(|_, w| w.iosv().bit(true));
745                            }
746                        }
747                    }
748                }
749                #[cfg(feature = "l5")]
750                // also for RM0351 L4 variants, which we don't currently support
751                // L5 RM: "[The IOSV bit] is used to validate the VDDIO2 supply for electrical and logical isolation purpose.
752                // Setting this bit is mandatory to use PG[15:2]."
753                {
754                    unsafe {
755                        (*crate::pac::PWR::ptr())
756                            .cr2()
757                            .modify(|_, w| w.iosv().bit(true));
758                    }
759                }
760            }
761            #[cfg(not(any(
762                feature = "f373",
763                feature = "f301",
764                feature = "f3x4",
765                feature = "f410",
766                feature = "l4x1",
767                feature = "l4x2",
768                feature = "l412",
769                feature = "l4x3",
770                feature = "g0",
771                feature = "c0",
772                feature = "g4",
773                feature = "wb",
774                feature = "wl"
775            )))]
776            Port::H => {
777                cfg_if! {
778                    if #[cfg(feature = "f3")] {
779                        if rcc.ahbenr().read().iophen().bit_is_clear() {
780                            rcc_en_reset!(ahb1, ioph, rcc);
781                        }
782                    } else if #[cfg(feature = "h7")] {
783                        if rcc.ahb4enr().read().gpiohen().bit_is_clear() {
784                            rcc.ahb4enr().modify(|_, w| w.gpiohen().bit(true));
785                            rcc.ahb4rstr().modify(|_, w| w.gpiohrst().bit(true));
786                            rcc.ahb4rstr().modify(|_, w| w.gpiohrst().clear_bit());
787                        }
788                    } else if #[cfg(feature = "f4")] {
789                        if rcc.ahb1enr().read().gpiohen().bit_is_clear() {
790                            rcc_en_reset!(ahb1, gpioh, rcc);
791                        }
792                    }else { // L4, L5, G4
793                        if rcc.ahb2enr().read().gpiohen().bit_is_clear() {
794                            rcc_en_reset!(ahb2, gpioh, rcc);
795                        }
796                    }
797                }
798            }
799            #[cfg(any(feature = "l4x6", feature = "h747cm4", feature = "h747cm7"))]
800            Port::I => {
801                cfg_if! {
802                    if #[cfg(feature = "h7")] {
803                        if rcc.ahb4enr().read().gpioien().bit_is_clear() {
804                            rcc.ahb4enr().modify(|_, w| w.gpioien().bit(true));
805                            rcc.ahb4rstr().modify(|_, w| w.gpioirst().bit(true));
806                            rcc.ahb4rstr().modify(|_, w| w.gpioirst().clear_bit());
807                        }
808                    } else if #[cfg(feature = "l4")] {
809                        if rcc.ahb2enr().read().gpioien().bit_is_clear() {
810                            rcc.ahb2enr().modify(|_,w| w.gpioien().bit(true));
811                            rcc.ahb2rstr().modify(|_, w| w.gpioirst().bit(true));
812                            rcc.ahb2rstr().modify(|_, w| w.gpioirst().clear_bit());
813                        }
814                    }
815                }
816            }
817        }
818
819        let mut result = Self { port, pin };
820        result.mode(mode);
821
822        result
823    }
824
825    /// Set pin mode. Eg, Output, Input, Analog, or Alt. Sets the `MODER` register.
826    pub fn mode(&mut self, value: PinMode) {
827        #[cfg(any(feature = "h5", feature = "c0"))] // todo: Probably needs a PAC fix for H5.
828        set_field!(
829            self.regs(),
830            self.pin,
831            moder,
832            mode,
833            bits,
834            value.val(),
835            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
836        );
837
838        #[cfg(not(any(feature = "h5", feature = "c0")))] // todo: Probably needs a PAC fix for H5.
839        set_field!(
840            self.regs(),
841            self.pin,
842            moder,
843            moder,
844            bits,
845            value.val(),
846            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
847        );
848
849        if let PinMode::Alt(alt) = value {
850            self.alt_fn(alt);
851        }
852    }
853
854    /// Set output type. Sets the `OTYPER` register.
855    pub fn output_type(&mut self, value: OutputType) {
856        set_field!(
857            self.regs(),
858            self.pin,
859            otyper,
860            ot,
861            bit,
862            value as u8 != 0,
863            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
864        );
865    }
866
867    /// Set output speed to Low, Medium, or High. Sets the `OSPEEDR` register.
868    pub fn output_speed(&mut self, value: OutputSpeed) {
869        #[cfg(not(any(feature = "h5", feature = "c0")))] // todo: impl on H5 and C0
870        set_field!(
871            self.regs(),
872            self.pin,
873            ospeedr,
874            ospeedr,
875            bits,
876            value as u8,
877            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
878        );
879    }
880
881    /// Set internal pull resistor: Pull up, pull down, or floating. Sets the `PUPDR` register.
882    pub fn pull(&mut self, value: Pull) {
883        #[cfg(any(feature = "h5", feature = "c0"))]
884        set_field!(
885            self.regs(),
886            self.pin,
887            pupdr,
888            pupd,
889            bits,
890            value as u8,
891            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
892        );
893
894        #[cfg(not(any(feature = "h5", feature = "c0")))]
895        set_field!(
896            self.regs(),
897            self.pin,
898            pupdr,
899            pupdr,
900            bits,
901            value as u8,
902            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
903        );
904    }
905
906    // TODO: F373 doesn't have LOCKR on ports C, E, F. You can impl for others
907    #[cfg(not(feature = "f373"))]
908    /// Lock or unlock a port configuration. Sets the `LCKR` register.
909    pub fn cfg_lock(&mut self, value: CfgLock) {
910        set_field!(
911            self.regs(),
912            self.pin,
913            lckr,
914            lck,
915            bit,
916            value as u8 != 0,
917            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
918        );
919    }
920
921    /// Read the input data register. Eg determine if the pin is high or low. See also `is_high()`
922    /// and `is_low()`. Reads from the `IDR` register.
923    pub fn get_state(&mut self) -> PinState {
924        let val = get_input_data!(
925            self.regs(),
926            self.pin,
927            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
928        );
929        if val { PinState::High } else { PinState::Low }
930    }
931
932    /// Set a pin state (ie set high or low output voltage level). See also `set_high()` and
933    /// `set_low()`. Sets the `BSRR` register. Atomic.
934    pub fn set_state(&mut self, value: PinState) {
935        let offset = match value {
936            PinState::Low => 16,
937            PinState::High => 0,
938        };
939
940        set_state!(
941            self.regs(),
942            self.pin,
943            offset,
944            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
945        );
946    }
947
948    /// Set up a pin's alternate function. We set this up initially using `mode()`.
949    fn alt_fn(&mut self, value: u8) {
950        assert!(value <= 15, "Alt function must be 0 to 15.");
951
952        cfg_if! {
953            if #[cfg(any(feature = "l5", feature = "g0", feature = "c0", feature = "wb", feature = "h5"))] {
954                set_alt!(self.regs(), self.pin, afrel, value, [(0, l), (1, l), (2, l),
955                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
956                    (13, h), (14, h), (15, h)])
957            } else if #[cfg(feature = "h7")] {
958                set_alt!(self.regs(), self.pin, afr, value, [(0, l), (1, l), (2, l),
959                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
960                    (13, h), (14, h), (15, h)])
961            } else {  // f3, f4, l4, g4, wl(?)
962                set_alt!(self.regs(), self.pin, afr, value, [(0, l), (1, l), (2, l),
963                    (3, l), (4, l), (5, l), (6, l), (7, l), (8, h), (9, h), (10, h), (11, h), (12, h),
964                    (13, h), (14, h), (15, h)])
965            }
966        }
967    }
968
969    #[cfg(not(any(feature = "f373", feature = "wl")))]
970    /// Configure this pin as an interrupt source. Set the edge as Rising or Falling.
971    pub fn enable_interrupt(&mut self, edge: Edge) {
972        let rising = match edge {
973            Edge::Falling => false,
974            _ => true, // rising or either.
975        };
976
977        let falling = match edge {
978            Edge::Rising => false,
979            _ => true, // falling or either.
980        };
981
982        cfg_if! {
983            if #[cfg(feature = "f4")] {
984                set_exti_f4!(self.pin, rising, falling, self.port.cr_val(), [(0, 1), (1, 1), (2, 1),
985                        (3, 1), (4, 2), (5, 2), (6, 2), (7, 2), (8, 3), (9, 3), (10, 3), (11, 3), (12, 4),
986                        (13, 4), (14, 4), (15, 4)]
987                );
988            } else {
989                set_exti!(self.pin, rising, falling, self.port.cr_val(), [(0, 1), (1, 1), (2, 1),
990                    (3, 1), (4, 2), (5, 2), (6, 2), (7, 2), (8, 3), (9, 3), (10, 3), (11, 3), (12, 4),
991                    (13, 4), (14, 4), (15, 4)]
992                );
993            }
994        }
995    }
996
997    #[cfg(feature = "l4x6")]
998    /// For the ADC, DAC, OPAMP and COMP, configure the desired I/O in analog mode
999    /// in the GPIOx_MODER register and configure the required function in the ADC,
1000    /// DAC, OPAMP, and COMP registers. For the ADC, it is necessary to configure the
1001    /// GPIOx_ASCR register (only for STM32L47x/L48x). Note that our `l4x6` feature
1002    /// gate is good enough here, since the most popular variants affected are L476 and L486.
1003    /// todo: Disconnect method?
1004    pub fn connect_to_adc(&mut self) {
1005        set_field!(
1006            self.regs(),
1007            self.pin,
1008            ascr,
1009            asc,
1010            bit,
1011            true,
1012            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1013        );
1014    }
1015
1016    /// Check if the pin's input voltage is high. Reads from the `IDR` register.
1017    pub fn is_high(&self) -> bool {
1018        get_input_data!(
1019            self.regs(),
1020            self.pin,
1021            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1022        )
1023    }
1024
1025    /// Check if the pin's input voltage is low. Reads from the `IDR` register.
1026    pub fn is_low(&self) -> bool {
1027        !self.is_high()
1028    }
1029
1030    /// Set the pin's output voltage to high. Sets the `BSRR` register. Atomic.
1031    pub fn set_high(&mut self) {
1032        self.set_state(PinState::High);
1033    }
1034
1035    /// Set the pin's output voltage to low. Sets the `BSRR` register. Atomic.
1036    pub fn set_low(&mut self) {
1037        self.set_state(PinState::Low);
1038    }
1039
1040    /// Toggle output voltage between low and high. Sets the `BSRR` register. Atomic.
1041    pub fn toggle(&mut self) {
1042        // if self.is_high() {
1043        if Pin::is_high(self) {
1044            Pin::set_low(self);
1045            // self.set_low();
1046        } else {
1047            // self.set_high();
1048            Pin::set_high(self);
1049        }
1050    }
1051}
1052
1053#[cfg(feature = "embedded_hal")]
1054impl ErrorType for Pin {
1055    type Error = Infallible;
1056}
1057
1058#[cfg(feature = "embedded_hal")]
1059impl InputPin for Pin {
1060    fn is_high(&mut self) -> Result<bool, Self::Error> {
1061        Ok(Pin::is_high(self))
1062    }
1063
1064    fn is_low(&mut self) -> Result<bool, Self::Error> {
1065        Ok(Pin::is_low(self))
1066    }
1067}
1068
1069#[cfg(feature = "embedded_hal")]
1070impl OutputPin for Pin {
1071    fn set_low(&mut self) -> Result<(), Self::Error> {
1072        Pin::set_low(self);
1073        Ok(())
1074    }
1075
1076    fn set_high(&mut self) -> Result<(), Self::Error> {
1077        Pin::set_high(self);
1078        Ok(())
1079    }
1080}
1081
1082#[cfg(feature = "embedded_hal")]
1083impl StatefulOutputPin for Pin {
1084    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
1085        Ok(Pin::is_high(self))
1086    }
1087
1088    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
1089        Ok(Pin::is_low(self))
1090    }
1091
1092    fn toggle(&mut self) -> Result<(), Self::Error> {
1093        Pin::toggle(self);
1094        Ok(())
1095    }
1096}
1097
1098/// Check if a pin's input voltage is high. Reads from the `IDR` register.
1099/// Does not require a `Pin` struct.
1100pub fn is_high(port: Port, pin: u8) -> bool {
1101    get_input_data!(
1102        regs(port),
1103        pin,
1104        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1105    )
1106}
1107
1108/// Check if a pin's input voltage is low. Reads from the `IDR` register.
1109/// Does not require a `Pin` struct.
1110pub fn is_low(port: Port, pin: u8) -> bool {
1111    !is_high(port, pin)
1112}
1113
1114/// Set a pin's output voltage to high. Sets the `BSRR` register. Atomic.
1115/// Does not require a `Pin` struct.
1116pub fn set_high(port: Port, pin: u8) {
1117    set_state(port, pin, PinState::High);
1118}
1119
1120/// Set a pin's output voltage to low. Sets the `BSRR` register. Atomic.
1121/// Does not require a `Pin` struct.
1122pub fn set_low(port: Port, pin: u8) {
1123    set_state(port, pin, PinState::Low);
1124}
1125
1126/// Set a pin state (ie set high or low output voltage level). See also `set_high()` and
1127/// `set_low()`. Sets the `BSRR` register. Atomic.
1128/// Does not require a `Pin` struct.
1129pub fn set_state(port: Port, pin: u8, value: PinState) {
1130    let offset = match value {
1131        PinState::Low => 16,
1132        PinState::High => 0,
1133    };
1134
1135    set_state!(
1136        regs(port),
1137        pin,
1138        offset,
1139        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1140    );
1141}
1142
1143/// Clear an EXTI interrupt, lines 0 - 15. Note that this function currently doesn't support
1144/// higher extis, but will work for all GPIO interrupts.
1145pub fn clear_exti_interrupt_edge(line: u8, edge: Edge) {
1146    // todo: Macro to avoid DRY?
1147    unsafe {
1148        cfg_if! {
1149            if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
1150                (*EXTI::ptr()).c1pr1().modify(|_, w| {
1151                    match line {
1152                        0 => w.pr0().bit(true),
1153                        1 => w.pr1().bit(true),
1154                        2 => w.pr2().bit(true),
1155                        3 => w.pr3().bit(true),
1156                        4 => w.pr4().bit(true),
1157                        5 => w.pr5().bit(true),
1158                        6 => w.pr6().bit(true),
1159                        7 => w.pr7().bit(true),
1160                        8 => w.pr8().bit(true),
1161                        9 => w.pr9().bit(true),
1162                        10 => w.pr10().bit(true),
1163                        11 => w.pr11().bit(true),
1164                        12 => w.pr12().bit(true),
1165                        13 => w.pr13().bit(true),
1166                        14 => w.pr14().bit(true),
1167                        15 => w.pr15().bit(true),
1168                        _ => panic!(),
1169                    }
1170                });
1171            } else if #[cfg(feature = "h7")] {
1172                (*EXTI::ptr()).cpupr1().modify(|_, w| {
1173                    match line {
1174                        0 => w.pr0().bit(true),
1175                        1 => w.pr1().bit(true),
1176                        2 => w.pr2().bit(true),
1177                        3 => w.pr3().bit(true),
1178                        4 => w.pr4().bit(true),
1179                        5 => w.pr5().bit(true),
1180                        6 => w.pr6().bit(true),
1181                        7 => w.pr7().bit(true),
1182                        8 => w.pr8().bit(true),
1183                        9 => w.pr9().bit(true),
1184                        10 => w.pr10().bit(true),
1185                        11 => w.pr11().bit(true),
1186                        12 => w.pr12().bit(true),
1187                        13 => w.pr13().bit(true),
1188                        14 => w.pr14().bit(true),
1189                        15 => w.pr15().bit(true),
1190                        _ => panic!(),
1191                    }
1192                });
1193            } else if #[cfg(any(feature = "l5", feature = "g0", feature = "c0"))] {
1194                match edge {
1195                    Edge::Rising => {
1196                        (*EXTI::ptr()).rpr1().modify(|_, w| {
1197                            match line {
1198                                0 => w.rpif0().bit(true),
1199                                1 => w.rpif1().bit(true),
1200                                2 => w.rpif2().bit(true),
1201                                3 => w.rpif3().bit(true),
1202                                4 => w.rpif4().bit(true),
1203                                5 => w.rpif5().bit(true),
1204                                6 => w.rpif6().bit(true),
1205                                7 => w.rpif7().bit(true),
1206                                8 => w.rpif8().bit(true),
1207                                9 => w.rpif9().bit(true),
1208                                10 => w.rpif10().bit(true),
1209                                11 => w.rpif11().bit(true),
1210                                12 => w.rpif12().bit(true),
1211                                13 => w.rpif13().bit(true),
1212                                14 => w.rpif14().bit(true),
1213                                15 => w.rpif15().bit(true),
1214                                _ => panic!(),
1215                            }
1216                        });
1217                    },
1218                    Edge::Falling => {
1219                        (*EXTI::ptr()).fpr1().modify(|_, w| {
1220                            match line {
1221                                0 => w.fpif0().bit(true),
1222                                1 => w.fpif1().bit(true),
1223                                2 => w.fpif2().bit(true),
1224                                3 => w.fpif3().bit(true),
1225                                4 => w.fpif4().bit(true),
1226                                5 => w.fpif5().bit(true),
1227                                6 => w.fpif6().bit(true),
1228                                7 => w.fpif7().bit(true),
1229                                8 => w.fpif8().bit(true),
1230                                9 => w.fpif9().bit(true),
1231                                10 => w.fpif10().bit(true),
1232                                11 => w.fpif11().bit(true),
1233                                12 => w.fpif12().bit(true),
1234                                13 => w.fpif13().bit(true),
1235                                14 => w.fpif14().bit(true),
1236                                15 => w.fpif15().bit(true),
1237                                _ => panic!(),
1238                            }
1239                        });
1240                    },
1241                    Edge::Either => panic!(),
1242                }
1243            } else if #[cfg(any(feature = "f373", feature = "f4"))] {
1244                (*EXTI::ptr()).pr().modify(|_, w| {
1245                    match line {
1246                        0 => w.pr0().bit(true),
1247                        1 => w.pr1().bit(true),
1248                        2 => w.pr2().bit(true),
1249                        3 => w.pr3().bit(true),
1250                        4 => w.pr4().bit(true),
1251                        5 => w.pr5().bit(true),
1252                        6 => w.pr6().bit(true),
1253                        7 => w.pr7().bit(true),
1254                        8 => w.pr8().bit(true),
1255                        9 => w.pr9().bit(true),
1256                        10 => w.pr10().bit(true),
1257                        11 => w.pr11().bit(true),
1258                        12 => w.pr12().bit(true),
1259                        13 => w.pr13().bit(true),
1260                        14 => w.pr14().bit(true),
1261                        15 => w.pr15().bit(true),
1262                        _ => panic!(),
1263                    }
1264                });
1265            } else if #[cfg(any(feature = "f3", feature = "l4"))] {
1266                (*EXTI::ptr()).pr1().modify(|_, w| {
1267                    match line {
1268                        0 => w.pr0().bit(true),
1269                        1 => w.pr1().bit(true),
1270                        2 => w.pr2().bit(true),
1271                        3 => w.pr3().bit(true),
1272                        4 => w.pr4().bit(true),
1273                        5 => w.pr5().bit(true),
1274                        6 => w.pr6().bit(true),
1275                        7 => w.pr7().bit(true),
1276                        8 => w.pr8().bit(true),
1277                        9 => w.pr9().bit(true),
1278                        10 => w.pr10().bit(true),
1279                        11 => w.pr11().bit(true),
1280                        12 => w.pr12().bit(true),
1281                        13 => w.pr13().bit(true),
1282                        14 => w.pr14().bit(true),
1283                        15 => w.pr15().bit(true),
1284                        _ => panic!(),
1285                    }
1286                });
1287              } else if #[cfg(feature = "h5")] {
1288                (*EXTI::ptr()).rpr1().modify(|_, w| {
1289                    match line {
1290                        0 => w.rpif0().bit(true),
1291                        1 => w.rpif1().bit(true),
1292                        2 => w.rpif2().bit(true),
1293                        3 => w.rpif3().bit(true),
1294                        4 => w.rpif4().bit(true),
1295                        5 => w.rpif5().bit(true),
1296                        6 => w.rpif6().bit(true),
1297                        7 => w.rpif7().bit(true),
1298                        8 => w.rpif8().bit(true),
1299                        9 => w.rpif9().bit(true),
1300                        10 => w.rpif10().bit(true),
1301                        11 => w.rpif11().bit(true),
1302                        12 => w.rpif12().bit(true),
1303                        13 => w.rpif13().bit(true),
1304                        14 => w.rpif14().bit(true),
1305                        15 => w.rpif15().bit(true),
1306                        _ => panic!(),
1307                    }
1308                });
1309            } else { // eg G4
1310                (*EXTI::ptr()).pr1().modify(|_, w| {
1311                    match line {
1312                        0 => w.pif0().bit(true),
1313                        1 => w.pif1().bit(true),
1314                        2 => w.pif2().bit(true),
1315                        3 => w.pif3().bit(true),
1316                        4 => w.pif4().bit(true),
1317                        5 => w.pif5().bit(true),
1318                        6 => w.pif6().bit(true),
1319                        7 => w.pif7().bit(true),
1320                        8 => w.pif8().bit(true),
1321                        9 => w.pif9().bit(true),
1322                        10 => w.pif10().bit(true),
1323                        11 => w.pif11().bit(true),
1324                        12 => w.pif12().bit(true),
1325                        13 => w.pif13().bit(true),
1326                        14 => w.pif14().bit(true),
1327                        15 => w.pif15().bit(true),
1328                        _ => panic!(),
1329                    }
1330                });
1331            }
1332        }
1333    }
1334}
1335
1336/// Clear an EXTI interrupt, lines 0 - 15. Note that this function currently doesn't support
1337/// higher extis, but will work for all GPIO interrupts.
1338#[cfg(any(feature = "l5", feature = "g0", feature = "c0"))]
1339pub fn clear_exti_interrupt(line: u8, edge: Edge) {
1340    // This is set as default to rising to keep the same behavior as before.
1341    clear_exti_interrupt_edge(line, edge);
1342}
1343
1344#[cfg(not(any(feature = "l5", feature = "g0", feature = "c0")))]
1345/// Clear an EXTI interrupt, lines 0 - 15. Note that this function currently doesn't support
1346/// higher extis, but will work for all GPIO interrupts.
1347pub fn clear_exti_interrupt(line: u8) {
1348    // This is set as default to rising to keep the same behavior as before.
1349    clear_exti_interrupt_edge(line, Edge::Rising); // Edge is unused for these variants.
1350}
1351
1352const fn regs(port: Port) -> *const pac::gpioa::RegisterBlock {
1353    // Note that we use this `const` fn and pointer casting since not all ports actually
1354    // deref to GPIOA in PAC.
1355    match port {
1356        Port::A => crate::pac::GPIOA::ptr(),
1357        Port::B => crate::pac::GPIOB::ptr() as _,
1358        #[cfg(not(feature = "wl"))]
1359        Port::C => crate::pac::GPIOC::ptr() as _,
1360        #[cfg(not(any(feature = "f410", feature = "wl", feature = "l412")))]
1361        Port::D => crate::pac::GPIOD::ptr() as _,
1362        #[cfg(not(any(
1363            feature = "f301",
1364            feature = "f3x4",
1365            feature = "f410",
1366            feature = "g0",
1367            feature = "c0",
1368            feature = "wb",
1369            feature = "wl",
1370            feature = "l412",
1371        )))]
1372        Port::E => crate::pac::GPIOE::ptr() as _,
1373        #[cfg(not(any(
1374            feature = "f401",
1375            feature = "f410",
1376            feature = "f411",
1377            feature = "l4x1",
1378            feature = "l4x2",
1379            feature = "l412",
1380            feature = "l4x3",
1381            feature = "wb",
1382            feature = "wl"
1383        )))]
1384        Port::F => crate::pac::GPIOF::ptr() as _,
1385        #[cfg(not(any(
1386            feature = "f373",
1387            feature = "f301",
1388            feature = "f3x4",
1389            feature = "f401",
1390            feature = "f410",
1391            feature = "f411",
1392            feature = "l4x1",
1393            feature = "l4x2",
1394            feature = "l412",
1395            feature = "l4x3",
1396            feature = "g0",
1397            feature = "c0",
1398            feature = "wb",
1399            feature = "wl"
1400        )))]
1401        Port::G => crate::pac::GPIOG::ptr() as _,
1402        #[cfg(not(any(
1403            feature = "f373",
1404            feature = "f301",
1405            feature = "f3x4",
1406            feature = "f410",
1407            feature = "l4x1",
1408            feature = "l4x2",
1409            feature = "l412",
1410            feature = "l4x3",
1411            feature = "g0",
1412            feature = "c0",
1413            feature = "g4",
1414            feature = "wb",
1415            feature = "wl"
1416        )))]
1417        Port::H => crate::pac::GPIOH::ptr() as _,
1418        #[cfg(any(feature = "h747cm4", feature = "h747cm7", feature = "l4x6"))]
1419        Port::I => crate::pac::GPIOI::ptr() as _,
1420    }
1421}
1422
1423#[cfg(not(any(
1424    feature = "f4",
1425    feature = "l5",
1426    feature = "f3",
1427    feature = "l4",
1428    feature = "h5"
1429)))]
1430/// Write a series of words to the BSRR (atomic output) register. Note that these are direct writes
1431/// to the full, 2-sided register - not a series of low/high values.
1432pub unsafe fn write_dma(
1433    buf: &[u32],
1434    port: Port,
1435    dma_channel: DmaChannel,
1436    channel_cfg: ChannelCfg,
1437    dma_periph: dma::DmaPeriph,
1438) -> crate::error::Result<()> {
1439    let (ptr, len) = (buf.as_ptr(), buf.len());
1440
1441    let periph_addr = unsafe { &(*(regs(port))).bsrr() as *const _ as u32 };
1442
1443    let num_data = len as u32;
1444
1445    match dma_periph {
1446        dma::DmaPeriph::Dma1 => {
1447            let mut regs = unsafe { &(*DMA1::ptr()) };
1448
1449            dma::cfg_channel(
1450                &mut regs,
1451                dma_channel,
1452                periph_addr,
1453                ptr as u32,
1454                num_data,
1455                dma::Direction::ReadFromMem,
1456                dma::DataSize::S32,
1457                dma::DataSize::S32,
1458                channel_cfg,
1459            )?;
1460        }
1461        #[cfg(dma2)]
1462        dma::DmaPeriph::Dma2 => {
1463            let mut regs = unsafe { &(*pac::DMA2::ptr()) };
1464            dma::cfg_channel(
1465                &mut regs,
1466                dma_channel,
1467                periph_addr,
1468                ptr as u32,
1469                num_data,
1470                dma::Direction::ReadFromMem,
1471                dma::DataSize::S32,
1472                dma::DataSize::S32,
1473                channel_cfg,
1474            )?;
1475        }
1476    }
1477    Ok(())
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) -> crate::error::Result<()> {
1495    let (ptr, len) = (buf.as_ptr(), buf.len());
1496
1497    // let periph_addr = unsafe { &(*(regs(port))).idr().as_ptr() as u32 };
1498    let periph_addr = unsafe { (*(regs(port))).idr().as_ptr() as u32 };
1499
1500    // #[cfg(feature = "h7")]
1501    let num_data = len as u32;
1502    // #[cfg(not(feature = "h7"))]
1503    // let num_data = len as u16;
1504
1505    match dma_periph {
1506        dma::DmaPeriph::Dma1 => {
1507            let mut regs = unsafe { &(*DMA1::ptr()) };
1508
1509            dma::cfg_channel(
1510                &mut regs,
1511                dma_channel,
1512                periph_addr,
1513                ptr as u32,
1514                num_data,
1515                dma::Direction::ReadFromPeriph,
1516                dma::DataSize::S32,
1517                dma::DataSize::S32,
1518                channel_cfg,
1519            )?;
1520        }
1521        #[cfg(not(any(feature = "g0", feature = "c0", feature = "wb")))]
1522        dma::DmaPeriph::Dma2 => {
1523            let mut regs = unsafe { &(*pac::DMA2::ptr()) };
1524            dma::cfg_channel(
1525                &mut regs,
1526                dma_channel,
1527                periph_addr,
1528                ptr as u32,
1529                num_data,
1530                dma::Direction::ReadFromPeriph,
1531                dma::DataSize::S32,
1532                dma::DataSize::S32,
1533                channel_cfg,
1534            )?;
1535        }
1536    }
1537
1538    Ok(())
1539}