low power for h5

This commit is contained in:
eZio Pan 2024-04-27 21:37:58 +08:00
parent 34074e6eb0
commit d9e59e8e42
13 changed files with 155 additions and 73 deletions

View file

@ -24,7 +24,7 @@ flavors = [
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] },
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
{ regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" },
@ -72,7 +72,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
critical-section = "1.1"
#stm32-metapac = { version = "15" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-823168933f3860770111f7bde2a82b912eac58c0" }
vcell = "0.1.3"
nb = "1.0.0"
@ -98,7 +98,7 @@ proc-macro2 = "1.0.36"
quote = "1.0.15"
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-823168933f3860770111f7bde2a82b912eac58c0", default-features = false, features = ["metadata"]}
[features]
default = ["rt"]

View file

@ -10,14 +10,14 @@
//! exceptions to this rule:
//!
//! * `GPIO`
//! * `RCC`
//! * `RTC`
//!
//! 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
//! consequently one must define their entrypoint manually. Moreover, you must relinquish control
//! of the `RTC` peripheral to the executor. This will typically look like
//!
//! ```rust,no_run
@ -99,7 +99,7 @@ pub fn stop_ready(stop_mode: StopMode) -> bool {
}
}
/// Available stop modes.
/// Available Stop modes.
#[non_exhaustive]
#[derive(PartialEq)]
pub enum StopMode {
@ -183,6 +183,12 @@ impl Executor {
fn configure_stop(&mut self, stop_mode: StopMode) {
#[cfg(stm32l5)]
crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
#[cfg(stm32h5)]
crate::pac::PWR.pmcr().modify(|v| {
use crate::pac::pwr::vals;
v.set_lpms(vals::Lpms::STOP);
v.set_svos(vals::Svos::SCALE3);
});
}
fn configure_pwr(&mut self) {
@ -191,21 +197,26 @@ impl Executor {
compiler_fence(Ordering::SeqCst);
let stop_mode = self.stop_mode();
if stop_mode.is_none() {
trace!("low power: not ready to stop");
} else if self.time_driver.pause_time().is_err() {
trace!("low power: failed to pause time");
} else {
let stop_mode = stop_mode.unwrap();
match stop_mode {
StopMode::Stop1 => trace!("low power: stop 1"),
StopMode::Stop2 => trace!("low power: stop 2"),
}
self.configure_stop(stop_mode);
#[cfg(not(feature = "low-power-debug-with-sleep"))]
self.scb.set_sleepdeep();
return;
}
if self.time_driver.pause_time().is_err() {
trace!("low power: failed to pause time");
return;
}
let stop_mode = stop_mode.unwrap();
match stop_mode {
StopMode::Stop1 => trace!("low power: stop 1"),
StopMode::Stop2 => trace!("low power: stop 2"),
}
self.configure_stop(stop_mode);
#[cfg(not(feature = "low-power-debug-with-sleep"))]
self.scb.set_sleepdeep();
}
/// Run the executor.

View file

@ -277,6 +277,7 @@ pub(crate) unsafe fn init(config: Config) {
pclk2_tim: Some(pclk2_tim),
rtc: rtc,
pll1_q: pll.q,
pll1_r: None, // TODO
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
plli2s1_p: plli2s.p,
@ -299,6 +300,8 @@ pub(crate) unsafe fn init(config: Config) {
hsi_div488: hsi.map(|hsi| hsi/488u32),
hsi_hse: None,
afif: None,
#[cfg(any(stm32f4, stm32f7))]
dsi_phy: None, // TODO
);
}

View file

@ -601,6 +601,8 @@ pub(crate) unsafe fn init(config: Config) {
#[cfg(stm32h5)]
audioclk: None,
i2s_ckin: None,
#[cfg(stm32h7)]
dsi_phy: None, // TODO
);
}

View file

@ -420,6 +420,8 @@ pub(crate) unsafe fn init(config: Config) {
sai2_extclk: None,
lsi: None,
lse: None,
#[cfg(stm32l4)]
dsi_phy: None,
);
}

View file

@ -297,6 +297,7 @@ pub(crate) unsafe fn init(config: Config) {
msik: None,
shsi: None,
shsi_div_2: None,
dsi_phy: None,
);
}

