mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-12-29 21:00:46 +00:00
low_power: misc cleanups and allow main macro
This commit is contained in:
parent
a51533c0b4
commit
29d4ade286
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased - ReleaseDate
|
||||
|
||||
- feat: allow embassy_executor::main for low power
|
||||
- feat: Add waveform methods to ComplementaryPwm
|
||||
- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890))
|
||||
- chore: cleanup low-power add time
|
||||
|
||||
@ -649,10 +649,7 @@ fn init_hw(config: Config) -> Peripherals {
|
||||
rcc::init_rcc(cs, config.rcc);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::rtc::init_rtc(cs, config.rtc);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause);
|
||||
rtc::init_rtc(cs, config.rtc, config.min_stop_pause);
|
||||
}
|
||||
|
||||
p
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! [`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. Moreover, you must relinquish control
|
||||
@ -22,21 +22,16 @@
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use embassy_executor::Spawner;
|
||||
//! use embassy_stm32::low_power::Executor;
|
||||
//! use embassy_stm32::low_power;
|
||||
//! use embassy_stm32::rtc::{Rtc, RtcConfig};
|
||||
//! use static_cell::StaticCell;
|
||||
//! use embassy_time::Duration;
|
||||
//!
|
||||
//! #[cortex_m_rt::entry]
|
||||
//! fn main() -> ! {
|
||||
//! Executor::take().run(|spawner| {
|
||||
//! spawner.spawn(unwrap!(async_main(spawner)));
|
||||
//! });
|
||||
//! }
|
||||
//!
|
||||
//! #[embassy_executor::task]
|
||||
//! #[embassy_executor::main(executor = "low_power::Executor")]
|
||||
//! async fn async_main(spawner: Spawner) {
|
||||
//! // initialize the platform...
|
||||
//! let mut config = embassy_stm32::Config::default();
|
||||
//! // the default value, but can be adjusted
|
||||
//! config.min_stop_pause = Duration::from_millis(250);
|
||||
//! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working
|
||||
//! config.enable_debug_during_sleep = false;
|
||||
//! let p = embassy_stm32::init(config);
|
||||
@ -45,11 +40,9 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
// TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.`
|
||||
#![allow(static_mut_refs)]
|
||||
|
||||
use core::arch::asm;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::sync::atomic::{Ordering, compiler_fence};
|
||||
|
||||
use cortex_m::peripheral::SCB;
|
||||
@ -57,11 +50,12 @@ use critical_section::CriticalSection;
|
||||
use embassy_executor::*;
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2};
|
||||
use crate::time_driver::get_driver;
|
||||
|
||||
const THREAD_PENDER: usize = usize::MAX;
|
||||
|
||||
static mut EXECUTOR: Option<Executor> = None;
|
||||
static mut EXECUTOR_TAKEN: bool = false;
|
||||
|
||||
/// Prevent the device from going into the stop mode if held
|
||||
pub struct DeviceBusy(StopMode);
|
||||
@ -182,42 +176,47 @@ impl Into<Lpms> for StopMode {
|
||||
pub struct Executor {
|
||||
inner: raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
scb: SCB,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
/// Create a new Executor.
|
||||
pub fn take() -> &'static mut Self {
|
||||
critical_section::with(|_| unsafe {
|
||||
assert!(EXECUTOR.is_none());
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
if EXECUTOR_TAKEN {
|
||||
panic!("Low power executor can only be taken once.");
|
||||
} else {
|
||||
EXECUTOR_TAKEN = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXECUTOR = Some(Self {
|
||||
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
|
||||
not_send: PhantomData,
|
||||
scb: cortex_m::Peripherals::steal().SCB,
|
||||
});
|
||||
|
||||
let executor = EXECUTOR.as_mut().unwrap();
|
||||
|
||||
executor
|
||||
})
|
||||
Self {
|
||||
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn on_wakeup_irq() {
|
||||
critical_section::with(|cs| {
|
||||
#[cfg(stm32wlex)]
|
||||
{
|
||||
let extscr = crate::pac::PWR.extscr().read();
|
||||
use crate::pac::rcc::vals::Sw;
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::{RCC_CONFIG, init as init_rcc};
|
||||
|
||||
let extscr = PWR.extscr().read();
|
||||
if extscr.c1stop2f() || extscr.c1stopf() {
|
||||
// when we wake from any stop mode we need to re-initialize the rcc
|
||||
crate::rcc::apply_resume_config();
|
||||
while RCC.cfgr().read().sws() != Sw::MSI {}
|
||||
|
||||
init_rcc(RCC_CONFIG.unwrap());
|
||||
|
||||
if extscr.c1stop2f() {
|
||||
// when we wake from STOP2, we need to re-initialize the time driver
|
||||
crate::time_driver::init_timer(cs);
|
||||
get_driver().init_timer(cs);
|
||||
// reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
|
||||
// and given that we just woke from STOP2, we can reset them
|
||||
crate::rcc::REFCOUNT_STOP2 = 0;
|
||||
crate::rcc::REFCOUNT_STOP1 = 0;
|
||||
REFCOUNT_STOP2 = 0;
|
||||
REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,11 +225,15 @@ impl Executor {
|
||||
});
|
||||
}
|
||||
|
||||
const fn get_scb() -> SCB {
|
||||
unsafe { mem::transmute(()) }
|
||||
}
|
||||
|
||||
fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
|
||||
if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||
if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {
|
||||
trace!("low power: stop 2");
|
||||
Some(StopMode::Stop2)
|
||||
} else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||
} else if unsafe { REFCOUNT_STOP1 == 0 } {
|
||||
trace!("low power: stop 1");
|
||||
Some(StopMode::Stop1)
|
||||
} else {
|
||||
@ -240,7 +243,7 @@ impl Executor {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn configure_stop(&mut self, stop_mode: StopMode) {
|
||||
fn configure_stop(&self, stop_mode: StopMode) {
|
||||
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))]
|
||||
crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
|
||||
#[cfg(stm32h5)]
|
||||
@ -251,8 +254,8 @@ impl Executor {
|
||||
});
|
||||
}
|
||||
|
||||
fn configure_pwr(&mut self) {
|
||||
self.scb.clear_sleepdeep();
|
||||
fn configure_pwr(&self) {
|
||||
Self::get_scb().clear_sleepdeep();
|
||||
// Clear any previous stop flags
|
||||
#[cfg(stm32wlex)]
|
||||
crate::pac::PWR.extscr().modify(|w| {
|
||||
@ -271,7 +274,7 @@ impl Executor {
|
||||
self.configure_stop(stop_mode);
|
||||
|
||||
#[cfg(not(feature = "low-power-debug-with-sleep"))]
|
||||
self.scb.set_sleepdeep();
|
||||
Self::get_scb().set_sleepdeep();
|
||||
});
|
||||
}
|
||||
|
||||
@ -294,12 +297,11 @@ impl Executor {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
let executor = unsafe { EXECUTOR.as_mut().unwrap() };
|
||||
init(executor.inner.spawner());
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
executor.inner.poll();
|
||||
self.inner.poll();
|
||||
self.configure_pwr();
|
||||
asm!("wfe");
|
||||
#[cfg(stm32wlex)]
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
#[cfg(any(stm32l0, stm32l1))]
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
use crate::pac::rcc::regs::Cfgr;
|
||||
@ -14,42 +11,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// Saved RCC Config
|
||||
///
|
||||
/// Used when exiting STOP2 to re-enable clocks to their last configured state
|
||||
/// for chips that need it.
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit();
|
||||
|
||||
/// Set the rcc config to be restored when exiting STOP2
|
||||
///
|
||||
/// Safety: Sets a mutable global.
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
pub(crate) unsafe fn set_resume_config(config: Config) {
|
||||
trace!("rcc set_resume_config()");
|
||||
RESUME_RCC_CONFIG = MaybeUninit::new(config);
|
||||
}
|
||||
|
||||
/// Get the rcc config to be restored when exiting STOP2
|
||||
///
|
||||
/// Safety: Reads a mutable global.
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
pub(crate) unsafe fn get_resume_config() -> Config {
|
||||
*(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
/// Safety: should only be called from low power executable just after resuming from STOP2
|
||||
pub(crate) unsafe fn apply_resume_config() {
|
||||
trace!("rcc apply_resume_config()");
|
||||
|
||||
while RCC.cfgr().read().sws() != Sysclk::MSI {}
|
||||
|
||||
let config = get_resume_config();
|
||||
|
||||
init(config);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// save the rcc config because if we enter stop 2 we need to re-apply it on wakeup
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
set_resume_config(config);
|
||||
|
||||
// Switch to MSI to prevent problems with PLL configuration.
|
||||
if !RCC.cr().read().msion() {
|
||||
// Turn on MSI and configure it to 4MHz.
|
||||
|
||||
@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
|
||||
/// May be read without a critical section
|
||||
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
pub(crate) static mut RCC_CONFIG: Option<Config> = None;
|
||||
|
||||
#[cfg(backup_sram)]
|
||||
pub(crate) static mut BKSRAM_RETAINED: bool = false;
|
||||
|
||||
@ -408,6 +411,7 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
{
|
||||
RCC_CONFIG = Some(config);
|
||||
REFCOUNT_STOP2 = 0;
|
||||
REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
|
||||
@ -379,13 +379,16 @@ trait SealedInstance {
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) {
|
||||
pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) {
|
||||
use crate::time_driver::get_driver;
|
||||
|
||||
#[cfg(feature = "_allow-disable-rtc")]
|
||||
if config._disable_rtc {
|
||||
return;
|
||||
}
|
||||
|
||||
crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config));
|
||||
get_driver().set_rtc(cs, Rtc::new_inner(config));
|
||||
get_driver().set_min_stop_pause(cs, min_stop_pause);
|
||||
|
||||
trace!("low power: stop with rtc configured");
|
||||
}
|
||||
|
||||
@ -245,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||
impl RtcDriver {
|
||||
/// initialize the timer, but don't start it. Used for chips like stm32wle5
|
||||
/// for low power where the timer config is lost in STOP2.
|
||||
fn init_timer(&'static self, cs: critical_section::CriticalSection) {
|
||||
pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
|
||||
let r = regs_gp16();
|
||||
|
||||
rcc::enable_and_reset_with_cs::<T>(cs);
|
||||
@ -516,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver {
|
||||
pub(crate) fn init(cs: CriticalSection) {
|
||||
DRIVER.init(cs)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "low-power", stm32wlex))]
|
||||
pub(crate) fn init_timer(cs: CriticalSection) {
|
||||
DRIVER.init_timer(cs)
|
||||
}
|
||||
|
||||
@ -7,20 +7,12 @@
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::rcc::{HSIPrescaler, LsConfig};
|
||||
use embassy_stm32::{Config, Peri};
|
||||
use embassy_stm32::{Config, Peri, low_power};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(spawner: Spawner) {
|
||||
defmt::info!("Program Start");
|
||||
|
||||
|
||||
@ -4,20 +4,12 @@
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::rcc::LsConfig;
|
||||
use embassy_stm32::{Config, Peri};
|
||||
use embassy_stm32::{Config, Peri, low_power};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.ls = LsConfig::default_lsi();
|
||||
|
||||
@ -6,20 +6,12 @@ use defmt::*;
|
||||
use defmt_rtt as _;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::{Adc, SampleTime};
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::low_power;
|
||||
use embassy_time::Timer;
|
||||
use panic_probe as _;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
info!("main: Starting!");
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
// enable HSI clock
|
||||
|
||||
@ -6,20 +6,12 @@ use defmt::*;
|
||||
use defmt_rtt as _;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::low_power;
|
||||
use embassy_time::Timer;
|
||||
use panic_probe as _;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
info!("main: Starting!");
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
// enable HSI clock
|
||||
|
||||
@ -7,19 +7,11 @@ use defmt_rtt as _;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::gpio::Pull;
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::low_power;
|
||||
use panic_probe as _;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
info!("main: Starting!");
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
// enable HSI clock
|
||||
|
||||
@ -6,9 +6,8 @@ use defmt::*;
|
||||
use defmt_rtt as _;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::i2c::I2c;
|
||||
use embassy_stm32::low_power::Executor;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
use embassy_stm32::{bind_interrupts, i2c, low_power, peripherals};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use panic_probe as _;
|
||||
use static_cell::StaticCell;
|
||||
@ -18,15 +17,7 @@ bind_interrupts!(struct IrqsI2C{
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
info!("main: Starting!");
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
// enable HSI clock
|
||||
|
||||
@ -7,21 +7,13 @@ mod common;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use common::*;
|
||||
use cortex_m_rt::entry;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::Config;
|
||||
use embassy_stm32::low_power::{Executor, StopMode, stop_ready};
|
||||
use embassy_stm32::low_power::{StopMode, stop_ready};
|
||||
use embassy_stm32::rcc::LsConfig;
|
||||
use embassy_stm32::rtc::Rtc;
|
||||
use embassy_stm32::{Config, low_power};
|
||||
use embassy_time::Timer;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
Executor::take().run(|spawner| {
|
||||
spawner.spawn(unwrap!(async_main(spawner)));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task_1() {
|
||||
for _ in 0..9 {
|
||||
@ -43,7 +35,7 @@ async fn task_2() {
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
#[embassy_executor::main(executor = "low_power::Executor")]
|
||||
async fn async_main(spawner: Spawner) {
|
||||
let _ = config();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user