diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index d5846f53..0d853fda 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -61,7 +61,7 @@ pub struct Executor { impl Executor { /// Create a new Executor. pub fn take() -> &'static mut Self { - unsafe { + critical_section::with(|_| unsafe { assert!(EXECUTOR.is_none()); EXECUTOR = Some(Self { @@ -72,7 +72,7 @@ impl Executor { }); EXECUTOR.as_mut().unwrap() - } + }) } unsafe fn on_wakeup_irq(&mut self) { diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index d897843d..f3428108 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -18,9 +18,13 @@ pub struct RtcInstant { } impl RtcInstant { - #[allow(dead_code)] - pub(super) fn from(second: u8, subsecond: u16) -> Result { - Ok(Self { second, subsecond }) + #[cfg(not(rtc_v2f2))] + pub(super) const fn from(second: u8, subsecond: u16) -> Result { + if second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(Self { second, subsecond }) + } } } @@ -226,7 +230,7 @@ impl From for chrono::Weekday { } } -fn day_of_week_from_u8(v: u8) -> Result { +pub(super) const fn day_of_week_from_u8(v: u8) -> Result { Ok(match v { 1 => DayOfWeek::Monday, 2 => DayOfWeek::Tuesday, @@ -239,24 +243,6 @@ fn day_of_week_from_u8(v: u8) -> Result { }) } -pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { +pub(super) const fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { dotw as u8 } - -pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { - if dt.year > 4095 { - Err(Error::InvalidYear) - } else if dt.month < 1 || dt.month > 12 { - Err(Error::InvalidMonth) - } else if dt.day < 1 || dt.day > 31 { - Err(Error::InvalidDay) - } else if dt.hour > 23 { - Err(Error::InvalidHour) - } else if dt.minute > 59 { - Err(Error::InvalidMinute) - } else if dt.second > 59 { - Err(Error::InvalidSecond) - } else { - Ok(()) - } -} diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index c8990c30..e527fdfe 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -9,8 +9,11 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; #[cfg(feature = "low-power")] use embassy_sync::blocking_mutex::Mutex; -pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError, RtcInstant}; -use crate::rtc::datetime::day_of_week_to_u8; +use self::datetime::day_of_week_to_u8; +#[cfg(not(rtc_v2f2))] +use self::datetime::RtcInstant; +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use crate::pac::rtc::regs::{Dr, Tr}; use crate::time::Hertz; /// refer to AN4759 to compare features of RTC2 and RTC3 @@ -31,11 +34,15 @@ use crate::peripherals::RTC; use crate::rtc::sealed::Instance; /// Errors that can occur on methods on [RtcClock] +#[non_exhaustive] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { /// An invalid DateTime was given or stored on the hardware. InvalidDateTime(DateTimeError), + /// The current time could not be read + ReadFailure, + /// The RTC clock is not running NotRunning, } @@ -45,48 +52,25 @@ pub struct RtcTimeProvider { } impl RtcTimeProvider { + #[cfg(not(rtc_v2f2))] + pub(crate) fn instant(&self) -> Result { + self.read(|_, tr, ss| { + let second = bcd2_to_byte((tr.st(), tr.su())); + + RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) + }) + } + /// Return the current datetime. /// /// # Errors /// /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. pub fn now(&self) -> Result { - // For RM0433 we use BYPSHAD=1 to work around errata ES0392 2.19.1 - #[cfg(rcc_h7rm0433)] - loop { - let r = RTC::regs(); - let ss = r.ssr().read().ss(); - let dr = r.dr().read(); - let tr = r.tr().read(); - - // If an RTCCLK edge occurs during read we may see inconsistent values - // so read ssr again and see if it has changed. (see RM0433 Rev 7 46.3.9) - let ss_after = r.ssr().read().ss(); - if ss == ss_after { - let second = bcd2_to_byte((tr.st(), tr.su())); - let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); - let hour = bcd2_to_byte((tr.ht(), tr.hu())); - - let weekday = dr.wdu(); - let day = bcd2_to_byte((dr.dt(), dr.du())); - let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); - let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; - - return DateTime::from(year, month, day, weekday, hour, minute, second) - .map_err(RtcError::InvalidDateTime); - } - } - - #[cfg(not(rcc_h7rm0433))] - { - let r = RTC::regs(); - let tr = r.tr().read(); + self.read(|dr, tr, _| { let second = bcd2_to_byte((tr.st(), tr.su())); let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); let hour = bcd2_to_byte((tr.ht(), tr.hu())); - // Reading either RTC_SSR or RTC_TR locks the values in the higher-order - // calendar shadow registers until RTC_DR is read. - let dr = r.dr().read(); let weekday = dr.wdu(); let day = bcd2_to_byte((dr.dt(), dr.du())); @@ -94,7 +78,33 @@ impl RtcTimeProvider { let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) + }) + } + + fn read(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result) -> Result { + let r = RTC::regs(); + + #[cfg(not(rtc_v2f2))] + let read_ss = || r.ssr().read().ss(); + #[cfg(rtc_v2f2)] + let read_ss = || 0; + + let mut ss = read_ss(); + for _ in 0..5 { + let tr = r.tr().read(); + let dr = r.dr().read(); + let ss_after = read_ss(); + + // If an RTCCLK edge occurs during read we may see inconsistent values + // so read ssr again and see if it has changed. (see RM0433 Rev 7 46.3.9) + if ss == ss_after { + return f(dr, tr, ss.try_into().unwrap()); + } else { + ss = ss_after + } } + + return Err(RtcError::ReadFailure); } } @@ -145,6 +155,7 @@ impl Rtc { #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] critical_section::with(|cs| { ::enable_and_reset_with_cs(cs); + #[cfg(feature = "low-power")] unsafe { crate::rcc::REFCOUNT_STOP2 -= 1 @@ -164,6 +175,14 @@ impl Rtc { this.configure(async_psc, sync_psc); + // Wait for the clock to update after initialization + #[cfg(not(rtc_v2f2))] + { + let now = this.instant().unwrap(); + + while this.instant().unwrap().subsecond == now.subsecond {} + } + this } @@ -183,7 +202,6 @@ 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::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; self.write(true, |rtc| { let (ht, hu) = byte_to_bcd2(t.hour() as u8); let (mnt, mnu) = byte_to_bcd2(t.minute() as u8); @@ -223,16 +241,8 @@ impl Rtc { #[cfg(not(rtc_v2f2))] /// Return the current instant. - pub fn instant(&self) -> Result { - let r = RTC::regs(); - let tr = r.tr().read(); - let subsecond = r.ssr().read().ss(); - let second = bcd2_to_byte((tr.st(), tr.su())); - - // Unlock the registers - r.dr().read(); - - RtcInstant::from(second, subsecond.try_into().unwrap()) + fn instant(&self) -> Result { + self.time_provider().instant() } /// Return the current datetime. diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index b6ab9b20..006fd63f 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -150,14 +150,14 @@ impl super::Rtc { pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { self.write(true, |rtc| { rtc.cr().modify(|w| { + #[cfg(not(rtc_v2f2))] + w.set_bypshad(true); #[cfg(rtc_v2f2)] w.set_fmt(false); #[cfg(not(rtc_v2f2))] w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); w.set_osel(Osel::DISABLED); w.set_pol(Pol::HIGH); - #[cfg(rcc_h7rm0433)] - w.set_bypshad(true); }); rtc.prer().modify(|w| { diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index a6b2655d..7bf757e7 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -11,6 +11,7 @@ impl super::Rtc { pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { self.write(true, |rtc| { rtc.cr().modify(|w| { + w.set_bypshad(true); w.set_fmt(Fmt::TWENTYFOURHOUR); w.set_osel(Osel::DISABLED); w.set_pol(Pol::HIGH);