From 4b4c28d8752aab0a96fc244f9eaabe256e7dbeb2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 8 Nov 2023 19:04:20 -0600 Subject: [PATCH] stm32: add low power for g4 --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/rtc/mod.rs | 162 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/rtc/v2.rs | 138 ----------------------------- embassy-stm32/src/rtc/v3.rs | 8 +- tests/stm32/Cargo.toml | 2 +- 5 files changed, 171 insertions(+), 141 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 01a9d482f..7b3a6c2bb 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -18,7 +18,7 @@ flavors = [ { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, - { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] }, diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index e94a58575..3f03d83a4 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -33,6 +33,60 @@ use embassy_hal_internal::Peripheral; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub(crate) enum WakeupPrescaler { + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, +} + +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +impl From for crate::pac::rtc::vals::Wucksel { + fn from(val: WakeupPrescaler) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + WakeupPrescaler::Div2 => Wucksel::DIV2, + WakeupPrescaler::Div4 => Wucksel::DIV4, + WakeupPrescaler::Div8 => Wucksel::DIV8, + WakeupPrescaler::Div16 => Wucksel::DIV16, + } + } +} + +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +impl From for WakeupPrescaler { + fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + Wucksel::DIV2 => WakeupPrescaler::Div2, + Wucksel::DIV4 => WakeupPrescaler::Div4, + Wucksel::DIV8 => WakeupPrescaler::Div8, + Wucksel::DIV16 => WakeupPrescaler::Div16, + _ => unreachable!(), + } + } +} + +#[cfg(feature = "low-power")] +impl WakeupPrescaler { + pub fn compute_min(val: u32) -> Self { + *[ + WakeupPrescaler::Div2, + WakeupPrescaler::Div4, + WakeupPrescaler::Div8, + WakeupPrescaler::Div16, + ] + .iter() + .skip_while(|psc| **psc as u32 <= val) + .next() + .unwrap_or(&WakeupPrescaler::Div16) + } +} + /// Errors that can occur on methods on [RtcClock] #[non_exhaustive] #[derive(Clone, Debug, PartialEq, Eq)] @@ -277,6 +331,114 @@ impl Rtc { pub fn write_backup_register(&self, register: usize, value: u32) { RTC::write_backup_register(&RTC::regs(), register, value) } + + #[cfg(feature = "low-power")] + /// start the wakeup alarm and wtih 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, + requested_duration: embassy_time::Duration, + cs: critical_section::CriticalSection, + ) { + use embassy_time::{Duration, TICK_HZ}; + + #[cfg(any(rtc_v3, rtc_v3u5))] + use crate::pac::rtc::vals::Calrf; + + // Panic if the rcc mod knows we're not using low-power rtc + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); + + let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); + let rtc_hz = Self::frequency().0 as u64; + let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; + let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); + + // adjust the rtc ticks to the prescaler and subtract one rtc tick + let rtc_ticks = rtc_ticks / prescaler as u64; + let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + { + regs.isr().modify(|w| w.set_wutf(false)); + while !regs.isr().read().wutwf() {} + } + + #[cfg(any(rtc_v3, rtc_v3u5))] + { + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + while !regs.icsr().read().wutwf() {} + } + + regs.cr().modify(|w| w.set_wucksel(prescaler.into())); + regs.wutr().write(|w| w.set_wut(rtc_ticks)); + regs.cr().modify(|w| w.set_wute(true)); + regs.cr().modify(|w| w.set_wutie(true)); + }); + + let instant = self.instant().unwrap(); + trace!( + "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", + Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), + prescaler as u32, + rtc_ticks, + instant, + ); + + assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) + } + + #[cfg(feature = "low-power")] + /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` + /// was called, otherwise none + pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { + use crate::interrupt::typelevel::Interrupt; + #[cfg(any(rtc_v3, rtc_v3u5))] + use crate::pac::rtc::vals::Calrf; + + let instant = self.instant().unwrap(); + if RTC::regs().cr().read().wute() { + trace!("rtc: stop wakeup alarm at {}", instant); + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + regs.isr().modify(|w| w.set_wutf(false)); + + #[cfg(any(rtc_v3, rtc_v3u5))] + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + + crate::pac::EXTI + .pr(0) + .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + + ::WakeupInterrupt::unpend(); + }); + } + + self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) + } + + #[cfg(feature = "low-power")] + pub(crate) fn enable_wakeup_line(&self) { + use crate::interrupt::typelevel::Interrupt; + use crate::pac::EXTI; + + ::WakeupInterrupt::unpend(); + unsafe { ::WakeupInterrupt::enable() }; + + EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + } } pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 006fd63f4..91f08fae4 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -6,145 +6,7 @@ use crate::peripherals::RTC; use crate::rtc::sealed::Instance; #[allow(dead_code)] -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub(crate) enum WakeupPrescaler { - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, -} - -#[cfg(any(stm32wb, stm32f4, stm32l0))] -impl From for crate::pac::rtc::vals::Wucksel { - fn from(val: WakeupPrescaler) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - WakeupPrescaler::Div2 => Wucksel::DIV2, - WakeupPrescaler::Div4 => Wucksel::DIV4, - WakeupPrescaler::Div8 => Wucksel::DIV8, - WakeupPrescaler::Div16 => Wucksel::DIV16, - } - } -} - -#[cfg(any(stm32wb, stm32f4, stm32l0))] -impl From for WakeupPrescaler { - fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - Wucksel::DIV2 => WakeupPrescaler::Div2, - Wucksel::DIV4 => WakeupPrescaler::Div4, - Wucksel::DIV8 => WakeupPrescaler::Div8, - Wucksel::DIV16 => WakeupPrescaler::Div16, - _ => unreachable!(), - } - } -} - -#[allow(dead_code)] -impl WakeupPrescaler { - pub fn compute_min(val: u32) -> Self { - *[ - WakeupPrescaler::Div2, - WakeupPrescaler::Div4, - WakeupPrescaler::Div8, - WakeupPrescaler::Div16, - ] - .iter() - .skip_while(|psc| **psc as u32 <= val) - .next() - .unwrap_or(&WakeupPrescaler::Div16) - } -} - impl super::Rtc { - #[cfg(feature = "low-power")] - /// start the wakeup alarm and wtih 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, - requested_duration: embassy_time::Duration, - cs: critical_section::CriticalSection, - ) { - use embassy_time::{Duration, TICK_HZ}; - - // Panic if the rcc mod knows we're not using low-power rtc - #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] - unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); - - let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); - let rtc_hz = Self::frequency().0 as u64; - let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; - let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); - - // adjust the rtc ticks to the prescaler and subtract one rtc tick - let rtc_ticks = rtc_ticks / prescaler as u64; - let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wute(false)); - regs.isr().modify(|w| w.set_wutf(false)); - while !regs.isr().read().wutwf() {} - - regs.cr().modify(|w| w.set_wucksel(prescaler.into())); - regs.wutr().write(|w| w.set_wut(rtc_ticks)); - regs.cr().modify(|w| w.set_wute(true)); - regs.cr().modify(|w| w.set_wutie(true)); - }); - - let instant = self.instant().unwrap(); - trace!( - "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", - Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), - prescaler as u32, - rtc_ticks, - instant, - ); - - assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) - } - - #[cfg(feature = "low-power")] - /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` - /// was called, otherwise none - pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { - use crate::interrupt::typelevel::Interrupt; - - let instant = self.instant().unwrap(); - if RTC::regs().cr().read().wute() { - trace!("rtc: stop wakeup alarm at {}", instant); - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(false)); - regs.cr().modify(|w| w.set_wute(false)); - regs.isr().modify(|w| w.set_wutf(false)); - - crate::pac::EXTI - .pr(0) - .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - - ::WakeupInterrupt::unpend(); - }); - } - - self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) - } - - #[cfg(feature = "low-power")] - pub(crate) fn enable_wakeup_line(&self) { - use crate::interrupt::typelevel::Interrupt; - use crate::pac::EXTI; - - ::WakeupInterrupt::unpend(); - unsafe { ::WakeupInterrupt::enable() }; - - EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - } - /// Applies the RTC config /// It this changes the RTC clock source the time will be reset pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7bf757e7d..d2d0d9309 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -95,7 +95,7 @@ impl super::Rtc { }) } - pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + pub(super) fn write(&self, init_mode: bool, f: F) -> R where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { @@ -129,6 +129,12 @@ impl super::Rtc { impl sealed::Instance 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", stm32g4))] + type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; + fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 4b769b1cb..9b9273a5c 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -10,7 +10,7 @@ stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] -stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "not-gpdma", "rng"] +stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]