View file

@ -148,9 +148,9 @@ impl DateTime {
) -> Result<Self, Error> {
if year > 4095 {
Err(Error::InvalidYear)
} else if month < 1 || month > 12 {
} else if !(1..=12).contains(&month) {
Err(Error::InvalidMonth)
} else if day < 1 || day > 31 {
} else if !(1..=31).contains(&day) {
Err(Error::InvalidDay)
} else if hour > 23 {
Err(Error::InvalidHour)

View file

@ -42,7 +42,7 @@ pub(crate) enum WakeupPrescaler {
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5, stm32g0))]
#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
@ -56,7 +56,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5, stm32g0))]
#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
@ -81,8 +81,7 @@ impl WakeupPrescaler {
WakeupPrescaler::Div16,
]
.iter()
.skip_while(|psc| **psc as u32 <= val)
.next()
.find(|psc| **psc as u32 > val)
.unwrap_or(&WakeupPrescaler::Div16)
}
}
@ -159,7 +158,7 @@ impl RtcTimeProvider {
}
}
return Err(RtcError::ReadFailure);
Err(RtcError::ReadFailure)
}
}
@ -190,7 +189,7 @@ impl Default for RtcConfig {
}
/// Calibration cycle period.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Default, Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum RtcCalibrationCyclePeriod {
/// 8-second calibration period
@ -198,15 +197,10 @@ pub enum RtcCalibrationCyclePeriod {
/// 16-second calibration period
Seconds16,
/// 32-second calibration period
#[default]
Seconds32,
}
impl Default for RtcCalibrationCyclePeriod {
fn default() -> Self {
RtcCalibrationCyclePeriod::Seconds32
}
}
impl Rtc {
/// Create a new RTC instance.
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
@ -254,13 +248,13 @@ impl Rtc {
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
self.write(true, |rtc| {
let (ht, hu) = byte_to_bcd2(t.hour() as u8);
let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
let (st, su) = byte_to_bcd2(t.second() as u8);
let (ht, hu) = byte_to_bcd2(t.hour());
let (mnt, mnu) = byte_to_bcd2(t.minute());
let (st, su) = byte_to_bcd2(t.second());
let (dt, du) = byte_to_bcd2(t.day() as u8);
let (mt, mu) = byte_to_bcd2(t.month() as u8);
let yr = t.year() as u16;
let (dt, du) = byte_to_bcd2(t.day());
let (mt, mu) = byte_to_bcd2(t.month());
let yr = t.year();
let yr_offset = (yr - 2000_u16) as u8;
let (yt, yu) = byte_to_bcd2(yr_offset);
@ -338,7 +332,7 @@ impl Rtc {
}
#[cfg(feature = "low-power")]
/// start the wakeup alarm and wtih a duration that is as close to but less than
/// start the wakeup alarm and with a duration that is as close to but less than
/// the requested duration, and record the instant the wakeup alarm was started
pub(crate) fn start_wakeup_alarm(
&self,
@ -422,20 +416,15 @@ impl Rtc {
#[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
#[cfg(all(stm32g0))]
crate::pac::EXTI
.rpr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
#[cfg(all(not(stm32g0), not(stm32l5)))]
// 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));
#[cfg(stm32l5)]
crate::pac::EXTI
.fpr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
});
}
@ -465,7 +454,7 @@ pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
value -= 10;
}
(bcd_high, ((bcd_high << 4) | value) as u8)
(bcd_high, ((bcd_high << 4) | value))
}
pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {

View file

@ -50,7 +50,7 @@ impl super::Rtc {
clock_drift = Self::RTC_CALR_MAX_PPM;
}
clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
clock_drift /= Self::RTC_CALR_RESOLUTION_PPM;
self.write(false, |rtc| {
rtc.calr().write(|w| {
@ -129,29 +129,25 @@ impl super::Rtc {
impl SealedInstance for crate::peripherals::RTC {
const BACKUP_REGISTER_COUNT: usize = 32;
#[cfg(all(feature = "low-power", stm32g4))]
const EXTI_WAKEUP_LINE: usize = 20;
#[cfg(all(feature = "low-power", stm32g0))]
const EXTI_WAKEUP_LINE: usize = 19;
#[cfg(all(feature = "low-power", stm32g0))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;
#[cfg(all(feature = "low-power", stm32g4))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
#[cfg(all(feature = "low-power", stm32l5))]
const EXTI_WAKEUP_LINE: usize = 17;
#[cfg(all(feature = "low-power", stm32l5))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC;
#[cfg(feature = "low-power")]
cfg_if::cfg_if!(
if #[cfg(stm32g4)] {
const EXTI_WAKEUP_LINE: usize = 20;
type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
} else if #[cfg(stm32g0)] {
const EXTI_WAKEUP_LINE: usize = 19;
type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;
} else if #[cfg(any(stm32l5, stm32h5))] {
const EXTI_WAKEUP_LINE: usize = 17;
type WakeupInterrupt = crate::interrupt::typelevel::RTC;
}
);
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
#[allow(clippy::if_same_then_else)]
if register < Self::BACKUP_REGISTER_COUNT {
//Some(rtc.bkpr()[register].read().bits())
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
None // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC
} else {
None
}
@ -159,7 +155,7 @@ impl SealedInstance for crate::peripherals::RTC {
fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
if register < Self::BACKUP_REGISTER_COUNT {
// RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
// RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC
//self.rtc.bkpr()[register].write(|w| w.bits(value))
}
}

View file

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
# Change stm32h563zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] }
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }

View file

@ -0,0 +1,71 @@
// Notice:
// the MCU might need an extra reset to make the code actually running
#![no_std]
#![no_main]
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::rtc::{Rtc, RtcConfig};
use embassy_stm32::Config;
use embassy_time::Timer;
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
#[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) {
defmt::info!("Program Start");
let mut config = Config::default();
// System Clock seems need to be equal or lower than 16 MHz
config.rcc.hsi = Some(HSIPrescaler::DIV4);
config.rcc.ls = LsConfig::default_lsi();
// when enabled the power-consumption is much higher during stop, but debugging and RTT is working
// if you wan't to measure the power-consumption, or for production: uncomment this line
// config.enable_debug_during_sleep = false;
let p = embassy_stm32::init(config);
// give the RTC to the executor...
let rtc = Rtc::new(p.RTC, RtcConfig::default());
static RTC: StaticCell<Rtc> = StaticCell::new();
let rtc = RTC.init(rtc);
embassy_stm32::low_power::stop_with_rtc(rtc);
unwrap!(spawner.spawn(blinky(p.PB4.into())));
unwrap!(spawner.spawn(timeout()));
}
#[embassy_executor::task]
async fn blinky(led: AnyPin) {
let mut led = Output::new(led, Level::Low, Speed::Low);
loop {
info!("high");
led.set_high();
Timer::after_millis(300).await;
info!("low");
led.set_low();
Timer::after_millis(300).await;
}
}
// when enable_debug_during_sleep is false, it is more difficult to reprogram the MCU
// therefore we block the MCU after 30s to be able to reprogram it easily
#[embassy_executor::task]
async fn timeout() -> ! {
Timer::after_secs(30).await;
#[allow(clippy::empty_loop)]
loop {}
}

View file

@ -15,7 +15,7 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma"
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"]
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan", "cordic"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "fdcan", "hash", "cordic"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "fdcan", "hash", "cordic", "stop"]
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"]
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"]
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
@ -31,7 +31,7 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac"
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
stm32h503rb = ["embassy-stm32/stm32h503rb", "rng"]
stm32h503rb = ["embassy-stm32/stm32h503rb", "rng", "stop"]
cryp = []
hash = []

View file

@ -51,6 +51,13 @@ async fn async_main(spawner: Spawner) {
let mut config = Config::default();
config.rcc.ls = LsConfig::default_lse();
// System Clock seems cannot be greater than 16 MHz
#[cfg(any(feature = "stm32h563zi", feature = "stm32h503rb"))]
{
use embassy_stm32::rcc::HSIPrescaler;
config.rcc.hsi = Some(HSIPrescaler::DIV4); // 64 MHz HSI will need a /4
}
let p = embassy_stm32::init(config);
info!("Hello World!");