Skip to content

Commit 32123a6

Browse files
committed
Add possibility to restore running rtc
So far the Rtc had to be reset on every startup, leading to time lacking behind. This has been addressed by adding method `Rtc::restore_or_new` that allows to restore the previous rtc, without resetting it. In case the rtc was not running, it is initialized. Documentation has been added and it was included in `Rtc::new` that `Rtc::restore_or_new` should be used for persistent time accuracy. Example `examples/rtc.rs` has been updated to use the new method.
1 parent 4e902d2 commit 32123a6

File tree

2 files changed

+135
-3
lines changed

2 files changed

+135
-3
lines changed

examples/rtc.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use panic_semihosting as _;
99
use cortex_m_semihosting::hprintln;
1010

1111
use cortex_m_rt::entry;
12+
use fugit::RateExtU32;
13+
use stm32f1xx_hal::rtc::RestoredOrNewRtc::{New, Restored};
1214
use stm32f1xx_hal::{pac, prelude::*, rtc::Rtc};
1315

1416
#[entry]
@@ -19,7 +21,24 @@ fn main() -> ! {
1921
let rcc = p.RCC.constrain();
2022
let mut backup_domain = rcc.bkp.constrain(p.BKP, &mut pwr);
2123

22-
let rtc = Rtc::new(p.RTC, &mut backup_domain);
24+
// Initializes rtc every startup, use only if you don't have a battery.
25+
// let rtc = Rtc::new(p.RTC, &mut backup_domain);
26+
27+
// Restores Rtc: that happens in case it was already running, a battery is connected,
28+
// and it was already initialized before.
29+
// If you are going to use ::new with battery, the time will lack behind
30+
// due to unnecessary reinitialization of the crystal,
31+
// as well as reset of the selected frequency.
32+
// Else, the rtc is initialized.
33+
let rtc = match Rtc::restore_or_new(p.RTC, &mut backup_domain) {
34+
Restored(rtc) => rtc, // The rtc is restored from previous configuration. You may verify the frequency you want if needed.
35+
New(mut rtc) => {
36+
// The rtc was just initialized, the clock source selected, frequency is 1.Hz()
37+
// Initialize rtc with desired parameters
38+
rtc.select_frequency(2u32.Hz()); // Set the frequency to 2 Hz. This will stay same after reset
39+
rtc
40+
}
41+
};
2342

2443
loop {
2544
hprintln!("time: {}", rtc.current_time());

src/rtc.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ pub struct RtcClkLse;
2020
/// RTC clock source LSI oscillator clock (type state)
2121
pub struct RtcClkLsi;
2222

23+
pub enum RestoredOrNewRtc<CS> {
24+
Restored(Rtc<CS>),
25+
New(Rtc<CS>),
26+
}
27+
2328
/**
2429
Real time clock
2530
@@ -44,14 +49,19 @@ pub struct Rtc<CS = RtcClkLse> {
4449

4550
impl Rtc<RtcClkLse> {
4651
/**
47-
Initialises the RTC. The `BackupDomain` struct is created by
48-
`Rcc.bkp.constrain()`.
52+
Initialises the RTC with low-speed external crystal source (lse).
53+
The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
4954
5055
The frequency is set to 1 Hz.
5156
5257
Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
5358
power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
5459
reset the counter.
60+
61+
In case application is running of a battery on VBAT,
62+
this method will reset the RTC every time, leading to lost time,
63+
you may want to use
64+
[`restore_or_new`](Rtc::<RtcClkLse>::restore_or_new) instead.
5565
*/
5666
pub fn new(regs: RTC, bkp: &mut BackupDomain) -> Self {
5767
let mut result = Rtc {
@@ -72,6 +82,37 @@ impl Rtc<RtcClkLse> {
7282
result
7383
}
7484

85+
/// Tries to obtain currently running RTC to prevent a reset in case it was running from VBAT.
86+
/// If the RTC is not running, or is not LSE, it will be reinitialized.
87+
///
88+
/// # Examples
89+
/// ```
90+
/// let rtc = match Rtc::restore_or_new(p.RTC, &mut backup_domain) {
91+
/// Restored(rtc) => rtc, // The rtc is restored from previous configuration. You may verify the frequency you want if needed.
92+
/// New(rtc) => { // The rtc was just initialized, the clock source selected, frequency is 1.Hz()
93+
/// // Initialize rtc with desired parameters
94+
/// rtc.select_frequency(2u16.Hz()); // Set the frequency to 2 Hz. This will stay same after reset
95+
/// rtc
96+
/// }
97+
/// };
98+
/// ```
99+
pub fn restore_or_new(regs: RTC, bkp: &mut BackupDomain) -> RestoredOrNewRtc<RtcClkLse> {
100+
if !Self::is_enabled() {
101+
RestoredOrNewRtc::New(Rtc::new(regs, bkp))
102+
} else {
103+
RestoredOrNewRtc::Restored(Rtc {
104+
regs,
105+
_clock_source: PhantomData,
106+
})
107+
}
108+
}
109+
110+
/// Returns whether the RTC is currently enabled and LSE is selected.
111+
fn is_enabled() -> bool {
112+
let rcc = unsafe { &*RCC::ptr() };
113+
rcc.bdcr.read().rtcen().bit() && rcc.bdcr.read().rtcsel().is_lse()
114+
}
115+
75116
/// Enables the RTC device with the lse as the clock
76117
fn enable_rtc(_bkp: &mut BackupDomain) {
77118
// NOTE: Safe RCC access because we are only accessing bdcr
@@ -93,6 +134,21 @@ impl Rtc<RtcClkLse> {
93134
}
94135

95136
impl Rtc<RtcClkLsi> {
137+
/**
138+
Initialises the RTC with low-speed internal oscillator source (lsi).
139+
The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
140+
141+
The frequency is set to 1 Hz.
142+
143+
Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
144+
power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
145+
reset the counter.
146+
147+
In case application is running of a battery on VBAT,
148+
this method will reset the RTC every time, leading to lost time,
149+
you may want to use
150+
[`restore_or_new_lsi`](Rtc::<RtcClkLsi>::restore_or_new_lsi) instead.
151+
*/
96152
pub fn new_lsi(regs: RTC, bkp: &mut BackupDomain) -> Self {
97153
let mut result = Rtc {
98154
regs,
@@ -112,6 +168,25 @@ impl Rtc<RtcClkLsi> {
112168
result
113169
}
114170

171+
/// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT.
172+
/// If the RTC is not running, or is not LSI, it will be reinitialized.
173+
pub fn restore_or_new_lsi(regs: RTC, bkp: &mut BackupDomain) -> RestoredOrNewRtc<RtcClkLsi> {
174+
if !Rtc::<RtcClkLsi>::is_enabled() {
175+
RestoredOrNewRtc::New(Rtc::new_lsi(regs, bkp))
176+
} else {
177+
RestoredOrNewRtc::Restored(Rtc {
178+
regs,
179+
_clock_source: PhantomData,
180+
})
181+
}
182+
}
183+
184+
/// Returns whether the RTC is currently enabled and LSI is selected.
185+
fn is_enabled() -> bool {
186+
let rcc = unsafe { &*RCC::ptr() };
187+
rcc.bdcr.read().rtcen().bit() && rcc.bdcr.read().rtcsel().is_lsi()
188+
}
189+
115190
/// Enables the RTC device with the lsi as the clock
116191
fn enable_rtc(_bkp: &mut BackupDomain) {
117192
// NOTE: Safe RCC access because we are only accessing bdcr
@@ -136,6 +211,22 @@ impl Rtc<RtcClkLsi> {
136211
}
137212

138213
impl Rtc<RtcClkHseDiv128> {
214+
/**
215+
Initialises the RTC with high-speed external oscillator source (hse)
216+
divided by 128.
217+
The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
218+
219+
The frequency is set to 1 Hz.
220+
221+
Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
222+
power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
223+
reset the counter.
224+
225+
In case application is running of a battery on VBAT,
226+
this method will reset the RTC every time, leading to lost time,
227+
you may want to use
228+
[`restore_or_new_hse`](Rtc::<RtcClkHseDiv128>::restore_or_new_hse) instead.
229+
*/
139230
pub fn new_hse(regs: RTC, bkp: &mut BackupDomain, hse: Hertz) -> Self {
140231
let mut result = Rtc {
141232
regs,
@@ -155,6 +246,28 @@ impl Rtc<RtcClkHseDiv128> {
155246
result
156247
}
157248

249+
/// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT.
250+
/// If the RTC is not running, or is not HSE, it will be reinitialized.
251+
pub fn restore_or_new_hse(
252+
regs: RTC,
253+
bkp: &mut BackupDomain,
254+
hse: Hertz,
255+
) -> RestoredOrNewRtc<RtcClkHseDiv128> {
256+
if !Self::is_enabled() {
257+
RestoredOrNewRtc::New(Rtc::new_hse(regs, bkp, hse))
258+
} else {
259+
RestoredOrNewRtc::Restored(Rtc {
260+
regs,
261+
_clock_source: PhantomData,
262+
})
263+
}
264+
}
265+
266+
fn is_enabled() -> bool {
267+
let rcc = unsafe { &*RCC::ptr() };
268+
rcc.bdcr.read().rtcen().bit() && rcc.bdcr.read().rtcsel().is_hse()
269+
}
270+
158271
/// Enables the RTC device with the lsi as the clock
159272
fn enable_rtc(_bkp: &mut BackupDomain) {
160273
// NOTE: Safe RCC access because we are only accessing bdcr

0 commit comments

Comments
 (0)