From d9e59e8e42ea009e365bb893b91d81fe47fe927d Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Sat, 27 Apr 2024 21:37:58 +0800 Subject: [PATCH] low power for h5 --- embassy-stm32/Cargo.toml | 6 +-- embassy-stm32/src/low_power.rs | 41 +++++++++++------- embassy-stm32/src/rcc/f247.rs | 3 ++ embassy-stm32/src/rcc/h.rs | 2 + embassy-stm32/src/rcc/l.rs | 2 + embassy-stm32/src/rcc/u5.rs | 1 + embassy-stm32/src/rtc/datetime.rs | 4 +- embassy-stm32/src/rtc/mod.rs | 49 +++++++++------------ embassy-stm32/src/rtc/v3.rs | 36 +++++++--------- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h5/src/bin/stop.rs | 71 +++++++++++++++++++++++++++++++ tests/stm32/Cargo.toml | 4 +- tests/stm32/src/bin/stop.rs | 7 +++ 13 files changed, 155 insertions(+), 73 deletions(-) create mode 100644 examples/stm32h5/src/bin/stop.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 669e6537a..653a376bd 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -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"] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 4c3d288fd..604bdf416 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -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. diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 7b252870c..e3ee9a039 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -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 ); } diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 1949fc891..aa8c4b1f7 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -601,6 +601,8 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(stm32h5)] audioclk: None, i2s_ckin: None, + #[cfg(stm32h7)] + dsi_phy: None, // TODO ); } diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index d7235ac7f..c625948fb 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -420,6 +420,8 @@ pub(crate) unsafe fn init(config: Config) { sai2_extclk: None, lsi: None, lse: None, + #[cfg(stm32l4)] + dsi_phy: None, ); } diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 9533e16c4..4013d0a46 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -297,6 +297,7 @@ pub(crate) unsafe fn init(config: Config) { msik: None, shsi: None, shsi_div_2: None, + dsi_phy: None, ); } diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index bab8cf4a3..77d89293d 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -148,9 +148,9 @@ impl DateTime { ) -> Result { 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) diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 00abe9356..b12a0db66 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -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 for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -56,7 +56,7 @@ impl From 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 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

, 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)); - ::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 { diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 8a78d16e1..e51e09e7c 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -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 { #[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)) } } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 4527f4bcb..82760db64 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -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"] } diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs new file mode 100644 index 000000000..0d14c0668 --- /dev/null +++ b/examples/stm32h5/src/bin/stop.rs @@ -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 = 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 {} +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 004846a9b..30669b88c 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -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 = [] diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index 000296d46..c1106bb2f 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs @@ -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!");