Skip to content
3 changes: 0 additions & 3 deletions embassy-stm32/src/exti.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ unsafe fn on_irq() {
EXTI.rpr(0).write_value(Lines(bits));
EXTI.fpr(0).write_value(Lines(bits));
}

#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();
}

struct BitIter(u32);
Expand Down
56 changes: 14 additions & 42 deletions embassy-stm32/src/low_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ 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};

const THREAD_PENDER: usize = usize::MAX;
Expand All @@ -70,33 +69,6 @@ use crate::rtc::Rtc;

static mut EXECUTOR: Option<Executor> = None;

#[cfg(not(stm32u0))]
foreach_interrupt! {
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
#[interrupt]
#[allow(non_snake_case)]
unsafe fn $irq() {
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
}
};
}

#[cfg(stm32u0)]
foreach_interrupt! {
(RTC, rtc, $block:ident, TAMP, $irq:ident) => {
#[interrupt]
#[allow(non_snake_case)]
unsafe fn $irq() {
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
}
};
}

#[allow(dead_code)]
pub(crate) unsafe fn on_wakeup_irq() {
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
}

/// Configure STOP mode with RTC.
pub fn stop_with_rtc(rtc: &'static Rtc) {
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
Expand Down Expand Up @@ -173,11 +145,6 @@ impl Executor {
})
}

unsafe fn on_wakeup_irq(&mut self) {
self.time_driver.resume_time();
trace!("low power: resume");
}

pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
self.time_driver.set_rtc(rtc);

Expand Down Expand Up @@ -213,29 +180,33 @@ impl Executor {

compiler_fence(Ordering::SeqCst);

let stop_mode = self.stop_mode();

if stop_mode.is_none() {
trace!("low power: not ready to stop");
let Some(stop_mode) = self.stop_mode() else {
trace!("low power: not ready to stop (peripherals busy)");
return;
}
};

if self.time_driver.pause_time().is_err() {
trace!("low power: failed to pause time");
trace!("low power: not ready to stop (alarm in near future)");
return;
}

let stop_mode = stop_mode.unwrap();
match stop_mode {
StopMode::Stop1 => trace!("low power: stop 1"),
StopMode::Stop2 => trace!("low power: stop 2"),
StopMode::Stop1 => trace!("low power: time paused for stop 1"),
StopMode::Stop2 => trace!("low power: time paused for stop 2"),
}
self.configure_stop(stop_mode);

#[cfg(not(feature = "low-power-debug-with-sleep"))]
self.scb.set_sleepdeep();
}

unsafe fn on_wakeup(&mut self) {
match self.time_driver.resume_time() {
Ok(()) => trace!("low power: time resumed"),
Err(()) => trace!("low power: time already running"),
}
}

/// Run the executor.
///
/// The `init` closure is called with a [`Spawner`] that spawns tasks on
Expand Down Expand Up @@ -263,6 +234,7 @@ impl Executor {
executor.inner.poll();
self.configure_pwr();
asm!("wfe");
self.on_wakeup();
};
}
}
Expand Down
39 changes: 39 additions & 0 deletions embassy-stm32/src/rtc/low_power.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError};
use crate::interrupt;
use crate::peripherals::RTC;
use crate::rtc::SealedInstance;

#[cfg(not(stm32u0))]
foreach_interrupt! {
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
#[interrupt]
#[allow(non_snake_case)]
unsafe fn $irq() {
unpend_wakeup_alarm();
}
};
}

#[cfg(stm32u0)]
foreach_interrupt! {
(RTC, rtc, $block:ident, TAMP, $irq:ident) => {
#[interrupt]
#[allow(non_snake_case)]
unsafe fn $irq() {
unpend_wakeup_alarm();
}
};
}

fn unpend_wakeup_alarm() {
critical_section::with(|_| {
// Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar,
// there is a table for every "Event input" / "EXTI Line".
// If you find the EXTI line related to "RTC wakeup" marks as "Configurable" (not "Direct"),
// then write 1 to related field of Pending Register, to clean it's pending state.
#[cfg(any(exti_v1, stm32h7, stm32wb))]
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));

use crate::interrupt::typelevel::Interrupt;
<RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
});
}

/// Represents an instant in time that can be substracted to compute a duration
pub(super) struct RtcInstant {
/// 0..59
Expand Down
13 changes: 4 additions & 9 deletions embassy-stm32/src/time_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,6 @@ impl RtcDriver {
/// Pause the timer if ready; return err if not
pub(crate) fn pause_time(&self) -> Result<(), ()> {
critical_section::with(|cs| {
/*
If the wakeup timer is currently running, then we need to stop it and
add the elapsed time to the current time, as this will impact the result
of `time_until_next_alarm`.
*/
self.stop_wakeup_alarm(cs);

let time_until_next_alarm = self.time_until_next_alarm(cs);
if time_until_next_alarm < Self::MIN_STOP_PAUSE {
Err(())
Expand All @@ -431,17 +424,19 @@ impl RtcDriver {

#[cfg(feature = "low-power")]
/// Resume the timer with the given offset
pub(crate) fn resume_time(&self) {
pub(crate) fn resume_time(&self) -> Result<(), ()> {
if regs_gp16().cr1().read().cen() {
// Time isn't currently stopped

return;
return Err(());
}

critical_section::with(|cs| {
self.stop_wakeup_alarm(cs);

regs_gp16().cr1().modify(|w| w.set_cen(true));

Ok(())
})
}

Expand Down
6 changes: 0 additions & 6 deletions embassy-stm32/src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,6 @@ pub struct UpdateInterruptHandler<T: CoreInstance> {

impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {
unsafe fn on_interrupt() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();

let regs = crate::pac::timer::TimCore::from_ptr(T::regs());

// Read TIM interrupt flags.
Expand Down Expand Up @@ -367,9 +364,6 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare
for CaptureCompareInterruptHandler<T>
{
unsafe fn on_interrupt() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();

let regs = crate::pac::timer::TimGp16::from_ptr(T::regs());

// Read TIM interrupt flags.
Expand Down