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}
617

Provided by KDAB

Privacy Policy