aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/rtc')
-rw-r--r--embassy-stm32/src/rtc/datetime.rs2
-rw-r--r--embassy-stm32/src/rtc/mod.rs215
-rw-r--r--embassy-stm32/src/rtc/v2.rs281
-rw-r--r--embassy-stm32/src/rtc/v3.rs71
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")]
90impl From<chrono::Weekday> for DayOfWeek { 90impl 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
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
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 @@
1use stm32_metapac::rtc::vals::{Init, Osel, Pol}; 1use stm32_metapac::rtc::vals::{Init, Osel, Pol};
2 2
3use super::{sealed, RtcClockSource, RtcConfig}; 3use super::sealed;
4use crate::pac::rtc::Rtc; 4use crate::pac::rtc::Rtc;
5use crate::peripherals::RTC; 5use crate::peripherals::RTC;
6use crate::rtc::sealed::Instance; 6use crate::rtc::sealed::Instance;
7 7
8#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
9pub struct RtcInstant {
10 ssr: u16,
11 st: u8,
12}
13
14#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
15impl 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)))]
41impl 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)]
67pub(crate) enum WakeupPrescaler { 11pub(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))]
75impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { 19impl 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))]
89impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { 33impl 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
103impl 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)]
115impl WakeupPrescaler { 48impl 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
130impl super::Rtc { 63impl 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 {
390impl sealed::Instance for crate::peripherals::RTC { 265impl 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 @@
1use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; 1use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
2 2
3use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; 3use super::{sealed, RtcCalibrationCyclePeriod};
4use crate::pac::rtc::Rtc; 4use crate::pac::rtc::Rtc;
5use crate::peripherals::RTC; 5use crate::peripherals::RTC;
6use crate::rtc::sealed::Instance; 6use crate::rtc::sealed::Instance;
7 7
8impl super::Rtc { 8impl 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