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