Skip to content

Commit c0e459a

Browse files
committed
RTC: Use RTCCLK for Wakeup timer when period is <32s
This allows a minimum time of 122µs and a maximum time of 32s, with a resolution of 61µs
1 parent 403fddb commit c0e459a

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
- add trait bound `RegisterBlockImpl` to type `RegisterBlock` associated with `serial::Instance` [#732]
1111
- remove unneeded trait bound for methods that take in a `serial::Instance` and use the associated `RegisterBlock`
1212
- bump `sdio-host` to 0.9.0, refactor SDIO initialization [#734]
13+
- use RTCCLK for RTC wakeup timer for short durations [#746]
1314

1415
### Fixed
1516

examples/rtc_alarm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ fn main() -> ! {
4242
// Set alarm A for 1 minute
4343
rtc.set_alarm(Alarm::AlarmA, today, time!(21:58:32))
4444
.unwrap();
45-
rtc.enable_wakeup(8.secs());
45+
rtc.enable_wakeup(8.secs().into());
4646
rtc.listen(&mut p.EXTI, Event::AlarmA);
4747
rtc.listen(&mut p.EXTI, Event::Wakeup);
4848

src/rtc.rs

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use crate::bb;
66
use crate::pac::rtc::{dr, tr, DR, TR};
77
use crate::pac::{self, rcc::RegisterBlock, PWR, RCC, RTC};
88
use crate::rcc::Enable;
9-
use core::convert::{TryFrom, TryInto};
109
use core::fmt;
1110
use core::marker::PhantomData;
11+
use fugit::{Duration, ExtU32, Rate, RateExtU32};
1212
use time::{Date, PrimitiveDateTime, Time, Weekday};
1313

1414
/// Invalid input error
@@ -67,8 +67,24 @@ pub struct Lse;
6767
/// RTC clock source LSI oscillator clock (type state)
6868
pub struct Lsi;
6969

70+
pub trait FrequencySource {
71+
fn frequency() -> fugit::Hertz<u32>;
72+
}
73+
74+
impl FrequencySource for Lse {
75+
fn frequency() -> fugit::Hertz<u32> {
76+
32_768u32.Hz()
77+
}
78+
}
79+
80+
impl FrequencySource for Lsi {
81+
fn frequency() -> fugit::Hertz<u32> {
82+
32_000u32.Hz()
83+
}
84+
}
85+
7086
/// Real Time Clock peripheral
71-
pub struct Rtc<CS = Lse> {
87+
pub struct Rtc<CS: FrequencySource = Lse> {
7288
/// RTC Peripheral register
7389
pub regs: RTC,
7490
_clock_source: PhantomData<CS>,
@@ -239,7 +255,7 @@ impl Rtc<Lsi> {
239255
}
240256
}
241257

242-
impl<CS> Rtc<CS> {
258+
impl<CS: FrequencySource> Rtc<CS> {
243259
fn unlock(&mut self, rcc: &RegisterBlock, pwr: &mut PWR) {
244260
// Enable the backup interface
245261
// Set APB1 - Bit 28 (PWREN)
@@ -530,23 +546,50 @@ impl<CS> Rtc<CS> {
530546
/// # Panics
531547
///
532548
/// Panics if interval is greater than 2¹⁷-1.
533-
pub fn enable_wakeup(&mut self, interval: fugit::SecsDurationU32) {
534-
let interval = interval.ticks();
549+
pub fn enable_wakeup(&mut self, interval: fugit::MicrosDurationU64) {
535550
self.modify(false, |regs| {
536551
regs.cr.modify(|_, w| w.wute().clear_bit());
537552
regs.isr.modify(|_, w| w.wutf().clear_bit());
538553
while regs.isr.read().wutwf().bit_is_clear() {}
539554

540-
if interval > 1 << 16 {
541-
regs.cr.modify(|_, w| unsafe { w.wucksel().bits(0b110) });
542-
let interval = u16::try_from(interval - (1 << 16) - 1)
543-
.expect("Interval was too large for wakeup timer");
555+
if interval < 32u32.secs::<1, 1_000_000>() {
556+
// Use RTCCLK as the wakeup timer clock source
557+
let frequency: Rate<u64, 1, 1> = (CS::frequency() / 2).into();
558+
let freq_duration: Duration<u64, 1, 1000000> = frequency.into_duration();
559+
let ticks_per_interval = interval / freq_duration;
560+
561+
let mut prescaler = 0;
562+
while ticks_per_interval >> prescaler > 1 << 16 {
563+
prescaler += 1;
564+
}
565+
566+
let prescaler_bits = match prescaler {
567+
0 => 0b11, // RTCCLK/2
568+
1 => 0b10, // RTCCLK/4
569+
2 => 0b01, // RTCCLK/8
570+
3 => 0b00, // RTCCLK/16
571+
_ => unreachable!("Longer durations should use ck_spre"),
572+
};
573+
574+
let interval = u16::try_from((ticks_per_interval >> prescaler) - 1).unwrap();
575+
576+
regs.cr
577+
.modify(|_, w| unsafe { w.wucksel().bits(prescaler_bits) });
544578
regs.wutr.write(|w| w.wut().bits(interval));
545579
} else {
546-
regs.cr.modify(|_, w| unsafe { w.wucksel().bits(0b100) });
547-
let interval =
548-
u16::try_from(interval - 1).expect("Interval was too large for wakeup timer");
549-
regs.wutr.write(|w| w.wut().bits(interval));
580+
// Use ck_spre (1Hz) as the wakeup timer clock source
581+
let interval = interval.to_secs();
582+
if interval > 1 << 16 {
583+
regs.cr.modify(|_, w| unsafe { w.wucksel().bits(0b110) });
584+
let interval = u16::try_from(interval - (1 << 16) - 1)
585+
.expect("Interval was too large for wakeup timer");
586+
regs.wutr.write(|w| w.wut().bits(interval));
587+
} else {
588+
regs.cr.modify(|_, w| unsafe { w.wucksel().bits(0b100) });
589+
let interval = u16::try_from(interval - 1)
590+
.expect("Interval was too large for wakeup timer");
591+
regs.wutr.write(|w| w.wut().bits(interval));
592+
}
550593
}
551594

552595
regs.cr.modify(|_, w| w.wute().set_bit());

0 commit comments

Comments
 (0)