diff options
Diffstat (limited to 'embassy-stm32/src/rtc/mod.rs')
| -rw-r--r-- | embassy-stm32/src/rtc/mod.rs | 215 |
1 files changed, 146 insertions, 69 deletions
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 |
