Skip to content

Commit 9516abc

Browse files
authored
Merge pull request #746 from X-yl/rtc
RTC: Use RTCCLK for Wakeup timer when period is <32s
2 parents b25aae5 + 8a72982 commit 9516abc

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
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: 60 additions & 15 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::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)
@@ -525,28 +541,57 @@ impl<CS> Rtc<CS> {
525541
)
526542
}
527543

528-
/// Configures the wakeup timer to trigger periodically every `interval` seconds
544+
/// Configures the wakeup timer to trigger periodically every `interval` duration
529545
///
530546
/// # Panics
531547
///
532-
/// Panics if interval is greater than 2¹⁷-1.
533-
pub fn enable_wakeup(&mut self, interval: fugit::SecsDurationU32) {
534-
let interval = interval.ticks();
548+
/// Panics if interval is greater than 2¹⁷-1 seconds.
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+
use crate::pac::rtc::cr::WUCKSEL_A;
556+
if interval < fugit::MicrosDurationU64::secs(32) {
557+
// Use RTCCLK as the wakeup timer clock source
558+
let frequency: fugit::Hertz<u64> = (CS::frequency() / 2).into();
559+
let freq_duration: fugit::MicrosDurationU64 = frequency.into_duration();
560+
let ticks_per_interval = interval / freq_duration;
561+
562+
let mut prescaler = 0;
563+
while ticks_per_interval >> prescaler > 1 << 16 {
564+
prescaler += 1;
565+
}
566+
567+
let wucksel = match prescaler {
568+
0 => WUCKSEL_A::Div2,
569+
1 => WUCKSEL_A::Div4,
570+
2 => WUCKSEL_A::Div8,
571+
3 => WUCKSEL_A::Div16,
572+
_ => unreachable!("Longer durations should use ck_spre"),
573+
};
574+
575+
let interval = u16::try_from((ticks_per_interval >> prescaler) - 1).unwrap();
576+
577+
regs.cr.modify(|_, w| w.wucksel().variant(wucksel));
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
584+
.modify(|_, w| w.wucksel().variant(WUCKSEL_A::ClockSpareWithOffset));
585+
let interval = u16::try_from(interval - (1 << 16) - 1)
586+
.expect("Interval was too large for wakeup timer");
587+
regs.wutr.write(|w| w.wut().bits(interval));
588+
} else {
589+
regs.cr
590+
.modify(|_, w| w.wucksel().variant(WUCKSEL_A::ClockSpare));
591+
let interval = u16::try_from(interval - 1)
592+
.expect("Interval was too large for wakeup timer");
593+
regs.wutr.write(|w| w.wut().bits(interval));
594+
}
550595
}
551596

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

0 commit comments

Comments
 (0)