{feature}
"#)]
@@ -227,8 +228,9 @@ pub fn init(config: Config) -> Peripherals {
#[cfg(feature = "low-power")]
{
- crate::rcc::REFCOUNT_STOP2 = 0
- };
+ crate::rcc::REFCOUNT_STOP2 = 0;
+ crate::rcc::REFCOUNT_STOP1 = 0;
+ }
}
p
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index d5846f53..20d8f904 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -1,3 +1,50 @@
+/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
+/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
+/// can use knowledge of which peripherals are currently blocked upon to transparently and safely
+/// enter such low-power modes (currently, only `STOP2`) when idle.
+///
+/// The executor determines which peripherals are active by their RCC state; consequently,
+/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few
+/// exceptions to this rule:
+///
+/// * `GPIO`
+/// * `RCC`
+///
+/// Since entering and leaving low-power modes typically incurs a significant latency, the
+/// low-power executor will only attempt to enter when the next timer event is at least
+/// [`time_driver::MIN_STOP_PAUSE`] in the future.
+///
+/// Currently there is no macro analogous to `embassy_executor::main` for this executor;
+/// consequently one must define their entrypoint manually. Moveover, you must relinquish control
+/// of the `RTC` peripheral to the executor. This will typically look like
+///
+/// ```rust,no_run
+/// use embassy_executor::Spawner;
+/// use embassy_stm32::low_power::Executor;
+/// use embassy_stm32::rtc::{Rtc, RtcConfig};
+/// use static_cell::make_static;
+///
+/// #[cortex_m_rt::entry]
+/// fn main() -> ! {
+/// Executor::take().run(|spawner| {
+/// unwrap!(spawner.spawn(async_main(spawner)));
+/// });
+/// }
+///
+/// #[embassy_executor::task]
+/// async fn async_main(spawner: Spawner) {
+/// // initialize the platform...
+/// let mut config = embassy_stm32::Config::default();
+/// let p = embassy_stm32::init(config);
+///
+/// // give the RTC to the executor...
+/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
+/// let rtc = make_static!(rtc);
+/// embassy_stm32::low_power::stop_with_rtc(rtc);
+///
+/// // your application here...
+/// }
+/// ```
use core::arch::asm;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
@@ -33,11 +80,17 @@ pub fn stop_with_rtc(rtc: &'static Rtc) {
}
pub fn stop_ready(stop_mode: StopMode) -> bool {
- unsafe { EXECUTOR.as_mut().unwrap() }.stop_ready(stop_mode)
+ match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() {
+ Some(StopMode::Stop2) => true,
+ Some(StopMode::Stop1) => stop_mode == StopMode::Stop1,
+ None => false,
+ }
}
#[non_exhaustive]
+#[derive(PartialEq)]
pub enum StopMode {
+ Stop1,
Stop2,
}
@@ -61,7 +114,7 @@ pub struct Executor {
impl Executor {
/// Create a new Executor.
pub fn take() -> &'static mut Self {
- unsafe {
+ critical_section::with(|_| unsafe {
assert!(EXECUTOR.is_none());
EXECUTOR = Some(Self {
@@ -72,7 +125,7 @@ impl Executor {
});
EXECUTOR.as_mut().unwrap()
- }
+ })
}
unsafe fn on_wakeup_irq(&mut self) {
@@ -88,23 +141,39 @@ impl Executor {
trace!("low power: stop with rtc configured");
}
- fn stop_ready(&self, stop_mode: StopMode) -> bool {
- match stop_mode {
- StopMode::Stop2 => unsafe { crate::rcc::REFCOUNT_STOP2 == 0 },
+ fn stop_mode(&self) -> Option, rtc_config: RtcConfig) -> Self { #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] - critical_section::with(|cs| { -