aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-08-22 21:50:53 +0000
committerGitHub <[email protected]>2023-08-22 21:50:53 +0000
commit7bff2ebab3b36cc922505e9db961840109c509ed (patch)
treed80a7b263fa8e500dec602d8be6d4224414b4c5a
parentb3212ae383e2cb4f167cbe9361b64bf83bf89049 (diff)
parent048bdf6968773a642c60ddcac46958d3a54af7c3 (diff)
Merge pull request #1766 from xoviat/rtc-w
stm32/rtc: add start/stop wakeup
-rw-r--r--embassy-stm32/src/rcc/f4.rs7
-rw-r--r--embassy-stm32/src/rcc/mod.rs2
-rw-r--r--embassy-stm32/src/rtc/mod.rs1
-rw-r--r--embassy-stm32/src/rtc/v2.rs218
4 files changed, 227 insertions, 1 deletions
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 2ae0d15cb..ee9cb2897 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -473,6 +473,11 @@ pub(crate) unsafe fn init(config: Config) {
473 Rtc::set_clock_source(clock_source); 473 Rtc::set_clock_source(clock_source);
474 }); 474 });
475 475
476 let rtc = match config.rtc {
477 Some(RtcClockSource::LSI) => Some(LSI_FREQ),
478 _ => None,
479 };
480
476 set_freqs(Clocks { 481 set_freqs(Clocks {
477 sys: Hertz(sysclk), 482 sys: Hertz(sysclk),
478 apb1: Hertz(pclk1), 483 apb1: Hertz(pclk1),
@@ -492,6 +497,8 @@ pub(crate) unsafe fn init(config: Config) {
492 497
493 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] 498 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
494 pllsai: None, 499 pllsai: None,
500
501 rtc: rtc,
495 }); 502 });
496} 503}
497 504
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index e5dd0019e..ac9ae9c6a 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -74,7 +74,7 @@ pub struct Clocks {
74 #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] 74 #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
75 pub adc: Option<Hertz>, 75 pub adc: Option<Hertz>,
76 76
77 #[cfg(rcc_wb)] 77 #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
78 /// Set only if the lsi or lse is configured 78 /// Set only if the lsi or lse is configured
79 pub rtc: Option<Hertz>, 79 pub rtc: Option<Hertz>,
80} 80}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 945bfafd6..a6102077a 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -13,6 +13,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
13)] 13)]
14#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] 14#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
15mod _version; 15mod _version;
16#[allow(unused_imports)]
16pub use _version::*; 17pub use _version::*;
17use embassy_hal_internal::Peripheral; 18use embassy_hal_internal::Peripheral;
18 19
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index 5b8960696..53d0161d6 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -5,6 +5,138 @@ use 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 trace!("self st: {}", self.st);
48 trace!("other st: {}", rhs.st);
49
50 trace!("self ssr: {}", self.ssr);
51 trace!("other ssr: {}", rhs.ssr);
52
53 let st = if self.st < rhs.st { self.st + 60 } else { self.st };
54
55 trace!("self st: {}", st);
56
57 let self_ticks = st as u32 * 256 + (255 - self.ssr as u32);
58 let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32);
59 let rtc_ticks = self_ticks - other_ticks;
60
61 trace!("self ticks: {}", self_ticks);
62 trace!("other ticks: {}", other_ticks);
63 trace!("rtc ticks: {}", rtc_ticks);
64
65 // TODO: read prescaler
66
67 Duration::from_ticks(
68 ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32)))
69 * TICK_HZ as u32) as u32
70 / 256u32) as u64,
71 )
72 }
73}
74
75#[allow(dead_code)]
76#[derive(Clone, Copy)]
77pub(crate) enum WakeupPrescaler {
78 Div2,
79 Div4,
80 Div8,
81 Div16,
82}
83
84#[cfg(any(stm32wb, stm32f4))]
85impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
86 fn from(val: WakeupPrescaler) -> Self {
87 use crate::pac::rtc::vals::Wucksel;
88
89 match val {
90 WakeupPrescaler::Div2 => Wucksel::DIV2,
91 WakeupPrescaler::Div4 => Wucksel::DIV4,
92 WakeupPrescaler::Div8 => Wucksel::DIV8,
93 WakeupPrescaler::Div16 => Wucksel::DIV16,
94 }
95 }
96}
97
98#[cfg(any(stm32wb, stm32f4))]
99impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
100 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
101 use crate::pac::rtc::vals::Wucksel;
102
103 match val {
104 Wucksel::DIV2 => WakeupPrescaler::Div2,
105 Wucksel::DIV4 => WakeupPrescaler::Div4,
106 Wucksel::DIV8 => WakeupPrescaler::Div8,
107 Wucksel::DIV16 => WakeupPrescaler::Div16,
108 _ => unreachable!(),
109 }
110 }
111}
112
113impl From<WakeupPrescaler> for u32 {
114 fn from(val: WakeupPrescaler) -> Self {
115 match val {
116 WakeupPrescaler::Div2 => 2,
117 WakeupPrescaler::Div4 => 4,
118 WakeupPrescaler::Div8 => 8,
119 WakeupPrescaler::Div16 => 16,
120 }
121 }
122}
123
124#[allow(dead_code)]
125impl WakeupPrescaler {
126 pub fn compute_min(val: u32) -> Self {
127 *[
128 WakeupPrescaler::Div2,
129 WakeupPrescaler::Div4,
130 WakeupPrescaler::Div8,
131 WakeupPrescaler::Div16,
132 ]
133 .iter()
134 .skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val)
135 .next()
136 .unwrap_or(&WakeupPrescaler::Div16)
137 }
138}
139
8impl super::Rtc { 140impl super::Rtc {
9 fn unlock_registers() { 141 fn unlock_registers() {
10 #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] 142 #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
@@ -23,6 +155,92 @@ impl super::Rtc {
23 } 155 }
24 156
25 #[allow(dead_code)] 157 #[allow(dead_code)]
158 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
159 /// start the wakeup alarm and return the actual duration of the alarm
160 /// the actual duration will be the closest value possible that is less
161 /// than the requested duration.
162 ///
163 /// note: this api is exposed for testing purposes until low power is implemented.
164 /// it is not intended to be public
165 pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant {
166 use embassy_time::{Duration, TICK_HZ};
167
168 use crate::interrupt::typelevel::Interrupt;
169 use crate::rcc::get_freqs;
170
171 let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
172
173 let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ;
174 let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
175
176 // adjust the rtc ticks to the prescaler
177 let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64);
178 let rtc_ticks = if rtc_ticks >= u16::MAX as u64 {
179 u16::MAX - 1
180 } else {
181 rtc_ticks as u16
182 };
183
184 let duration = Duration::from_ticks(
185 rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
186 );
187
188 trace!("set wakeup timer for {} ms", duration.as_millis());
189
190 RTC::regs().wpr().write(|w| w.set_key(0xca));
191 RTC::regs().wpr().write(|w| w.set_key(0x53));
192
193 RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks));
194
195 RTC::regs().cr().modify(|w| {
196 w.set_wucksel(prescaler.into());
197
198 w.set_wutie(true);
199 w.set_wute(true);
200 });
201
202 if !RTC::regs().cr().read().wute() {
203 trace!("wakeup timer not enabled");
204 } else {
205 trace!("wakeup timer enabled");
206 }
207
208 crate::interrupt::typelevel::RTC_WKUP::unpend();
209 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
210
211 RtcInstant::now()
212 }
213
214 #[allow(dead_code)]
215 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
216 /// stop the wakeup alarm and return the time remaining
217 ///
218 /// note: this api is exposed for testing purposes until low power is implemented.
219 /// it is not intended to be public
220 pub fn stop_wakeup_alarm() -> RtcInstant {
221 use crate::interrupt::typelevel::Interrupt;
222
223 crate::interrupt::typelevel::RTC_WKUP::disable();
224
225 trace!("disable wakeup timer...");
226
227 RTC::regs().cr().modify(|w| {
228 w.set_wute(false);
229 });
230
231 trace!("wait for wakeup timer stop...");
232
233 // Wait for the wakeup timer to stop
234 // while !RTC::regs().isr().read().wutf() {}
235 //
236 // RTC::regs().isr().modify(|w| w.set_wutf(false));
237
238 trace!("wait for wakeup timer stop...done");
239
240 RtcInstant::now()
241 }
242
243 #[allow(dead_code)]
26 pub(crate) fn set_clock_source(clock_source: RtcClockSource) { 244 pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
27 #[cfg(not(rtc_v2wb))] 245 #[cfg(not(rtc_v2wb))]
28 use stm32_metapac::rcc::vals::Rtcsel; 246 use stm32_metapac::rcc::vals::Rtcsel;