Merge pull request #1766 from xoviat/rtc-w
stm32/rtc: add start/stop wakeup
This commit is contained in:
commit
7bff2ebab3
4 changed files with 227 additions and 1 deletions
|
@ -473,6 +473,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
Rtc::set_clock_source(clock_source);
|
Rtc::set_clock_source(clock_source);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let rtc = match config.rtc {
|
||||||
|
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: Hertz(sysclk),
|
sys: Hertz(sysclk),
|
||||||
apb1: Hertz(pclk1),
|
apb1: Hertz(pclk1),
|
||||||
|
@ -492,6 +497,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
|
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
pllsai: None,
|
pllsai: None,
|
||||||
|
|
||||||
|
rtc: rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub struct Clocks {
|
||||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
||||||
pub adc: Option<Hertz>,
|
pub adc: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(rcc_wb)]
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
/// Set only if the lsi or lse is configured
|
/// Set only if the lsi or lse is configured
|
||||||
pub rtc: Option<Hertz>,
|
pub rtc: Option<Hertz>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
|
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
use embassy_hal_internal::Peripheral;
|
use embassy_hal_internal::Peripheral;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,138 @@ use crate::pac::rtc::Rtc;
|
||||||
use crate::peripherals::RTC;
|
use crate::peripherals::RTC;
|
||||||
use crate::rtc::sealed::Instance;
|
use crate::rtc::sealed::Instance;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
|
||||||
|
pub struct RtcInstant {
|
||||||
|
ssr: u16,
|
||||||
|
st: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
|
||||||
|
impl RtcInstant {
|
||||||
|
pub fn now() -> Self {
|
||||||
|
// TODO: read value twice
|
||||||
|
use crate::rtc::bcd2_to_byte;
|
||||||
|
|
||||||
|
let tr = RTC::regs().tr().read();
|
||||||
|
let tr2 = RTC::regs().tr().read();
|
||||||
|
let ssr = RTC::regs().ssr().read().ss();
|
||||||
|
let ssr2 = RTC::regs().ssr().read().ss();
|
||||||
|
|
||||||
|
let st = bcd2_to_byte((tr.st(), tr.su()));
|
||||||
|
let st2 = bcd2_to_byte((tr2.st(), tr2.su()));
|
||||||
|
|
||||||
|
assert!(st == st2);
|
||||||
|
assert!(ssr == ssr2);
|
||||||
|
|
||||||
|
let _ = RTC::regs().dr().read();
|
||||||
|
|
||||||
|
trace!("ssr: {}", ssr);
|
||||||
|
trace!("st: {}", st);
|
||||||
|
|
||||||
|
Self { ssr, st }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
|
||||||
|
impl core::ops::Sub for RtcInstant {
|
||||||
|
type Output = embassy_time::Duration;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
use embassy_time::{Duration, TICK_HZ};
|
||||||
|
|
||||||
|
trace!("self st: {}", self.st);
|
||||||
|
trace!("other st: {}", rhs.st);
|
||||||
|
|
||||||
|
trace!("self ssr: {}", self.ssr);
|
||||||
|
trace!("other ssr: {}", rhs.ssr);
|
||||||
|
|
||||||
|
let st = if self.st < rhs.st { self.st + 60 } else { self.st };
|
||||||
|
|
||||||
|
trace!("self st: {}", st);
|
||||||
|
|
||||||
|
let self_ticks = st as u32 * 256 + (255 - self.ssr as u32);
|
||||||
|
let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32);
|
||||||
|
let rtc_ticks = self_ticks - other_ticks;
|
||||||
|
|
||||||
|
trace!("self ticks: {}", self_ticks);
|
||||||
|
trace!("other ticks: {}", other_ticks);
|
||||||
|
trace!("rtc ticks: {}", rtc_ticks);
|
||||||
|
|
||||||
|
// TODO: read prescaler
|
||||||
|
|
||||||
|
Duration::from_ticks(
|
||||||
|
((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32)))
|
||||||
|
* TICK_HZ as u32) as u32
|
||||||
|
/ 256u32) as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) enum WakeupPrescaler {
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(stm32wb, stm32f4))]
|
||||||
|
impl From<WakeupPrescaler> 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))]
|
||||||
|
impl From<crate::pac::rtc::vals::Wucksel> 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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WakeupPrescaler> for u32 {
|
||||||
|
fn from(val: WakeupPrescaler) -> Self {
|
||||||
|
match val {
|
||||||
|
WakeupPrescaler::Div2 => 2,
|
||||||
|
WakeupPrescaler::Div4 => 4,
|
||||||
|
WakeupPrescaler::Div8 => 8,
|
||||||
|
WakeupPrescaler::Div16 => 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl WakeupPrescaler {
|
||||||
|
pub fn compute_min(val: u32) -> Self {
|
||||||
|
*[
|
||||||
|
WakeupPrescaler::Div2,
|
||||||
|
WakeupPrescaler::Div4,
|
||||||
|
WakeupPrescaler::Div8,
|
||||||
|
WakeupPrescaler::Div16,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val)
|
||||||
|
.next()
|
||||||
|
.unwrap_or(&WakeupPrescaler::Div16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl super::Rtc {
|
impl super::Rtc {
|
||||||
fn unlock_registers() {
|
fn unlock_registers() {
|
||||||
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
|
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
|
||||||
|
@ -22,6 +154,92 @@ impl super::Rtc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
|
||||||
|
/// start the wakeup alarm and return the actual duration of the alarm
|
||||||
|
/// the actual duration will be the closest value possible that is less
|
||||||
|
/// than the requested duration.
|
||||||
|
///
|
||||||
|
/// note: this api is exposed for testing purposes until low power is implemented.
|
||||||
|
/// it is not intended to be public
|
||||||
|
pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant {
|
||||||
|
use embassy_time::{Duration, TICK_HZ};
|
||||||
|
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::rcc::get_freqs;
|
||||||
|
|
||||||
|
let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
|
||||||
|
|
||||||
|
let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ;
|
||||||
|
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
|
||||||
|
|
||||||
|
// adjust the rtc ticks to the prescaler
|
||||||
|
let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64);
|
||||||
|
let rtc_ticks = if rtc_ticks >= u16::MAX as u64 {
|
||||||
|
u16::MAX - 1
|
||||||
|
} else {
|
||||||
|
rtc_ticks as u16
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = Duration::from_ticks(
|
||||||
|
rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
|
||||||
|
);
|
||||||
|
|
||||||
|
trace!("set wakeup timer for {} ms", duration.as_millis());
|
||||||
|
|
||||||
|
RTC::regs().wpr().write(|w| w.set_key(0xca));
|
||||||
|
RTC::regs().wpr().write(|w| w.set_key(0x53));
|
||||||
|
|
||||||
|
RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks));
|
||||||
|
|
||||||
|
RTC::regs().cr().modify(|w| {
|
||||||
|
w.set_wucksel(prescaler.into());
|
||||||
|
|
||||||
|
w.set_wutie(true);
|
||||||
|
w.set_wute(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if !RTC::regs().cr().read().wute() {
|
||||||
|
trace!("wakeup timer not enabled");
|
||||||
|
} else {
|
||||||
|
trace!("wakeup timer enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::interrupt::typelevel::RTC_WKUP::unpend();
|
||||||
|
unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
|
||||||
|
|
||||||
|
RtcInstant::now()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
|
||||||
|
/// stop the wakeup alarm and return the time remaining
|
||||||
|
///
|
||||||
|
/// note: this api is exposed for testing purposes until low power is implemented.
|
||||||
|
/// it is not intended to be public
|
||||||
|
pub fn stop_wakeup_alarm() -> RtcInstant {
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
|
||||||
|
crate::interrupt::typelevel::RTC_WKUP::disable();
|
||||||
|
|
||||||
|
trace!("disable wakeup timer...");
|
||||||
|
|
||||||
|
RTC::regs().cr().modify(|w| {
|
||||||
|
w.set_wute(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
trace!("wait for wakeup timer stop...");
|
||||||
|
|
||||||
|
// Wait for the wakeup timer to stop
|
||||||
|
// while !RTC::regs().isr().read().wutf() {}
|
||||||
|
//
|
||||||
|
// RTC::regs().isr().modify(|w| w.set_wutf(false));
|
||||||
|
|
||||||
|
trace!("wait for wakeup timer stop...done");
|
||||||
|
|
||||||
|
RtcInstant::now()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
|
pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
|
||||||
#[cfg(not(rtc_v2wb))]
|
#[cfg(not(rtc_v2wb))]
|
||||||
|
|
Loading…
Reference in a new issue