aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/rtc/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/rtc/mod.rs')
-rw-r--r--embassy-stm32/src/rtc/mod.rs215
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
2mod datetime; 2mod datetime;
3 3
4#[cfg(feature = "low-power")]
5use core::cell::Cell;
6
7#[cfg(feature = "low-power")]
8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9#[cfg(feature = "low-power")]
10use embassy_sync::blocking_mutex::Mutex;
11
4pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; 12pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
13pub use crate::rcc::RtcClockSource;
14use 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")]
34pub struct Rtc { 44/// Represents an instant in time that can be substracted to compute a duration
35 rtc_config: RtcConfig, 45struct 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)] 51impl defmt::Format for RtcInstant {
40pub 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")]
63impl 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
85pub struct RtcTimeProvider {
86 _private: (),
87}
88
89impl 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
115pub 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)]
52pub struct RtcConfig { 124pub 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
65impl Default for RtcConfig { 131impl 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
76impl 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
107impl Rtc { 156impl 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