aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-16 07:50:49 -0600
committerxoviat <[email protected]>2025-11-16 07:50:49 -0600
commit29d4ade2866e6c8d2114b393853354ded1e61db7 (patch)
tree13d38bc6cce8f71ceccbde7877ffe66a8643e30d /embassy-stm32/src
parenta51533c0b4edd551a1b9587b9272026b0b256d54 (diff)
low_power: misc cleanups and allow main macro
Diffstat (limited to 'embassy-stm32/src')
-rw-r--r--embassy-stm32/src/lib.rs5
-rw-r--r--embassy-stm32/src/low_power.rs88
-rw-r--r--embassy-stm32/src/rcc/l.rs43
-rw-r--r--embassy-stm32/src/rcc/mod.rs4
-rw-r--r--embassy-stm32/src/rtc/mod.rs7
-rw-r--r--embassy-stm32/src/time_driver.rs7
6 files changed, 56 insertions, 98 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 6e492946a..7c3770643 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -649,10 +649,7 @@ fn init_hw(config: Config) -> Peripherals {
649 rcc::init_rcc(cs, config.rcc); 649 rcc::init_rcc(cs, config.rcc);
650 650
651 #[cfg(feature = "low-power")] 651 #[cfg(feature = "low-power")]
652 crate::rtc::init_rtc(cs, config.rtc); 652 rtc::init_rtc(cs, config.rtc, config.min_stop_pause);
653
654 #[cfg(feature = "low-power")]
655 crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause);
656 } 653 }
657 654
658 p 655 p
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index 36c7e2242..cf8f2b393 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -14,7 +14,7 @@
14//! 14//!
15//! Since entering and leaving low-power modes typically incurs a significant latency, the 15//! Since entering and leaving low-power modes typically incurs a significant latency, the
16//! low-power executor will only attempt to enter when the next timer event is at least 16//! low-power executor will only attempt to enter when the next timer event is at least
17//! [`time_driver::MIN_STOP_PAUSE`] in the future. 17//! [`time_driver::min_stop_pause`] in the future.
18//! 18//!
19//! Currently there is no macro analogous to `embassy_executor::main` for this executor; 19//! Currently there is no macro analogous to `embassy_executor::main` for this executor;
20//! consequently one must define their entrypoint manually. Moreover, you must relinquish control 20//! consequently one must define their entrypoint manually. Moreover, you must relinquish control
@@ -22,21 +22,16 @@
22//! 22//!
23//! ```rust,no_run 23//! ```rust,no_run
24//! use embassy_executor::Spawner; 24//! use embassy_executor::Spawner;
25//! use embassy_stm32::low_power::Executor; 25//! use embassy_stm32::low_power;
26//! use embassy_stm32::rtc::{Rtc, RtcConfig}; 26//! use embassy_stm32::rtc::{Rtc, RtcConfig};
27//! use static_cell::StaticCell; 27//! use embassy_time::Duration;
28//! 28//!
29//! #[cortex_m_rt::entry] 29//! #[embassy_executor::main(executor = "low_power::Executor")]
30//! fn main() -> ! {
31//! Executor::take().run(|spawner| {
32//! spawner.spawn(unwrap!(async_main(spawner)));
33//! });
34//! }
35//!
36//! #[embassy_executor::task]
37//! async fn async_main(spawner: Spawner) { 30//! async fn async_main(spawner: Spawner) {
38//! // initialize the platform... 31//! // initialize the platform...
39//! let mut config = embassy_stm32::Config::default(); 32//! let mut config = embassy_stm32::Config::default();
33//! // the default value, but can be adjusted
34//! config.min_stop_pause = Duration::from_millis(250);
40//! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working 35//! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working
41//! config.enable_debug_during_sleep = false; 36//! config.enable_debug_during_sleep = false;
42//! let p = embassy_stm32::init(config); 37//! let p = embassy_stm32::init(config);
@@ -45,11 +40,9 @@
45//! } 40//! }
46//! ``` 41//! ```
47 42
48// TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.`
49#![allow(static_mut_refs)]
50
51use core::arch::asm; 43use core::arch::asm;
52use core::marker::PhantomData; 44use core::marker::PhantomData;
45use core::mem;
53use core::sync::atomic::{Ordering, compiler_fence}; 46use core::sync::atomic::{Ordering, compiler_fence};
54 47
55use cortex_m::peripheral::SCB; 48use cortex_m::peripheral::SCB;
@@ -57,11 +50,12 @@ use critical_section::CriticalSection;
57use embassy_executor::*; 50use embassy_executor::*;
58 51
59use crate::interrupt; 52use crate::interrupt;
53use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2};
60use crate::time_driver::get_driver; 54use crate::time_driver::get_driver;
61 55
62const THREAD_PENDER: usize = usize::MAX; 56const THREAD_PENDER: usize = usize::MAX;
63 57
64static mut EXECUTOR: Option<Executor> = None; 58static mut EXECUTOR_TAKEN: bool = false;
65 59
66/// Prevent the device from going into the stop mode if held 60/// Prevent the device from going into the stop mode if held
67pub struct DeviceBusy(StopMode); 61pub struct DeviceBusy(StopMode);
@@ -182,42 +176,47 @@ impl Into<Lpms> for StopMode {
182pub struct Executor { 176pub struct Executor {
183 inner: raw::Executor, 177 inner: raw::Executor,
184 not_send: PhantomData<*mut ()>, 178 not_send: PhantomData<*mut ()>,
185 scb: SCB,
186} 179}
187 180
188impl Executor { 181impl Executor {
189 /// Create a new Executor. 182 /// Create a new Executor.
190 pub fn take() -> &'static mut Self { 183 pub fn new() -> Self {
191 critical_section::with(|_| unsafe { 184 unsafe {
192 assert!(EXECUTOR.is_none()); 185 if EXECUTOR_TAKEN {
193 186 panic!("Low power executor can only be taken once.");
194 EXECUTOR = Some(Self { 187 } else {
195 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 188 EXECUTOR_TAKEN = true;
196 not_send: PhantomData, 189 }
197 scb: cortex_m::Peripherals::steal().SCB, 190 }
198 });
199
200 let executor = EXECUTOR.as_mut().unwrap();
201 191
202 executor 192 Self {
203 }) 193 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
194 not_send: PhantomData,
195 }
204 } 196 }
205 197
206 pub(crate) unsafe fn on_wakeup_irq() { 198 pub(crate) unsafe fn on_wakeup_irq() {
207 critical_section::with(|cs| { 199 critical_section::with(|cs| {
208 #[cfg(stm32wlex)] 200 #[cfg(stm32wlex)]
209 { 201 {
210 let extscr = crate::pac::PWR.extscr().read(); 202 use crate::pac::rcc::vals::Sw;
203 use crate::pac::{PWR, RCC};
204 use crate::rcc::{RCC_CONFIG, init as init_rcc};
205
206 let extscr = PWR.extscr().read();
211 if extscr.c1stop2f() || extscr.c1stopf() { 207 if extscr.c1stop2f() || extscr.c1stopf() {
212 // when we wake from any stop mode we need to re-initialize the rcc 208 // when we wake from any stop mode we need to re-initialize the rcc
213 crate::rcc::apply_resume_config(); 209 while RCC.cfgr().read().sws() != Sw::MSI {}
210
211 init_rcc(RCC_CONFIG.unwrap());
212
214 if extscr.c1stop2f() { 213 if extscr.c1stop2f() {
215 // when we wake from STOP2, we need to re-initialize the time driver 214 // when we wake from STOP2, we need to re-initialize the time driver
216 crate::time_driver::init_timer(cs); 215 get_driver().init_timer(cs);
217 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) 216 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
218 // and given that we just woke from STOP2, we can reset them 217 // and given that we just woke from STOP2, we can reset them
219 crate::rcc::REFCOUNT_STOP2 = 0; 218 REFCOUNT_STOP2 = 0;
220 crate::rcc::REFCOUNT_STOP1 = 0; 219 REFCOUNT_STOP1 = 0;
221 } 220 }
222 } 221 }
223 } 222 }
@@ -226,11 +225,15 @@ impl Executor {
226 }); 225 });
227 } 226 }
228 227
228 const fn get_scb() -> SCB {
229 unsafe { mem::transmute(()) }
230 }
231
229 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { 232 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
230 if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { 233 if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {
231 trace!("low power: stop 2"); 234 trace!("low power: stop 2");
232 Some(StopMode::Stop2) 235 Some(StopMode::Stop2)
233 } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { 236 } else if unsafe { REFCOUNT_STOP1 == 0 } {
234 trace!("low power: stop 1"); 237 trace!("low power: stop 1");
235 Some(StopMode::Stop1) 238 Some(StopMode::Stop1)
236 } else { 239 } else {
@@ -240,7 +243,7 @@ impl Executor {
240 } 243 }
241 244
242 #[allow(unused_variables)] 245 #[allow(unused_variables)]
243 fn configure_stop(&mut self, stop_mode: StopMode) { 246 fn configure_stop(&self, stop_mode: StopMode) {
244 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] 247 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))]
245 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 248 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
246 #[cfg(stm32h5)] 249 #[cfg(stm32h5)]
@@ -251,8 +254,8 @@ impl Executor {
251 }); 254 });
252 } 255 }
253 256
254 fn configure_pwr(&mut self) { 257 fn configure_pwr(&self) {
255 self.scb.clear_sleepdeep(); 258 Self::get_scb().clear_sleepdeep();
256 // Clear any previous stop flags 259 // Clear any previous stop flags
257 #[cfg(stm32wlex)] 260 #[cfg(stm32wlex)]
258 crate::pac::PWR.extscr().modify(|w| { 261 crate::pac::PWR.extscr().modify(|w| {
@@ -271,7 +274,7 @@ impl Executor {
271 self.configure_stop(stop_mode); 274 self.configure_stop(stop_mode);
272 275
273 #[cfg(not(feature = "low-power-debug-with-sleep"))] 276 #[cfg(not(feature = "low-power-debug-with-sleep"))]
274 self.scb.set_sleepdeep(); 277 Self::get_scb().set_sleepdeep();
275 }); 278 });
276 } 279 }
277 280
@@ -294,12 +297,11 @@ impl Executor {
294 /// 297 ///
295 /// This function never returns. 298 /// This function never returns.
296 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 299 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
297 let executor = unsafe { EXECUTOR.as_mut().unwrap() }; 300 init(self.inner.spawner());
298 init(executor.inner.spawner());
299 301
300 loop { 302 loop {
301 unsafe { 303 unsafe {
302 executor.inner.poll(); 304 self.inner.poll();
303 self.configure_pwr(); 305 self.configure_pwr();
304 asm!("wfe"); 306 asm!("wfe");
305 #[cfg(stm32wlex)] 307 #[cfg(stm32wlex)]
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs
index 584957c6d..2e1cbd702 100644
--- a/embassy-stm32/src/rcc/l.rs
+++ b/embassy-stm32/src/rcc/l.rs
@@ -1,6 +1,3 @@
1#[cfg(all(feature = "low-power", stm32wlex))]
2use core::mem::MaybeUninit;
3
4#[cfg(any(stm32l0, stm32l1))] 1#[cfg(any(stm32l0, stm32l1))]
5pub use crate::pac::pwr::vals::Vos as VoltageScale; 2pub use crate::pac::pwr::vals::Vos as VoltageScale;
6use crate::pac::rcc::regs::Cfgr; 3use crate::pac::rcc::regs::Cfgr;
@@ -14,42 +11,6 @@ use crate::time::Hertz;
14/// HSI speed 11/// HSI speed
15pub const HSI_FREQ: Hertz = Hertz(16_000_000); 12pub const HSI_FREQ: Hertz = Hertz(16_000_000);
16 13
17/// Saved RCC Config
18///
19/// Used when exiting STOP2 to re-enable clocks to their last configured state
20/// for chips that need it.
21#[cfg(all(feature = "low-power", stm32wlex))]
22static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit();
23
24/// Set the rcc config to be restored when exiting STOP2
25///
26/// Safety: Sets a mutable global.
27#[cfg(all(feature = "low-power", stm32wlex))]
28pub(crate) unsafe fn set_resume_config(config: Config) {
29 trace!("rcc set_resume_config()");
30 RESUME_RCC_CONFIG = MaybeUninit::new(config);
31}
32
33/// Get the rcc config to be restored when exiting STOP2
34///
35/// Safety: Reads a mutable global.
36#[cfg(all(feature = "low-power", stm32wlex))]
37pub(crate) unsafe fn get_resume_config() -> Config {
38 *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref()
39}
40
41#[cfg(all(feature = "low-power", stm32wlex))]
42/// Safety: should only be called from low power executable just after resuming from STOP2
43pub(crate) unsafe fn apply_resume_config() {
44 trace!("rcc apply_resume_config()");
45
46 while RCC.cfgr().read().sws() != Sysclk::MSI {}
47
48 let config = get_resume_config();
49
50 init(config);
51}
52
53#[derive(Clone, Copy, Eq, PartialEq)] 14#[derive(Clone, Copy, Eq, PartialEq)]
54pub enum HseMode { 15pub enum HseMode {
55 /// crystal/ceramic oscillator (HSEBYP=0) 16 /// crystal/ceramic oscillator (HSEBYP=0)
@@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) {
193} 154}
194 155
195pub(crate) unsafe fn init(config: Config) { 156pub(crate) unsafe fn init(config: Config) {
196 // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup
197 #[cfg(all(feature = "low-power", stm32wlex))]
198 set_resume_config(config);
199
200 // Switch to MSI to prevent problems with PLL configuration. 157 // Switch to MSI to prevent problems with PLL configuration.
201 if !RCC.cr().read().msion() { 158 if !RCC.cr().read().msion() {
202 // Turn on MSI and configure it to 4MHz. 159 // Turn on MSI and configure it to 4MHz.
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index ca7c28cbc..66ee06e17 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
49/// May be read without a critical section 49/// May be read without a critical section
50pub(crate) static mut REFCOUNT_STOP2: u32 = 0; 50pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
51 51
52#[cfg(feature = "low-power")]
53pub(crate) static mut RCC_CONFIG: Option<Config> = None;
54
52#[cfg(backup_sram)] 55#[cfg(backup_sram)]
53pub(crate) static mut BKSRAM_RETAINED: bool = false; 56pub(crate) static mut BKSRAM_RETAINED: bool = false;
54 57
@@ -408,6 +411,7 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
408 411
409 #[cfg(feature = "low-power")] 412 #[cfg(feature = "low-power")]
410 { 413 {
414 RCC_CONFIG = Some(config);
411 REFCOUNT_STOP2 = 0; 415 REFCOUNT_STOP2 = 0;
412 REFCOUNT_STOP1 = 0; 416 REFCOUNT_STOP1 = 0;
413 } 417 }
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 116b3c7ed..e88bd7ab2 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -379,13 +379,16 @@ trait SealedInstance {
379} 379}
380 380
381#[cfg(feature = "low-power")] 381#[cfg(feature = "low-power")]
382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { 382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) {
383 use crate::time_driver::get_driver;
384
383 #[cfg(feature = "_allow-disable-rtc")] 385 #[cfg(feature = "_allow-disable-rtc")]
384 if config._disable_rtc { 386 if config._disable_rtc {
385 return; 387 return;
386 } 388 }
387 389
388 crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); 390 get_driver().set_rtc(cs, Rtc::new_inner(config));
391 get_driver().set_min_stop_pause(cs, min_stop_pause);
389 392
390 trace!("low power: stop with rtc configured"); 393 trace!("low power: stop with rtc configured");
391} 394}
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 6d93b430a..0b75aef92 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -245,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
245impl RtcDriver { 245impl RtcDriver {
246 /// initialize the timer, but don't start it. Used for chips like stm32wle5 246 /// initialize the timer, but don't start it. Used for chips like stm32wle5
247 /// for low power where the timer config is lost in STOP2. 247 /// for low power where the timer config is lost in STOP2.
248 fn init_timer(&'static self, cs: critical_section::CriticalSection) { 248 pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
249 let r = regs_gp16(); 249 let r = regs_gp16();
250 250
251 rcc::enable_and_reset_with_cs::<T>(cs); 251 rcc::enable_and_reset_with_cs::<T>(cs);
@@ -516,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver {
516pub(crate) fn init(cs: CriticalSection) { 516pub(crate) fn init(cs: CriticalSection) {
517 DRIVER.init(cs) 517 DRIVER.init(cs)
518} 518}
519
520#[cfg(all(feature = "low-power", stm32wlex))]
521pub(crate) fn init_timer(cs: CriticalSection) {
522 DRIVER.init_timer(cs)
523}