diff options
Diffstat (limited to 'embassy-stm32/src/rtc')
| -rw-r--r-- | embassy-stm32/src/rtc/datetime.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/mod.rs | 215 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/v2.rs | 281 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/v3.rs | 71 |
4 files changed, 227 insertions, 342 deletions
diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index a9c48d88d..3efe9be5d 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs | |||
| @@ -89,7 +89,7 @@ pub enum DayOfWeek { | |||
| 89 | #[cfg(feature = "chrono")] | 89 | #[cfg(feature = "chrono")] |
| 90 | impl From<chrono::Weekday> for DayOfWeek { | 90 | impl From<chrono::Weekday> for DayOfWeek { |
| 91 | fn from(weekday: Weekday) -> Self { | 91 | fn from(weekday: Weekday) -> Self { |
| 92 | day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() | 92 | day_of_week_from_u8(weekday.num_days_from_monday() as u8).unwrap() |
| 93 | } | 93 | } |
| 94 | } | 94 | } |
| 95 | 95 | ||
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index a6102077a..73b78f253 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -1,7 +1,17 @@ | |||
| 1 | //! RTC peripheral abstraction | 1 | //! RTC peripheral abstraction |
| 2 | mod datetime; | 2 | mod datetime; |
| 3 | 3 | ||
| 4 | #[cfg(feature = "low-power")] | ||
| 5 | use core::cell::Cell; | ||
| 6 | |||
| 7 | #[cfg(feature = "low-power")] | ||
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 9 | #[cfg(feature = "low-power")] | ||
| 10 | use embassy_sync::blocking_mutex::Mutex; | ||
| 11 | |||
| 4 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | 12 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; |
| 13 | pub use crate::rcc::RtcClockSource; | ||
| 14 | use crate::time::Hertz; | ||
| 5 | 15 | ||
| 6 | /// refer to AN4759 to compare features of RTC2 and RTC3 | 16 | /// refer to AN4759 to compare features of RTC2 and RTC3 |
| 7 | #[cfg_attr(any(rtc_v1), path = "v1.rs")] | 17 | #[cfg_attr(any(rtc_v1), path = "v1.rs")] |
| @@ -30,60 +40,99 @@ pub enum RtcError { | |||
| 30 | NotRunning, | 40 | NotRunning, |
| 31 | } | 41 | } |
| 32 | 42 | ||
| 33 | /// RTC Abstraction | 43 | #[cfg(feature = "low-power")] |
| 34 | pub struct Rtc { | 44 | /// Represents an instant in time that can be substracted to compute a duration |
| 35 | rtc_config: RtcConfig, | 45 | struct RtcInstant { |
| 46 | second: u8, | ||
| 47 | subsecond: u16, | ||
| 36 | } | 48 | } |
| 37 | 49 | ||
| 38 | #[derive(Copy, Clone, Debug, PartialEq)] | 50 | #[cfg(all(feature = "low-power", feature = "defmt"))] |
| 39 | #[repr(u8)] | 51 | impl defmt::Format for RtcInstant { |
| 40 | pub enum RtcClockSource { | 52 | fn format(&self, fmt: defmt::Formatter) { |
| 41 | /// 00: No clock | 53 | defmt::write!( |
| 42 | NoClock = 0b00, | 54 | fmt, |
| 43 | /// 01: LSE oscillator clock used as RTC clock | 55 | "{}:{}", |
| 44 | LSE = 0b01, | 56 | self.second, |
| 45 | /// 10: LSI oscillator clock used as RTC clock | 57 | RTC::regs().prer().read().prediv_s() - self.subsecond, |
| 46 | LSI = 0b10, | 58 | ) |
| 47 | /// 11: HSE oscillator clock divided by 32 used as RTC clock | 59 | } |
| 48 | HSE = 0b11, | 60 | } |
| 61 | |||
| 62 | #[cfg(feature = "low-power")] | ||
| 63 | impl core::ops::Sub for RtcInstant { | ||
| 64 | type Output = embassy_time::Duration; | ||
| 65 | |||
| 66 | fn sub(self, rhs: Self) -> Self::Output { | ||
| 67 | use embassy_time::{Duration, TICK_HZ}; | ||
| 68 | |||
| 69 | let second = if self.second < rhs.second { | ||
| 70 | self.second + 60 | ||
| 71 | } else { | ||
| 72 | self.second | ||
| 73 | }; | ||
| 74 | |||
| 75 | let psc = RTC::regs().prer().read().prediv_s() as u32; | ||
| 76 | |||
| 77 | let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); | ||
| 78 | let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); | ||
| 79 | let rtc_ticks = self_ticks - other_ticks; | ||
| 80 | |||
| 81 | Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | pub struct RtcTimeProvider { | ||
| 86 | _private: (), | ||
| 87 | } | ||
| 88 | |||
| 89 | impl RtcTimeProvider { | ||
| 90 | /// Return the current datetime. | ||
| 91 | /// | ||
| 92 | /// # Errors | ||
| 93 | /// | ||
| 94 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | ||
| 95 | pub fn now(&self) -> Result<DateTime, RtcError> { | ||
| 96 | let r = RTC::regs(); | ||
| 97 | let tr = r.tr().read(); | ||
| 98 | let second = bcd2_to_byte((tr.st(), tr.su())); | ||
| 99 | let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); | ||
| 100 | let hour = bcd2_to_byte((tr.ht(), tr.hu())); | ||
| 101 | // Reading either RTC_SSR or RTC_TR locks the values in the higher-order | ||
| 102 | // calendar shadow registers until RTC_DR is read. | ||
| 103 | let dr = r.dr().read(); | ||
| 104 | |||
| 105 | let weekday = dr.wdu(); | ||
| 106 | let day = bcd2_to_byte((dr.dt(), dr.du())); | ||
| 107 | let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); | ||
| 108 | let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; | ||
| 109 | |||
| 110 | self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | /// RTC Abstraction | ||
| 115 | pub struct Rtc { | ||
| 116 | #[cfg(feature = "low-power")] | ||
| 117 | stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>, | ||
| 118 | #[cfg(not(feature = "low-power"))] | ||
| 119 | _private: (), | ||
| 49 | } | 120 | } |
| 50 | 121 | ||
| 122 | #[non_exhaustive] | ||
| 51 | #[derive(Copy, Clone, PartialEq)] | 123 | #[derive(Copy, Clone, PartialEq)] |
| 52 | pub struct RtcConfig { | 124 | pub struct RtcConfig { |
| 53 | /// Asynchronous prescaler factor | 125 | /// The subsecond counter frequency; default is 256 |
| 54 | /// This is the asynchronous division factor: | 126 | /// |
| 55 | /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) | 127 | /// A high counter frequency may impact stop power consumption |
| 56 | /// ck_apre drives the subsecond register | 128 | pub frequency: Hertz, |
| 57 | async_prescaler: u8, | ||
| 58 | /// Synchronous prescaler factor | ||
| 59 | /// This is the synchronous division factor: | ||
| 60 | /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1) | ||
| 61 | /// ck_spre must be 1Hz | ||
| 62 | sync_prescaler: u16, | ||
| 63 | } | 129 | } |
| 64 | 130 | ||
| 65 | impl Default for RtcConfig { | 131 | impl Default for RtcConfig { |
| 66 | /// LSI with prescalers assuming 32.768 kHz. | 132 | /// LSI with prescalers assuming 32.768 kHz. |
| 67 | /// Raw sub-seconds in 1/256. | 133 | /// Raw sub-seconds in 1/256. |
| 68 | fn default() -> Self { | 134 | fn default() -> Self { |
| 69 | RtcConfig { | 135 | RtcConfig { frequency: Hertz(256) } |
| 70 | async_prescaler: 127, | ||
| 71 | sync_prescaler: 255, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | impl RtcConfig { | ||
| 77 | /// Set the asynchronous prescaler of RTC config | ||
| 78 | pub fn async_prescaler(mut self, prescaler: u8) -> Self { | ||
| 79 | self.async_prescaler = prescaler; | ||
| 80 | self | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Set the synchronous prescaler of RTC config | ||
| 84 | pub fn sync_prescaler(mut self, prescaler: u16) -> Self { | ||
| 85 | self.sync_prescaler = prescaler; | ||
| 86 | self | ||
| 87 | } | 136 | } |
| 88 | } | 137 | } |
| 89 | 138 | ||
| @@ -106,16 +155,44 @@ impl Default for RtcCalibrationCyclePeriod { | |||
| 106 | 155 | ||
| 107 | impl Rtc { | 156 | impl Rtc { |
| 108 | pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { | 157 | pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { |
| 109 | RTC::enable_peripheral_clk(); | 158 | #[cfg(any(rcc_wle, rcc_wl5, rcc_g4, rcc_g0, rtc_v2l4, rtc_v2wb))] |
| 159 | <RTC as crate::rcc::sealed::RccPeripheral>::enable(); | ||
| 160 | |||
| 161 | let mut this = Self { | ||
| 162 | #[cfg(feature = "low-power")] | ||
| 163 | stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | ||
| 164 | #[cfg(not(feature = "low-power"))] | ||
| 165 | _private: (), | ||
| 166 | }; | ||
| 167 | |||
| 168 | let frequency = Self::frequency(); | ||
| 169 | let async_psc = ((frequency.0 / rtc_config.frequency.0) - 1) as u8; | ||
| 170 | let sync_psc = (rtc_config.frequency.0 - 1) as u16; | ||
| 110 | 171 | ||
| 111 | let mut rtc_struct = Self { rtc_config }; | 172 | this.configure(async_psc, sync_psc); |
| 112 | 173 | ||
| 113 | Self::enable(); | 174 | this |
| 175 | } | ||
| 176 | |||
| 177 | fn frequency() -> Hertz { | ||
| 178 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||
| 179 | let freqs = unsafe { crate::rcc::get_freqs() }; | ||
| 180 | |||
| 181 | // Load the clock frequency from the rcc mod, if supported | ||
| 182 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||
| 183 | match freqs.rtc { | ||
| 184 | Some(hertz) => hertz, | ||
| 185 | None => freqs.rtc_hse.unwrap(), | ||
| 186 | } | ||
| 114 | 187 | ||
| 115 | rtc_struct.configure(rtc_config); | 188 | // Assume the default value, if not supported |
| 116 | rtc_struct.rtc_config = rtc_config; | 189 | #[cfg(not(any(rcc_wb, rcc_f4, rcc_f410)))] |
| 190 | Hertz(32_768) | ||
| 191 | } | ||
| 117 | 192 | ||
| 118 | rtc_struct | 193 | /// Acquire a [`RtcTimeProvider`] instance. |
| 194 | pub const fn time_provider(&self) -> RtcTimeProvider { | ||
| 195 | RtcTimeProvider { _private: () } | ||
| 119 | } | 196 | } |
| 120 | 197 | ||
| 121 | /// Set the datetime to a new value. | 198 | /// Set the datetime to a new value. |
| @@ -130,27 +207,27 @@ impl Rtc { | |||
| 130 | Ok(()) | 207 | Ok(()) |
| 131 | } | 208 | } |
| 132 | 209 | ||
| 210 | #[cfg(feature = "low-power")] | ||
| 211 | /// Return the current instant. | ||
| 212 | fn instant(&self) -> RtcInstant { | ||
| 213 | let r = RTC::regs(); | ||
| 214 | let tr = r.tr().read(); | ||
| 215 | let subsecond = r.ssr().read().ss(); | ||
| 216 | let second = bcd2_to_byte((tr.st(), tr.su())); | ||
| 217 | |||
| 218 | // Unlock the registers | ||
| 219 | r.dr().read(); | ||
| 220 | |||
| 221 | RtcInstant { second, subsecond } | ||
| 222 | } | ||
| 223 | |||
| 133 | /// Return the current datetime. | 224 | /// Return the current datetime. |
| 134 | /// | 225 | /// |
| 135 | /// # Errors | 226 | /// # Errors |
| 136 | /// | 227 | /// |
| 137 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | 228 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. |
| 138 | pub fn now(&self) -> Result<DateTime, RtcError> { | 229 | pub fn now(&self) -> Result<DateTime, RtcError> { |
| 139 | let r = RTC::regs(); | 230 | self.time_provider().now() |
| 140 | let tr = r.tr().read(); | ||
| 141 | let second = bcd2_to_byte((tr.st(), tr.su())); | ||
| 142 | let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); | ||
| 143 | let hour = bcd2_to_byte((tr.ht(), tr.hu())); | ||
| 144 | // Reading either RTC_SSR or RTC_TR locks the values in the higher-order | ||
| 145 | // calendar shadow registers until RTC_DR is read. | ||
| 146 | let dr = r.dr().read(); | ||
| 147 | |||
| 148 | let weekday = dr.wdu(); | ||
| 149 | let day = bcd2_to_byte((dr.dt(), dr.du())); | ||
| 150 | let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); | ||
| 151 | let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; | ||
| 152 | |||
| 153 | self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) | ||
| 154 | } | 231 | } |
| 155 | 232 | ||
| 156 | /// Check if daylight savings time is active. | 233 | /// Check if daylight savings time is active. |
| @@ -166,10 +243,6 @@ impl Rtc { | |||
| 166 | }) | 243 | }) |
| 167 | } | 244 | } |
| 168 | 245 | ||
| 169 | pub fn get_config(&self) -> RtcConfig { | ||
| 170 | self.rtc_config | ||
| 171 | } | ||
| 172 | |||
| 173 | pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; | 246 | pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; |
| 174 | 247 | ||
| 175 | /// Read content of the backup register. | 248 | /// Read content of the backup register. |
| @@ -215,12 +288,16 @@ pub(crate) mod sealed { | |||
| 215 | pub trait Instance { | 288 | pub trait Instance { |
| 216 | const BACKUP_REGISTER_COUNT: usize; | 289 | const BACKUP_REGISTER_COUNT: usize; |
| 217 | 290 | ||
| 291 | #[cfg(feature = "low-power")] | ||
| 292 | const EXTI_WAKEUP_LINE: usize; | ||
| 293 | |||
| 294 | #[cfg(feature = "low-power")] | ||
| 295 | type WakeupInterrupt: crate::interrupt::typelevel::Interrupt; | ||
| 296 | |||
| 218 | fn regs() -> Rtc { | 297 | fn regs() -> Rtc { |
| 219 | crate::pac::RTC | 298 | crate::pac::RTC |
| 220 | } | 299 | } |
| 221 | 300 | ||
| 222 | fn enable_peripheral_clk() {} | ||
| 223 | |||
| 224 | /// Read content of the backup register. | 301 | /// Read content of the backup register. |
| 225 | /// | 302 | /// |
| 226 | /// The registers retain their values during wakes from standby mode or system resets. They also | 303 | /// The registers retain their values during wakes from standby mode or system resets. They also |
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index bcb127ecb..4608d3114 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -1,77 +1,21 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Init, Osel, Pol}; | 1 | use stm32_metapac::rtc::vals::{Init, Osel, Pol}; |
| 2 | 2 | ||
| 3 | use super::{sealed, RtcClockSource, RtcConfig}; | 3 | use super::sealed; |
| 4 | use crate::pac::rtc::Rtc; | 4 | use crate::pac::rtc::Rtc; |
| 5 | use crate::peripherals::RTC; | 5 | use crate::peripherals::RTC; |
| 6 | use crate::rtc::sealed::Instance; | 6 | use crate::rtc::sealed::Instance; |
| 7 | 7 | ||
| 8 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 9 | pub struct RtcInstant { | ||
| 10 | ssr: u16, | ||
| 11 | st: u8, | ||
| 12 | } | ||
| 13 | |||
| 14 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 15 | impl RtcInstant { | ||
| 16 | pub fn now() -> Self { | ||
| 17 | // TODO: read value twice | ||
| 18 | use crate::rtc::bcd2_to_byte; | ||
| 19 | |||
| 20 | let tr = RTC::regs().tr().read(); | ||
| 21 | let tr2 = RTC::regs().tr().read(); | ||
| 22 | let ssr = RTC::regs().ssr().read().ss(); | ||
| 23 | let ssr2 = RTC::regs().ssr().read().ss(); | ||
| 24 | |||
| 25 | let st = bcd2_to_byte((tr.st(), tr.su())); | ||
| 26 | let st2 = bcd2_to_byte((tr2.st(), tr2.su())); | ||
| 27 | |||
| 28 | assert!(st == st2); | ||
| 29 | assert!(ssr == ssr2); | ||
| 30 | |||
| 31 | let _ = RTC::regs().dr().read(); | ||
| 32 | |||
| 33 | trace!("ssr: {}", ssr); | ||
| 34 | trace!("st: {}", st); | ||
| 35 | |||
| 36 | Self { ssr, st } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 41 | impl core::ops::Sub for RtcInstant { | ||
| 42 | type Output = embassy_time::Duration; | ||
| 43 | |||
| 44 | fn sub(self, rhs: Self) -> Self::Output { | ||
| 45 | use embassy_time::{Duration, TICK_HZ}; | ||
| 46 | |||
| 47 | let st = if self.st < rhs.st { self.st + 60 } else { self.st }; | ||
| 48 | |||
| 49 | // TODO: read prescaler | ||
| 50 | |||
| 51 | let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); | ||
| 52 | let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); | ||
| 53 | let rtc_ticks = self_ticks - other_ticks; | ||
| 54 | |||
| 55 | trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); | ||
| 56 | |||
| 57 | Duration::from_ticks( | ||
| 58 | ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) | ||
| 59 | * TICK_HZ as u32) as u32 | ||
| 60 | / 256u32) as u64, | ||
| 61 | ) | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | #[allow(dead_code)] | 8 | #[allow(dead_code)] |
| 9 | #[repr(u8)] | ||
| 66 | #[derive(Clone, Copy, Debug)] | 10 | #[derive(Clone, Copy, Debug)] |
| 67 | pub(crate) enum WakeupPrescaler { | 11 | pub(crate) enum WakeupPrescaler { |
| 68 | Div2, | 12 | Div2 = 2, |
| 69 | Div4, | 13 | Div4 = 4, |
| 70 | Div8, | 14 | Div8 = 8, |
| 71 | Div16, | 15 | Div16 = 16, |
| 72 | } | 16 | } |
| 73 | 17 | ||
| 74 | #[cfg(any(stm32wb, stm32f4))] | 18 | #[cfg(any(stm32wb, stm32f4, stm32l0))] |
| 75 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | 19 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { |
| 76 | fn from(val: WakeupPrescaler) -> Self { | 20 | fn from(val: WakeupPrescaler) -> Self { |
| 77 | use crate::pac::rtc::vals::Wucksel; | 21 | use crate::pac::rtc::vals::Wucksel; |
| @@ -85,7 +29,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | |||
| 85 | } | 29 | } |
| 86 | } | 30 | } |
| 87 | 31 | ||
| 88 | #[cfg(any(stm32wb, stm32f4))] | 32 | #[cfg(any(stm32wb, stm32f4, stm32l0))] |
| 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | 33 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { |
| 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | 34 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { |
| 91 | use crate::pac::rtc::vals::Wucksel; | 35 | use crate::pac::rtc::vals::Wucksel; |
| @@ -100,17 +44,6 @@ impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | |||
| 100 | } | 44 | } |
| 101 | } | 45 | } |
| 102 | 46 | ||
| 103 | impl From<WakeupPrescaler> for u32 { | ||
| 104 | fn from(val: WakeupPrescaler) -> Self { | ||
| 105 | match val { | ||
| 106 | WakeupPrescaler::Div2 => 2, | ||
| 107 | WakeupPrescaler::Div4 => 4, | ||
| 108 | WakeupPrescaler::Div8 => 8, | ||
| 109 | WakeupPrescaler::Div16 => 16, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | #[allow(dead_code)] | 47 | #[allow(dead_code)] |
| 115 | impl WakeupPrescaler { | 48 | impl WakeupPrescaler { |
| 116 | pub fn compute_min(val: u32) -> Self { | 49 | pub fn compute_min(val: u32) -> Self { |
| @@ -121,158 +54,100 @@ impl WakeupPrescaler { | |||
| 121 | WakeupPrescaler::Div16, | 54 | WakeupPrescaler::Div16, |
| 122 | ] | 55 | ] |
| 123 | .iter() | 56 | .iter() |
| 124 | .skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val) | 57 | .skip_while(|psc| **psc as u32 <= val) |
| 125 | .next() | 58 | .next() |
| 126 | .unwrap_or(&WakeupPrescaler::Div16) | 59 | .unwrap_or(&WakeupPrescaler::Div16) |
| 127 | } | 60 | } |
| 128 | } | 61 | } |
| 129 | 62 | ||
| 130 | impl super::Rtc { | 63 | impl super::Rtc { |
| 131 | fn unlock_registers() { | 64 | #[cfg(feature = "low-power")] |
| 132 | #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] | 65 | /// start the wakeup alarm and wtih a duration that is as close to but less than |
| 133 | let cr = crate::pac::PWR.cr(); | 66 | /// the requested duration, and record the instant the wakeup alarm was started |
| 134 | #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] | 67 | pub(crate) fn start_wakeup_alarm( |
| 135 | let cr = crate::pac::PWR.cr1(); | 68 | &self, |
| 136 | 69 | requested_duration: embassy_time::Duration, | |
| 137 | // TODO: Missing from PAC for l0 and f0? | 70 | cs: critical_section::CriticalSection, |
| 138 | #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] | 71 | ) { |
| 139 | { | ||
| 140 | if !cr.read().dbp() { | ||
| 141 | cr.modify(|w| w.set_dbp(true)); | ||
| 142 | while !cr.read().dbp() {} | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | #[allow(dead_code)] | ||
| 148 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 149 | /// start the wakeup alarm and return the actual duration of the alarm | ||
| 150 | /// the actual duration will be the closest value possible that is less | ||
| 151 | /// than the requested duration. | ||
| 152 | /// | ||
| 153 | /// note: this api is exposed for testing purposes until low power is implemented. | ||
| 154 | /// it is not intended to be public | ||
| 155 | pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { | ||
| 156 | use embassy_time::{Duration, TICK_HZ}; | 72 | use embassy_time::{Duration, TICK_HZ}; |
| 157 | 73 | ||
| 158 | use crate::rcc::get_freqs; | 74 | // Panic if the rcc mod knows we're not using low-power rtc |
| 75 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | ||
| 76 | unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); | ||
| 159 | 77 | ||
| 160 | let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; | 78 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); |
| 161 | 79 | let rtc_hz = Self::frequency().0 as u64; | |
| 162 | let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; | 80 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; |
| 163 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | 81 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); |
| 164 | 82 | ||
| 165 | // adjust the rtc ticks to the prescaler | 83 | // adjust the rtc ticks to the prescaler and subtract one rtc tick |
| 166 | let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64); | 84 | let rtc_ticks = rtc_ticks / prescaler as u64; |
| 167 | let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { | 85 | let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; |
| 168 | u16::MAX - 1 | ||
| 169 | } else { | ||
| 170 | rtc_ticks as u16 | ||
| 171 | }; | ||
| 172 | |||
| 173 | let duration = Duration::from_ticks( | ||
| 174 | rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, | ||
| 175 | ); | ||
| 176 | |||
| 177 | trace!("set wakeup timer for {} ms", duration.as_millis()); | ||
| 178 | 86 | ||
| 179 | self.write(false, |regs| { | 87 | self.write(false, |regs| { |
| 180 | regs.cr().modify(|w| w.set_wutie(true)); | ||
| 181 | |||
| 182 | regs.cr().modify(|w| w.set_wute(false)); | 88 | regs.cr().modify(|w| w.set_wute(false)); |
| 183 | regs.isr().modify(|w| w.set_wutf(false)); | 89 | regs.isr().modify(|w| w.set_wutf(false)); |
| 184 | while !regs.isr().read().wutwf() {} | 90 | while !regs.isr().read().wutwf() {} |
| 185 | 91 | ||
| 186 | regs.cr().modify(|w| w.set_wucksel(prescaler.into())); | 92 | regs.cr().modify(|w| w.set_wucksel(prescaler.into())); |
| 93 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); | ||
| 187 | regs.cr().modify(|w| w.set_wute(true)); | 94 | regs.cr().modify(|w| w.set_wute(true)); |
| 95 | regs.cr().modify(|w| w.set_wutie(true)); | ||
| 188 | }); | 96 | }); |
| 189 | 97 | ||
| 190 | RtcInstant::now() | 98 | trace!( |
| 99 | "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", | ||
| 100 | Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), | ||
| 101 | prescaler as u32, | ||
| 102 | rtc_ticks, | ||
| 103 | self.instant(), | ||
| 104 | ); | ||
| 105 | |||
| 106 | assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none()) | ||
| 191 | } | 107 | } |
| 192 | 108 | ||
| 193 | #[allow(dead_code)] | 109 | #[cfg(feature = "low-power")] |
| 194 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | 110 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 195 | /// stop the wakeup alarm and return the time remaining | 111 | /// was called, otherwise none |
| 196 | /// | 112 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { |
| 197 | /// note: this api is exposed for testing purposes until low power is implemented. | 113 | use crate::interrupt::typelevel::Interrupt; |
| 198 | /// it is not intended to be public | 114 | |
| 199 | pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { | 115 | trace!("rtc: stop wakeup alarm at {}", self.instant()); |
| 200 | trace!("disable wakeup timer..."); | ||
| 201 | 116 | ||
| 202 | self.write(false, |regs| { | 117 | self.write(false, |regs| { |
| 118 | regs.cr().modify(|w| w.set_wutie(false)); | ||
| 203 | regs.cr().modify(|w| w.set_wute(false)); | 119 | regs.cr().modify(|w| w.set_wute(false)); |
| 204 | regs.isr().modify(|w| w.set_wutf(false)); | 120 | regs.isr().modify(|w| w.set_wutf(false)); |
| 205 | }); | ||
| 206 | |||
| 207 | RtcInstant::now() | ||
| 208 | } | ||
| 209 | |||
| 210 | #[allow(dead_code)] | ||
| 211 | pub(crate) fn set_clock_source(clock_source: RtcClockSource) { | ||
| 212 | #[cfg(not(rtc_v2wb))] | ||
| 213 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 214 | 121 | ||
| 215 | #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | 122 | crate::pac::EXTI |
| 216 | let cr = crate::pac::RCC.bdcr(); | 123 | .pr(0) |
| 217 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 124 | .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); |
| 218 | let cr = crate::pac::RCC.csr(); | ||
| 219 | 125 | ||
| 220 | Self::unlock_registers(); | 126 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); |
| 221 | |||
| 222 | cr.modify(|w| { | ||
| 223 | // Select RTC source | ||
| 224 | #[cfg(not(rtc_v2wb))] | ||
| 225 | w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); | ||
| 226 | #[cfg(rtc_v2wb)] | ||
| 227 | w.set_rtcsel(clock_source as u8); | ||
| 228 | }); | 127 | }); |
| 229 | } | ||
| 230 | 128 | ||
| 231 | pub(super) fn enable() { | 129 | if let Some(stop_time) = self.stop_time.borrow(cs).take() { |
| 232 | #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | 130 | Some(self.instant() - stop_time) |
| 233 | let reg = crate::pac::RCC.bdcr().read(); | 131 | } else { |
| 234 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 132 | None |
| 235 | let reg = crate::pac::RCC.csr().read(); | 133 | } |
| 236 | 134 | } | |
| 237 | #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] | ||
| 238 | assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | ||
| 239 | |||
| 240 | if !reg.rtcen() { | ||
| 241 | Self::unlock_registers(); | ||
| 242 | |||
| 243 | #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] | ||
| 244 | crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); | ||
| 245 | #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||
| 246 | let cr = crate::pac::RCC.bdcr(); | ||
| 247 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | ||
| 248 | let cr = crate::pac::RCC.csr(); | ||
| 249 | |||
| 250 | cr.modify(|w| { | ||
| 251 | // Reset | ||
| 252 | #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] | ||
| 253 | w.set_bdrst(false); | ||
| 254 | |||
| 255 | w.set_rtcen(true); | ||
| 256 | w.set_rtcsel(reg.rtcsel()); | ||
| 257 | 135 | ||
| 258 | // Restore bcdr | 136 | #[cfg(feature = "low-power")] |
| 259 | #[cfg(any(rtc_v2l4, rtc_v2wb))] | 137 | pub(crate) fn enable_wakeup_line(&self) { |
| 260 | w.set_lscosel(reg.lscosel()); | 138 | use crate::interrupt::typelevel::Interrupt; |
| 261 | #[cfg(any(rtc_v2l4, rtc_v2wb))] | 139 | use crate::pac::EXTI; |
| 262 | w.set_lscoen(reg.lscoen()); | ||
| 263 | 140 | ||
| 264 | w.set_lseon(reg.lseon()); | 141 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); |
| 142 | unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() }; | ||
| 265 | 143 | ||
| 266 | #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] | 144 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); |
| 267 | w.set_lsedrv(reg.lsedrv()); | 145 | EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); |
| 268 | w.set_lsebyp(reg.lsebyp()); | ||
| 269 | }); | ||
| 270 | } | ||
| 271 | } | 146 | } |
| 272 | 147 | ||
| 273 | /// Applies the RTC config | 148 | /// Applies the RTC config |
| 274 | /// It this changes the RTC clock source the time will be reset | 149 | /// It this changes the RTC clock source the time will be reset |
| 275 | pub(super) fn configure(&mut self, rtc_config: RtcConfig) { | 150 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { |
| 276 | self.write(true, |rtc| { | 151 | self.write(true, |rtc| { |
| 277 | rtc.cr().modify(|w| { | 152 | rtc.cr().modify(|w| { |
| 278 | #[cfg(rtc_v2f2)] | 153 | #[cfg(rtc_v2f2)] |
| @@ -284,8 +159,8 @@ impl super::Rtc { | |||
| 284 | }); | 159 | }); |
| 285 | 160 | ||
| 286 | rtc.prer().modify(|w| { | 161 | rtc.prer().modify(|w| { |
| 287 | w.set_prediv_s(rtc_config.sync_prescaler); | 162 | w.set_prediv_s(sync_psc); |
| 288 | w.set_prediv_a(rtc_config.async_prescaler); | 163 | w.set_prediv_a(async_psc); |
| 289 | }); | 164 | }); |
| 290 | }); | 165 | }); |
| 291 | } | 166 | } |
| @@ -390,21 +265,17 @@ impl super::Rtc { | |||
| 390 | impl sealed::Instance for crate::peripherals::RTC { | 265 | impl sealed::Instance for crate::peripherals::RTC { |
| 391 | const BACKUP_REGISTER_COUNT: usize = 20; | 266 | const BACKUP_REGISTER_COUNT: usize = 20; |
| 392 | 267 | ||
| 393 | fn enable_peripheral_clk() { | 268 | #[cfg(all(feature = "low-power", stm32f4))] |
| 394 | #[cfg(any(rtc_v2l4, rtc_v2wb))] | 269 | const EXTI_WAKEUP_LINE: usize = 22; |
| 395 | { | ||
| 396 | // enable peripheral clock for communication | ||
| 397 | crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); | ||
| 398 | 270 | ||
| 399 | // read to allow the pwr clock to enable | 271 | #[cfg(all(feature = "low-power", stm32l0))] |
| 400 | crate::pac::PWR.cr1().read(); | 272 | const EXTI_WAKEUP_LINE: usize = 20; |
| 401 | } | 273 | |
| 402 | #[cfg(any(rtc_v2f2))] | 274 | #[cfg(all(feature = "low-power", stm32f4))] |
| 403 | { | 275 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 404 | crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); | 276 | |
| 405 | crate::pac::PWR.cr().read(); | 277 | #[cfg(all(feature = "low-power", stm32l0))] |
| 406 | } | 278 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; |
| 407 | } | ||
| 408 | 279 | ||
| 409 | fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> { | 280 | fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> { |
| 410 | if register < Self::BACKUP_REGISTER_COUNT { | 281 | if register < Self::BACKUP_REGISTER_COUNT { |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 3297303ee..a6b2655d8 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -1,77 +1,14 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; | 1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; |
| 2 | 2 | ||
| 3 | use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; | 3 | use super::{sealed, RtcCalibrationCyclePeriod}; |
| 4 | use crate::pac::rtc::Rtc; | 4 | use crate::pac::rtc::Rtc; |
| 5 | use crate::peripherals::RTC; | 5 | use crate::peripherals::RTC; |
| 6 | use crate::rtc::sealed::Instance; | 6 | use crate::rtc::sealed::Instance; |
| 7 | 7 | ||
| 8 | impl super::Rtc { | 8 | impl super::Rtc { |
| 9 | fn unlock_registers() { | ||
| 10 | // Unlock the backup domain | ||
| 11 | #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] | ||
| 12 | { | ||
| 13 | if !crate::pac::PWR.cr1().read().dbp() { | ||
| 14 | crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); | ||
| 15 | while !crate::pac::PWR.cr1().read().dbp() {} | ||
| 16 | } | ||
| 17 | } | ||
| 18 | #[cfg(any(rcc_wl5, rcc_wle))] | ||
| 19 | { | ||
| 20 | use crate::pac::pwr::vals::Dbp; | ||
| 21 | |||
| 22 | if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED { | ||
| 23 | crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); | ||
| 24 | while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | #[allow(dead_code)] | ||
| 30 | pub(crate) fn set_clock_source(clock_source: RtcClockSource) { | ||
| 31 | let clock_source = clock_source as u8; | ||
| 32 | #[cfg(not(any(rcc_wl5, rcc_wle)))] | ||
| 33 | let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); | ||
| 34 | |||
| 35 | Self::unlock_registers(); | ||
| 36 | |||
| 37 | crate::pac::RCC.bdcr().modify(|w| { | ||
| 38 | // Select RTC source | ||
| 39 | w.set_rtcsel(clock_source); | ||
| 40 | }); | ||
| 41 | } | ||
| 42 | |||
| 43 | pub(super) fn enable() { | ||
| 44 | let bdcr = crate::pac::RCC.bdcr(); | ||
| 45 | |||
| 46 | let reg = bdcr.read(); | ||
| 47 | assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | ||
| 48 | |||
| 49 | if !reg.rtcen() { | ||
| 50 | Self::unlock_registers(); | ||
| 51 | |||
| 52 | bdcr.modify(|w| w.set_bdrst(true)); | ||
| 53 | |||
| 54 | bdcr.modify(|w| { | ||
| 55 | // Reset | ||
| 56 | w.set_bdrst(false); | ||
| 57 | |||
| 58 | w.set_rtcen(true); | ||
| 59 | w.set_rtcsel(reg.rtcsel()); | ||
| 60 | |||
| 61 | // Restore bcdr | ||
| 62 | w.set_lscosel(reg.lscosel()); | ||
| 63 | w.set_lscoen(reg.lscoen()); | ||
| 64 | |||
| 65 | w.set_lseon(reg.lseon()); | ||
| 66 | w.set_lsedrv(reg.lsedrv()); | ||
| 67 | w.set_lsebyp(reg.lsebyp()); | ||
| 68 | }); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Applies the RTC config | 9 | /// Applies the RTC config |
| 73 | /// It this changes the RTC clock source the time will be reset | 10 | /// It this changes the RTC clock source the time will be reset |
| 74 | pub(super) fn configure(&mut self, rtc_config: RtcConfig) { | 11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { |
| 75 | self.write(true, |rtc| { | 12 | self.write(true, |rtc| { |
| 76 | rtc.cr().modify(|w| { | 13 | rtc.cr().modify(|w| { |
| 77 | w.set_fmt(Fmt::TWENTYFOURHOUR); | 14 | w.set_fmt(Fmt::TWENTYFOURHOUR); |
| @@ -80,8 +17,8 @@ impl super::Rtc { | |||
| 80 | }); | 17 | }); |
| 81 | 18 | ||
| 82 | rtc.prer().modify(|w| { | 19 | rtc.prer().modify(|w| { |
| 83 | w.set_prediv_s(rtc_config.sync_prescaler); | 20 | w.set_prediv_s(sync_psc); |
| 84 | w.set_prediv_a(rtc_config.async_prescaler); | 21 | w.set_prediv_a(async_psc); |
| 85 | }); | 22 | }); |
| 86 | 23 | ||
| 87 | // TODO: configuration for output pins | 24 | // TODO: configuration for output pins |
