diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 7aa9f0fd..2ae0d15c 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -9,6 +9,7 @@ use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -33,6 +34,7 @@ pub struct Config { pub plli2s: Option, pub pll48: bool, + pub rtc: Option, } #[cfg(stm32f410)] @@ -459,6 +461,18 @@ pub(crate) unsafe fn init(config: Config) { }) }); + match config.rtc { + Some(RtcClockSource::LSI) => { + RCC.csr().modify(|w| w.set_lsion(true)); + while !RCC.csr().read().lsirdy() {} + } + _ => {} + } + + config.rtc.map(|clock_source| { + Rtc::set_clock_source(clock_source); + }); + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 36c9eb2f..b2828e58 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -10,6 +10,7 @@ use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -426,6 +427,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSE is running while !RCC.bdcr().read().lserdy() {} + + Rtc::set_clock_source(RCS::LSE); } RtcClockSource::LSI32 => { // Turn on the internal 32 kHz LSI oscillator @@ -433,6 +436,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 5c69037e..62c19bda 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -73,6 +73,10 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, + + #[cfg(rcc_wb)] + /// Set only if the lsi or lse is configured + pub rtc: Option, } /// Frozen clock frequencies diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 21aacec5..ae708b37 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,5 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::rcc::Clocks; +use crate::rtc::{Rtc, RtcClockSource}; use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -110,6 +111,7 @@ pub struct Config { pub sys: Sysclk, pub mux: Option, pub pll48: Option, + pub rtc: Option, pub pll: Option, pub pllsai: Option, @@ -133,6 +135,7 @@ pub const WPAN_DEFAULT: Config = Config { prediv: 2, }), pll48: None, + rtc: None, pll: Some(Pll { mul: 12, @@ -160,6 +163,7 @@ impl Default for Config { pll48: None, pll: None, pllsai: None, + rtc: None, ahb1_pre: AHBPrescaler::NotDivided, ahb2_pre: AHBPrescaler::NotDivided, @@ -251,6 +255,12 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { } }; + let rtc_clk = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + Some(RtcClockSource::LSE) => Some(config.lse.unwrap()), + _ => None, + }; + Clocks { sys: sys_clk, ahb1: ahb1_clk, @@ -260,6 +270,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { apb2: apb2_clk, apb1_tim: apb1_tim_clk, apb2_tim: apb2_tim_clk, + rtc: rtc_clk, } } @@ -281,6 +292,18 @@ pub(crate) fn configure_clocks(config: &Config) { while !rcc.cr().read().hsirdy() {} } + let needs_lsi = if let Some(rtc_mux) = &config.rtc { + *rtc_mux == RtcClockSource::LSI + } else { + false + }; + + if needs_lsi { + rcc.csr().modify(|w| w.set_lsi1on(true)); + + while !rcc.csr().read().lsi1rdy() {} + } + match &config.lse { Some(_) => { rcc.cfgr().modify(|w| w.set_stopwuck(true)); @@ -351,4 +374,6 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_c2hpre(config.ahb2_pre.into()); w.set_shdhpre(config.ahb3_pre.into()); }); + + config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source)); } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 323be318..945bfafd 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,5 +1,4 @@ //! RTC peripheral abstraction -use core::marker::PhantomData; mod datetime; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; @@ -17,6 +16,9 @@ mod _version; pub use _version::*; use embassy_hal_internal::Peripheral; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; + /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { @@ -28,8 +30,7 @@ pub enum RtcError { } /// RTC Abstraction -pub struct Rtc<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +pub struct Rtc { rtc_config: RtcConfig, } @@ -48,8 +49,6 @@ pub enum RtcClockSource { #[derive(Copy, Clone, PartialEq)] pub struct RtcConfig { - /// RTC clock source - clock_config: RtcClockSource, /// Asynchronous prescaler factor /// This is the asynchronous division factor: /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) @@ -67,7 +66,6 @@ impl Default for RtcConfig { /// Raw sub-seconds in 1/256. fn default() -> Self { RtcConfig { - clock_config: RtcClockSource::LSI, async_prescaler: 127, sync_prescaler: 255, } @@ -75,12 +73,6 @@ impl Default for RtcConfig { } impl RtcConfig { - /// Sets the clock source of RTC config - pub fn clock_config(mut self, cfg: RtcClockSource) -> Self { - self.clock_config = cfg; - self - } - /// Set the asynchronous prescaler of RTC config pub fn async_prescaler(mut self, prescaler: u8) -> Self { self.async_prescaler = prescaler; @@ -111,16 +103,16 @@ impl Default for RtcCalibrationCyclePeriod { } } -impl<'d, T: Instance> Rtc<'d, T> { - pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { - T::enable_peripheral_clk(); +impl Rtc { + pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { + RTC::enable_peripheral_clk(); - let mut rtc_struct = Self { - phantom: PhantomData, - rtc_config, - }; + let mut rtc_struct = Self { rtc_config }; - rtc_struct.apply_config(rtc_config); + Self::enable(); + + rtc_struct.configure(rtc_config); + rtc_struct.rtc_config = rtc_config; rtc_struct } @@ -143,7 +135,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. pub fn now(&self) -> Result { - let r = T::regs(); + let r = RTC::regs(); let tr = r.tr().read(); let second = bcd2_to_byte((tr.st(), tr.su())); let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); @@ -162,7 +154,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { - let cr = T::regs().cr().read(); + let cr = RTC::regs().cr().read(); cr.bkp() } @@ -177,14 +169,14 @@ impl<'d, T: Instance> Rtc<'d, T> { self.rtc_config } - pub const BACKUP_REGISTER_COUNT: usize = T::BACKUP_REGISTER_COUNT; + pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - T::read_backup_register(&T::regs(), register) + RTC::read_backup_register(&RTC::regs(), register) } /// Set content of the backup register. @@ -192,7 +184,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - T::write_backup_register(&T::regs(), register, value) + RTC::write_backup_register(&RTC::regs(), register, value) } } @@ -243,5 +235,3 @@ pub(crate) mod sealed { // fn apply_config(&mut self, rtc_config: RtcConfig); } } - -pub trait Instance: sealed::Instance + 'static {} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e3b9dfb8..5b896069 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,18 +1,12 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{sealed, Instance, RtcConfig}; +use super::{sealed, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { - // Unlock the backup domain - let clock_config = rtc_config.clock_config as u8; - - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; - +impl super::Rtc { + fn unlock_registers() { #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] let cr = crate::pac::PWR.cr(); #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] @@ -21,10 +15,35 @@ impl<'d, T: Instance> super::Rtc<'d, T> { // TODO: Missing from PAC for l0 and f0? #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} + if !cr.read().dbp() { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} + } } + } + #[allow(dead_code)] + pub(crate) fn set_clock_source(clock_source: RtcClockSource) { + #[cfg(not(rtc_v2wb))] + use stm32_metapac::rcc::vals::Rtcsel; + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let cr = crate::pac::RCC.bdcr(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let cr = crate::pac::RCC.csr(); + + Self::unlock_registers(); + + cr.modify(|w| { + // Select RTC source + #[cfg(not(rtc_v2wb))] + w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); + #[cfg(rtc_v2wb)] + w.set_rtcsel(clock_source as u8); + }); + } + + pub(super) fn enable() { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] let reg = crate::pac::RCC.bdcr().read(); #[cfg(any(rtc_v2l0, rtc_v2l1))] @@ -33,12 +52,9 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - #[cfg(rtc_v2wb)] - let rtcsel = reg.rtcsel(); - #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().to_bits(); + if !reg.rtcen() { + Self::unlock_registers(); - if !reg.rtcen() || rtcsel != clock_config { #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] @@ -51,12 +67,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] w.set_bdrst(false); - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel::from_bits(clock_config)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_config); w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr #[cfg(any(rtc_v2l4, rtc_v2wb))] @@ -71,7 +83,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_lsebyp(reg.lsebyp()); }); } + } + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn configure(&mut self, rtc_config: RtcConfig) { self.write(true, |rtc| { rtc.cr().modify(|w| { #[cfg(rtc_v2f2)] @@ -87,8 +103,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_prediv_a(rtc_config.async_prescaler); }); }); - - self.rtc_config = rtc_config; } /// Calibrate the clock drift. @@ -160,7 +174,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(0xca)); @@ -221,5 +235,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 8ef0ec51..3297303e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,44 +1,62 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { +impl super::Rtc { + fn unlock_registers() { // Unlock the backup domain #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} + if !crate::pac::PWR.cr1().read().dbp() { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + } } #[cfg(any(rcc_wl5, rcc_wle))] { use crate::pac::pwr::vals::Dbp; - crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + } } + } - let reg = crate::pac::RCC.bdcr().read(); + #[allow(dead_code)] + pub(crate) fn set_clock_source(clock_source: RtcClockSource) { + let clock_source = clock_source as u8; + #[cfg(not(any(rcc_wl5, rcc_wle)))] + let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); + + Self::unlock_registers(); + + crate::pac::RCC.bdcr().modify(|w| { + // Select RTC source + w.set_rtcsel(clock_source); + }); + } + + pub(super) fn enable() { + let bdcr = crate::pac::RCC.bdcr(); + + let reg = bdcr.read(); assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel); + if !reg.rtcen() { + Self::unlock_registers(); - if !reg.rtcen() || reg.rtcsel() != config_rtcsel { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + bdcr.modify(|w| w.set_bdrst(true)); - crate::pac::RCC.bdcr().modify(|w| { + bdcr.modify(|w| { // Reset w.set_bdrst(false); - // Select RTC source - w.set_rtcsel(config_rtcsel); - w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr w.set_lscosel(reg.lscosel()); @@ -49,7 +67,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_lsebyp(reg.lsebyp()); }); } + } + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn configure(&mut self, rtc_config: RtcConfig) { self.write(true, |rtc| { rtc.cr().modify(|w| { w.set_fmt(Fmt::TWENTYFOURHOUR); @@ -69,8 +91,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_tampalrm_pu(TampalrmPu::NOPULLUP); }); }); - - self.rtc_config = rtc_config; } const RTC_CALR_MIN_PPM: f32 = -487.1; @@ -141,7 +161,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); @@ -188,5 +208,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index d72d5ddb..294ea456 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -33,10 +33,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index e1182549..fb1bc6e3 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -27,10 +27,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 194b153d..7df415b4 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -10,13 +10,16 @@ use chrono::{NaiveDate, NaiveDateTime}; use common::*; use defmt::assert; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; use embassy_time::{Duration, Timer}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + + config.rcc.rtc = Some(RtcClockSource::LSI); + + let p = embassy_stm32::init(config); info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) @@ -24,13 +27,6 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - info!("Starting LSI"); - - pac::RCC.csr().modify(|w| w.set_lsion(true)); - while !pac::RCC.csr().read().lsirdy() {} - - info!("Started LSI"); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.into()).expect("datetime not set");