aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/low_power.rs47
-rw-r--r--embassy-stm32/src/time_driver.rs6
2 files changed, 52 insertions, 1 deletions
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index d5846f530..385795bce 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -1,3 +1,50 @@
1/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
2/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
3/// can use knowledge of which peripherals are currently blocked upon to transparently and safely
4/// enter such low-power modes (currently, only `STOP2`) when idle.
5///
6/// The executor determines which peripherals are active by their RCC state; consequently,
7/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few
8/// exceptions to this rule:
9///
10/// * `GPIO`
11/// * `RCC`
12///
13/// Since entering and leaving low-power modes typically incurs a significant latency, the
14/// low-power executor will only attempt to enter when the next timer event is at least
15/// [`time_driver::MIN_STOP_PAUSE`] in the future.
16///
17/// Currently there is no macro analogous to `embassy_executor::main` for this executor;
18/// consequently one must define their entrypoint manually. Moveover, you must relinquish control
19/// of the `RTC` peripheral to the executor. This will typically look like
20///
21/// ```rust,no_run
22/// use embassy_executor::Spawner;
23/// use embassy_stm32::low_power::Executor;
24/// use embassy_stm32::rtc::{Rtc, RtcConfig};
25/// use static_cell::make_static;
26///
27/// #[cortex_m_rt::entry]
28/// fn main() -> ! {
29/// Executor::take().run(|spawner| {
30/// unwrap!(spawner.spawn(async_main(spawner)));
31/// });
32/// }
33///
34/// #[embassy_executor::task]
35/// async fn async_main(spawner: Spawner) {
36/// // initialize the platform...
37/// let mut config = embassy_stm32::Config::default();
38/// let p = embassy_stm32::init(config);
39///
40/// // give the RTC to the executor...
41/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
42/// let rtc = make_static!(rtc);
43/// embassy_stm32::low_power::stop_with_rtc(rtc);
44///
45/// // your application here...
46/// }
47/// ```
1use core::arch::asm; 48use core::arch::asm;
2use core::marker::PhantomData; 49use core::marker::PhantomData;
3use core::sync::atomic::{compiler_fence, Ordering}; 50use core::sync::atomic::{compiler_fence, Ordering};
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index add8be831..564c9d086 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -346,6 +346,10 @@ impl RtcDriver {
346 } 346 }
347 347
348 #[cfg(feature = "low-power")] 348 #[cfg(feature = "low-power")]
349 /// The minimum pause time beyond which the executor will enter a low-power state.
350 pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250);
351
352 #[cfg(feature = "low-power")]
349 /// Pause the timer if ready; return err if not 353 /// Pause the timer if ready; return err if not
350 pub(crate) fn pause_time(&self) -> Result<(), ()> { 354 pub(crate) fn pause_time(&self) -> Result<(), ()> {
351 critical_section::with(|cs| { 355 critical_section::with(|cs| {
@@ -357,7 +361,7 @@ impl RtcDriver {
357 self.stop_wakeup_alarm(cs); 361 self.stop_wakeup_alarm(cs);
358 362
359 let time_until_next_alarm = self.time_until_next_alarm(cs); 363 let time_until_next_alarm = self.time_until_next_alarm(cs);
360 if time_until_next_alarm < embassy_time::Duration::from_millis(250) { 364 if time_until_next_alarm < Self::MIN_STOP_PAUSE {
361 Err(()) 365 Err(())
362 } else { 366 } else {
363 self.rtc 367 self.rtc