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