Skip to content

Commit 3fc796e

Browse files
authored
rtc: add setup_wakeup_timer
1 parent b0072df commit 3fc796e

File tree

2 files changed

+143
-9
lines changed

2 files changed

+143
-9
lines changed

hal/src/rtc.rs

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Real-time clock.
22
33
use crate::{pac, rcc::lsi_hz};
4-
54
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
6-
7-
use pac::rcc::{
8-
bdcr::RTCSEL_A,
9-
csr::LSIPRE_A::{DIV1, DIV128},
5+
use pac::{
6+
rcc::{
7+
bdcr::RTCSEL_A,
8+
csr::LSIPRE_A::{DIV1, DIV128},
9+
},
10+
rtc::cr::WUCKSEL_A,
1011
};
1112

1213
/// RTC clock selection
@@ -22,6 +23,29 @@ pub enum Clk {
2223
Hse = RTCSEL_A::HSE32 as u8,
2324
}
2425

26+
/// Status (interrupt) masks.
27+
///
28+
/// Used for [`Rtc::clear_status`].
29+
pub mod stat {
30+
/// SSR underflow flag
31+
pub const SSRU: u32 = 1 << 6;
32+
/// Internal timestamp flag
33+
pub const ITS: u32 = 1 << 5;
34+
/// Timestamp overflow flag
35+
pub const TSOV: u32 = 1 << 4;
36+
/// Timestamp flag
37+
pub const TS: u32 = 1 << 3;
38+
/// Wakeup timer flag
39+
pub const WUT: u32 = 1 << 2;
40+
/// Alarm B flag
41+
pub const ALRB: u32 = 1 << 1;
42+
/// Alarm A flag
43+
pub const ALRA: u32 = 1 << 0;
44+
45+
/// All status flags.
46+
pub const ALL: u32 = SSRU | ITS | TSOV | TS | WUT | ALRB | ALRA;
47+
}
48+
2549
/// Real-time clock driver.
2650
#[derive(Debug)]
2751
pub struct Rtc {
@@ -183,6 +207,29 @@ impl Rtc {
183207
}
184208
}
185209

210+
/// Read the RTC status (interrupt) register.
211+
#[inline]
212+
pub fn status() -> pac::rtc::sr::R {
213+
// saftey: atomic read with no side-effects
214+
unsafe { (*pac::RTC::ptr()).sr.read() }
215+
}
216+
217+
/// Read the RTC masked status (interrupt) register.
218+
#[inline]
219+
pub fn masked_status() -> pac::rtc::misr::R {
220+
// saftey: atomic read with no side-effects
221+
unsafe { (*pac::RTC::ptr()).misr.read() }
222+
}
223+
224+
/// Clear status (interrupt) flags.
225+
///
226+
/// Status flag masks can be found in [`stat`].
227+
#[inline]
228+
pub fn clear_status(mask: u32) {
229+
// safety: mask is masked with valid register fields
230+
unsafe { (*pac::RTC::ptr()).scr.write(|w| w.bits(mask & stat::ALL)) }
231+
}
232+
186233
// configure prescaler for a 1Hz clock
187234
//
188235
// RM0453 Rev 2 page 996:
@@ -409,6 +456,64 @@ impl Rtc {
409456
}
410457
}
411458

