embassy_stm32/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![allow(async_fn_in_trait)]
3#![cfg_attr(
4    docsrs,
5    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"
6)]
7#![doc = include_str!("../README.md")]
8#![warn(missing_docs)]
9
10//! ## Feature flags
11#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
12
13// This must go FIRST so that all the other modules see its macros.
14mod fmt;
15include!(concat!(env!("OUT_DIR"), "/_macros.rs"));
16
17// Utilities
18mod macros;
19pub mod time;
20/// Operating modes for peripherals.
21pub mod mode {
22    trait SealedMode {}
23
24    /// Operating mode for a peripheral.
25    #[allow(private_bounds)]
26    pub trait Mode: SealedMode {}
27
28    macro_rules! impl_mode {
29        ($name:ident) => {
30            impl SealedMode for $name {}
31            impl Mode for $name {}
32        };
33    }
34
35    /// Blocking mode.
36    pub struct Blocking;
37    /// Async mode.
38    pub struct Async;
39
40    impl_mode!(Blocking);
41    impl_mode!(Async);
42}
43
44// Always-present hardware
45pub mod dma;
46pub mod gpio;
47pub mod rcc;
48#[cfg(feature = "_time-driver")]
49mod time_driver;
50pub mod timer;
51
52// Sometimes-present hardware
53
54#[cfg(adc)]
55pub mod adc;
56#[cfg(can)]
57pub mod can;
58// FIXME: Cordic driver cause stm32u5a5zj crash
59#[cfg(all(cordic, not(any(stm32u5a5, stm32u5a9))))]
60pub mod cordic;
61#[cfg(crc)]
62pub mod crc;
63#[cfg(cryp)]
64pub mod cryp;
65#[cfg(dac)]
66pub mod dac;
67#[cfg(dcmi)]
68pub mod dcmi;
69#[cfg(dsihost)]
70pub mod dsihost;
71#[cfg(dts)]
72pub mod dts;
73#[cfg(eth)]
74pub mod eth;
75#[cfg(feature = "exti")]
76pub mod exti;
77pub mod flash;
78#[cfg(fmc)]
79pub mod fmc;
80#[cfg(hash)]
81pub mod hash;
82#[cfg(hrtim)]
83pub mod hrtim;
84#[cfg(hsem)]
85pub mod hsem;
86#[cfg(hspi)]
87pub mod hspi;
88#[cfg(i2c)]
89pub mod i2c;
90#[cfg(any(all(spi_v1, rcc_f4), spi_v3))]
91pub mod i2s;
92#[cfg(stm32wb)]
93pub mod ipcc;
94#[cfg(feature = "low-power")]
95pub mod low_power;
96#[cfg(lptim)]
97pub mod lptim;
98#[cfg(ltdc)]
99pub mod ltdc;
100#[cfg(opamp)]
101pub mod opamp;
102#[cfg(octospi)]
103pub mod ospi;
104#[cfg(quadspi)]
105pub mod qspi;
106#[cfg(rng)]
107pub mod rng;
108#[cfg(all(rtc, not(rtc_v1)))]
109pub mod rtc;
110#[cfg(sai)]
111pub mod sai;
112#[cfg(sdmmc)]
113pub mod sdmmc;
114#[cfg(spdifrx)]
115pub mod spdifrx;
116#[cfg(spi)]
117pub mod spi;
118#[cfg(tsc)]
119pub mod tsc;
120#[cfg(ucpd)]
121pub mod ucpd;
122#[cfg(uid)]
123pub mod uid;
124#[cfg(usart)]
125pub mod usart;
126#[cfg(any(usb, otg))]
127pub mod usb;
128#[cfg(iwdg)]
129pub mod wdg;
130
131// This must go last, so that it sees all the impl_foo! macros defined earlier.
132pub(crate) mod _generated {
133    #![allow(dead_code)]
134    #![allow(unused_imports)]
135    #![allow(non_snake_case)]
136    #![allow(missing_docs)]
137
138    include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
139}
140
141pub use crate::_generated::interrupt;
142
143/// Macro to bind interrupts to handlers.
144///
145/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
146/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
147/// prove at compile-time that the right interrupts have been bound.
148///
149/// Example of how to bind one interrupt:
150///
151/// ```rust,ignore
152/// use embassy_stm32::{bind_interrupts, usb, peripherals};
153///
154/// bind_interrupts!(struct Irqs {
155///     OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
156/// });
157/// ```
158///
159/// Example of how to bind multiple interrupts, and multiple handlers to each interrupt, in a single macro invocation:
160///
161/// ```rust,ignore
162/// use embassy_stm32::{bind_interrupts, i2c, peripherals};
163///
164/// bind_interrupts!(struct Irqs {
165///     I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
166///     I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,
167///         i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;
168/// });
169/// ```
170
171// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
172#[macro_export]
173macro_rules! bind_interrupts {
174    ($vis:vis struct $name:ident {
175        $(
176            $(#[cfg($cond_irq:meta)])?
177            $irq:ident => $(
178                $(#[cfg($cond_handler:meta)])?
179                $handler:ty
180            ),*;
181        )*
182    }) => {
183        #[derive(Copy, Clone)]
184        $vis struct $name;
185
186        $(
187            #[allow(non_snake_case)]
188            #[no_mangle]
189            $(#[cfg($cond_irq)])?
190            unsafe extern "C" fn $irq() {
191                $(
192                    $(#[cfg($cond_handler)])?
193                    <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
194
195                )*
196            }
197
198            $(#[cfg($cond_irq)])?
199            $crate::bind_interrupts!(@inner
200                $(
201                    $(#[cfg($cond_handler)])?
202                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
203                )*
204            );
205        )*
206    };
207    (@inner $($t:tt)*) => {
208        $($t)*
209    }
210}
211
212// Reexports
213pub use _generated::{peripherals, Peripherals};
214pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
215#[cfg(feature = "unstable-pac")]
216pub use stm32_metapac as pac;
217#[cfg(not(feature = "unstable-pac"))]
218pub(crate) use stm32_metapac as pac;
219
220use crate::interrupt::Priority;
221#[cfg(feature = "rt")]
222pub use crate::pac::NVIC_PRIO_BITS;
223
224/// `embassy-stm32` global configuration.
225#[non_exhaustive]
226#[derive(Clone, Copy)]
227pub struct Config {
228    /// RCC config.
229    pub rcc: rcc::Config,
230
231    /// Enable debug during sleep and stop.
232    ///
233    /// May increase power consumption. Defaults to true.
234    #[cfg(dbgmcu)]
235    pub enable_debug_during_sleep: bool,
236
237    /// On low-power boards (eg. `stm32l4`, `stm32l5` and `stm32u5`),
238    /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`),
239    /// which needs to be enabled before these pins can be used.
240    ///
241    /// May increase power consumption. Defaults to true.
242    #[cfg(any(stm32l4, stm32l5, stm32u5))]
243    pub enable_independent_io_supply: bool,
244
245    /// On the U5 series all analog peripherals are powered by a separate supply.
246    #[cfg(stm32u5)]
247    pub enable_independent_analog_supply: bool,
248
249    /// BDMA interrupt priority.
250    ///
251    /// Defaults to P0 (highest).
252    #[cfg(bdma)]
253    pub bdma_interrupt_priority: Priority,
254
255    /// DMA interrupt priority.
256    ///
257    /// Defaults to P0 (highest).
258    #[cfg(dma)]
259    pub dma_interrupt_priority: Priority,
260
261    /// GPDMA interrupt priority.
262    ///
263    /// Defaults to P0 (highest).
264    #[cfg(gpdma)]
265    pub gpdma_interrupt_priority: Priority,
266
267    /// Enables UCPD1 dead battery functionality.
268    ///
269    /// Defaults to false (disabled).
270    #[cfg(peri_ucpd1)]
271    pub enable_ucpd1_dead_battery: bool,
272
273    /// Enables UCPD2 dead battery functionality.
274    ///
275    /// Defaults to false (disabled).
276    #[cfg(peri_ucpd2)]
277    pub enable_ucpd2_dead_battery: bool,
278}
279
280impl Default for Config {
281    fn default() -> Self {
282        Self {
283            rcc: Default::default(),
284            #[cfg(dbgmcu)]
285            enable_debug_during_sleep: true,
286            #[cfg(any(stm32l4, stm32l5, stm32u5))]
287            enable_independent_io_supply: true,
288            #[cfg(stm32u5)]
289            enable_independent_analog_supply: true,
290            #[cfg(bdma)]
291            bdma_interrupt_priority: Priority::P0,
292            #[cfg(dma)]
293            dma_interrupt_priority: Priority::P0,
294            #[cfg(gpdma)]
295            gpdma_interrupt_priority: Priority::P0,
296            #[cfg(peri_ucpd1)]
297            enable_ucpd1_dead_battery: false,
298            #[cfg(peri_ucpd2)]
299            enable_ucpd2_dead_battery: false,
300        }
301    }
302}
303
304/// Initialize the `embassy-stm32` HAL with the provided configuration.
305///
306/// This returns the peripheral singletons that can be used for creating drivers.
307///
308/// This should only be called once at startup, otherwise it panics.
309#[cfg(not(feature = "_dual-core"))]
310pub fn init(config: Config) -> Peripherals {
311    init_hw(config)
312}
313
314#[cfg(feature = "_dual-core")]
315mod dual_core {
316    use core::cell::UnsafeCell;
317    use core::mem::MaybeUninit;
318    use core::sync::atomic::{AtomicUsize, Ordering};
319
320    use rcc::Clocks;
321
322    use super::*;
323
324    /// Object containing data that embassy needs to share between cores.
325    ///
326    /// It cannot be initialized by the user. The intended use is:
327    ///
328    /// ```
329    /// use core::mem::MaybeUninit;
330    /// use embassy_stm32::{init_secondary, SharedData};
331    ///
332    /// #[link_section = ".ram_d3"]
333    /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
334    ///
335    /// init_secondary(&SHARED_DATA);
336    /// ```
337    ///
338    /// This static must be placed in the same position for both cores. How and where this is done is left to the user.
339    #[repr(C)]
340    pub struct SharedData {
341        init_flag: AtomicUsize,
342        clocks: UnsafeCell<MaybeUninit<Clocks>>,
343        config: UnsafeCell<MaybeUninit<SharedConfig>>,
344    }
345
346    unsafe impl Sync for SharedData {}
347
348    const INIT_DONE_FLAG: usize = 0xca11ab1e;
349
350    /// Initialize the `embassy-stm32` HAL with the provided configuration.
351    /// This function does the actual initialization of the hardware, in contrast to [init_secondary] or [try_init_secondary].
352    /// Any core can do the init, but it's important only one core does it.
353    ///
354    /// This returns the peripheral singletons that can be used for creating drivers.
355    ///
356    /// This should only be called once at startup, otherwise it panics.
357    ///
358    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
359    /// for more information on its requirements.
360    pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {
361        let shared_data = unsafe { shared_data.assume_init_ref() };
362
363        // Write the flag as soon as possible. Reading this flag uninitialized in the `init_secondary`
364        // is maybe unsound? Unclear. If it is indeed unsound, writing it sooner doesn't fix it all,
365        // but improves the odds of it going right
366        shared_data.init_flag.store(0, Ordering::SeqCst);
367
368        rcc::set_freqs_ptr(shared_data.clocks.get());
369        let p = init_hw(config);
370
371        unsafe { *shared_data.config.get() }.write(config.into());
372
373        shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst);
374
375        p
376    }
377
378    /// Try to initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].
379    ///
380    /// This returns the peripheral singletons that can be used for creating drivers if the other core is done with its init.
381    /// If the other core is not done yet, this will return `None`.
382    ///
383    /// This should only be called once at startup, otherwise it may panic.
384    ///
385    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
386    /// for more information on its requirements.
387    pub fn try_init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Option<Peripherals> {
388        let shared_data = unsafe { shared_data.assume_init_ref() };
389
390        if shared_data.init_flag.load(Ordering::SeqCst) != INIT_DONE_FLAG {
391            return None;
392        }
393
394        // Separate load and store to support the CM0 of the STM32WL
395        shared_data.init_flag.store(0, Ordering::SeqCst);
396
397        Some(init_secondary_hw(shared_data))
398    }
399
400    /// Initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].
401    ///
402    /// This returns the peripheral singletons that can be used for creating drivers when the other core is done with its init.
403    /// If the other core is not done yet, this will spinloop wait on it.
404    ///
405    /// This should only be called once at startup, otherwise it may panic.
406    ///
407    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs
408    /// for more information on its requirements.
409    pub fn init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {
410        loop {
411            if let Some(p) = try_init_secondary(shared_data) {
412                return p;
413            }
414        }
415    }
416
417    fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals {
418        rcc::set_freqs_ptr(shared_data.clocks.get());
419
420        let config = unsafe { (*shared_data.config.get()).assume_init() };
421
422        // We use different timers on the different cores, so we have to still initialize one here
423        critical_section::with(|cs| {
424            unsafe {
425                dma::init(
426                    cs,
427                    #[cfg(bdma)]
428                    config.bdma_interrupt_priority,
429                    #[cfg(dma)]
430                    config.dma_interrupt_priority,
431                    #[cfg(gpdma)]
432                    config.gpdma_interrupt_priority,
433                )
434            }
435
436            #[cfg(feature = "_time-driver")]
437            // must be after rcc init
438            time_driver::init(cs);
439        });
440
441        Peripherals::take()
442    }
443
444    #[repr(C)]
445    #[derive(Clone, Copy)]
446    struct SharedConfig {
447        #[cfg(bdma)]
448        bdma_interrupt_priority: Priority,
449        #[cfg(dma)]
450        dma_interrupt_priority: Priority,
451        #[cfg(gpdma)]
452        gpdma_interrupt_priority: Priority,
453    }
454
455    impl From<Config> for SharedConfig {
456        fn from(value: Config) -> Self {
457            let Config {
458                #[cfg(bdma)]
459                bdma_interrupt_priority,
460                #[cfg(dma)]
461                dma_interrupt_priority,
462                #[cfg(gpdma)]
463                gpdma_interrupt_priority,
464                ..
465            } = value;
466
467            SharedConfig {
468                #[cfg(bdma)]
469                bdma_interrupt_priority,
470                #[cfg(dma)]
471                dma_interrupt_priority,
472                #[cfg(gpdma)]
473                gpdma_interrupt_priority,
474            }
475        }
476    }
477}
478
479#[cfg(feature = "_dual-core")]
480pub use dual_core::*;
481
482fn init_hw(config: Config) -> Peripherals {
483    critical_section::with(|cs| {
484        let p = Peripherals::take_with_cs(cs);
485
486        #[cfg(dbgmcu)]
487        crate::pac::DBGMCU.cr().modify(|cr| {
488            #[cfg(dbgmcu_h5)]
489            {
490                cr.set_stop(config.enable_debug_during_sleep);
491                cr.set_standby(config.enable_debug_during_sleep);
492            }
493            #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u0, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5))]
494            {
495                cr.set_dbg_stop(config.enable_debug_during_sleep);
496                cr.set_dbg_standby(config.enable_debug_during_sleep);
497            }
498            #[cfg(any(
499                dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
500                dbgmcu_l4, dbgmcu_wb, dbgmcu_wl
501            ))]
502            {
503                cr.set_dbg_sleep(config.enable_debug_during_sleep);
504                cr.set_dbg_stop(config.enable_debug_during_sleep);
505                cr.set_dbg_standby(config.enable_debug_during_sleep);
506            }
507            #[cfg(dbgmcu_h7)]
508            {
509                cr.set_d1dbgcken(config.enable_debug_during_sleep);
510                cr.set_d3dbgcken(config.enable_debug_during_sleep);
511                cr.set_dbgsleep_d1(config.enable_debug_during_sleep);
512                cr.set_dbgstby_d1(config.enable_debug_during_sleep);
513                cr.set_dbgstop_d1(config.enable_debug_during_sleep);
514            }
515        });
516
517        #[cfg(not(any(stm32f1, stm32wb, stm32wl)))]
518        rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs);
519        #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))]
520        rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs);
521        #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs)))]
522        rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs);
523
524        // Enable the VDDIO2 power supply on chips that have it.
525        // Note that this requires the PWR peripheral to be enabled first.
526        #[cfg(any(stm32l4, stm32l5))]
527        {
528            crate::pac::PWR.cr2().modify(|w| {
529                // The official documentation states that we should ideally enable VDDIO2
530                // through the PVME2 bit, but it looks like this isn't required,
531                // and CubeMX itself skips this step.
532                w.set_iosv(config.enable_independent_io_supply);
533            });
534        }
535        #[cfg(stm32u5)]
536        {
537            crate::pac::PWR.svmcr().modify(|w| {
538                w.set_io2sv(config.enable_independent_io_supply);
539            });
540            if config.enable_independent_analog_supply {
541                crate::pac::PWR.svmcr().modify(|w| {
542                    w.set_avm1en(true);
543                });
544                while !crate::pac::PWR.svmsr().read().vdda1rdy() {}
545                crate::pac::PWR.svmcr().modify(|w| {
546                    w.set_asv(true);
547                });
548            } else {
549                crate::pac::PWR.svmcr().modify(|w| {
550                    w.set_avm1en(false);
551                    w.set_avm2en(false);
552                });
553            }
554        }
555
556        // dead battery functionality is still present on these
557        // chips despite them not having UCPD- disable it
558        #[cfg(any(stm32g070, stm32g0b0))]
559        {
560            crate::pac::SYSCFG.cfgr1().modify(|w| {
561                w.set_ucpd1_strobe(true);
562                w.set_ucpd2_strobe(true);
563            });
564        }
565
566        unsafe {
567            #[cfg(ucpd)]
568            ucpd::init(
569                cs,
570                #[cfg(peri_ucpd1)]
571                config.enable_ucpd1_dead_battery,
572                #[cfg(peri_ucpd2)]
573                config.enable_ucpd2_dead_battery,
574            );
575
576            #[cfg(feature = "_split-pins-enabled")]
577            crate::pac::SYSCFG.pmcr().modify(|pmcr| {
578                #[cfg(feature = "split-pa0")]
579                pmcr.set_pa0so(true);
580                #[cfg(feature = "split-pa1")]
581                pmcr.set_pa1so(true);
582                #[cfg(feature = "split-pc2")]
583                pmcr.set_pc2so(true);
584                #[cfg(feature = "split-pc3")]
585                pmcr.set_pc3so(true);
586            });
587
588            gpio::init(cs);
589            dma::init(
590                cs,
591                #[cfg(bdma)]
592                config.bdma_interrupt_priority,
593                #[cfg(dma)]
594                config.dma_interrupt_priority,
595                #[cfg(gpdma)]
596                config.gpdma_interrupt_priority,
597            );
598            #[cfg(feature = "exti")]
599            exti::init(cs);
600
601            rcc::init(config.rcc);
602
603            // must be after rcc init
604            #[cfg(feature = "_time-driver")]
605            time_driver::init(cs);
606
607            #[cfg(feature = "low-power")]
608            {
609                crate::rcc::REFCOUNT_STOP2 = 0;
610                crate::rcc::REFCOUNT_STOP1 = 0;
611            }
612        }
613
614        p
615    })
616}