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}