459+
/// Setup the periodic wakeup timer for `sec + 1` seconds.
460+
///
461+
/// `sec` can only go up to 2<sup>17</sup> (36 hours), values greater than
462+
/// this will be set to the maximum.
463+
///
464+
/// # Example
465+
///
466+
/// Setup the wakeup timer to go off in 1 hour, without interrupts.
467+
///
468+
/// ```no_run
469+
/// # use stm32wl_hal::{pac, rtc};
470+
/// # let mut dp: pac::Peripherals = pac::Peripherals::take().unwrap();
471+
/// # let mut rtc = rtc::Rtc::new(dp.RTC, rtc::Clk::Lse, &mut dp.PWR, &mut dp.RCC);
472+
/// rtc.setup_wakeup_timer(3599, false);
473+
/// ```
474+
pub fn setup_wakeup_timer(&mut self, sec: u32, irq_en: bool) {
475+
// The following sequence is required to configure or change the wakeup
476+
// timer auto-reload value (WUT[15:0] in RTC_WUTR):
477+
478+
// 1. Clear WUTE in RTC_CR to disable the wakeup timer.
479+
self.rtc.cr.modify(|_, w| w.wute().clear_bit());
480+
// 2. Poll WUTWF until it is set in RTC_ICSR to make sure the access to
481+
// wakeup auto-reload counter and to WUCKSEL[2:0] bits is allowed.
482+
// This step must be skipped in calendar initialization mode.
483+
// If WUCKSEL[2] = 0:
484+
// WUTWF is set around 1 ck_wut + 1 RTCCLK cycles after WUTE bit is cleared.
485+
// If WUCKSEL[2] = 1:
486+
// WUTWF is set up to 1 ck_apre + 1 RTCCLK cycles after WUTE bit is cleared.
487+
while self.rtc.icsr.read().wutwf().bit_is_clear() {}
488+
// 3. Program the wakeup auto-reload value WUT[15:0], WUTOCLR[15:0]
489+
// and the wakeup clock selection (WUCKSEL[2:0] bits in RTC_CR).
490+
// Set WUTE in RTC_CR to enable the timer again.
491+
// The wakeup timer restarts down-counting.
492+
// If WUCKSEL[2] = 0:
493+
// WUTWF is cleared around 1 ck_wut + 1 RTCCLK cycles after WUTE bit is set.
494+
// If WUCKSEL[2] = 1:
495+
// WUTWF is cleared up to 1 ck_apre + 1 RTCCLK cycles after WUTE bit is set.
496+
let (wucksel, sec): (WUCKSEL_A, u16) = match u16::try_from(sec) {
497+
Ok(sec) => (WUCKSEL_A::CLOCKSPARE, sec),
498+
Err(_) => (
499+
WUCKSEL_A::CLOCKSPAREWITHOFFSET,
500+
u16::try_from(sec - (1 << 16) - 1).unwrap_or(u16::MAX),
501+
),
502+
};
503+
504+
self.rtc
505+
.cr
506+
.modify(|_, w| w.wucksel().variant(wucksel).wutie().bit(irq_en));
507+
self.rtc.wutr.write(|w| w.wut().bits(sec).wutoclr().bits(0));
508+
self.rtc.cr.modify(|_, w| w.wute().set_bit());
509+
}
510+
511+
/// Disable the wakeup timer.
512+
#[inline]
513+
pub fn disable_wakeup_timer(&mut self) {
514+
self.rtc.cr.modify(|_, w| w.wute().clear_bit());
515+
}
516+
412517
/// Disable the RTC write protection.
413518
#[inline]
414519
pub fn disable_write_protect(&mut self) {

testsuite/src/rtc.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const CYC_PER_MICRO: u32 = FREQ / 1000 / 1000;
2020
// WARNING will wrap-around eventually, use this for relative timing only
2121
defmt::timestamp!("{=u32:µs}", DWT::get_cycle_count() / CYC_PER_MICRO);
2222

23-
fn test_set_date_time_with_clk(clk: rtc::Clk) {
23+
fn test_set_date_time_with_clk(clk: rtc::Clk) -> Rtc {
2424
let mut dp: pac::Peripherals = unsafe { pac::Peripherals::steal() };
2525
let mut rtc: Rtc = Rtc::new(dp.RTC, clk, &mut dp.PWR, &mut dp.RCC);
2626

@@ -89,6 +89,8 @@ fn test_set_date_time_with_clk(clk: rtc::Clk) {
8989
after - before
9090
);
9191
defmt::assert!(after > before);
92+
93+
rtc
9294
}
9395

9496
#[defmt_test::tests]
@@ -126,7 +128,7 @@ mod tests {
126128
ta.rcc.bdcr.modify(|_, w| w.lseon().on());
127129
while ta.rcc.bdcr.read().lserdy().is_not_ready() {}
128130

129-
test_set_date_time_with_clk(rtc::Clk::Lse)
131+
test_set_date_time_with_clk(rtc::Clk::Lse);
130132
}
131133

132134
// must come directly after set_date_time_lse
@@ -152,7 +154,7 @@ mod tests {
152154
fn set_date_time_lsi(ta: &mut TestArgs) {
153155
unsafe { pulse_reset_backup_domain(&mut ta.rcc, &mut ta.pwr) };
154156
unsafe { setup_lsi(&mut ta.rcc, LsiPre::DIV1) };
155-
test_set_date_time_with_clk(rtc::Clk::Lsi)
157+
test_set_date_time_with_clk(rtc::Clk::Lsi);
156158
}
157159

158160
#[test]
@@ -162,6 +164,33 @@ mod tests {
162164
.cr
163165
.modify(|_, w| w.hseon().enabled().hsebyppwr().vddtcxo());
164166
while ta.rcc.cr.read().hserdy().is_not_ready() {}
165-
test_set_date_time_with_clk(rtc::Clk::Hse)
167+
test_set_date_time_with_clk(rtc::Clk::Hse);
168+
}
169+
170+
#[test]
171+
fn wakeup_timer(ta: &mut TestArgs) {
172+
unsafe { pulse_reset_backup_domain(&mut ta.rcc, &mut ta.pwr) };
173+
ta.pwr.cr1.modify(|_, w| w.dbp().enabled());
174+
ta.rcc.bdcr.modify(|_, w| w.lseon().on());
175+
while ta.rcc.bdcr.read().lserdy().is_not_ready() {}
176+
let mut rtc: Rtc = test_set_date_time_with_clk(rtc::Clk::Lse);
177+
178+
rtc.setup_wakeup_timer(0, false);
179+
180+
let start: u32 = DWT::get_cycle_count();
181+
loop {
182+
let elapsed_micros: u32 = DWT::get_cycle_count().wrapping_sub(start) / CYC_PER_MICRO;
183+
if Rtc::status().wutf().bit_is_set() {
184+
defmt::info!("elapsed: {=u32:µs}", elapsed_micros);
185+
// 100ms tolerance
186+
defmt::assert!(elapsed_micros > 900_000 && elapsed_micros < 1_100_000);
187+
return;
188+
} else if elapsed_micros > 3 * 1000 * 1000 {
189+
defmt::panic!(
190+
"this is taking too long, elapsed: {=u32:µs}",
191+
elapsed_micros
192+
);
193+
}
194+
}
166195
}
167196
}

0 commit comments

Comments
 (0)