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