stm32l4xx_hal/
rtc.rs

1//! RTC peripheral abstraction
2
3/// refer to AN4759 to compare features of RTC2 and RTC3
4#[cfg(not(any(
5    feature = "stm32l412",
6    feature = "stm32l422",
7    feature = "stm32l4p5",
8    feature = "stm32l4q5"
9)))]
10pub mod rtc2;
11#[cfg(not(any(
12    feature = "stm32l412",
13    feature = "stm32l422",
14    feature = "stm32l4p5",
15    feature = "stm32l4q5"
16)))]
17pub use rtc2 as rtc_registers;
18
19/// refer to AN4759 to compare features of RTC2 and RTC3
20#[cfg(any(
21    feature = "stm32l412",
22    feature = "stm32l422",
23    feature = "stm32l4p5",
24    feature = "stm32l4q5"
25))]
26pub mod rtc3;
27#[cfg(any(
28    feature = "stm32l412",
29    feature = "stm32l422",
30    feature = "stm32l4p5",
31    feature = "stm32l4q5"
32))]
33pub use rtc3 as rtc_registers;
34
35use fugit::ExtU32;
36use void::Void;
37
38use crate::{
39    datetime::*,
40    hal::timer::{self, Cancel as _},
41    pwr,
42    rcc::{APB1R1, BDCR},
43    stm32::{EXTI, RTC},
44};
45
46/// Interrupt event
47pub enum Event {
48    WakeupTimer,
49    AlarmA,
50    AlarmB,
51    Timestamp,
52}
53
54pub enum Alarm {
55    AlarmA,
56    AlarmB,
57}
58
59impl From<Alarm> for Event {
60    fn from(a: Alarm) -> Self {
61        match a {
62            Alarm::AlarmA => Event::AlarmA,
63            Alarm::AlarmB => Event::AlarmB,
64        }
65    }
66}
67
68/// RTC Abstraction
69pub struct Rtc {
70    rtc: RTC,
71    rtc_config: RtcConfig,
72}
73
74#[derive(Copy, Clone, Debug, PartialEq)]
75#[repr(u8)]
76pub enum RtcClockSource {
77    /// 00: No clock
78    NoClock = 0b00,
79    /// 01: LSE oscillator clock used as RTC clock
80    LSE = 0b01,
81    /// 10: LSI oscillator clock used as RTC clock
82    LSI = 0b10,
83    /// 11: HSE oscillator clock divided by 32 used as RTC clock
84    HSE = 0b11,
85}
86
87#[derive(Copy, Clone, Debug, PartialEq)]
88#[repr(u8)]
89pub enum RtcWakeupClockSource {
90    /// RTC/16 clock is selected
91    RtcClkDiv16 = 0b000,
92    /// RTC/8 clock is selected
93    RtcClkDiv8 = 0b001,
94    /// RTC/4 clock is selected
95    RtcClkDiv4 = 0b010,
96    /// RTC/2 clock is selected
97    RtcClkDiv2 = 0b011,
98    /// ck_spre (usually 1 Hz) clock is selected. Handling of the 2 ** 16 bit is done if values
99    /// larger than 2 ** 16 are passed to the timer start function.
100    CkSpre = 0b100,
101}
102
103#[derive(Copy, Clone, Debug, PartialEq)]
104pub struct RtcConfig {
105    /// RTC clock source
106    clock_config: RtcClockSource,
107    /// Wakeup clock source
108    wakeup_clock_config: RtcWakeupClockSource,
109    /// Asynchronous prescaler factor
110    /// This is the asynchronous division factor:
111    /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
112    /// ck_apre drives the subsecond register
113    async_prescaler: u8,
114    /// Synchronous prescaler factor
115    /// This is the synchronous division factor:
116    /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
117    /// ck_spre must be 1Hz
118    sync_prescaler: u16,
119}
120
121impl Default for RtcConfig {
122    /// LSI with prescalers assuming 32.768 kHz.
123    /// Raw sub-seconds in 1/256.
124    fn default() -> Self {
125        RtcConfig {
126            clock_config: RtcClockSource::LSI,
127            wakeup_clock_config: RtcWakeupClockSource::CkSpre,
128            async_prescaler: 127,
129            sync_prescaler: 255,
130        }
131    }
132}
133
134impl RtcConfig {
135    /// Sets the clock source of RTC config
136    pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
137        self.clock_config = cfg;
138        self
139    }
140
141    /// Set the asynchronous prescaler of RTC config
142    pub fn async_prescaler(mut self, prescaler: u8) -> Self {
143        self.async_prescaler = prescaler;
144        self
145    }
146
147    /// Set the synchronous prescaler of RTC config
148    pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
149        self.sync_prescaler = prescaler;
150        self
151    }
152
153    /// Set the Clock Source for the Wakeup Timer
154    pub fn wakeup_clock_config(mut self, cfg: RtcWakeupClockSource) -> Self {
155        self.wakeup_clock_config = cfg;
156        self
157    }
158}
159
160impl Rtc {
161    pub fn rtc(
162        rtc: RTC,
163        apb1r1: &mut APB1R1,
164        bdcr: &mut BDCR,
165        pwrcr1: &mut pwr::CR1,
166        rtc_config: RtcConfig,
167    ) -> Self {
168        // assert_eq!(clocks.lsi(), true); // make sure LSI is enabled
169        // enable peripheral clock for communication
170        apb1r1.enr().modify(|_, w| w.rtcapben().set_bit());
171        pwrcr1.reg().read(); // read to allow the pwr clock to enable
172
173        let mut rtc_struct = Self { rtc, rtc_config };
174        rtc_struct.set_config(bdcr, pwrcr1, rtc_config);
175
176        rtc_struct
177    }
178
179    /// Get date and time touple
180    pub fn get_date_time(&self) -> (Date, Time) {
181        let time;
182        let date;
183
184        let sync_p = self.rtc_config.sync_prescaler as u32;
185        let micros =
186            1_000_000u32 / (sync_p + 1) * (sync_p - self.rtc.ssr.read().ss().bits() as u32);
187        let timer = self.rtc.tr.read();
188        let cr = self.rtc.cr.read();
189
190        // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
191        // calendar shadow registers until RTC_DR is read.
192        let dater = self.rtc.dr.read();
193
194        time = Time::new(
195            (bcd2_to_byte((timer.ht().bits(), timer.hu().bits())) as u32).hours(),
196            (bcd2_to_byte((timer.mnt().bits(), timer.mnu().bits())) as u32).minutes(),
197            (bcd2_to_byte((timer.st().bits(), timer.su().bits())) as u32).secs(),
198            micros.micros(),
199            cr.bkp().bit(),
200        );
201
202        date = Date::new(
203            dater.wdu().bits().into(),
204            bcd2_to_byte((dater.dt().bits(), dater.du().bits())).into(),
205            bcd2_to_byte((dater.mt().bit() as u8, dater.mu().bits())).into(),
206            (bcd2_to_byte((dater.yt().bits(), dater.yu().bits())) as u16 + 1970_u16).into(),
207        );
208
209        (date, time)
210    }
211
212    /// Set Date and Time
213    pub fn set_date_time(&mut self, date: Date, time: Time) {
214        self.write(true, |rtc| {
215            set_time_raw(rtc, time);
216            set_date_raw(rtc, date);
217        })
218    }
219
220    /// Set Time
221    /// Note: If setting both time and date, use set_date_time(...) to avoid errors.
222    pub fn set_time(&mut self, time: Time) {
223        self.write(true, |rtc| {
224            set_time_raw(rtc, time);
225        })
226    }
227
228    /// Set Date
229    /// Note: If setting both time and date, use set_date_time(...) to avoid errors.
230    pub fn set_date(&mut self, date: Date) {
231        self.write(true, |rtc| {
232            set_date_raw(rtc, date);
233        })
234    }
235
236    pub fn get_config(&self) -> RtcConfig {
237        self.rtc_config
238    }
239
240    /// Sets the time at which an alarm will be triggered
241    /// This also clears the alarm flag if it is set
242    pub fn set_alarm(&mut self, alarm: Alarm, date: Date, time: Time) {
243        let (dt, du) = byte_to_bcd2(date.date as u8);
244        let (ht, hu) = byte_to_bcd2(time.hours as u8);
245        let (mnt, mnu) = byte_to_bcd2(time.minutes as u8);
246        let (st, su) = byte_to_bcd2(time.seconds as u8);
247
248        self.write(false, |rtc| match alarm {
249            Alarm::AlarmA => {
250                rtc.cr.modify(|_, w| w.alrae().clear_bit()); // Disable Alarm A
251                rtc_registers::clear_alarm_a_flag(rtc);
252                while !rtc_registers::is_alarm_a_accessible(rtc) {}
253
254                rtc.alrmar.modify(|_, w| unsafe {
255                    w.dt()
256                        .bits(dt)
257                        .du()
258                        .bits(du)
259                        .ht()
260                        .bits(ht)
261                        .hu()
262                        .bits(hu)
263                        .mnt()
264                        .bits(mnt)
265                        .mnu()
266                        .bits(mnu)
267                        .st()
268                        .bits(st)
269                        .su()
270                        .bits(su)
271                        .pm()
272                        .clear_bit()
273                        .wdsel()
274                        .clear_bit()
275                });
276                // binary mode alarm not implemented (RTC3 only)
277                // subsecond alarm not implemented
278                // would need a conversion method between `time.micros` and RTC ticks
279                // write the SS value and mask to `rtc.alrmassr`
280
281                // enable alarm and reenable interrupt if it was enabled
282                rtc.cr.modify(|_, w| w.alrae().set_bit());
283            }
284            Alarm::AlarmB => {
285                rtc.cr.modify(|_, w| w.alrbe().clear_bit());
286
287                rtc_registers::clear_alarm_b_flag(rtc);
288                while !rtc_registers::is_alarm_b_accessible(rtc) {}
289
290                rtc.alrmbr.modify(|_, w| unsafe {
291                    w.dt()
292                        .bits(dt)
293                        .du()
294                        .bits(du)
295                        .ht()
296                        .bits(ht)
297                        .hu()
298                        .bits(hu)
299                        .mnt()
300                        .bits(mnt)
301                        .mnu()
302                        .bits(mnu)
303                        .st()
304                        .bits(st)
305                        .su()
306                        .bits(su)
307                        .pm()
308                        .clear_bit()
309                        .wdsel()
310                        .clear_bit()
311                });
312                // binary mode alarm not implemented (RTC3 only)
313                // subsecond alarm not implemented
314                // would need a conversion method between `time.micros` and RTC ticks
315                // write the SS value and mask to `rtc.alrmbssr`
316
317                // enable alarm and reenable interrupt if it was enabled
318                rtc.cr.modify(|_, w| w.alrbe().set_bit());
319            }
320        });
321    }
322
323    /// Starts listening for an interrupt event
324    pub fn listen(&mut self, exti: &mut EXTI, event: Event) {
325        self.write(false, |rtc| match event {
326            Event::WakeupTimer => {
327                exti.rtsr1.modify(|_, w| w.tr20().set_bit());
328                exti.imr1.modify(|_, w| w.mr20().set_bit());
329                rtc.cr.modify(|_, w| w.wutie().set_bit())
330            }
331            Event::AlarmA => {
332                // Workaround until tr17() is implemented ()
333                exti.rtsr1.modify(|_, w| w.tr18().set_bit());
334                exti.imr1.modify(|_, w| w.mr18().set_bit());
335                rtc.cr.modify(|_, w| w.alraie().set_bit())
336            }
337            Event::AlarmB => {
338                exti.rtsr1.modify(|_, w| w.tr18().set_bit());
339                exti.imr1.modify(|_, w| w.mr18().set_bit());
340                rtc.cr.modify(|_, w| w.alrbie().set_bit())
341            }
342            Event::Timestamp => {
343                exti.rtsr1.modify(|_, w| w.tr19().set_bit());
344                exti.imr1.modify(|_, w| w.mr19().set_bit());
345                rtc.cr.modify(|_, w| w.tsie().set_bit())
346            }
347        })
348    }
349
350    /// Stops listening for an interrupt event
351    pub fn unlisten(&mut self, exti: &mut EXTI, event: Event) {
352        self.write(false, |rtc| match event {
353            Event::WakeupTimer => {
354                exti.rtsr1.modify(|_, w| w.tr20().clear_bit());
355                exti.imr1.modify(|_, w| w.mr20().clear_bit());
356                rtc.cr.modify(|_, w| w.wutie().clear_bit())
357            }
358            Event::AlarmA => {
359                // Workaround until tr17() is implemented ()
360                exti.rtsr1.modify(|_, w| w.tr18().clear_bit());
361                exti.imr1.modify(|_, w| w.mr18().clear_bit());
362                rtc.cr.modify(|_, w| w.alraie().clear_bit())
363            }
364            Event::AlarmB => {
365                exti.rtsr1.modify(|_, w| w.tr18().clear_bit());
366                exti.imr1.modify(|_, w| w.mr18().clear_bit());
367                rtc.cr.modify(|_, w| w.alrbie().clear_bit())
368            }
369            Event::Timestamp => {
370                exti.rtsr1.modify(|_, w| w.tr19().clear_bit());
371                exti.imr1.modify(|_, w| w.mr19().clear_bit());
372                rtc.cr.modify(|_, w| w.tsie().clear_bit())
373            }
374        })
375    }
376
377    /// Checks for an interrupt event
378    pub fn check_interrupt(&mut self, event: Event, clear: bool) -> bool {
379        let result = match event {
380            Event::WakeupTimer => rtc_registers::is_wakeup_timer_flag_set(&self.rtc),
381            Event::AlarmA => rtc_registers::is_alarm_a_flag_set(&self.rtc),
382            Event::AlarmB => rtc_registers::is_alarm_b_flag_set(&self.rtc),
383            Event::Timestamp => rtc_registers::is_timestamp_flag_set(&self.rtc),
384        };
385        if clear {
386            self.write(false, |rtc| match event {
387                Event::WakeupTimer => {
388                    rtc_registers::clear_wakeup_timer_flag(rtc);
389                    unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 20)) };
390                }
391                Event::AlarmA => {
392                    rtc_registers::clear_alarm_a_flag(rtc);
393                    unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 18)) };
394                }
395                Event::AlarmB => {
396                    rtc_registers::clear_alarm_b_flag(rtc);
397                    unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 18)) };
398                }
399                Event::Timestamp => {
400                    rtc_registers::clear_timestamp_flag(rtc);
401                    unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 19)) };
402                }
403            })
404        }
405
406        result
407    }
408
409    /// Applies the RTC config
410    /// It this changes the RTC clock source the time will be reset
411    pub fn set_config(&mut self, bdcr: &mut BDCR, pwrcr1: &mut pwr::CR1, rtc_config: RtcConfig) {
412        // Unlock the backup domain
413        pwrcr1.reg().modify(|_, w| w.dbp().set_bit());
414        while pwrcr1.reg().read().dbp().bit_is_clear() {}
415
416        let reg = bdcr.enr().read();
417        assert!(
418            !reg.lsecsson().bit(),
419            "RTC is not compatible with LSE CSS, yet."
420        );
421
422        if !reg.rtcen().bit() || reg.rtcsel().bits() != rtc_config.clock_config as u8 {
423            bdcr.enr().modify(|_, w| w.bdrst().set_bit());
424
425            bdcr.enr().modify(|_, w| unsafe {
426                // Reset
427                w.bdrst().clear_bit();
428                // Select RTC source
429                w.rtcsel()
430                    .bits(rtc_config.clock_config as u8)
431                    .rtcen()
432                    .set_bit();
433
434                // Restore bcdr
435                w.lscosel()
436                    .bit(reg.lscosel().bit())
437                    .lscoen()
438                    .bit(reg.lscoen().bit());
439
440                w.lseon()
441                    .bit(reg.lseon().bit())
442                    .lsedrv()
443                    .bits(reg.lsedrv().bits())
444                    .lsebyp()
445                    .bit(reg.lsebyp().bit())
446            });
447        }
448
449        self.write(true, |rtc| {
450            rtc.cr.modify(|_, w| unsafe {
451                w.fmt()
452                    .clear_bit() // 24hr
453                    .osel()
454                    /*
455                        00: Output disabled
456                        01: Alarm A output enabled
457                        10: Alarm B output enabled
458                        11: Wakeup output enabled
459                    */
460                    .bits(0b00)
461                    .pol()
462                    .clear_bit() // pol high
463            });
464
465            rtc.prer.modify(|_, w| unsafe {
466                w.prediv_s()
467                    .bits(rtc_config.sync_prescaler)
468                    .prediv_a()
469                    .bits(rtc_config.async_prescaler)
470            });
471
472            // TODO configuration for output pins
473            rtc_registers::reset_gpio(rtc);
474        });
475
476        self.rtc_config = rtc_config;
477    }
478
479    /// Access the wakeup timer
480    pub fn wakeup_timer(&mut self) -> WakeupTimer {
481        WakeupTimer { rtc: self }
482    }
483
484    fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
485    where
486        F: FnOnce(&RTC) -> R,
487    {
488        // Disable write protection.
489        // This is safe, as we're only writin the correct and expected values.
490        self.rtc.wpr.write(|w| unsafe { w.key().bits(0xca) });
491        self.rtc.wpr.write(|w| unsafe { w.key().bits(0x53) });
492
493        if init_mode && !rtc_registers::is_init_mode(&self.rtc) {
494            rtc_registers::enter_init_mode(&self.rtc);
495            // wait till init state entered
496            // ~2 RTCCLK cycles
497            while !rtc_registers::is_init_mode(&self.rtc) {}
498        }
499
500        let result = f(&self.rtc);
501        if init_mode {
502            rtc_registers::exit_init_mode(&self.rtc);
503        }
504
505        // Re-enable write protection.
506        // This is safe, as the field accepts the full range of 8-bit values.
507        self.rtc.wpr.write(|w| unsafe { w.key().bits(0xff) });
508
509        result
510    }
511
512    pub const BACKUP_REGISTER_COUNT: usize = rtc_registers::BACKUP_REGISTER_COUNT;
513
514    /// Read content of the backup register.
515    ///
516    /// The registers retain their values during wakes from standby mode or system resets. They also
517    /// retain their value when Vdd is switched off as long as V_BAT is powered.
518    pub fn read_backup_register(&self, register: usize) -> Option<u32> {
519        rtc_registers::read_backup_register(&self.rtc, register)
520    }
521
522    /// Set content of the backup register.
523    ///
524    /// The registers retain their values during wakes from standby mode or system resets. They also
525    /// retain their value when Vdd is switched off as long as V_BAT is powered.
526    pub fn write_backup_register(&self, register: usize, value: u32) {
527        rtc_registers::write_backup_register(&self.rtc, register, value)
528    }
529}
530
531/// The RTC wakeup timer
532///
533/// This timer can be used in two ways:
534/// 1. Continually call `wait` until it returns `Ok(())`.
535/// 2. Set up the RTC interrupt.
536///
537/// If you use an interrupt, you should still call `wait` once, after the
538/// interrupt fired. This should return `Ok(())` immediately. Doing this will
539/// reset the timer flag. If you don't do this, the interrupt will not fire
540/// again, if you go to sleep.
541///
542/// You don't need to call `wait`, if you call `cancel`, as that also resets the
543/// flag. Restarting the timer by calling `start` will also reset the flag.
544pub struct WakeupTimer<'r> {
545    rtc: &'r mut Rtc,
546}
547
548impl timer::Periodic for WakeupTimer<'_> {}
549
550impl timer::CountDown for WakeupTimer<'_> {
551    type Time = u32;
552
553    /// Starts the wakeup timer
554    ///
555    /// The `delay` argument specifies the timer delay. If the wakeup_clock_config is set to
556    /// CkSpre, the value is in seconds and up to 17 bits
557    /// of delay are supported, giving us a range of over 36 hours.
558    /// Otherwise, the timeunit depends on the RTCCLK and the configured wakeup_clock_config value.
559    ///
560    /// # Panics
561    ///
562    /// The `delay` argument must be in the range `1 <= delay <= 2^17`.
563    /// Panics, if `delay` is outside of that range.
564    fn start<T>(&mut self, delay: T)
565    where
566        T: Into<Self::Time>,
567    {
568        let delay = delay.into();
569        assert!(1 <= delay);
570
571        if self.rtc.rtc_config.wakeup_clock_config == RtcWakeupClockSource::CkSpre {
572            assert!(delay <= 1 << 17);
573        } else {
574            assert!(delay <= 1 << 16);
575        }
576
577        // Determine the value for the wucksel register
578        let wucksel = self.rtc.rtc_config.wakeup_clock_config as u8;
579        let wucksel = wucksel
580            | if self.rtc.rtc_config.wakeup_clock_config == RtcWakeupClockSource::CkSpre
581                && delay & 0x1_00_00 != 0
582            {
583                0b010
584            } else {
585                0b000
586            };
587
588        let delay = delay - 1;
589
590        // Can't panic, as the error type is `Void`.
591        self.cancel().unwrap();
592
593        self.rtc.write(false, |rtc| {
594            // Set the wakeup delay
595            rtc.wutr.write(|w|
596                // Write the lower 16 bits of `delay`. The 17th bit is taken
597                // care of via WUCKSEL in CR (see below).
598                // This is safe, as the field accepts a full 16 bit value.
599                unsafe { w.wut().bits(delay as u16) });
600
601            rtc.cr.modify(|_, w| {
602                // Write WUCKSEL depending on value determined previously.
603                unsafe {
604                    w.wucksel().bits(wucksel);
605                }
606                // Enable wakeup timer
607                w.wute().set_bit()
608            });
609        });
610
611        // Let's wait for WUTWF to clear. Otherwise we might run into a race
612        // condition, if the user calls this method again really quickly.
613        while rtc_registers::is_wakeup_timer_write_flag_set(&self.rtc.rtc) {}
614    }
615
616    fn wait(&mut self) -> nb::Result<(), Void> {
617        if self.rtc.check_interrupt(Event::WakeupTimer, true) {
618            return Ok(());
619        }
620
621        Err(nb::Error::WouldBlock)
622    }
623}
624
625impl timer::Cancel for WakeupTimer<'_> {
626    type Error = Void;
627
628    fn cancel(&mut self) -> Result<(), Self::Error> {
629        self.rtc.write(false, |rtc| {
630            // Disable the wakeup timer
631            rtc.cr.modify(|_, w| w.wute().clear_bit());
632            while !rtc_registers::is_wakeup_timer_write_flag_set(rtc) {}
633            rtc_registers::clear_wakeup_timer_flag(rtc);
634
635            // According to the reference manual, section 26.7.4, the WUTF flag
636            // must be cleared at least 1.5 RTCCLK periods "before WUTF is set
637            // to 1 again". If that's true, we're on the safe side, because we
638            // use ck_spre as the clock for this timer, which we've scaled to 1
639            // Hz.
640            //
641            // I have the sneaking suspicion though that this is a typo, and the
642            // quote in the previous paragraph actually tries to refer to WUTE
643            // instead of WUTF. In that case, this might be a bug, so if you're
644            // seeing something weird, adding a busy loop of some length here
645            // would be a good start of your investigation.
646        });
647
648        Ok(())
649    }
650}
651
652/// Raw set time
653/// Expects init mode enabled and write protection disabled
654fn set_time_raw(rtc: &RTC, time: Time) {
655    let (ht, hu) = byte_to_bcd2(time.hours as u8);
656    let (mnt, mnu) = byte_to_bcd2(time.minutes as u8);
657    let (st, su) = byte_to_bcd2(time.seconds as u8);
658
659    rtc.tr.write(|w| unsafe {
660        w.ht()
661            .bits(ht)
662            .hu()
663            .bits(hu)
664            .mnt()
665            .bits(mnt)
666            .mnu()
667            .bits(mnu)
668            .st()
669            .bits(st)
670            .su()
671            .bits(su)
672            .pm()
673            .clear_bit()
674    });
675
676    rtc.cr.modify(|_, w| w.bkp().bit(time.daylight_savings));
677}
678
679/// Raw set date
680/// Expects init mode enabled and write protection disabled
681fn set_date_raw(rtc: &RTC, date: Date) {
682    let (dt, du) = byte_to_bcd2(date.date as u8);
683    let (mt, mu) = byte_to_bcd2(date.month as u8);
684    let yr = date.year as u16;
685    let yr_offset = (yr - 1970_u16) as u8;
686    let (yt, yu) = byte_to_bcd2(yr_offset);
687
688    rtc.dr.write(|w| unsafe {
689        w.dt()
690            .bits(dt)
691            .du()
692            .bits(du)
693            .mt()
694            .bit(mt > 0)
695            .mu()
696            .bits(mu)
697            .yt()
698            .bits(yt)
699            .yu()
700            .bits(yu)
701            .wdu()
702            .bits(date.day as u8)
703    });
704}
705
706fn byte_to_bcd2(byte: u8) -> (u8, u8) {
707    let mut bcd_high: u8 = 0;
708    let mut value = byte;
709
710    while value >= 10 {
711        bcd_high += 1;
712        value -= 10;
713    }
714
715    (bcd_high, ((bcd_high << 4) | value) as u8)
716}
717
718fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
719    let value = bcd.1 | bcd.0 << 4;
720
721    let tmp = ((value & 0xF0) >> 0x4) * 10;
722
723    tmp + (value & 0x0F)
724}