From 761b66bba252570fc2f169179d9a518e7fd4ebb2 Mon Sep 17 00:00:00 2001 From: apeng2012 Date: Fri, 19 Nov 2021 13:08:35 +0800 Subject: [PATCH] RTC clock source can be selected --- CHANGELOG.md | 1 + examples/blinky_rtc.rs | 2 +- examples/blinky_rtcalarm_irq.rs | 2 +- examples/rtc.rs | 2 +- src/rtc.rs | 111 ++++++++++++++++++++++++++++++-- 5 files changed, 110 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e34c836..db0b3428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- RTC clock source can be selected. - `rcc::Config` with prescalers for direct setting of clocks without calculating - `From` for `serial::Config` - `From>` for `i2c::Mode` diff --git a/examples/blinky_rtc.rs b/examples/blinky_rtc.rs index ef92ba39..d53c44be 100644 --- a/examples/blinky_rtc.rs +++ b/examples/blinky_rtc.rs @@ -32,7 +32,7 @@ fn main() -> ! { // Enable writes to the backup domain let mut backup_domain = rcc.bkp.constrain(dp.BKP, &mut pwr); // Start the RTC - let mut rtc = Rtc::rtc(dp.RTC, &mut backup_domain); + let mut rtc = Rtc::new(dp.RTC, &mut backup_domain); let mut led_on = false; loop { diff --git a/examples/blinky_rtcalarm_irq.rs b/examples/blinky_rtcalarm_irq.rs index 1e91c746..a5c984b0 100644 --- a/examples/blinky_rtcalarm_irq.rs +++ b/examples/blinky_rtcalarm_irq.rs @@ -96,7 +96,7 @@ fn main() -> ! { // Enable writes to the backup domain let mut backup_domain = rcc.bkp.constrain(dp.BKP, &mut pwr); // Start the RTC - let mut rtc = Rtc::rtc(dp.RTC, &mut backup_domain); + let mut rtc = Rtc::new(dp.RTC, &mut backup_domain); rtc.set_time(0); rtc.set_alarm(TOGGLE_INTERVAL_SECONDS); rtc.listen_alarm(); diff --git a/examples/rtc.rs b/examples/rtc.rs index f9aa85e9..e3910415 100644 --- a/examples/rtc.rs +++ b/examples/rtc.rs @@ -19,7 +19,7 @@ fn main() -> ! { let rcc = p.RCC.constrain(); let mut backup_domain = rcc.bkp.constrain(p.BKP, &mut pwr); - let rtc = Rtc::rtc(p.RTC, &mut backup_domain); + let rtc = Rtc::new(p.RTC, &mut backup_domain); loop { hprintln!("time: {}", rtc.current_time()).unwrap(); diff --git a/src/rtc.rs b/src/rtc.rs index c5025538..7b259371 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -7,9 +7,18 @@ use crate::backup_domain::BackupDomain; use crate::time::Hertz; use core::convert::Infallible; +use core::marker::PhantomData; // The LSE runs at at 32 768 hertz unless an external clock is provided const LSE_HERTZ: u32 = 32_768; +const LSI_HERTZ: u32 = 40_000; + +/// RTC clock source HSE clock divided by 128 (type state) +pub struct RtcClkHseDiv128; +/// RTC clock source LSE oscillator clock (type state) +pub struct RtcClkLse; +/// RTC clock source LSI oscillator clock (type state) +pub struct RtcClkLsi; /** Real time clock @@ -28,11 +37,12 @@ const LSE_HERTZ: u32 = 32_768; [examples/blinky_rtc.rs]: https://github.com/stm32-rs/stm32f1xx-hal/blob/v0.7.0/examples/blinky_rtc.rs */ -pub struct Rtc { +pub struct Rtc { regs: RTC, + _clock_source: PhantomData, } -impl Rtc { +impl Rtc { /** Initialises the RTC. The `BackupDomain` struct is created by `Rcc.bkp.constrain()`. @@ -43,10 +53,13 @@ impl Rtc { power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to reset the counter. */ - pub fn rtc(regs: RTC, bkp: &mut BackupDomain) -> Self { - let mut result = Rtc { regs }; + pub fn new(regs: RTC, bkp: &mut BackupDomain) -> Self { + let mut result = Rtc { + regs, + _clock_source: PhantomData, + }; - Rtc::enable_rtc(bkp); + Self::enable_rtc(bkp); // Set the prescaler to make it count up once every second. let prl = LSE_HERTZ - 1; @@ -77,7 +90,95 @@ impl Rtc { .lse() }) } +} + +impl Rtc { + pub fn rtc(regs: RTC, bkp: &mut BackupDomain) -> Self { + let mut result = Rtc { + regs, + _clock_source: PhantomData, + }; + + Self::enable_rtc(bkp); + + // Set the prescaler to make it count up once every second. + let prl = LSI_HERTZ - 1; + assert!(prl < 1 << 20); + result.perform_write(|s| { + s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) }); + s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) }); + }); + + result + } + + /// Enables the RTC device with the lsi as the clock + fn enable_rtc(_bkp: &mut BackupDomain) { + // NOTE: Safe RCC access because we are only accessing bdcr + // and we have a &mut on BackupDomain + let rcc = unsafe { &*RCC::ptr() }; + rcc.csr.modify(|_, w| { + w + // start the LSI oscillator + .lsion() + .set_bit() + }); + rcc.bdcr.modify(|_, w| { + w + // Enable the RTC + .rtcen() + .set_bit() + // Set the source of the RTC to LSI + .rtcsel() + .lsi() + }) + } +} + +impl Rtc { + pub fn rtc(regs: RTC, bkp: &mut BackupDomain, hse: F) -> Self + where + F: Into, + { + let mut result = Rtc { + regs, + _clock_source: PhantomData, + }; + + Self::enable_rtc(bkp); + + // Set the prescaler to make it count up once every second. + let prl = hse.into().0 / 128 - 1; + assert!(prl < 1 << 20); + result.perform_write(|s| { + s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) }); + s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) }); + }); + + result + } + + /// Enables the RTC device with the lsi as the clock + fn enable_rtc(_bkp: &mut BackupDomain) { + // NOTE: Safe RCC access because we are only accessing bdcr + // and we have a &mut on BackupDomain + let rcc = unsafe { &*RCC::ptr() }; + if rcc.cr.read().hserdy().bit_is_clear() { + panic!("HSE oscillator not ready"); + } + rcc.bdcr.modify(|_, w| { + w + // Enable the RTC + .rtcen() + .set_bit() + // Set the source of the RTC to HSE/128 + .rtcsel() + .hse() + }) + } +} +impl Rtc { /// Selects the frequency of the RTC Timer /// NOTE: Maximum frequency of 16384 Hz using the internal LSE pub fn select_frequency(&mut self, timeout: impl Into) {