stm32f1xx_hal/rtc.rs
1/*!
2 Real time clock
3*/
4use crate::pac::{RCC, RTC};
5
6use crate::backup_domain::BackupDomain;
7use crate::time::{Hertz, Hz};
8
9use core::convert::Infallible;
10use core::marker::PhantomData;
11use fugit::RateExtU32;
12
13// The LSE runs at at 32 768 hertz unless an external clock is provided
14const LSE_HERTZ: Hertz = Hz(32_768);
15const LSI_HERTZ: Hertz = Hz(40_000);
16
17/// RTC clock source HSE clock divided by 128 (type state)
18pub struct RtcClkHseDiv128;
19/// RTC clock source LSE oscillator clock (type state)
20pub struct RtcClkLse;
21/// RTC clock source LSI oscillator clock (type state)
22pub struct RtcClkLsi;
23
24pub enum RestoredOrNewRtc<CS> {
25 Restored(Rtc<CS>),
26 New(Rtc<CS>),
27}
28
29/**
30 Real time clock
31
32 A continuously running clock that counts seconds¹. It is part of the backup domain which means
33 that the counter is not affected by system resets or standby mode. If Vbat is connected, it is
34 not reset even if the rest of the device is powered off. This allows it to be used to wake the
35 CPU when it is in low power mode.
36
37
38 See [examples/rtc.rs] and [examples/blinky_rtc.rs] for usage examples.
39
40 1: Unless configured to another frequency using [select_frequency](struct.Rtc.html#method.select_frequency)
41
42 [examples/rtc.rs]: https://github.com/stm32-rs/stm32f1xx-hal/blob/v0.7.0/examples/rtc.rs
43 [examples/blinky_rtc.rs]: https://github.com/stm32-rs/stm32f1xx-hal/blob/v0.7.0/examples/blinky_rtc.rs
44*/
45pub struct Rtc<CS = RtcClkLse> {
46 regs: RTC,
47 frequency: Hertz,
48 _clock_source: PhantomData<CS>,
49}
50
51impl Rtc<RtcClkLse> {
52 /**
53 Initialises the RTC with low-speed external crystal source (lse).
54 The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
55
56 The frequency is set to 1 Hz.
57
58 Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
59 power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
60 reset the counter.
61
62 In case application is running of a battery on VBAT,
63 this method will reset the RTC every time, leading to lost time,
64 you may want to use
65 [`restore_or_new`](Rtc::<RtcClkLse>::restore_or_new) instead.
66 */
67 pub fn new(regs: RTC, bkp: &mut BackupDomain, rcc: &mut RCC) -> Self {
68 let mut result = Self::init(regs);
69
70 Self::enable_rtc(bkp, rcc);
71
72 // Set the prescaler to make it count up once every second.
73 result.select_frequency(1u32.Hz());
74
75 result
76 }
77
78 /// Tries to obtain currently running RTC to prevent a reset in case it was running from VBAT.
79 /// If the RTC is not running, or is not LSE, it will be reinitialized.
80 ///
81 /// # Examples
82 /// ```
83 /// let rtc = match Rtc::restore_or_new(p.RTC, &mut backup_domain) {
84 /// Restored(rtc) => rtc, // The rtc is restored from previous configuration. You may verify the frequency you want if needed.
85 /// New(rtc) => { // The rtc was just initialized, the clock source selected, frequency is 1.Hz()
86 /// // Initialize rtc with desired parameters
87 /// rtc.select_frequency(2u16.Hz()); // Set the frequency to 2 Hz. This will stay same after reset
88 /// rtc
89 /// }
90 /// };
91 /// ```
92 pub fn restore_or_new(
93 regs: RTC,
94 bkp: &mut BackupDomain,
95 rcc: &mut RCC,
96 ) -> RestoredOrNewRtc<RtcClkLse> {
97 if !Self::is_enabled() {
98 RestoredOrNewRtc::New(Rtc::new(regs, bkp, rcc))
99 } else {
100 RestoredOrNewRtc::Restored(Self::init(regs))
101 }
102 }
103
104 fn init(regs: RTC) -> Self {
105 Self {
106 regs,
107 frequency: LSE_HERTZ,
108 _clock_source: PhantomData,
109 }
110 }
111
112 /// Returns whether the RTC is currently enabled and LSE is selected.
113 fn is_enabled() -> bool {
114 let rcc = unsafe { &*RCC::ptr() };
115 let bdcr = rcc.bdcr().read();
116 bdcr.rtcen().is_enabled() && bdcr.rtcsel().is_lse()
117 }
118
119 /// Enables the RTC device with the lse as the clock
120 fn enable_rtc(_bkp: &mut BackupDomain, rcc: &mut RCC) {
121 rcc.bdcr().modify(|_, w| {
122 // start the LSE oscillator
123 w.lseon().set_bit();
124 // Enable the RTC
125 w.rtcen().set_bit();
126 // Set the source of the RTC to LSE
127 w.rtcsel().lse()
128 });
129 }
130}
131
132impl Rtc<RtcClkLsi> {
133 /**
134 Initialises the RTC with low-speed internal oscillator source (lsi).
135 The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
136
137 The frequency is set to 1 Hz.
138
139 Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
140 power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
141 reset the counter.
142
143 In case application is running of a battery on VBAT,
144 this method will reset the RTC every time, leading to lost time,
145 you may want to use
146 [`restore_or_new_lsi`](Rtc::<RtcClkLsi>::restore_or_new_lsi) instead.
147 */
148 pub fn new_lsi(regs: RTC, bkp: &mut BackupDomain) -> Self {
149 let mut result = Self::init(regs);
150
151 Self::enable_rtc(bkp);
152
153 // Set the prescaler to make it count up once every second.
154 result.select_frequency(1u32.Hz());
155
156 result
157 }
158
159 /// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT.
160 /// If the RTC is not running, or is not LSI, it will be reinitialized.
161 pub fn restore_or_new_lsi(regs: RTC, bkp: &mut BackupDomain) -> RestoredOrNewRtc<RtcClkLsi> {
162 if !Rtc::<RtcClkLsi>::is_enabled() {
163 RestoredOrNewRtc::New(Rtc::new_lsi(regs, bkp))
164 } else {
165 RestoredOrNewRtc::Restored(Self::init(regs))
166 }
167 }
168
169 fn init(regs: RTC) -> Self {
170 Self {
171 regs,
172 frequency: LSI_HERTZ,
173 _clock_source: PhantomData,
174 }
175 }
176
177 /// Returns whether the RTC is currently enabled and LSI is selected.
178 fn is_enabled() -> bool {
179 let rcc = unsafe { &*RCC::ptr() };
180 rcc.bdcr().read().rtcen().bit() && rcc.bdcr().read().rtcsel().is_lsi()
181 }
182
183 /// Enables the RTC device with the lsi as the clock
184 fn enable_rtc(_bkp: &mut BackupDomain) {
185 // NOTE: Safe RCC access because we are only accessing bdcr
186 // and we have a &mut on BackupDomain
187 let rcc = unsafe { &*RCC::ptr() };
188 rcc.csr().modify(|_, w| {
189 // start the LSI oscillator
190 w.lsion().set_bit()
191 });
192 rcc.bdcr().modify(|_, w| {
193 // Enable the RTC
194 w.rtcen().set_bit();
195 // Set the source of the RTC to LSI
196 w.rtcsel().lsi()
197 });
198 }
199}
200
201impl Rtc<RtcClkHseDiv128> {
202 /**
203 Initialises the RTC with high-speed external oscillator source (hse)
204 divided by 128.
205 The `BackupDomain` struct is created by `Rcc.bkp.constrain()`.
206
207 The frequency is set to 1 Hz.
208
209 Since the RTC is part of the backup domain, The RTC counter is not reset by normal resets or
210 power cycles where (VBAT) still has power. Use [set_time](#method.set_time) if you want to
211 reset the counter.
212
213 In case application is running of a battery on VBAT,
214 this method will reset the RTC every time, leading to lost time,
215 you may want to use
216 [`restore_or_new_hse`](Rtc::<RtcClkHseDiv128>::restore_or_new_hse) instead.
217 */
218 pub fn new_hse(regs: RTC, bkp: &mut BackupDomain, hse: Hertz) -> Self {
219 let mut result = Self::init(regs, hse);
220
221 Self::enable_rtc(bkp);
222
223 // Set the prescaler to make it count up once every second.
224 result.select_frequency(1u32.Hz());
225
226 result
227 }
228
229 /// Tries to obtain currently running RTC to prevent reset in case it was running from VBAT.
230 /// If the RTC is not running, or is not HSE, it will be reinitialized.
231 pub fn restore_or_new_hse(
232 regs: RTC,
233 bkp: &mut BackupDomain,
234 hse: Hertz,
235 ) -> RestoredOrNewRtc<RtcClkHseDiv128> {
236 if !Self::is_enabled() {
237 RestoredOrNewRtc::New(Rtc::new_hse(regs, bkp, hse))
238 } else {
239 RestoredOrNewRtc::Restored(Self::init(regs, hse))
240 }
241 }
242
243 fn init(regs: RTC, hse: Hertz) -> Self {
244 Self {
245 regs,
246 frequency: hse / 128,
247 _clock_source: PhantomData,
248 }
249 }
250
251 fn is_enabled() -> bool {
252 let rcc = unsafe { &*RCC::ptr() };
253 let bdcr = rcc.bdcr().read();
254 bdcr.rtcen().is_enabled() && bdcr.rtcsel().is_hse()
255 }
256
257 /// Enables the RTC device with the lsi as the clock
258 fn enable_rtc(_bkp: &mut BackupDomain) {
259 // NOTE: Safe RCC access because we are only accessing bdcr
260 // and we have a &mut on BackupDomain
261 let rcc = unsafe { &*RCC::ptr() };
262 if rcc.cr().read().hserdy().bit_is_clear() {
263 panic!("HSE oscillator not ready");
264 }
265 rcc.bdcr().modify(|_, w| {
266 // Enable the RTC
267 w.rtcen().set_bit();
268 // Set the source of the RTC to HSE/128
269 w.rtcsel().hse()
270 });
271 }
272}
273
274impl<CS> Rtc<CS> {
275 /// Selects the frequency of the RTC Timer
276 /// NOTE: Maximum frequency of 16384 Hz using the internal LSE
277 pub fn select_frequency(&mut self, frequency: Hertz) {
278 // The manual says that the zero value for the prescaler is not recommended, thus the
279 // minimum division factor is 2 (prescaler + 1)
280 assert!(frequency <= self.frequency / 2);
281
282 let prescaler = self.frequency / frequency - 1;
283 assert!(prescaler < 1 << 20);
284 self.perform_write(|s| {
285 s.regs.prlh().write(|w| unsafe { w.bits(prescaler >> 16) });
286 s.regs
287 .prll()
288 .write(|w| unsafe { w.bits(prescaler as u16 as u32) });
289 });
290 }
291
292 /// Set the current RTC counter value to the specified amount
293 pub fn set_time(&mut self, counter_value: u32) {
294 self.perform_write(|s| {
295 s.regs
296 .cnth()
297 .write(|w| unsafe { w.bits(counter_value >> 16) });
298 s.regs
299 .cntl()
300 .write(|w| unsafe { w.bits(counter_value as u16 as u32) });
301 });
302 }
303
304 /**
305 Sets the time at which an alarm will be triggered
306
307 This also clears the alarm flag if it is set
308 */
309 pub fn set_alarm(&mut self, counter_value: u32) {
310 // Set alarm time
311 // See section 18.3.5 for explanation
312 let alarm_value = counter_value - 1;
313
314 // TODO: Remove this `allow` once these fields are made safe for stm32f100
315 #[allow(unused_unsafe)]
316 self.perform_write(|s| {
317 s.regs
318 .alrh()
319 .write(|w| unsafe { w.alrh().bits((alarm_value >> 16) as u16) });
320 s.regs
321 .alrl()
322 .write(|w| unsafe { w.alrl().bits(alarm_value as u16) });
323 });
324
325 self.clear_alarm_flag();
326 }
327
328 /// Enables the RTC interrupt to trigger when the counter reaches the alarm value. In addition,
329 /// if the EXTI controller has been set up correctly, this function also enables the RTCALARM
330 /// interrupt.
331 pub fn listen_alarm(&mut self) {
332 // Enable alarm interrupt
333 self.perform_write(|s| {
334 s.regs.crh().modify(|_, w| w.alrie().set_bit());
335 })
336 }
337
338 /// Stops the RTC alarm from triggering the RTC and RTCALARM interrupts
339 pub fn unlisten_alarm(&mut self) {
340 // Disable alarm interrupt
341 self.perform_write(|s| {
342 s.regs.crh().modify(|_, w| w.alrie().clear_bit());
343 })
344 }
345
346 /// Reads the current counter
347 pub fn current_time(&self) -> u32 {
348 // Wait for the APB1 interface to be ready
349 while !self.regs.crl().read().rsf().bit() {}
350
351 (self.regs.cnth().read().bits() << 16) | self.regs.cntl().read().bits()
352 }
353
354 /// Enables triggering the RTC interrupt every time the RTC counter is increased
355 pub fn listen_seconds(&mut self) {
356 self.perform_write(|s| {
357 s.regs.crh().modify(|_, w| w.secie().set_bit());
358 })
359 }
360
361 /// Disables the RTC second interrupt
362 pub fn unlisten_seconds(&mut self) {
363 self.perform_write(|s| {
364 s.regs.crh().modify(|_, w| w.secie().clear_bit());
365 })
366 }
367
368 /// Clears the RTC second interrupt flag
369 pub fn clear_second_flag(&mut self) {
370 self.perform_write(|s| {
371 s.regs.crl().modify(|_, w| w.secf().clear_bit());
372 })
373 }
374
375 /// Clears the RTC alarm interrupt flag
376 pub fn clear_alarm_flag(&mut self) {
377 self.perform_write(|s| {
378 s.regs.crl().modify(|_, w| w.alrf().clear_bit());
379 })
380 }
381
382 /**
383 Return `Ok(())` if the alarm flag is set, `Err(nb::WouldBlock)` otherwise.
384
385 ```rust
386 use nb::block;
387
388 rtc.set_alarm(rtc.read_counts() + 5);
389 // NOTE: Safe unwrap because Infallible can't be returned
390 block!(rtc.wait_alarm()).unwrap();
391 ```
392 */
393 pub fn wait_alarm(&mut self) -> nb::Result<(), Infallible> {
394 if self.regs.crl().read().alrf().bit() {
395 self.regs.crl().modify(|_, w| w.alrf().clear_bit());
396 Ok(())
397 } else {
398 Err(nb::Error::WouldBlock)
399 }
400 }
401
402 /**
403 The RTC registers can not be written to at any time as documented on page
404 485 of the manual. Performing writes using this function ensures that
405 the writes are done correctly.
406 */
407 fn perform_write(&mut self, func: impl Fn(&mut Self)) {
408 // Wait for the last write operation to be done
409 while !self.regs.crl().read().rtoff().bit() {}
410 // Put the clock into config mode
411 self.regs.crl().modify(|_, w| w.cnf().set_bit());
412
413 // Perform the write operation
414 func(self);
415
416 // Take the device out of config mode
417 self.regs.crl().modify(|_, w| w.cnf().clear_bit());
418 // Wait for the write to be done
419 while !self.regs.crl().read().rtoff().bit() {}
420 }
421}