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/low_power.rs82
-rw-r--r--embassy-stm32/src/rtc/mod.rs121
-rw-r--r--embassy-stm32/src/rtc/v2.rs2
-rw-r--r--embassy-stm32/src/rtc/v3.rs8
4 files changed, 125 insertions, 88 deletions
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
index a81ac6746..f049d6b12 100644
--- a/embassy-stm32/src/rtc/low_power.rs
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -1,10 +1,11 @@
1#[cfg(feature = "time")] 1#[cfg(feature = "time")]
2use embassy_time::{Duration, TICK_HZ}; 2use embassy_time::{Duration, TICK_HZ};
3 3
4use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; 4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte};
5use crate::interrupt::typelevel::Interrupt; 5use crate::interrupt::typelevel::Interrupt;
6use crate::pac::rtc::vals::Wucksel;
6use crate::peripherals::RTC; 7use crate::peripherals::RTC;
7use crate::rtc::SealedInstance; 8use crate::rtc::{RtcTimeProvider, SealedInstance};
8 9
9/// Represents an instant in time that can be substracted to compute a duration 10/// Represents an instant in time that can be substracted to compute a duration
10pub(super) struct RtcInstant { 11pub(super) struct RtcInstant {
@@ -58,66 +59,22 @@ impl core::ops::Sub for RtcInstant {
58 } 59 }
59} 60}
60 61
61#[repr(u8)] 62fn wucksel_compute_min(val: u32) -> (Wucksel, u32) {
62#[derive(Clone, Copy, Debug)] 63 *[
63pub(crate) enum WakeupPrescaler { 64 (Wucksel::DIV2, 2),
64 Div2 = 2, 65 (Wucksel::DIV4, 4),
65 Div4 = 4, 66 (Wucksel::DIV8, 8),
66 Div8 = 8, 67 (Wucksel::DIV16, 16),
67 Div16 = 16, 68 ]
68} 69 .iter()
69 70 .find(|(_, psc)| *psc as u32 > val)
70#[cfg(any( 71 .unwrap_or(&(Wucksel::DIV16, 16))
71 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba
72))]
73impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
74 fn from(val: WakeupPrescaler) -> Self {
75 use crate::pac::rtc::vals::Wucksel;
76
77 match val {
78 WakeupPrescaler::Div2 => Wucksel::DIV2,
79 WakeupPrescaler::Div4 => Wucksel::DIV4,
80 WakeupPrescaler::Div8 => Wucksel::DIV8,
81 WakeupPrescaler::Div16 => Wucksel::DIV16,
82 }
83 }
84}
85
86#[cfg(any(
87 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba
88))]
89impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
90 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
91 use crate::pac::rtc::vals::Wucksel;
92
93 match val {
94 Wucksel::DIV2 => WakeupPrescaler::Div2,
95 Wucksel::DIV4 => WakeupPrescaler::Div4,
96 Wucksel::DIV8 => WakeupPrescaler::Div8,
97 Wucksel::DIV16 => WakeupPrescaler::Div16,
98 _ => unreachable!(),
99 }
100 }
101}
102
103impl WakeupPrescaler {
104 pub fn compute_min(val: u32) -> Self {
105 *[
106 WakeupPrescaler::Div2,
107 WakeupPrescaler::Div4,
108 WakeupPrescaler::Div8,
109 WakeupPrescaler::Div16,
110 ]
111 .iter()
112 .find(|psc| **psc as u32 > val)
113 .unwrap_or(&WakeupPrescaler::Div16)
114 }
115} 72}
116 73
117impl Rtc { 74impl Rtc {
118 /// Return the current instant. 75 /// Return the current instant.
119 fn instant(&self) -> Result<RtcInstant, RtcError> { 76 fn instant(&self) -> Result<RtcInstant, RtcError> {
120 self.time_provider().read(|_, tr, ss| { 77 RtcTimeProvider::new().read(|_, tr, ss| {
121 let second = bcd2_to_byte((tr.st(), tr.su())); 78 let second = bcd2_to_byte((tr.st(), tr.su()));
122 79
123 RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) 80 RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime)
@@ -127,7 +84,7 @@ impl Rtc {
127 /// start the wakeup alarm and with a duration that is as close to but less than 84 /// start the wakeup alarm and with a duration that is as close to but less than
128 /// the requested duration, and record the instant the wakeup alarm was started 85 /// the requested duration, and record the instant the wakeup alarm was started
129 pub(crate) fn start_wakeup_alarm( 86 pub(crate) fn start_wakeup_alarm(
130 &self, 87 &mut self,
131 requested_duration: embassy_time::Duration, 88 requested_duration: embassy_time::Duration,
132 cs: critical_section::CriticalSection, 89 cs: critical_section::CriticalSection,
133 ) { 90 ) {
@@ -138,7 +95,7 @@ impl Rtc {
138 let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); 95 let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
139 let rtc_hz = Self::frequency().0 as u64; 96 let rtc_hz = Self::frequency().0 as u64;
140 let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; 97 let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
141 let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); 98 let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32);
142 99
143 // adjust the rtc ticks to the prescaler and subtract one rtc tick 100 // adjust the rtc ticks to the prescaler and subtract one rtc tick
144 let rtc_ticks = rtc_ticks / prescaler as u64; 101 let rtc_ticks = rtc_ticks / prescaler as u64;
@@ -159,7 +116,7 @@ impl Rtc {
159 while !regs.icsr().read().wutwf() {} 116 while !regs.icsr().read().wutwf() {}
160 } 117 }
161 118
162 regs.cr().modify(|w| w.set_wucksel(prescaler.into())); 119 regs.cr().modify(|w| w.set_wucksel(wucksel));
163 regs.wutr().write(|w| w.set_wut(rtc_ticks)); 120 regs.wutr().write(|w| w.set_wut(rtc_ticks));
164 regs.cr().modify(|w| w.set_wute(true)); 121 regs.cr().modify(|w| w.set_wute(true));
165 regs.cr().modify(|w| w.set_wutie(true)); 122 regs.cr().modify(|w| w.set_wutie(true));
@@ -179,7 +136,10 @@ impl Rtc {
179 136
180 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` 137 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
181 /// was called, otherwise none 138 /// was called, otherwise none
182 pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { 139 pub(crate) fn stop_wakeup_alarm(
140 &mut self,
141 cs: critical_section::CriticalSection,
142 ) -> Option<embassy_time::Duration> {
183 let instant = self.instant().unwrap(); 143 let instant = self.instant().unwrap();
184 if RTC::regs().cr().read().wute() { 144 if RTC::regs().cr().read().wute() {
185 trace!("rtc: stop wakeup alarm at {}", instant); 145 trace!("rtc: stop wakeup alarm at {}", instant);
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 92dec0960..e88bd7ab2 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -5,15 +5,19 @@ mod datetime;
5mod low_power; 5mod low_power;
6 6
7#[cfg(feature = "low-power")] 7#[cfg(feature = "low-power")]
8use core::cell::Cell; 8use core::cell::{Cell, RefCell, RefMut};
9#[cfg(feature = "low-power")]
10use core::ops;
9 11
10#[cfg(feature = "low-power")] 12#[cfg(feature = "low-power")]
11use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 13use critical_section::CriticalSection;
12#[cfg(feature = "low-power")] 14#[cfg(feature = "low-power")]
13use embassy_sync::blocking_mutex::Mutex; 15use embassy_sync::blocking_mutex::Mutex;
16#[cfg(feature = "low-power")]
17use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
14 18
15use self::datetime::{day_of_week_from_u8, day_of_week_to_u8};
16pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; 19pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
20use self::datetime::{day_of_week_from_u8, day_of_week_to_u8};
17use crate::pac::rtc::regs::{Dr, Tr}; 21use crate::pac::rtc::regs::{Dr, Tr};
18use crate::time::Hertz; 22use crate::time::Hertz;
19 23
@@ -25,8 +29,8 @@ mod _version;
25#[allow(unused_imports)] 29#[allow(unused_imports)]
26pub use _version::*; 30pub use _version::*;
27 31
28use crate::peripherals::RTC;
29use crate::Peri; 32use crate::Peri;
33use crate::peripherals::RTC;
30 34
31/// Errors that can occur on methods on [RtcClock] 35/// Errors that can occur on methods on [RtcClock]
32#[non_exhaustive] 36#[non_exhaustive]
@@ -44,11 +48,17 @@ pub enum RtcError {
44} 48}
45 49
46/// Provides immutable access to the current time of the RTC. 50/// Provides immutable access to the current time of the RTC.
51#[derive(Clone)]
47pub struct RtcTimeProvider { 52pub struct RtcTimeProvider {
48 _private: (), 53 _private: (),
49} 54}
50 55
51impl RtcTimeProvider { 56impl RtcTimeProvider {
57 /// Create a new RTC time provider instance.
58 pub(self) const fn new() -> Self {
59 Self { _private: () }
60 }
61
52 /// Return the current datetime. 62 /// Return the current datetime.
53 /// 63 ///
54 /// # Errors 64 /// # Errors
@@ -106,6 +116,50 @@ impl RtcTimeProvider {
106 } 116 }
107} 117}
108 118
119#[cfg(feature = "low-power")]
120/// Contains an RTC driver.
121pub struct RtcContainer {
122 pub(self) mutex: &'static Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,
123}
124
125#[cfg(feature = "low-power")]
126impl RtcContainer {
127 pub(self) const fn new() -> Self {
128 Self {
129 mutex: &crate::time_driver::get_driver().rtc,
130 }
131 }
132
133 /// Acquire an RTC borrow.
134 pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> {
135 RtcBorrow {
136 ref_mut: self.mutex.borrow(cs).borrow_mut(),
137 }
138 }
139}
140
141#[cfg(feature = "low-power")]
142/// Contains an RTC borrow.
143pub struct RtcBorrow<'a> {
144 pub(self) ref_mut: RefMut<'a, Option<Rtc>>,
145}
146
147#[cfg(feature = "low-power")]
148impl<'a> ops::Deref for RtcBorrow<'a> {
149 type Target = Rtc;
150
151 fn deref(&self) -> &Self::Target {
152 self.ref_mut.as_ref().unwrap()
153 }
154}
155
156#[cfg(feature = "low-power")]
157impl<'a> ops::DerefMut for RtcBorrow<'a> {
158 fn deref_mut(&mut self) -> &mut Self::Target {
159 self.ref_mut.as_mut().unwrap()
160 }
161}
162
109/// RTC driver. 163/// RTC driver.
110pub struct Rtc { 164pub struct Rtc {
111 #[cfg(feature = "low-power")] 165 #[cfg(feature = "low-power")]
@@ -121,13 +175,21 @@ pub struct RtcConfig {
121 /// 175 ///
122 /// A high counter frequency may impact stop power consumption 176 /// A high counter frequency may impact stop power consumption
123 pub frequency: Hertz, 177 pub frequency: Hertz,
178
179 #[cfg(feature = "_allow-disable-rtc")]
180 /// Allow disabling the rtc, even when stop is configured
181 pub _disable_rtc: bool,
124} 182}
125 183
126impl Default for RtcConfig { 184impl Default for RtcConfig {
127 /// LSI with prescalers assuming 32.768 kHz. 185 /// LSI with prescalers assuming 32.768 kHz.
128 /// Raw sub-seconds in 1/256. 186 /// Raw sub-seconds in 1/256.
129 fn default() -> Self { 187 fn default() -> Self {
130 RtcConfig { frequency: Hertz(256) } 188 RtcConfig {
189 frequency: Hertz(256),
190 #[cfg(feature = "_allow-disable-rtc")]
191 _disable_rtc: false,
192 }
131 } 193 }
132} 194}
133 195
@@ -145,8 +207,19 @@ pub enum RtcCalibrationCyclePeriod {
145} 207}
146 208
147impl Rtc { 209impl Rtc {
210 #[cfg(not(feature = "low-power"))]
148 /// Create a new RTC instance. 211 /// Create a new RTC instance.
149 pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { 212 pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) {
213 (Self::new_inner(rtc_config), RtcTimeProvider::new())
214 }
215
216 #[cfg(feature = "low-power")]
217 /// Create a new RTC instance.
218 pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) {
219 (RtcContainer::new(), RtcTimeProvider::new())
220 }
221
222 pub(self) fn new_inner(rtc_config: RtcConfig) -> Self {
150 #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] 223 #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
151 crate::rcc::enable_and_reset::<RTC>(); 224 crate::rcc::enable_and_reset::<RTC>();
152 225
@@ -165,10 +238,13 @@ impl Rtc {
165 // Wait for the clock to update after initialization 238 // Wait for the clock to update after initialization
166 #[cfg(not(rtc_v2_f2))] 239 #[cfg(not(rtc_v2_f2))]
167 { 240 {
168 let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); 241 let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap();
169 while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} 242 while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {}
170 } 243 }
171 244
245 #[cfg(feature = "low-power")]
246 this.enable_wakeup_line();
247
172 this 248 this
173 } 249 }
174 250
@@ -177,11 +253,6 @@ impl Rtc {
177 freqs.rtc.to_hertz().unwrap() 253 freqs.rtc.to_hertz().unwrap()
178 } 254 }
179 255
180 /// Acquire a [`RtcTimeProvider`] instance.
181 pub const fn time_provider(&self) -> RtcTimeProvider {
182 RtcTimeProvider { _private: () }
183 }
184
185 /// Set the datetime to a new value. 256 /// Set the datetime to a new value.
186 /// 257 ///
187 /// # Errors 258 /// # Errors
@@ -225,15 +296,6 @@ impl Rtc {
225 Ok(()) 296 Ok(())
226 } 297 }
227 298
228 /// Return the current datetime.
229 ///
230 /// # Errors
231 ///
232 /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
233 pub fn now(&self) -> Result<DateTime, RtcError> {
234 self.time_provider().now()
235 }
236
237 /// Check if daylight savings time is active. 299 /// Check if daylight savings time is active.
238 pub fn get_daylight_savings(&self) -> bool { 300 pub fn get_daylight_savings(&self) -> bool {
239 let cr = RTC::regs().cr().read(); 301 let cr = RTC::regs().cr().read();
@@ -315,3 +377,18 @@ trait SealedInstance {
315 377
316 // fn apply_config(&mut self, rtc_config: RtcConfig); 378 // fn apply_config(&mut self, rtc_config: RtcConfig);
317} 379}
380
381#[cfg(feature = "low-power")]
382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) {
383 use crate::time_driver::get_driver;
384
385 #[cfg(feature = "_allow-disable-rtc")]
386 if config._disable_rtc {
387 return;
388 }
389
390 get_driver().set_rtc(cs, Rtc::new_inner(config));
391 get_driver().set_min_stop_pause(cs, min_stop_pause);
392
393 trace!("low power: stop with rtc configured");
394}
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index 23f6ccb0c..8ac022536 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -93,7 +93,7 @@ impl super::Rtc {
93 }) 93 })
94 } 94 }
95 95
96 pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R 96 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
97 where 97 where
98 F: FnOnce(crate::pac::rtc::Rtc) -> R, 98 F: FnOnce(crate::pac::rtc::Rtc) -> R,
99 { 99 {
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index d0b52049e..f7ebea73e 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -1,4 +1,4 @@
1use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; 1use stm32_metapac::rtc::vals::{Calp, Calw8, Calw16, Fmt, Key, Osel, Pol, TampalrmType};
2 2
3use super::RtcCalibrationCyclePeriod; 3use super::RtcCalibrationCyclePeriod;
4use crate::pac::rtc::Rtc; 4use crate::pac::rtc::Rtc;
@@ -95,7 +95,7 @@ impl super::Rtc {
95 }) 95 })
96 } 96 }
97 97
98 pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R 98 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
99 where 99 where
100 F: FnOnce(crate::pac::rtc::Rtc) -> R, 100 F: FnOnce(crate::pac::rtc::Rtc) -> R,
101 { 101 {
@@ -131,7 +131,7 @@ impl SealedInstance for crate::peripherals::RTC {
131 131
132 #[cfg(feature = "low-power")] 132 #[cfg(feature = "low-power")]
133 cfg_if::cfg_if!( 133 cfg_if::cfg_if!(
134 if #[cfg(stm32g4)] { 134 if #[cfg(any(stm32g4, stm32wlex))] {
135 const EXTI_WAKEUP_LINE: usize = 20; 135 const EXTI_WAKEUP_LINE: usize = 20;
136 } else if #[cfg(stm32g0)] { 136 } else if #[cfg(stm32g0)] {
137 const EXTI_WAKEUP_LINE: usize = 19; 137 const EXTI_WAKEUP_LINE: usize = 19;
@@ -142,7 +142,7 @@ impl SealedInstance for crate::peripherals::RTC {
142 142
143 #[cfg(feature = "low-power")] 143 #[cfg(feature = "low-power")]
144 cfg_if::cfg_if!( 144 cfg_if::cfg_if!(
145 if #[cfg(stm32g4)] { 145 if #[cfg(any(stm32g4, stm32wlex))] {
146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; 146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
147 } else if #[cfg(any(stm32g0, stm32u0))] { 147 } else if #[cfg(any(stm32g0, stm32u0))] {
148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; 148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;