stm32_hal2/
rtc.rs

1//! Support for the Real Time Clock (RTC) peripheral.
2//! For more details, see
3//! [ST AN4759](https:/www.st.com%2Fresource%2Fen%2Fapplication_note%2Fdm00226326-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf&usg=AOvVaw3PzvL2TfYtwS32fw-Uv37h)
4
5//! Uses [Chrono](https://docs.rs/chrono) for dates and times.
6
7use core::convert::TryInto;
8
9use cfg_if::cfg_if;
10use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
11
12use crate::{
13    error::{Error, Result},
14    pac::{EXTI, PWR, RCC, RTC},
15    util::bounded_loop,
16};
17
18// todo: QC use of ICSR vice SR and ISR wherever used in this module!
19
20/// RTC Clock source.
21#[derive(Clone, Copy, Debug, PartialEq)]
22#[repr(u8)]
23pub enum RtcClockSource {
24    /// 01: LSE oscillator clock used as RTC clock
25    Lse = 0b01,
26    /// 10: LSI oscillator clock used as RTC clock
27    Lsi = 0b10,
28    /// 11: HSE oscillator clock divided by 32 used as RTC clock
29    Hse = 0b11,
30}
31
32/// RTC error type.
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34#[derive(Debug, Clone, Copy, Eq, PartialEq)]
35pub enum RtcError {
36    /// Invalid input error.
37    InvalidInputData,
38}
39
40/// See ref man, section 27.6.3, or AN4769, section 2.4.2.
41/// To be used with WakeupPrescaler
42#[derive(Clone, Copy, Debug)]
43enum WakeupDivision {
44    Sixteen,
45    Eight,
46    Four,
47    Two,
48}
49
50/// See AN4759, table 13.
51#[derive(Clone, Copy, Debug)]
52enum ClockConfig {
53    One(WakeupDivision),
54    Two,
55    Three,
56}
57
58/// Interrupt event
59pub enum Event {
60    WakeupTimer,
61    AlarmA,
62    AlarmB,
63    Timestamp,
64}
65
66pub enum Alarm {
67    AlarmA,
68    AlarmB,
69}
70
71impl From<Alarm> for Event {
72    fn from(a: Alarm) -> Self {
73        match a {
74            Alarm::AlarmA => Event::AlarmA,
75            Alarm::AlarmB => Event::AlarmB,
76        }
77    }
78}
79
80/// Represents a Real Time Clock (RTC) peripheral.
81pub struct Rtc {
82    /// RTC Peripheral register definition
83    regs: RTC,
84    config: RtcConfig,
85}
86
87#[derive(Copy, Clone, Debug, PartialEq)]
88/// Configuration data for the RTC.
89pub struct RtcConfig {
90    /// RTC clock source. Defaults to LSI (Low speed internal oscillator)
91    pub clock_source: RtcClockSource,
92    /// Asynchronous prescaler factor
93    /// This is the asynchronous division factor:
94    /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
95    /// ck_apre drives the subsecond register. Defaults to 127.
96    pub async_prescaler: u8,
97    /// Synchronous prescaler factor
98    /// This is the synchronous division factor:
99    /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
100    /// ck_spre must be 1Hz. Defaults to 255.
101    pub sync_prescaler: u16,
102    /// Bypass LSE output - eg if you're using a self-powered external oscillator. This
103    /// saves power, and lets you use the LSE output pin as a GPIO.
104    pub bypass_lse_output: bool,
105}
106
107impl Default for RtcConfig {
108    /// LSI with prescalers assuming 32.768 kHz.
109    /// Raw sub-seconds in 1/256.
110    fn default() -> Self {
111        RtcConfig {
112            clock_source: RtcClockSource::Lsi,
113            async_prescaler: 127,
114            sync_prescaler: 255,
115            bypass_lse_output: false,
116        }
117    }
118}
119
120impl Rtc {
121    /// Initialize the RTC, including configuration register writes.
122    pub fn new(regs: RTC, config: RtcConfig) -> Result<Self> {
123        let mut rtc = Self { regs, config };
124
125        // Enable the peripheral clock for communication
126        // You must enable the `pwren()` bit before making RTC register writes, or they won't stay
127        // set. Enable the backup interface by setting PWREN
128
129        // Note that unlock other RCC enableing processes, there's no corresponding reset
130        // field here.
131
132        // See L4 RM, `Backup domain access` section.
133        let rcc = unsafe { &(*RCC::ptr()) };
134        let pwr = unsafe { &(*PWR::ptr()) };
135
136        cfg_if! {
137            if #[cfg(any(feature = "f3", feature = "f4"))] {
138                rcc.apb1enr().modify(|_, w| w.pwren().bit(true));
139                pwr.cr().read(); // read to allow the pwr clock to enable
140                pwr.cr().modify(|_, w| w.dbp().bit(true));
141                bounded_loop!(pwr.cr().read().dbp().bit_is_clear(), Error::RegisterUnchanged);
142            } else if #[cfg(any(feature = "l4", feature = "l5", feature = "g4", feature = "l412", feature = "wb", feature = "wl"))] {
143                // 1. Enable the power interface clock by setting the PWREN bits in the Section 6.4.18:
144                // APB1 peripheral clock enable register 1 (RCC_APB1ENR1)
145                #[cfg(not(any(feature = "wb", feature = "wl")))]
146                rcc.apb1enr1().modify(|_, w| {
147                    w.pwren().bit(true);
148                    w.rtcapben().bit(true)
149                });
150                #[cfg(any(feature = "wb", feature = "wl"))]
151                rcc.apb1enr1().modify(|_, w| w.rtcapben().bit(true));
152
153                rcc.apb1smenr1().modify(|_, w| w.rtcapbsmen().bit(true));  // In sleep and stop modes.
154                pwr.cr1().read(); // Read to allow the pwr clock to enable
155                // 2. Set the DBP bit in the Power control register 1 (PWR_CR1) to enable access to the
156                // backup domain
157                pwr.cr1().modify( | _, w| w.dbp().bit(true)); // Unlock the backup domain
158                bounded_loop!(pwr.cr1().read().dbp().bit_is_clear(), Error::RegisterUnchanged);
159            } else if #[cfg(feature = "c0")] {
160                // todo: QC this.
161                rcc.apbenr1().modify(|_, w| {
162                    w.pwren().bit(true);
163                    w.rtcapben().bit(true)
164                });
165
166                rcc.apbsmenr1().modify(|_, w| w.rtcapbsmen().bit(true));  // In sleep and stop modes.
167                pwr.cr1().read(); // Read to allow the pwr clock to enable
168            } else if #[cfg(any(feature = "g0"))] {
169                rcc.apbenr1().modify(|_, w| {
170                    w.pwren().bit(true);
171                    w.rtcapben().bit(true)
172                });
173                rcc.apbsmenr1().modify(|_, w| w.rtcapbsmen().bit(true));  // In sleep and stop modes.
174                pwr.cr1().read();
175                pwr.cr1().modify( | _, w| w.dbp().bit(true));
176                bounded_loop!(pwr.cr1().read().dbp().bit_is_clear(), Error::RegisterUnchanged);
177                while pwr.cr1().read().dbp().bit_is_clear() {}
178            } else if #[cfg(feature = "h5")] {
179                rcc.apb3enr().modify(|_, w| w.rtcapben().bit(true));
180                rcc.apb3lpenr().modify(|_, w| w.rtcapblpen().bit(true));  // In sleep and stop modes.
181                pwr.dbpcr().read(); // read to allow the pwr clock to enable // todo??
182                pwr.dbpcr().modify( | _, w| w.dbp().bit(true));
183                bounded_loop!(pwr.dbpcr().read().dbp().bit_is_clear(), Error::RegisterUnchanged);
184            } else { // eg h7
185                rcc.apb4enr().modify(|_, w| w.rtcapben().bit(true));
186                rcc.apb4lpenr().modify(|_, w| w.rtcapblpen().bit(true));  // In sleep and stop modes.
187                pwr.cr1().read(); // read to allow the pwr clock to enable
188                pwr.cr1().modify( | _, w| w.dbp().bit(true));
189                bounded_loop!(pwr.cr1().read().dbp().bit_is_clear(), Error::RegisterUnchanged);
190            }
191        }
192
193        // Set up the LSI or LSE as required.
194        match config.clock_source {
195            RtcClockSource::Lsi => {
196                cfg_if! {
197                    if #[cfg(feature = "wb")] {
198                    // todo: LSI2?
199                        rcc.csr().modify(|_, w| w.lsi1on().bit(true));
200                        bounded_loop!(rcc.csr().read().lsi1rdy().bit_is_clear(), Error::RegisterUnchanged);
201                    } else if #[cfg(feature = "h5")] {
202                        rcc.bdcr().modify(|_, w| w.lsion().bit(true));
203                        bounded_loop!(rcc.bdcr().read().lsirdy().bit_is_clear(), Error::RegisterUnchanged);
204                    } else if #[cfg(feature = "c0")] {
205                        rcc.csr2().modify(|_, w| w.lsion().bit(true));
206                        bounded_loop!(rcc.csr2().read().lsirdy().bit_is_clear(), Error::RegisterUnchanged);
207                    } else {
208                        rcc.csr().modify(|_, w| w.lsion().bit(true));
209                        bounded_loop!(rcc.csr().read().lsirdy().bit_is_clear(), Error::RegisterUnchanged);
210                    }
211                }
212            }
213            #[cfg(not(feature = "c0"))]
214            RtcClockSource::Lse => {
215                // Can only set lsebyp when lse is off, so do this as a separate step.
216                rcc.bdcr()
217                    .modify(|_, w| w.lsebyp().bit(config.bypass_lse_output));
218                rcc.bdcr().modify(|_, w| w.lseon().bit(true));
219                bounded_loop!(
220                    rcc.bdcr().read().lserdy().bit_is_clear(),
221                    Error::RegisterUnchanged
222                );
223            }
224            #[cfg(feature = "c0")]
225            RtcClockSource::Lse => {
226                // Can only set lsebyp when lse is off, so do this as a separate step.
227                rcc.csr1()
228                    .modify(|_, w| w.lsebyp().bit(config.bypass_lse_output));
229                rcc.csr1().modify(|_, w| w.lseon().bit(true));
230
231                bounded_loop!(
232                    rcc.csr1().read().lserdy().bit_is_clear(),
233                    Error::RegisterUnchanged
234                );
235            }
236            _ => (),
237        }
238
239        #[cfg(not(feature = "c0"))]
240        rcc.bdcr().modify(|_, w| {
241            // 3. Select the RTC clock source in the Backup domain control register (RCC_BDCR).
242            unsafe { w.rtcsel().bits(rtc.config.clock_source as u8) };
243            // 4. Enable the RTC clock by setting the RTCEN [15] bit in the Backup domain control
244            // register (RCC_BDCR)
245            w.rtcen().bit(true)
246        });
247
248        #[cfg(feature = "c0")]
249        rcc.csr1().modify(|_, w| {
250            unsafe { w.rtcsel().bits(rtc.config.clock_source as u8) };
251            w.rtcen().bit(true)
252        });
253
254        rtc.edit_regs(false, |regs| {
255            regs.cr().modify(
256                |_, w| {
257                    unsafe {
258                        w.fmt()
259                            .clear_bit() // 24hr
260                            .osel()
261                            /*
262                                00: Output disabled
263                                01: Alarm A output enabled
264                                10: Alarm B output enabled
265                                11: Wakeup output enabled
266                            */
267                            .bits(0b00)
268                            .pol()
269                            .clear_bit()
270                    }
271                }, // pol high
272            );
273
274            regs.prer().modify(|_, w| unsafe {
275                w.prediv_s().bits(config.sync_prescaler);
276                w.prediv_a().bits(config.async_prescaler)
277            });
278        })?;
279
280        Ok(rtc)
281    }
282
283    /// Sets calendar clock to 24 hr format
284    pub fn set_24h_fmt(&mut self) -> Result<()> {
285        self.edit_regs(true, |regs| {
286            regs.cr().modify(|_, w| w.fmt().clear_bit());
287        })
288    }
289
290    /// Sets calendar clock to 12 hr format
291    pub fn set_12h_fmt(&mut self) -> Result<()> {
292        self.edit_regs(true, |regs| {
293            regs.cr().modify(|_, w| w.fmt().set_bit());
294        })
295    }
296
297    /// Reads current hour format selection
298    pub fn is_24h_fmt(&self) -> bool {
299        !self.regs.cr().read().fmt().bit()
300    }
301
302    // /// Setup the alarm. See AN4759, section 2.3.1.
303    // /// `sleep_time` is in ms. `Table 8` desribes these steps.
304    // pub fn set_alarm(&mut self, exti: &mut EXTI) {
305    // note: STM3241x and 42x have diff addresses, and are PAC incompatible!
306    //     exti.imr1().modify(|_, w| w.mr18().unmasked());
307    //     exti.rtsr1().modify(|_, w| w.tr18().bit(true));
308    //     exti.ftsr1().modify(|_, w| w.tr18().clear_bit());
309    //
310    //     self.edit_regs(false, |regs| {
311    //         regs.cr().modify(|_, w| w.alrae().clear_bit());
312    //
313    //         while regs.cr().read().alrae().bit_is_set() {}
314    //
315    //         // todo: Set the alarm time. This function will be broken until this is accomplished.
316    //         // self.regs.alrmar.modify(|_, w| unsafe {});
317    //
318    //         regs.cr().modify(|_, w| w.alrae().bit(true));
319    //         while regs.cr().read().alrae().bit_is_clear() {}
320    //     })
321    // }
322
323    /// Helper fn, to do the important bits of setting the interval, with
324    /// the registers already unlocked.
325    fn set_wakeup_interval_inner(&mut self, sleep_time: f32) {
326        // Program the value into the wakeup timer
327        // Set WUT[15:0] in RTC_WUTR register. For RTC3 the user must also program
328        // WUTOCLR bits.
329        // See ref man Section 2.4.2: Maximum and minimum RTC wakeup period.
330        // todo check ref man register table
331
332        // See notes reffed below about WUCKSEL. We choose one of 3 "modes" described in AN4759 based
333        // on sleep time. If in the overlap area, choose the lower (more precise) mode.
334        // These all assume a 1hz `ck_spre`.
335        let lfe_freq = match self.config.clock_source {
336            RtcClockSource::Lse => 32_768.,
337            RtcClockSource::Lsi => 40_000.,
338            RtcClockSource::Hse => 250_000., // Assuming 8Mhz HSE, which may not be the case
339        };
340
341        // sleep_time = (1/lfe_freq) * div * (wutr + 1)
342        // res = 1/lfe_freq * div
343        // sleep_time = res * WUTR = 1/lfe_freq * div * (wutr + 1)
344        // wutr = sleep_time * lfe_freq / div - 1
345
346        let clock_cfg;
347        let wutr;
348
349        if sleep_time >= 0.00012207 && sleep_time < 32. {
350            let division;
351            let div;
352            if sleep_time < 4. {
353                division = WakeupDivision::Two; // Resolution: 61.035µs
354                div = 2.;
355            } else if sleep_time < 8. {
356                division = WakeupDivision::Four; // Resolution: 122.08µs
357                div = 4.;
358            } else if sleep_time < 16. {
359                division = WakeupDivision::Eight; // Resolution: 244.141
360                div = 8.;
361            } else {
362                division = WakeupDivision::Sixteen; // Resolution: 488.281
363                div = 16.;
364            }
365            clock_cfg = ClockConfig::One(division);
366            wutr = sleep_time * lfe_freq / div - 1.
367        } else if sleep_time < 65_536. {
368            // 32s to 18 hours (This mode goes 1s to 18 hours; we use Config1 for the overlap)
369            clock_cfg = ClockConfig::Two;
370            wutr = sleep_time; // This works out conveniently!
371        } else if sleep_time < 131_072. {
372            // 18 to 36 hours
373            clock_cfg = ClockConfig::Three;
374            wutr = sleep_time - 65_537.;
375        } else {
376            panic!("Wakeup period must be between 0122.07µs and 36 hours.")
377        }
378
379        #[cfg(not(feature = "c0"))]
380        self.regs
381            .wutr()
382            .modify(|_, w| unsafe { w.wut().bits(wutr as u16) });
383
384        // Select the desired clock source. Program WUCKSEL[2:0] bits in RTC_CR register.
385        // See ref man Section 2.4.2: Maximum and minimum RTC wakeup period.
386        // todo: Check register docs and see what to set here.
387
388        // See AN4759, Table 13. RM, 27.3.6
389
390        // When ck_spre frequency is 1Hz, this allows to achieve a wakeup time from 1 s to
391        // around 36 hours with one-second resolution. This large programmable time range is
392        // divided in 2 parts:
393        // – from 1s to 18 hours when WUCKSEL [2:1] = 10
394        // – and from around 18h to 36h when WUCKSEL[2:1] = 11. In this last case 216 is
395        // added to the 16-bit counter current value.When the initialization sequence is
396        // complete (see Programming the wakeup timer on page 781), the timer starts
397        // counting down.When the wakeup function is enabled, the down-counting remains
398        // active in low-power modes. In addition, when it reaches 0, the WUTF flag is set in
399        // the RTC_ISR register, and the wakeup counter is automatically reloaded with its
400        // reload value (RTC_WUTR register value).
401        let word = match clock_cfg {
402            ClockConfig::One(division) => match division {
403                WakeupDivision::Sixteen => 0b000,
404                WakeupDivision::Eight => 0b001,
405                WakeupDivision::Four => 0b010,
406                WakeupDivision::Two => 0b011,
407            },
408            // for 2 and 3, what does `x` mean in the docs? Best guess is it doesn't matter.
409            ClockConfig::Two => 0b100,   // eg 1s to 18h.
410            ClockConfig::Three => 0b110, // eg 18h to 36h
411        };
412
413        #[cfg(not(feature = "c0"))]
414        // 000: RTC/16 clock is selected
415        // 001: RTC/8 clock is selected
416        // 010: RTC/4 clock is selected
417        // 011: RTC/2 clock is selected
418        // 10x: ck_spre (usually 1 Hz) clock is selected
419        // 11x: ck_spre (usually 1 Hz) clock is selected and 216 is added to the WUT counter value
420        self.regs
421            .cr()
422            .modify(|_, w| unsafe { w.wucksel().bits(word) });
423    }
424
425    #[cfg(not(any(feature = "f373", feature = "c0")))]
426    /// Setup periodic auto-wakeup interrupts. See ST AN4759, Table 11, and more broadly,
427    /// section 2.4.1. See also reference manual, section 27.5.
428    /// In addition to running this function, set up the interrupt handling function by
429    /// adding the line `make_rtc_interrupt_handler!(RTC_WKUP);` somewhere in the body
430    /// of your program.
431    /// `sleep_time` is in ms.
432    pub fn set_wakeup(&mut self, sleep_time: f32) -> Result<()> {
433        // Configure and enable the EXTI line corresponding to the Wakeup timer even in
434        // interrupt mode and select the rising edge sensitivity.
435        // Sleep time is in seconds.  See L4 RM, Table 47 to see that exti line 20 is the RTC wakeup
436        // timer. This appears to be the case for all families.
437
438        // L4 RM, 5.3.11: To wakeup from Stop mode with an RTC wakeup event, it is necessary to:
439        // • Configure the EXTI Line 20 to be sensitive to rising edge
440        // • Configure the RTC to generate the RTC alarm
441
442        let exti = unsafe { &(*EXTI::ptr()) };
443
444        cfg_if! {
445            if #[cfg(any(feature = "f3", feature = "l4"))] {
446                exti.imr1().modify(|_, w| w.mr20().unmasked());
447                exti.rtsr1().modify(|_, w| w.tr20().bit(true));
448                exti.ftsr1().modify(|_, w| w.tr20().clear_bit());
449            } else if #[cfg(feature = "f4")] {
450                exti.imr().modify(|_, w| w.mr20().unmasked());
451                exti.rtsr().modify(|_, w| w.tr20().bit(true));
452                exti.ftsr().modify(|_, w| w.tr20().clear_bit());
453            } else if #[cfg(feature = "g4")]{
454                exti.imr1().modify(|_, w| w.im20().unmasked());
455                exti.rtsr1().modify(|_, w| w.rt20().bit(true));
456                exti.ftsr1().modify(|_, w| w.ft20().clear_bit());
457            } else if #[cfg(any(feature = "l5", feature = "g0", feature = "wb", feature = "wl", feature = "h5"))] {
458                // exti.imr1().modify(|_, w| w.mr20().unmasked());
459                // exti.rtsr1().modify(|_, w| w.rt20().bit(true));
460                // exti.ftsr1().modify(|_, w| w.ft20().clear_bit());
461
462           } else if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
463                exti.c1imr1().modify(|_, w| w.mr20().unmasked());
464                exti.rtsr1().modify(|_, w| w.tr20().bit(true));
465                exti.ftsr1().modify(|_, w| w.tr20().clear_bit());
466           } else { // H7
467                exti.cpuimr1().modify(|_, w| w.mr20().unmasked());
468                exti.rtsr1().modify(|_, w| w.tr20().bit(true));
469                exti.ftsr1().modify(|_, w| w.tr20().clear_bit());
470            }
471        }
472
473        // We can't use the `edit_regs` abstraction here due to being unable to call a method
474        // in the closure.
475        self.regs.wpr().write(|w| unsafe { w.bits(0xCA) });
476        self.regs.wpr().write(|w| unsafe { w.bits(0x53) });
477
478        // Disable the wakeup timer. Clear WUTE bit in RTC_CR register
479        self.regs.cr().modify(|_, w| w.wute().clear_bit());
480
481        // Ensure access to Wakeup auto-reload counter and bits WUCKSEL[2:0] is allowed.
482        // Poll WUTWF until it is set in RTC_ISR (RTC2)/RTC_ICSR (RTC3) (May not be avail on F3)
483        cfg_if! {
484            if #[cfg(rtc_icsr)] {
485                bounded_loop!(self.regs.icsr().read().wutwf().bit_is_clear(), Error::RegisterUnchanged);
486            } else {
487                bounded_loop!(self.regs.isr().read().wutwf().bit_is_clear(), Error::RegisterUnchanged);
488            }
489        }
490
491        self.set_wakeup_interval_inner(sleep_time);
492        // Re-enable the wakeup timer. Set WUTE bit in RTC_CR register.
493        // The wakeup timer restarts counting down.
494        self.regs.cr().modify(|_, w| w.wute().bit(true));
495
496        // Enable the wakeup timer interrupt.
497        self.regs.cr().modify(|_, w| w.wutie().bit(true));
498
499        cfg_if! {
500            if #[cfg(rtc_icsr)] {
501                self.regs.scr().write(|w| w.cwutf().bit(true));
502            } else {
503                self.regs.isr().modify(|_, w| w.wutf().clear_bit());
504            }
505        }
506
507        self.regs.wpr().write(|w| unsafe { w.bits(0xFF) });
508
509        Ok(())
510    }
511
512    #[cfg(not(feature = "c0"))]
513    /// Enable the wakeup timer.
514    pub fn enable_wakeup(&mut self) {
515        unsafe {
516            self.regs.wpr().write(|w| w.bits(0xCA));
517            self.regs.wpr().write(|w| w.bits(0x53));
518            self.regs.cr().modify(|_, w| w.wute().bit(true));
519            self.regs.wpr().write(|w| w.bits(0xFF));
520        }
521    }
522
523    #[cfg(not(feature = "c0"))]
524    /// Disable the wakeup timer.
525    pub fn disable_wakeup(&mut self) {
526        unsafe {
527            self.regs.wpr().write(|w| w.bits(0xCA));
528            self.regs.wpr().write(|w| w.bits(0x53));
529            self.regs.cr().modify(|_, w| w.wute().clear_bit());
530            self.regs.wpr().write(|w| w.bits(0xFF));
531        }
532    }
533
534    #[cfg(not(feature = "c0"))]
535    /// Change the sleep time for the auto wakeup, after it's been set up.
536    /// Sleep time is in MS. Major DRY from `set_wakeup`.
537    pub fn set_wakeup_interval(&mut self, sleep_time: f32) -> Result<()> {
538        // `sleep_time` is in seconds.
539        // See comments in `set_auto_wakeup` for what these writes do.
540
541        // We can't use the `edit_regs` abstraction here due to being unable to call a method
542        // in the closure.
543        self.regs.wpr().write(|w| unsafe { w.bits(0xCA) });
544        self.regs.wpr().write(|w| unsafe { w.bits(0x53) });
545
546        let started_enabled = self.regs.cr().read().wute().bit_is_set();
547        if started_enabled {
548            self.regs.cr().modify(|_, w| w.wute().clear_bit());
549        }
550
551        cfg_if! {
552            if #[cfg(rtc_icsr)] {
553                bounded_loop!(self.regs.icsr().read().wutwf().bit_is_clear(), Error::RegisterUnchanged);
554            } else {
555                bounded_loop!(self.regs.isr().read().wutwf().bit_is_clear(), Error::RegisterUnchanged);
556            }
557        }
558
559        self.set_wakeup_interval_inner(sleep_time);
560
561        if started_enabled {
562            self.regs.cr().modify(|_, w| w.wute().bit(true));
563        }
564
565        self.regs.wpr().write(|w| unsafe { w.bits(0xFF) });
566
567        Ok(())
568    }
569
570    #[cfg(not(feature = "c0"))]
571    /// Clears the wakeup flag. Must be cleared manually after every RTC wakeup.
572    /// Alternatively, you could call this in the RTC wakeup interrupt handler.
573    pub fn clear_wakeup_flag(&mut self) -> Result<()> {
574        self.edit_regs(false, |regs| {
575            regs.cr().modify(|_, w| w.wute().clear_bit());
576
577            cfg_if! {
578                if #[cfg(any(feature = "l412", feature = "l5", feature = "g0", feature = "g4", feature = "l412", 
579                    feature = "wl", feature = "h5", feature = "c0"))] {
580                    regs.scr().write(|w| w.cwutf().bit(true));
581                } else {
582                    // Note that we clear this by writing 0, which isn't
583                    // the standard convention, eg in other families, and
584                    // other peripherals.
585                    regs.isr().modify(|_, w| w.wutf().clear_bit());
586                }
587            }
588
589            regs.cr().modify(|_, w| w.wute().bit(true));
590        })
591    }
592
593    /// this function is used to disable write protection when modifying an RTC register.
594    /// It also optionally handles the additional step required to set a clock or calendar
595    /// value.
596    fn edit_regs<F>(&mut self, init_mode: bool, mut closure: F) -> Result<()>
597    where
598        F: FnMut(&mut RTC),
599    {
600        // Disable write protection
601        // This is safe, as we're only writin the correct and expected values.
602        self.regs.wpr().write(|w| unsafe { w.bits(0xCA) });
603        self.regs.wpr().write(|w| unsafe { w.bits(0x53) });
604
605        // todo: L4 has ICSR and ISR regs. Maybe both for backwards compat?
606
607        cfg_if! {
608            if #[cfg(rtc_icsr)] {
609                // Enter init mode if required. This is generally used to edit the clock or calendar,
610                // but not for initial enabling steps.
611                if init_mode && self.regs.icsr().read().initf().bit_is_clear() {
612                    // are we already in init mode?
613                    self.regs.icsr().modify(|_, w| w.init().bit(true));
614                    // wait to return to init state
615                    bounded_loop!(self.regs.icsr().read().initf().bit_is_clear(), Error::RegisterUnchanged);
616                 }
617
618                // Edit the regs specified in the closure, now that they're writable.
619                closure(&mut self.regs);
620
621                if init_mode {
622                    self.regs.icsr().modify(|_, w| w.init().clear_bit()); // Exits init mode
623                    bounded_loop!(self.regs.icsr().read().initf().bit_is_set(), Error::RegisterUnchanged);
624                }
625            // } else if #[cfg(feature = "wl")] {
626            //     if init_mode && self.regs.isr().read().initf().bit_is_clear() {
627            //         self.regs.icsr().modify(|_, w| w.init().bit(true));
628            //         while self.regs.icsr().read().initf().bit_is_clear() {} // wait to return to init state
629            //     }
630
631            //     closure(&mut self.regs);
632
633            //     if init_mode {
634            //         self.regs.icsr().modify(|_, w| w.init().clear_bit()); // Exits init mode
635            //         while self.regs.sr().read().initf().bit_is_set() {}
636            //     }
637            } else {
638                if init_mode && self.regs.isr().read().initf().bit_is_clear() {
639                    self.regs.isr().modify(|_, w| w.init().bit(true));
640                    // wait to return to init state
641                    bounded_loop!(self.regs.isr().read().initf().bit_is_clear(), Error::RegisterUnchanged);
642                }
643
644                closure(&mut self.regs);
645
646                if init_mode {
647                    self.regs.isr().modify(|_, w| w.init().clear_bit()); // Exits init mode
648                    bounded_loop!(self.regs.isr().read().initf().bit_is_set(), Error::RegisterUnchanged);
649                }
650            }
651        }
652
653        // Re-enable write protection.
654        // This is safe, as the field accepts the full range of 8-bit values.
655        self.regs.wpr().write(|w| unsafe { w.bits(0xFF) });
656
657        Ok(())
658    }
659
660    /// set time using NaiveTime (ISO 8601 time without timezone)
661    /// Hour format is 24h
662    pub fn set_time(&mut self, time: &NaiveTime) -> Result<()> {
663        self.set_24h_fmt()?;
664        let (ht, hu) = bcd2_encode(time.hour())?;
665        let (mnt, mnu) = bcd2_encode(time.minute())?;
666        let (st, su) = bcd2_encode(time.second())?;
667
668        self.edit_regs(true, |regs| {
669            regs.tr().write(|w| unsafe {
670                w.ht().bits(ht);
671                w.hu().bits(hu);
672                w.mnt().bits(mnt);
673                w.mnu().bits(mnu);
674                w.st().bits(st);
675                w.su().bits(su);
676                w.pm().clear_bit()
677            });
678        })
679    }
680
681    /// Set the seconds component of the RTC's current time.
682    pub fn set_seconds(&mut self, seconds: u8) -> Result<()> {
683        if seconds > 59 {
684            return Err(Error::RtcError(RtcError::InvalidInputData));
685        }
686        let (st, su) = bcd2_encode(seconds as u32)?;
687        self.edit_regs(true, |regs| {
688            regs.tr()
689                .modify(|_, w| unsafe { w.st().bits(st).su().bits(su) });
690        })
691    }
692
693    /// Set the minutes component of the RTC's current time.
694    pub fn set_minutes(&mut self, minutes: u8) -> Result<()> {
695        if minutes > 59 {
696            return Err(Error::RtcError(RtcError::InvalidInputData));
697        }
698        let (mnt, mnu) = bcd2_encode(minutes as u32)?;
699        self.edit_regs(true, |regs| {
700            regs.tr()
701                .modify(|_, w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) });
702        })
703    }
704
705    /// Set the hours component of the RTC's current time.
706    pub fn set_hours(&mut self, hours: u8) -> Result<()> {
707        let (ht, hu) = bcd2_encode(hours as u32)?;
708
709        self.edit_regs(true, |regs| {
710            regs.tr()
711                .modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) });
712        })
713    }
714
715    /// Set the weekday component of the RTC's current date.
716    pub fn set_weekday(&mut self, weekday: u8) -> Result<()> {
717        if !(1..=7).contains(&weekday) {
718            return Err(Error::RtcError(RtcError::InvalidInputData));
719        }
720        self.edit_regs(true, |regs| {
721            regs.dr().modify(|_, w| unsafe { w.wdu().bits(weekday) });
722        })
723    }
724
725    /// Set the day component of the RTC's current date.
726    pub fn set_day(&mut self, day: u8) -> Result<()> {
727        if !(1..=31).contains(&day) {
728            return Err(Error::RtcError(RtcError::InvalidInputData));
729        }
730        let (dt, du) = bcd2_encode(day as u32)?;
731        self.edit_regs(true, |regs| {
732            regs.dr()
733                .modify(unsafe { |_, w| w.dt().bits(dt).du().bits(du) });
734        })
735    }
736
737    /// Set the month component of the RTC's current date.
738    pub fn set_month(&mut self, month: u8) -> Result<()> {
739        if !(1..=12).contains(&month) {
740            return Err(Error::RtcError(RtcError::InvalidInputData));
741        }
742        let (mt, mu) = bcd2_encode(month as u32)?;
743        self.edit_regs(true, |regs| {
744            regs.dr()
745                .modify(|_, w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) });
746        })
747    }
748
749    /// Set the year component of the RTC's current date.
750    pub fn set_year(&mut self, year: u16) -> Result<()> {
751        if !(1970..=2038).contains(&year) {
752            // todo: Is this right?
753            return Err(Error::RtcError(RtcError::InvalidInputData));
754        }
755        let (yt, yu) = bcd2_encode(year as u32 - 2_000)?;
756        // todo RTC is 2000 based ? Not sure best way to handle this.
757        self.edit_regs(true, |regs| {
758            regs.dr()
759                .modify(|_, w| unsafe { w.yt().bits(yt).yu().bits(yu) });
760        })
761    }
762
763    /// Set the date using NaiveDate (ISO 8601 calendar date without timezone).
764    /// WeekDay is set using the `set_weekday` method
765    pub fn set_date(&mut self, date: &NaiveDate) -> Result<()> {
766        if date.year() < 1970 {
767            // todo: Is this right?
768            return Err(Error::RtcError(RtcError::InvalidInputData));
769        }
770
771        let (yt, yu) = bcd2_encode((date.year() - 2_000) as u32)?;
772        let (mt, mu) = bcd2_encode(date.month())?;
773        let (dt, du) = bcd2_encode(date.day())?;
774
775        self.edit_regs(true, |regs| {
776            regs.dr().write(|w| unsafe {
777                w.dt().bits(dt);
778                w.du().bits(du);
779                w.mt().bit(mt > 0);
780                w.mu().bits(mu);
781                w.yt().bits(yt);
782                w.yu().bits(yu)
783            });
784        })
785    }
786
787    /// Set the current datetime.
788    pub fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<()> {
789        if date.year() < 1970 {
790            // todo is this right?
791            return Err(Error::RtcError(RtcError::InvalidInputData));
792        }
793
794        self.set_24h_fmt()?;
795        let (yt, yu) = bcd2_encode((date.year() - 2_000) as u32)?;
796        let (mt, mu) = bcd2_encode(date.month())?;
797        let (dt, du) = bcd2_encode(date.day())?;
798
799        let (ht, hu) = bcd2_encode(date.hour())?;
800        let (mnt, mnu) = bcd2_encode(date.minute())?;
801        let (st, su) = bcd2_encode(date.second())?;
802
803        self.edit_regs(true, |regs| {
804            regs.dr().write(|w| unsafe {
805                w.dt().bits(dt);
806                w.du().bits(du);
807                w.mt().bit(mt > 0);
808                w.mu().bits(mu);
809                w.yt().bits(yt);
810                w.yu().bits(yu)
811            });
812        })?;
813
814        self.edit_regs(true, |regs| {
815            regs.tr().write(|w| unsafe {
816                w.ht().bits(ht);
817                w.hu().bits(hu);
818                w.mnt().bits(mnt);
819                w.mnu().bits(mnu);
820                w.st().bits(st);
821                w.su().bits(su);
822                w.pm().clear_bit()
823            });
824        })?;
825
826        Ok(())
827    }
828
829    /// Get the seconds component of the current time.
830    pub fn get_seconds(&mut self) -> u8 {
831        let tr = self.regs.tr().read();
832        bcd2_decode(tr.st().bits(), tr.su().bits()) as u8
833    }
834
835    /// Get the minutes component of the current time.
836    pub fn get_minutes(&mut self) -> u8 {
837        let tr = self.regs.tr().read();
838        bcd2_decode(tr.mnt().bits(), tr.mnu().bits()) as u8
839    }
840
841    /// Get the hours component of the current time.
842    pub fn get_hours(&mut self) -> u8 {
843        let tr = self.regs.tr().read();
844        bcd2_decode(tr.ht().bits(), tr.hu().bits()) as u8
845    }
846
847    /// Get the current time.
848    pub fn get_time(&mut self) -> NaiveTime {
849        NaiveTime::from_hms_opt(
850            self.get_hours().into(),
851            self.get_minutes().into(),
852            self.get_seconds().into(),
853        )
854        .unwrap()
855    }
856
857    /// Get the weekday component of the current date.
858    pub fn get_weekday(&mut self) -> u8 {
859        let dr = self.regs.dr().read();
860        bcd2_decode(dr.wdu().bits(), 0x00) as u8
861    }
862
863    /// Get the day component of the current date.
864    pub fn get_day(&mut self) -> u8 {
865        let dr = self.regs.dr().read();
866        bcd2_decode(dr.dt().bits(), dr.du().bits()) as u8
867    }
868
869    /// Get the month component of the current date.
870    pub fn get_month(&mut self) -> u8 {
871        let dr = self.regs.dr().read();
872        let mt: u8 = if dr.mt().bit() { 1 } else { 0 };
873        bcd2_decode(mt, dr.mu().bits()) as u8
874    }
875
876    /// Get the year component of the current date.
877    pub fn get_year(&mut self) -> u16 {
878        let dr = self.regs.dr().read();
879        (bcd2_decode(dr.yt().bits(), dr.yu().bits()) + 2000) as u16
880    }
881
882    /// Get the current date.
883    pub fn get_date(&mut self) -> NaiveDate {
884        NaiveDate::from_ymd_opt(
885            self.get_year().into(),
886            self.get_month().into(),
887            self.get_day().into(),
888        )
889        .unwrap()
890    }
891
892    /// Get the current datetime.
893    pub fn get_datetime(&mut self) -> NaiveDateTime {
894        NaiveDate::from_ymd_opt(
895            self.get_year().into(),
896            self.get_month().into(),
897            self.get_day().into(),
898        )
899        .unwrap()
900        .and_hms_opt(
901            self.get_hours().into(),
902            self.get_minutes().into(),
903            self.get_seconds().into(),
904        )
905        .unwrap()
906    }
907}
908
909// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day
910// of week), date (day of month), month, and year, expressed in binary coded decimal format
911// (BCD). The sub-seconds value is also available in binary format.
912//
913// The following helper functions encode into BCD format from integer and
914// decode to an integer from a BCD value respectively.
915fn bcd2_encode(word: u32) -> Result<(u8, u8)> {
916    let l = match (word / 10).try_into() {
917        Ok(v) => v,
918        Err(_) => {
919            return Err(Error::RtcError(RtcError::InvalidInputData));
920        }
921    };
922    let r = match (word % 10).try_into() {
923        Ok(v) => v,
924        Err(_) => {
925            return Err(Error::RtcError(RtcError::InvalidInputData));
926        }
927    };
928
929    Ok((l, r))
930}
931
932fn bcd2_decode(fst: u8, snd: u8) -> u32 {
933    (fst * 10 + snd).into()
934}