From 7c8831c4c2d3005071a34ed8a23c62c2fbeef629 Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Thu, 17 Apr 2025 15:13:25 +0200 Subject: [PATCH 1/3] fix: core was not recognized and 1-step lock did not work --- embassy-stm32/src/hsem/mod.rs | 224 ++++++++++++++++++++++++---------- 1 file changed, 162 insertions(+), 62 deletions(-) diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 31527bcdbf..3a0013e5d6 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -4,19 +4,98 @@ use embassy_hal_internal::PeripheralType; use crate::pac; use crate::rcc::RccPeripheral; -// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. +// TODO: This code works for all HSEM implementations except for the STM32WBA52/4/5xx MCUs. // Those MCUs have a different HSEM implementation (Secure semaphore lock support, // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), // which is not yet supported by this code. use crate::Peri; /// HSEM error. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HsemError { /// Locking the semaphore failed. LockFailed, } +/// HSEM identifier +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SemId { + /// Semaphore 0 + Id0 = 0, + /// Semaphore 1 + Id1 = 1, + /// Semaphore 2 + Id2 = 2, + /// Semaphore 3 + Id3 = 3, + /// Semaphore 4 + Id4 = 4, + /// Semaphore 5 + Id5 = 5, + /// Semaphore 6 + Id6 = 6, + /// Semaphore 7 + Id7 = 7, + /// Semaphore 8 + Id8 = 8, + /// Semaphore 9 + Id9 = 9, + /// Semaphore 10 + Id10 = 10, + /// Semaphore 11 + Id11 = 11, + /// Semaphore 12 + Id12 = 12, + /// Semaphore 13 + Id13 = 13, + /// Semaphore 14 + Id14 = 14, + /// Semaphore 15 + Id15 = 15, + /// Semaphore 16 + Id16 = 16, + /// Semaphore 17 + Id17 = 17, + /// Semaphore 18 + Id18 = 18, + /// Semaphore 19 + Id19 = 19, + /// Semaphore 20 + Id20 = 20, + /// Semaphore 21 + Id21 = 21, + /// Semaphore 22 + Id22 = 22, + /// Semaphore 23 + Id23 = 23, + /// Semaphore 24 + Id24 = 24, + /// Semaphore 25 + Id25 = 25, + /// Semaphore 26 + Id26 = 26, + /// Semaphore 27 + Id27 = 27, + /// Semaphore 28 + Id28 = 28, + /// Semaphore 29 + Id29 = 29, + /// Semaphore 30 + Id30 = 30, + /// Semaphore 31 + Id31 = 31, +} + +impl From for usize { + fn from(id: SemId) -> Self { + id as usize + } +} + /// CPU core. /// The enum values are identical to the bus master IDs / core Ids defined for each /// chip family (i.e. stm32h747 see rm0399 table 95) @@ -24,51 +103,67 @@ pub enum HsemError { #[repr(u8)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CoreId { - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - /// Cortex-M7, core 1. - Core0 = 0x3, + /// Main processor + Core0 = 0, + /// Coprocessor + Core1, +} - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - /// Cortex-M4, core 2. - Core1 = 0x1, +impl From for usize { + fn from(core: CoreId) -> Self { + core as usize + } +} - #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] - /// Cortex-M4, core 1 - Core0 = 0x4, +/// The core ID used in the HSEM_RLRx register. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RlrCoreId { + #[cfg(any(stm32h7a3, stm32h7b3, stm32h7b0))] + /// Cortex-M7, core 1. MASTERID = 1 + Core0 = 1, - #[cfg(any(stm32wb, stm32wl))] - /// Cortex-M0+, core 2. - Core1 = 0x8, -} + #[cfg(any( + stm32h723, stm32h725, stm32h730, stm32h733, stm32h735, stm32h742, stm32h743, stm32h745, stm32h747, stm32h750, + stm32h753, stm32h755, stm32h757, + ))] + /// Cortex-M7, core 1. MASTERID = 3 + Core0 = 3, -/// Get the current core id -/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. -#[inline(always)] -pub fn get_current_coreid() -> CoreId { - let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; - match cpuid & 0x000000F0 { - #[cfg(any(stm32wb, stm32wl))] - 0x0 => CoreId::Core1, + #[cfg(any(stm32wb, stm32wl))] + /// Cortex-M4, core 1 + Core0 = 4, - #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] - 0x4 => CoreId::Core0, + #[cfg(not(any(stm32wb, stm32wl, stm32h7a3, stm32h7b3, stm32h7b0)))] + /// Cortex-M4, core 2 + Core1 = 1, - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - 0x4 => CoreId::Core1, + #[cfg(any(stm32wb, stm32wl))] + /// Cortex M0+, core 2 + Core1 = 8, +} - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - 0x7 => CoreId::Core0, - _ => panic!("Unknown Cortex-M core"), +impl From for RlrCoreId { + fn from(core: CoreId) -> Self { + match core { + CoreId::Core0 => RlrCoreId::Core0, + CoreId::Core1 => RlrCoreId::Core1, + } } } -/// Translates the core ID to an index into the interrupt registers. -#[inline(always)] -fn core_id_to_index(core: CoreId) -> usize { - match core { - CoreId::Core0 => 0, - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] - CoreId::Core1 => 1, +impl CoreId { + /// Returns the ID of the current running core. + pub fn get_current() -> Self { + #[cfg(any( + all(stm32wl, not(feature = "_core-cm0p")), + all(not(stm32wl), any(feature = "_core-cm7", not(feature = "_core-cm4"))), + ))] + return CoreId::Core0; + + #[cfg(any(all(not(stm32wl), feature = "_core-cm4"), all(stm32wl, feature = "_core-cm0p")))] + return CoreId::Core1; } } @@ -86,16 +181,16 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Locks the semaphore. /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to /// check if the lock has been successful, carried out from the HSEM_Rx register. - pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { - T::regs().r(sem_id as usize).write(|w| { + pub fn two_step_lock(&mut self, sem_id: SemId, process_id: u8) -> Result<(), HsemError> { + T::regs().r(sem_id.into()).write(|w| { w.set_procid(process_id); - w.set_coreid(get_current_coreid() as u8); + w.set_coreid(RlrCoreId::from(CoreId::get_current()) as u8); w.set_lock(true); }); - let reg = T::regs().r(sem_id as usize).read(); + let reg = T::regs().r(sem_id.into()).read(); match ( reg.lock(), - reg.coreid() == get_current_coreid() as u8, + reg.coreid() == RlrCoreId::from(CoreId::get_current()) as u8, reg.procid() == process_id, ) { (true, true, true) => Ok(()), @@ -106,10 +201,15 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Locks the semaphore. /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, /// carried out from the HSEM_RLRx register. - pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { - let reg = T::regs().rlr(sem_id as usize).read(); - match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { - (false, true, 0) => Ok(()), + pub fn one_step_lock(&mut self, sem_id: SemId) -> Result<(), HsemError> { + let reg = T::regs().rlr(sem_id.into()).read(); + + match ( + reg.lock(), + reg.coreid() == RlrCoreId::from(CoreId::get_current()) as u8, + reg.procid(), + ) { + (true, true, 0) => Ok(()), _ => Err(HsemError::LockFailed), } } @@ -117,10 +217,12 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Unlocks the semaphore. /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus /// core ID or by a process not having the semaphore lock right. - pub fn unlock(&mut self, sem_id: u8, process_id: u8) { - T::regs().r(sem_id as usize).write(|w| { + pub fn unlock(&mut self, sem_id: SemId, process_id: Option) { + let process_id = process_id.unwrap_or(0); + + T::regs().r(sem_id.into()).write(|w| { w.set_procid(process_id); - w.set_coreid(get_current_coreid() as u8); + w.set_coreid(RlrCoreId::from(CoreId::get_current()) as u8); w.set_lock(false); }); } @@ -129,16 +231,16 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a /// matching COREID are unlocked, and may generate an interrupt when enabled. - pub fn unlock_all(&mut self, key: u16, core_id: u8) { + pub fn unlock_all(&mut self, key: u16, core_id: CoreId) { T::regs().cr().write(|w| { w.set_key(key); - w.set_coreid(core_id); + w.set_coreid(RlrCoreId::from(core_id) as u8); }); } /// Checks if the semaphore is locked. - pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { - T::regs().r(sem_id as usize).read().lock() + pub fn is_semaphore_locked(&self, sem_id: SemId) -> bool { + T::regs().r(sem_id.into()).read().lock() } /// Sets the clear (unlock) key @@ -152,22 +254,20 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { } /// Sets the interrupt enable bit for the semaphore. - pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { + pub fn enable_interrupt(&mut self, core_id: CoreId, sem_id: SemId, enable: bool) { T::regs() - .ier(core_id_to_index(core_id)) - .modify(|w| w.set_ise(sem_x, enable)); + .ier(core_id.into()) + .modify(|w| w.set_ise(sem_id.into(), enable)); } /// Gets the interrupt flag for the semaphore. - pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { - T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) + pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_id: SemId) -> bool { + T::regs().isr(core_id.into()).read().isf(sem_id.into()) } /// Clears the interrupt flag for the semaphore. - pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { - T::regs() - .icr(core_id_to_index(core_id)) - .write(|w| w.set_isc(sem_x, false)); + pub fn clear_interrupt(&mut self, core_id: CoreId, sem_id: SemId) { + T::regs().icr(core_id.into()).write(|w| w.set_isc(sem_id.into(), false)); } } From 053c94701d7c09ce9b1cc8e2ec0e60f0d5828ed0 Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Sat, 19 Apr 2025 10:12:07 +0200 Subject: [PATCH 2/3] fix(stm32wb55): sync cpu2 via hsem when entering sleep --- embassy-stm32/src/low_power.rs | 59 +++++++++++++++++++--- embassy-stm32/src/rcc/l.rs | 12 ++++- embassy-stm32/src/rcc/low_power.rs | 81 ++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 3 ++ 4 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 embassy-stm32/src/rcc/low_power.rs diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7734365f1f..6d884975d2 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -58,11 +58,10 @@ use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; -use cortex_m::peripheral::SCB; -use embassy_executor::*; - use crate::interrupt; use crate::time_driver::{get_driver, RtcDriver}; +use cortex_m::peripheral::SCB; +use embassy_executor::*; const THREAD_PENDER: usize = usize::MAX; @@ -98,10 +97,22 @@ pub(crate) unsafe fn on_wakeup_irq() { } /// Configure STOP mode with RTC. +#[cfg(all(feature = "low-power", not(any(stm32wb))))] pub fn stop_with_rtc(rtc: &'static Rtc) { unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) } +/// Configure STOP mode with RTC and HSEM. The hardware semaphore is needed for configuring the coprocessor +#[cfg(all(feature = "low-power", any(stm32wb)))] +pub fn stop_with_rtc_and_hsem( + rtc: &'static Rtc, + hsem: &'static mut crate::hsem::HardwareSemaphore<'static, crate::peripherals::HSEM>, +) { + let exec = unsafe { EXECUTOR.as_mut().unwrap() }; + exec.set_hsem(hsem); + exec.stop_with_rtc(rtc) +} + /// Get whether the core is ready to enter the given stop mode. /// /// This will return false if some peripheral driver is in use that @@ -124,10 +135,10 @@ pub enum StopMode { Stop2, } -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))] use stm32_metapac::pwr::vals::Lpms; -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))] impl Into for StopMode { fn into(self) -> Lpms { match self { @@ -152,6 +163,12 @@ pub struct Executor { not_send: PhantomData<*mut ()>, scb: SCB, time_driver: &'static RtcDriver, + + #[cfg(all(feature = "low-power", any(stm32wb)))] + /// Special treatment of coprocessor requires a hardware semaphore + hsem: embassy_sync::blocking_mutex::NoopMutex< + core::cell::Cell>>, + >, } impl Executor { @@ -165,6 +182,8 @@ impl Executor { not_send: PhantomData, scb: cortex_m::Peripherals::steal().SCB, time_driver: get_driver(), + #[cfg(all(feature = "low-power", any(stm32wb)))] + hsem: embassy_sync::blocking_mutex::NoopMutex::new(core::cell::Cell::default()), }); let executor = EXECUTOR.as_mut().unwrap(); @@ -178,6 +197,15 @@ impl Executor { trace!("low power: resume"); } + #[cfg(all(feature = "low-power", any(stm32wb)))] + pub(self) fn set_hsem( + &mut self, + hsem: &'static mut crate::hsem::HardwareSemaphore<'static, crate::peripherals::HSEM>, + ) { + assert!(self.hsem.lock(|x| x.replace(Some(hsem))).is_none()); + trace!("low power: hsem configured"); + } + pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { self.time_driver.set_rtc(rtc); @@ -198,7 +226,17 @@ impl Executor { #[allow(unused_variables)] fn configure_stop(&mut self, stop_mode: StopMode) { - #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] + #[cfg(all(feature = "low-power", any(stm32wb)))] + { + // Special treatment for MCUs with a coprocessor + unsafe { + self.hsem.lock_mut(|x| { + crate::rcc::low_power::stm32wb_configure_clocks_enter_stop_mode(x.get_mut().as_mut().unwrap()); + }); + } + } + + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] crate::pac::PWR.pmcr().modify(|v| { @@ -263,6 +301,15 @@ impl Executor { executor.inner.poll(); self.configure_pwr(); asm!("wfe"); + + #[cfg(all(feature = "low-power", any(stm32wb)))] + { + // Special treatment for MCUs with a coprocessor + self.hsem.lock_mut(|x| + + // Inentionally not calling from a critical section, as the critical section is handled internally + crate::rcc::low_power::stm32wb_configure_clocks_exit_stop_mode(x.get_mut().as_mut().unwrap())); + } }; } } diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 863942f1af..fc78bc8066 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -133,6 +133,9 @@ pub const WPAN_DEFAULT: Config = Config { mux: super::mux::ClockMux::default(), }; +#[cfg(all(feature = "low-power", any(stm32wb)))] +pub(super) static mut CURRENT_RCC_CONFIG: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); + fn msi_enable(range: MSIRange) { #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] RCC.cr().modify(|w| { @@ -149,6 +152,13 @@ fn msi_enable(range: MSIRange) { } pub(crate) unsafe fn init(config: Config) { + #[cfg(all(feature = "low-power", any(stm32wb)))] + { + // Store the current RCC config in to restore it when exiting low power mode. + // This is needed because these MCUs require a clock source switch before entering low power mode. + CURRENT_RCC_CONFIG = core::mem::MaybeUninit::new(config); + } + // Switch to MSI to prevent problems with PLL configuration. if !RCC.cr().read().msion() { // Turn on MSI and configure it to 4MHz. @@ -670,4 +680,4 @@ mod pll { PllOutput { p, q, r } } -} +} \ No newline at end of file diff --git a/embassy-stm32/src/rcc/low_power.rs b/embassy-stm32/src/rcc/low_power.rs new file mode 100644 index 0000000000..020060b7c1 --- /dev/null +++ b/embassy-stm32/src/rcc/low_power.rs @@ -0,0 +1,81 @@ +use crate::hsem::{self, HardwareSemaphore}; + +/// It is required to switch to the HSI clock before entering stop mode +/// See an5289-how-to-build-wireless-applications-with-stm32wb-mcus-stmicroelectronics p. 23-24 +#[cfg(all(feature = "low-power", stm32wb))] +pub(crate) fn stm32wb_configure_clocks_enter_stop_mode(hsem: &mut HardwareSemaphore) { + use crate::pac::PWR; + use crate::pac::{rcc::vals::Sw, RCC}; + use hsem::SemId; + use stm32_metapac::rcc::vals::Smps; + + critical_section::with(|_cs| { + // Poll Sem3 until granted + // TODO: Add some kind of timeout here and panic? + trace!("Polling Sem3 until granted"); + while let Err(_) = hsem.one_step_lock(SemId::Id3) {} + trace!("Sem3 granted"); + + // Get Sem4 + match hsem.one_step_lock(SemId::Id4) { + Ok(_) => { + // Sem4 granted + if PWR.extscr().read().c2ds() { + // C2DS set - unlock Sem4 + hsem.unlock(SemId::Id4, None); + } + } + Err(_) => { + // Sem4 not granted + // Set HSION + RCC.cr().modify(|w| { + w.set_hsion(true); + }); + + // Wait for HSIRDY + while !RCC.cr().read().hsirdy() {} + + // Set SW to HSI + RCC.cfgr().modify(|w| { + w.set_sw(Sw::HSI); + }); + + // Wait for SWS to report HSI + while !RCC.cfgr().read().sws().eq(&Sw::HSI) {} + + // Set SMPSSEL to HSI + RCC.smpscr().modify(|w| { + w.set_smpssel(Smps::HSI); + }); + } + } + + // Unlock Sem3 + hsem.unlock(SemId::Id3, None); + }); +} + +/// __Attention__: Must not be called from within a critical section +#[cfg(all(feature = "low-power", stm32wb))] +pub(crate) fn stm32wb_configure_clocks_exit_stop_mode(hsem: &mut HardwareSemaphore) { + use super::{init, CURRENT_RCC_CONFIG}; + use crate::hsem::SemId; + + // Release Sem4 + hsem.unlock(SemId::Id4, None); + + // Enter the critical section afterwards + critical_section::with(|_cs| { + // Poll Sem3 until granted + // TODO: Add some kind of timeout here and panic? + while let Err(_) = hsem.one_step_lock(SemId::Id3) {} + + // Restore the RCC config from before entering stop mode + // Diveating from The flow chart of Figure 7 in AN5289 + // We always reconfigure the clocks as we don't know if HSE is suitable for the application + let config = unsafe { CURRENT_RCC_CONFIG.assume_init() }; + unsafe { init(config) }; + + hsem.unlock(SemId::Id3, None); + }); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4f43d3748c..aaedd8122b 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -30,6 +30,9 @@ pub use hsi48::*; #[cfg_attr(stm32wba, path = "wba.rs")] mod _version; +#[cfg(any(feature = "low-power", any(stm32wb)))] +pub mod low_power; + pub use _version::*; use stm32_metapac::RCC; From 3e4b016df0a6671fef997d11237ca5aca91e5cb3 Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Fri, 25 Apr 2025 08:23:29 +0200 Subject: [PATCH 3/3] add low-power example and hsem traces for #4092 --- embassy-stm32/src/hsem/mod.rs | 21 +++++++++-- examples/stm32wb/Cargo.toml | 5 +++ examples/stm32wb/src/bin/low_power.rs | 50 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 examples/stm32wb/src/bin/low_power.rs diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 3a0013e5d6..8c40922efc 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -203,14 +203,31 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// carried out from the HSEM_RLRx register. pub fn one_step_lock(&mut self, sem_id: SemId) -> Result<(), HsemError> { let reg = T::regs().rlr(sem_id.into()).read(); + trace!( + "RLR (bits: 0x{:x}) - lock: {}, coreid: {}, procid: {}", + reg.0, + reg.lock(), + reg.coreid(), + reg.procid() + ); match ( reg.lock(), reg.coreid() == RlrCoreId::from(CoreId::get_current()) as u8, reg.procid(), ) { - (true, true, 0) => Ok(()), - _ => Err(HsemError::LockFailed), + (true, true, 0) => { + trace!( + "Locked successfully (coreid: {}, procid: {})", + reg.coreid(), + reg.procid() + ); + Ok(()) + } + _ => { + trace!("Failed to lock (coreid: {}, procid: {})", reg.coreid(), reg.procid()); + Err(HsemError::LockFailed) + } } } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 96f66f3af3..611106cb6d 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -27,6 +27,7 @@ static_cell = "2" default = ["ble", "mac"] mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] ble = ["embassy-stm32-wpan/ble"] +low-power = ["embassy-stm32/low-power"] [[bin]] name = "tl_mbox_ble" @@ -52,5 +53,9 @@ required-features = ["ble"] name = "gatt_server" required-features = ["ble"] +[[bin]] +name = "low_power" +required-features = ["low-power"] + [profile.release] debug = 2 diff --git a/examples/stm32wb/src/bin/low_power.rs b/examples/stm32wb/src/bin/low_power.rs new file mode 100644 index 0000000000..f508785659 --- /dev/null +++ b/examples/stm32wb/src/bin/low_power.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::peripherals; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; +use static_cell::StaticCell; + +#[cortex_m_rt::entry] +fn main() -> ! { + embassy_stm32::low_power::Executor::take().run(|spawner| { + spawner.spawn(async_main(spawner)).unwrap(); + }); +} + +#[embassy_executor::task] +async fn async_main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + // when enabled the power-consumption is much higher during stop, but debugging and RTT is working + config.enable_debug_during_sleep = true; + // Enable LSE and configure RTC to use LSE + config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lse(); + config.rcc.ls.rtc = embassy_stm32::rcc::RtcClockSource::LSE; + let p = embassy_stm32::init(config); + + static RTC: StaticCell = StaticCell::new(); + static HSEM: StaticCell> = StaticCell::new(); + let rtc = RTC.init(Rtc::new(p.RTC, RtcConfig::default())); + let hsem = HSEM.init(embassy_stm32::hsem::HardwareSemaphore::new(p.HSEM)); + + embassy_stm32::low_power::stop_with_rtc_and_hsem(rtc, hsem); + + info!("Hello World!"); + + let mut led = Output::new(p.PB0, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(1000).await; + + info!("low"); + led.set_low(); + Timer::after_millis(1000).await; + } +}