Skip to main content

embassy_stm32/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![cfg_attr(
5    docsrs,
6    doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n"
7)]
8#![doc = include_str!("../README.md")]
9#![warn(missing_docs)]
10
11//! ## Feature flags
12#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
13
14// This must go FIRST so that all the other modules see its macros.
15mod fmt;
16include!(concat!(env!("OUT_DIR"), "/_macros.rs"));
17
18// Utilities
19mod macros;
20pub mod time;
21/// Operating modes for peripherals.
22pub mod mode {
23    trait SealedMode {}
24
25    /// Operating mode for a peripheral.
26    #[allow(private_bounds)]
27    pub trait Mode: SealedMode {}
28
29    macro_rules! impl_mode {
30        ($name:ident) => {
31            impl SealedMode for $name {}
32            impl Mode for $name {}
33        };
34    }
35
36    /// Blocking mode.
37    pub struct Blocking;
38    /// Async mode.
39    pub struct Async;
40
41    impl_mode!(Blocking);
42    impl_mode!(Async);
43}
44
45// Always-present hardware
46pub mod dma;
47pub mod gpio;
48pub mod rcc;
49#[cfg(feature = "_time-driver")]
50mod time_driver;
51pub mod timer;
52
53// Sometimes-present hardware
54
55#[cfg(adc)]
56pub mod adc;
57#[cfg(aes_v3b)]
58pub mod aes;
59#[cfg(backup_sram)]
60pub mod backup_sram;
61#[cfg(can)]
62pub mod can;
63#[cfg(any(comp_u5, comp_v2))]
64pub mod comp;
65#[cfg(cordic)]
66pub mod cordic;
67
68// Stub macros for COMP pin implementations when comp module is not compiled.
69// These are needed because build.rs generates macro calls for all chips with COMP,
70// but the actual macros are only defined in the comp module.
71#[cfg(all(comp, not(any(comp_u5, comp_v2))))]
72#[allow(unused_macros)]
73macro_rules! impl_comp_inp_pin {
74    ($inst:ident, $pin:ident, $ch:expr) => {};
75}
76#[cfg(all(comp, not(any(comp_u5, comp_v2))))]
77#[allow(unused_macros)]
78macro_rules! impl_comp_inm_pin {
79    ($inst:ident, $pin:ident, $ch:expr) => {};
80}
81#[cfg(any(ipcc, hsem))]
82pub mod cpu;
83#[cfg(crc)]
84pub mod crc;
85#[cfg(cryp)]
86pub mod cryp;
87#[cfg(dac)]
88pub mod dac;
89#[cfg(dcmi)]
90pub mod dcmi;
91#[cfg(dsihost)]
92pub mod dsihost;
93#[cfg(dts)]
94pub mod dts;
95#[cfg(eth)]
96pub mod eth;
97#[cfg(feature = "exti")]
98pub mod exti;
99#[cfg(flash)]
100pub mod flash;
101#[cfg(fmc)]
102pub mod fmc;
103#[cfg(hash)]
104pub mod hash;
105#[cfg(all(hrtim, feature = "stm32-hrtim"))]
106pub mod hrtim;
107#[cfg(hsem)]
108pub mod hsem;
109#[cfg(hspi)]
110pub mod hspi;
111#[cfg(i2c)]
112pub mod i2c;
113#[cfg(any(spi_v1_i2s, spi_v2_i2s, spi_v3_i2s, spi_v4_i2s, spi_v5_i2s))]
114pub mod i2s;
115#[cfg(any(stm32wb, stm32wl5x))]
116pub mod ipcc;
117#[cfg(lcd)]
118pub mod lcd;
119#[cfg(feature = "low-power")]
120pub mod low_power;
121#[cfg(lptim)]
122pub mod lptim;
123#[cfg(ltdc)]
124pub mod ltdc;
125#[cfg(opamp)]
126pub mod opamp;
127#[cfg(octospi)]
128pub mod ospi;
129#[cfg(pka_v1a)]
130pub mod pka;
131#[cfg(quadspi)]
132pub mod qspi;
133#[cfg(rng)]
134pub mod rng;
135#[cfg(all(rtc, not(rtc_v1)))]
136pub mod rtc;
137#[cfg(saes_v1a)]
138pub mod saes;
139#[cfg(sai)]
140pub mod sai;
141#[cfg(sdmmc)]
142pub mod sdmmc;
143#[cfg(spdifrx)]
144pub mod spdifrx;
145#[cfg(spi)]
146pub mod spi;
147#[cfg(tsc)]
148pub mod tsc;
149#[cfg(ucpd)]
150pub mod ucpd;
151#[cfg(uid)]
152pub mod uid;
153#[cfg(usart)]
154pub mod usart;
155#[cfg(any(usb, otg))]
156pub mod usb;
157#[cfg(vrefbuf)]
158pub mod vrefbuf;
159#[cfg(iwdg)]
160pub mod wdg;
161#[cfg(xspi)]
162pub mod xspi;
163
164#[cfg(feature = "_executor")]
165pub mod executor;
166
167// This must go last, so that it sees all the impl_foo! macros defined earlier.
168pub(crate) mod _generated {
169    #![allow(dead_code)]
170    #![allow(unused_imports)]
171    #![allow(non_snake_case)]
172    #![allow(missing_docs)]
173
174    include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
175}
176
177pub use crate::_generated::{interrupt, triggers};
178
179/// Macro to bind interrupts to handlers.
180///
181/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
182/// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to
183/// prove at compile-time that the right interrupts have been bound.
184///
185/// Example of how to bind one interrupt:
186///
187/// ```rust,ignore
188/// use embassy_stm32::{bind_interrupts, usb, peripherals};
189///
190/// bind_interrupts!(struct Irqs {
191///     OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
192/// });
193/// ```
194///
195/// Example of how to bind multiple interrupts, and multiple handlers to each interrupt, in a single macro invocation:
196///
197/// ```rust,ignore
198/// use embassy_stm32::{bind_interrupts, i2c, peripherals};
199///
200/// bind_interrupts!(
201///     /// Binds the I2C interrupts.
202///     struct Irqs {
203///         I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
204///         I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,
205///             i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;
206///     }
207/// );
208/// ```
209///
210/// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a
211/// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list
212/// of interrupt vectors if you get an unexpected compile error trying to bind the standard name.
213// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
214#[macro_export]
215macro_rules! bind_interrupts {
216    ($(#[$outer:meta])* $vis:vis struct $name:ident {
217        $(
218            $(#[doc = $doc:literal])*
219            $(#[cfg($cond_irq:meta)])?
220            $irq:ident => $(
221                $(#[cfg($cond_handler:meta)])?
222                $handler:ty
223            ),*;
224        )*
225    }) => {
226        #[derive(Copy, Clone)]
227        $(#[$outer])*
228        $vis struct $name;
229
230        $(
231            #[allow(non_snake_case)]
232            #[unsafe(no_mangle)]
233            $(#[cfg($cond_irq)])?
234            $(#[doc = $doc])*
235            unsafe extern "C" fn $irq() {
236                unsafe {
237                    $(
238                        $(#[cfg($cond_handler)])?
239                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
240
241                    )*
242                }
243            }
244
245            $(#[cfg($cond_irq)])?
246            $crate::bind_interrupts!(@inner
247                $(
248                    $(#[cfg($cond_handler)])?
249                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
250                )*
251            );
252        )*
253    };
254    (@inner $($t:tt)*) => {
255        $($t)*
256    }
257}
258
259// Reexports
260pub use _generated::{Peripherals, peripherals};
261pub use embassy_hal_internal::{Peri, PeripheralType};
262#[cfg(feature = "unstable-pac")]
263pub use stm32_metapac as pac;
264#[cfg(not(feature = "unstable-pac"))]
265pub(crate) use stm32_metapac as pac;
266
267use crate::interrupt::Priority;
268#[cfg(feature = "rt")]
269pub use crate::pac::NVIC_PRIO_BITS;
270
271/// `embassy-stm32` global configuration.
272#[non_exhaustive]
273#[derive(Clone, Copy)]
274pub struct Config {
275    /// RCC config.
276    pub rcc: rcc::Config,
277
278    #[cfg(feature = "low-power")]
279    /// RTC config
280    pub rtc: rtc::RtcConfig,
281
282    #[cfg(feature = "low-power")]
283    /// Minimum time to stop
284    pub min_stop_pause: embassy_time::Duration,
285
286    /// Enable debug during sleep and stop.
287    ///
288    /// May increase power consumption. Defaults to true.
289    #[cfg(dbgmcu)]
290    pub enable_debug_during_sleep: bool,
291
292    /// On low-power boards (eg. `stm32l4`, `stm32l5`, `stm32wba` and `stm32u5`),
293    /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`),
294    /// which needs to be enabled before these pins can be used.
295    ///
296    /// May increase power consumption. Defaults to true.
297    #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba))]
298    pub enable_independent_io_supply: bool,
299
300    /// On the U5 series all analog peripherals are powered by a separate supply.
301    #[cfg(any(stm32u5, stm32u3))]
302    pub enable_independent_analog_supply: bool,
303
304    /// BDMA interrupt priority.
305    ///
306    /// Defaults to P0 (highest).
307    #[cfg(bdma)]
308    pub bdma_interrupt_priority: Priority,
309
310    /// DMA interrupt priority.
311    ///
312    /// Defaults to P0 (highest).
313    #[cfg(dma)]
314    pub dma_interrupt_priority: Priority,
315
316    /// GPDMA interrupt priority.
317    ///
318    /// Defaults to P0 (highest).
319    #[cfg(gpdma)]
320    pub gpdma_interrupt_priority: Priority,
321
322    /// MDMA interrupt priority.
323    ///
324    /// Defaults to P0 (highest).
325    #[cfg(mdma)]
326    pub mdma_interrupt_priority: Priority,
327
328    /// Enables UCPD1 dead battery functionality.
329    ///
330    /// Defaults to false (disabled).
331    #[cfg(peri_ucpd1)]
332    pub enable_ucpd1_dead_battery: bool,
333
334    /// Enables UCPD2 dead battery functionality.
335    ///
336    /// Defaults to false (disabled).
337    #[cfg(peri_ucpd2)]
338    pub enable_ucpd2_dead_battery: bool,
339
340    /// Allows JTAG pins to be used for GPIO
341    #[cfg(stm32f1)]
342    pub swj: gpio::SwjCfg,
343}
344
345impl Default for Config {
346    fn default() -> Self {
347        Self {
348            rcc: Default::default(),
349            #[cfg(feature = "low-power")]
350            rtc: Default::default(),
351            #[cfg(feature = "low-power")]
352            min_stop_pause: embassy_time::Duration::from_millis(250),
353            #[cfg(dbgmcu)]
354            enable_debug_during_sleep: true,
355            #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba))]
356            enable_independent_io_supply: true,
357            #[cfg(any(stm32u5, stm32u3))]
358            enable_independent_analog_supply: true,
359            #[cfg(bdma)]
360            bdma_interrupt_priority: Priority::P0,
361            #[cfg(dma)]
362            dma_interrupt_priority: Priority::P0,
363            #[cfg(gpdma)]
364            gpdma_interrupt_priority: Priority::P0,
365            #[cfg(mdma)]
366            mdma_interrupt_priority: Priority::P0,
367            #[cfg(peri_ucpd1)]
368            enable_ucpd1_dead_battery: false,
369            #[cfg(peri_ucpd2)]
370            enable_ucpd2_dead_battery: false,
371            #[cfg(stm32f1)]
372            swj: Default::default(),
373        }
374    }
375}
376
377/// Initialize the `embassy-stm32` HAL with the provided configuration.
378///
379/// This returns the peripheral singletons that can be used for creating drivers.
380///
381/// This should only be called once at startup, otherwise it panics.
382#[cfg(not(feature = "_dual-core"))]
383pub fn init(config: Config) -> Peripherals {
384    init_hw(config)
385}
386
387#[cfg(feature = "_dual-core")]
388mod dual_core {
389    use core::cell::UnsafeCell;
390    use core::mem::MaybeUninit;
391    use core::sync::atomic::{AtomicUsize, Ordering};
392
393    use rcc::Clocks;
394
395    use super::*;
396
397    /// Object containing data that embassy needs to share between cores.
398    ///
399    /// It cannot be initialized by the user. The intended use is:
400    ///
401    /// ```
402    /// use core::mem::MaybeUninit;
403    /// use embassy_stm32::{init_secondary, SharedData};
404    ///
405    /// #[link_section = ".ram_d3"]
406    /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
407    ///
408    /// init_secondary(&SHARED_DATA);
409    /// ```
410    ///
411    /// This static must be placed in the same position for both cores. How and where this is done is left to the user.
412    #[repr(C)]
413    pub struct SharedData {
414        init_flag: AtomicUsize,
415        clocks: UnsafeCell<MaybeUninit<Clocks>>,
416        config: UnsafeCell<MaybeUninit<SharedConfig>>,
417        #[cfg(feature = "low-power")]
418        rcc_config: UnsafeCell<MaybeUninit<Option<rcc::Config>>>,
419    }
420
421    unsafe impl Sync for SharedData {}
422
423    const INIT_DONE_FLAG: usize = 0xca11ab1e;
424
425    /// Initialize the `embassy-stm32` HAL with the provided configuration.
426    /// This function does the actual initialization of the hardware, in contrast to [init_secondary] or [try_init_secondary].
427    /// Any core can do the init, but it's important only one core does it.
428    ///
429    /// This returns the peripheral singletons that can be used for creating drivers.
430    ///
431    /// This should only be called once at startup, otherwise it panics.
432    ///
433    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
434    /// for more information on its requirements.
435    pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {
436        let shared_data = unsafe { shared_data.assume_init_ref() };
437
438        // Write the flag as soon as possible. Reading this flag uninitialized in the `init_secondary`
439        // is maybe unsound? Unclear. If it is indeed unsound, writing it sooner doesn't fix it all,
440        // but improves the odds of it going right
441        shared_data.init_flag.store(0, Ordering::SeqCst);
442
443        rcc::set_freqs_ptr(shared_data.clocks.get());
444        #[cfg(feature = "low-power")]
445        rcc::set_rcc_config_ptr(shared_data.rcc_config.get());
446        let p = init_hw(config);
447
448        unsafe { *shared_data.config.get() }.write(config.into());
449
450        shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst);
451
452        p
453    }
454
455    /// Try to initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].
456    ///
457    /// This returns the peripheral singletons that can be used for creating drivers if the other core is done with its init.
458    /// If the other core is not done yet, this will return `None`.
459    ///
460    /// This should only be called once at startup, otherwise it may panic.
461    ///
462    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
463    /// for more information on its requirements.
464    pub fn try_init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Option<Peripherals> {
465        let shared_data = unsafe { shared_data.assume_init_ref() };
466
467        if shared_data.init_flag.load(Ordering::SeqCst) != INIT_DONE_FLAG {
468            return None;
469        }
470
471        // Separate load and store to support the CM0 of the STM32WL
472        shared_data.init_flag.store(0, Ordering::SeqCst);
473
474        Some(init_secondary_hw(shared_data))
475    }
476
477    /// Initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].
478    ///
479    /// This returns the peripheral singletons that can be used for creating drivers when the other core is done with its init.
480    /// If the other core is not done yet, this will spinloop wait on it.
481    ///
482    /// This should only be called once at startup, otherwise it may panic.
483    ///
484    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
485    /// for more information on its requirements.
486    pub fn init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {
487        loop {
488            if let Some(p) = try_init_secondary(shared_data) {
489                return p;
490            }
491        }
492    }
493
494    fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals {
495        rcc::set_freqs_ptr(shared_data.clocks.get());
496        #[cfg(feature = "low-power")]
497        rcc::set_rcc_config_ptr(shared_data.rcc_config.get());
498
499        let config = unsafe { (*shared_data.config.get()).assume_init() };
500
501        // We use different timers on the different cores, so we have to still initialize one here
502        critical_section::with(|cs| {
503            unsafe {
504                dma::init(
505                    cs,
506                    #[cfg(bdma)]
507                    config.bdma_interrupt_priority,
508                    #[cfg(dma)]
509                    config.dma_interrupt_priority,
510                    #[cfg(gpdma)]
511                    config.gpdma_interrupt_priority,
512                    #[cfg(mdma)]
513                    config.mdma_interrupt_priority,
514                )
515            }
516
517            #[cfg(feature = "_time-driver")]
518            // must be after rcc init
519            time_driver::init(cs);
520        });
521
522        Peripherals::take()
523    }
524
525    #[repr(C)]
526    #[derive(Clone, Copy)]
527    struct SharedConfig {
528        #[cfg(bdma)]
529        bdma_interrupt_priority: Priority,
530        #[cfg(dma)]
531        dma_interrupt_priority: Priority,
532        #[cfg(gpdma)]
533        gpdma_interrupt_priority: Priority,
534        #[cfg(mdma)]
535        mdma_interrupt_priority: Priority,
536    }
537
538    impl From<Config> for SharedConfig {
539        fn from(value: Config) -> Self {
540            let Config {
541                #[cfg(bdma)]
542                bdma_interrupt_priority,
543                #[cfg(dma)]
544                dma_interrupt_priority,
545                #[cfg(gpdma)]
546                gpdma_interrupt_priority,
547                #[cfg(mdma)]
548                mdma_interrupt_priority,
549                ..
550            } = value;
551
552            SharedConfig {
553                #[cfg(bdma)]
554                bdma_interrupt_priority,
555                #[cfg(dma)]
556                dma_interrupt_priority,
557                #[cfg(gpdma)]
558                gpdma_interrupt_priority,
559                #[cfg(mdma)]
560                mdma_interrupt_priority,
561            }
562        }
563    }
564}
565
566#[cfg(feature = "_dual-core")]
567pub use dual_core::*;
568
569fn init_hw(config: Config) -> Peripherals {
570    critical_section::with(|cs| {
571        let p = Peripherals::take_with_cs(cs);
572
573        #[cfg(dbgmcu_n6)]
574        {
575            crate::pac::RCC.miscensr().write(|w| w.set_dbgens(true));
576            crate::pac::RCC.miscenr().read(); // volatile read
577            crate::pac::DBGMCU
578                .cr()
579                .modify(|w| w.set_dbgclken(stm32_metapac::dbgmcu::vals::Dbgclken::B_0X1));
580            crate::pac::DBGMCU.cr().read();
581        }
582
583        #[cfg(dbgmcu)]
584        crate::pac::DBGMCU.cr().modify(|cr| {
585            #[cfg(dbgmcu_h5)]
586            {
587                cr.set_stop(config.enable_debug_during_sleep);
588                cr.set_standby(config.enable_debug_during_sleep);
589            }
590            #[cfg(any(
591                dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u0, dbgmcu_u3, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5
592            ))]
593            {
594                cr.set_dbg_stop(config.enable_debug_during_sleep);
595                cr.set_dbg_standby(config.enable_debug_during_sleep);
596            }
597            #[cfg(any(
598                dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
599                dbgmcu_l4, dbgmcu_wb, dbgmcu_wl, dbgmcu_n6
600            ))]
601            {
602                cr.set_dbg_sleep(config.enable_debug_during_sleep);
603                cr.set_dbg_stop(config.enable_debug_during_sleep);
604                cr.set_dbg_standby(config.enable_debug_during_sleep);
605            }
606            #[cfg(dbgmcu_h7)]
607            {
608                cr.set_d1dbgcken(config.enable_debug_during_sleep);
609                cr.set_d3dbgcken(config.enable_debug_during_sleep);
610                cr.set_dbgsleep_d1(config.enable_debug_during_sleep);
611                cr.set_dbgstby_d1(config.enable_debug_during_sleep);
612                cr.set_dbgstop_d1(config.enable_debug_during_sleep);
613            }
614        });
615
616        #[cfg(any(stm32h7rs))]
617        // On the H7RS the SYSCFG should not be reset if it is already enabled. This is typically the case when running from external flash and the bootloader enables the SYSCFG.
618        rcc::enable_with_cs::<peripherals::SYSCFG>(cs);
619        #[cfg(not(any(stm32f1, stm32wb, stm32wl, stm32h7rs)))]
620        rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs);
621        #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))]
622        rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs);
623        #[cfg(all(flash, not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs))))]
624        rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs);
625
626        // Enable the VDDIO2 power supply on chips that have it.
627        // Note that this requires the PWR peripheral to be enabled first.
628        #[cfg(any(stm32l4, stm32l5))]
629        {
630            crate::pac::PWR.cr2().modify(|w| {
631                // The official documentation states that we should ideally enable VDDIO2
632                // through the PVME2 bit, but it looks like this isn't required,
633                // and CubeMX itself skips this step.
634                w.set_iosv(config.enable_independent_io_supply);
635            });
636        }
637        #[cfg(stm32wba)]
638        {
639            use crate::pac::pwr::vals;
640            crate::pac::PWR.svmcr().modify(|w| {
641                w.set_io2sv(if config.enable_independent_io_supply {
642                    vals::Io2sv::B_0X1
643                } else {
644                    vals::Io2sv::B_0X0
645                });
646            });
647        }
648        #[cfg(any(stm32u5, stm32u3))]
649        {
650            crate::pac::PWR.svmcr().modify(|w| {
651                w.set_io2sv(config.enable_independent_io_supply);
652            });
653            if config.enable_independent_analog_supply {
654                crate::pac::PWR.svmcr().modify(|w| {
655                    w.set_avm1en(true);
656                });
657                while !crate::pac::PWR.svmsr().read().vdda1rdy() {}
658                crate::pac::PWR.svmcr().modify(|w| {
659                    w.set_asv(true);
660                });
661            } else {
662                crate::pac::PWR.svmcr().modify(|w| {
663                    w.set_avm1en(false);
664                    w.set_avm2en(false);
665                });
666            }
667        }
668
669        // dead battery functionality is still present on these
670        // chips despite them not having UCPD- disable it
671        #[cfg(any(stm32g070, stm32g0b0))]
672        {
673            crate::pac::SYSCFG.cfgr1().modify(|w| {
674                w.set_ucpd1_strobe(true);
675                w.set_ucpd2_strobe(true);
676            });
677        }
678
679        unsafe {
680            #[cfg(ucpd)]
681            ucpd::init(
682                cs,
683                #[cfg(all(peri_ucpd1, not(stm32n6)))]
684                config.enable_ucpd1_dead_battery,
685                #[cfg(peri_ucpd2)]
686                config.enable_ucpd2_dead_battery,
687            );
688
689            #[cfg(feature = "_split-pins-enabled")]
690            crate::pac::SYSCFG.pmcr().modify(|pmcr| {
691                #[cfg(feature = "split-pa0")]
692                pmcr.set_pa0so(true);
693                #[cfg(feature = "split-pa1")]
694                pmcr.set_pa1so(true);
695                #[cfg(feature = "split-pc2")]
696                pmcr.set_pc2so(true);
697                #[cfg(feature = "split-pc3")]
698                pmcr.set_pc3so(true);
699            });
700
701            gpio::init(cs);
702
703            #[cfg(stm32f1)]
704            crate::pac::AFIO.mapr().modify(|w| w.set_swj_cfg(config.swj.into()));
705
706            dma::init(
707                cs,
708                #[cfg(bdma)]
709                config.bdma_interrupt_priority,
710                #[cfg(dma)]
711                config.dma_interrupt_priority,
712                #[cfg(gpdma)]
713                config.gpdma_interrupt_priority,
714                #[cfg(mdma)]
715                config.mdma_interrupt_priority,
716            );
717            #[cfg(feature = "exti")]
718            exti::init(cs);
719
720            rcc::init_rcc(cs, config.rcc);
721
722            // must be before time_driver init to allow refcount reset
723            #[cfg(all(any(stm32wb, stm32wl5x), feature = "low-power"))]
724            hsem::init_hsem(cs);
725
726            // must be after rcc init
727            #[cfg(feature = "_time-driver")]
728            crate::time_driver::init(cs);
729
730            // must be after time-driver init
731            #[cfg(all(feature = "low-power", not(feature = "_lp-time-driver")))]
732            rtc::init_rtc(cs, config.rtc, config.min_stop_pause);
733        }
734
735        p
736    })
737}
738
739/// Performs a busy-wait delay for a specified number of microseconds.
740#[allow(unused)]
741pub(crate) fn block_for_us(us: u64) {
742    cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32);
743}