atsamd_hal/rtc/
modes.rs

1//! Provides low-level access to the [Real Time Clock (RTC)](https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-14/GUID-E17D8859-D42B-4B0E-9B81-76168A0C38AC.html) peripheral on ATSAMD chips.
2//!
3//! The main abstraction is the [`RtcMode`] trait, which exposes
4//! static/associated functions to use the RTC in in a particular mode. All functions are marked as [`inline`](https://matklad.github.io/2021/07/09/inline-in-rust.html)
5//! so that this should be a zero cost abstraction.
6//!
7//! This module is intended to serve as the basis for the safe
8//! [`Rtc`](crate::rtc::Rtc) abstraction as well as RTIC and embassy time
9//! drivers.
10//!
11//! Abstraction benefits:
12//! - Handles all RTC register accesses.
13//! - Handles RTC [register synchronization](https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-14/GUID-ABE2D37F-8125-4279-9955-BC3900046CFF.html).
14//! - Handles ATSAMD chip variations.
15//!
16//! The idea is that various higher-level users of these abstractions will not
17//! have to handle these low-level aspects of using the RTC. However, this
18//! module does not present a safe interface. For example, many of the methods
19//! in [`RtcMode`] assume that the RTC has already been put into the correct
20//! mode (using [`RtcMode::set_mode`]), but without enforcing this in any way.
21
22// As explained in the [datasheets](https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-14/GUID-ABE2D37F-8125-4279-9955-BC3900046CFF.html),
23// reading a read-synced register may result in
24// an old value, which we try to avoid by ensuring that SYNCBUSY is clear
25// before reading. A write to a write-synced register will be discarded if
26// syncing is happening during the write. As such, we also ensure that SYNCBUSY
27// is clear before writing to a synced register. Throughout the crate, every
28// register access should be prefaced by a `SYNC` comment indicating the
29// required synchronization. The presence of this comment signals that this
30// access was checked in the datasheet and accounted for.
31
32use crate::pac;
33use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
34use pac::Rtc;
35
36// Import prescaler divider enum
37#[hal_cfg(any("rtc-d11", "rtc-d21"))]
38use crate::pac::rtc::mode0::ctrl::Prescalerselect;
39#[hal_cfg("rtc-d5x")]
40use crate::pac::rtc::mode0::ctrla::Prescalerselect;
41
42/// Type-level enum for RTC interrupts.
43pub trait RtcInterrupt {
44    /// Enable this interrupt.
45    fn enable(rtc: &Rtc);
46    /// Disable this interrupt.
47    fn disable(rtc: &Rtc);
48    /// Returns whether the interrupt has been triggered.
49    fn check_flag(rtc: &Rtc) -> bool;
50    /// Clears the interrupt flag so the ISR will not be called again
51    /// immediately.
52    fn clear_flag(rtc: &Rtc);
53}
54
55/// Macro to easily declare an RTC interrupt.
56macro_rules! create_rtc_interrupt {
57    ($mode:ident, $name:ident, $bit:ident) => {
58        #[doc = concat!("Type-level variant for the ", stringify!($name), " interrupt in ", stringify!($mode))]
59        pub enum $name {}
60        impl RtcInterrupt for $name {
61            #[inline]
62            fn enable(rtc: &Rtc) {
63                // SYNC: None
64                rtc.$mode().intenset().write(|w| w.$bit().set_bit());
65            }
66
67            #[inline]
68            fn disable(rtc: &Rtc) {
69                // SYNC: None
70                rtc.$mode().intenclr().write(|w| w.$bit().set_bit());
71            }
72
73            #[inline]
74            fn check_flag(rtc: &Rtc) -> bool {
75                // SYNC: None
76                rtc.$mode().intflag().read().$bit().bit_is_set()
77            }
78
79            #[inline]
80            fn clear_flag(rtc: &Rtc) {
81                // SYNC: None
82                rtc.$mode().intflag().write(|w| w.$bit().set_bit());
83            }
84        }
85    };
86}
87
88/// An abstraction of an RTC in a particular mode that provides low-level
89/// access and handles all register syncing issues using only associated
90/// functions.
91pub trait RtcMode {
92    /// The type of the COUNT register.
93    type Count: Copy + PartialEq + Eq;
94
95    /// Sets this mode in the CTRL register.
96    ///
97    /// # Safety
98    ///
99    /// This should only be called when the RTC is disabled, and is typically
100    /// only called once before calling most other methods.
101    fn set_mode(rtc: &Rtc);
102
103    /// Sets a compare value.
104    ///
105    /// # Safety
106    ///
107    /// Should be called only after setting the RTC mode using
108    /// [`set_mode`](RtcMode::set_mode).
109    fn set_compare(rtc: &Rtc, number: usize, value: Self::Count);
110
111    /// Retrieves a compare from the register.
112    ///
113    /// # Safety
114    ///
115    /// Should be called only after setting the RTC mode using
116    /// [`set_mode`](RtcMode::set_mode).
117    #[cfg(feature = "rtic")]
118    fn get_compare(rtc: &Rtc, number: usize) -> Self::Count;
119
120    /// Returns the current synced COUNT value.
121    ///
122    /// # Safety
123    ///
124    /// Should be called only after setting the RTC mode using
125    /// [`set_mode`](RtcMode::set_mode).
126    fn count(rtc: &Rtc) -> Self::Count;
127
128    /// Sets the current synced COUNT value.
129    ///
130    /// # Safety
131    ///
132    /// Should be called only after setting the RTC mode using
133    /// [`set_mode`](RtcMode::set_mode).
134    fn set_count(rtc: &Rtc, count: Self::Count);
135
136    /// Returns whether register syncing is currently happening.
137    ///
138    /// # Safety
139    ///
140    /// Can be called any time.
141    #[inline]
142    #[hal_macro_helper]
143    fn sync_busy(rtc: &Rtc) -> bool {
144        // NOTE: This register and field are the same in all modes.
145        // SYNC: None
146        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
147        return rtc.mode0().status().read().syncbusy().bit_is_set();
148        // SYNC: None
149        #[hal_cfg("rtc-d5x")]
150        return rtc.mode0().syncbusy().read().bits() != 0;
151    }
152
153    /// Resets the RTC, leaving it disabled in MODE0.
154    ///
155    /// # Safety
156    ///
157    /// Can be called any time.
158    #[inline]
159    #[hal_macro_helper]
160    fn reset(rtc: &Rtc) {
161        // Reset RTC back to initial settings, which disables it and enters mode 0.
162        // NOTE: This register and field are the same in all modes.
163        // SYNC: Write
164        Self::sync(rtc);
165        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
166        rtc.mode0().ctrl().modify(|_, w| w.swrst().set_bit());
167        #[hal_cfg("rtc-d5x")]
168        rtc.mode0().ctrla().modify(|_, w| w.swrst().set_bit());
169
170        // Wait for the reset to complete
171        // SYNC: Write (we just read though)
172        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
173        while rtc.mode0().ctrl().read().swrst().bit_is_set() {}
174        #[hal_cfg("rtc-d5x")]
175        // NOTE: There is also a SWRST bit in the SYNCBUSY register but the bit CTRLA register
176        // is the one that clears when the reset is complete.
177        while rtc.mode0().ctrla().read().swrst().bit_is_set() {}
178    }
179
180    /// Sets the clock prescaler divider to lower the tick rate.
181    ///
182    /// # Safety
183    ///
184    /// Should be called only when the RTC is disabled.
185    #[inline]
186    #[hal_macro_helper]
187    fn set_prescaler(rtc: &Rtc, divider: Prescalerselect) {
188        // NOTE: This register and field are the same in all modes.
189        // SYNC: None
190        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
191        rtc.mode0()
192            .ctrl()
193            .modify(|_, w| w.prescaler().variant(divider));
194        #[hal_cfg("rtc-d5x")]
195        rtc.mode0()
196            .ctrla()
197            .modify(|_, w| w.prescaler().variant(divider));
198    }
199
200    /// Starts the RTC and does any required initialization for this mode.
201    ///
202    /// # Safety
203    ///
204    /// Should be called only after setting the RTC mode using
205    /// [`set_mode`](RtcMode::set_mode).
206    #[inline]
207    #[hal_macro_helper]
208    fn start_and_initialize(rtc: &Rtc) {
209        Self::enable(rtc);
210
211        // Enable counter sync on SAMx5x, the counter cannot be read otherwise.
212        #[hal_cfg("rtc-d5x")]
213        {
214            // Enable counter synchronization
215            // NOTE: This register and field are the same in all modes.
216            // SYNC: Write
217            Self::sync(rtc);
218            rtc.mode0().ctrla().modify(|_, w| {
219                // Notifications may not work with prescaler disabled
220                w.prescaler().div1();
221                w.countsync().set_bit();
222                w
223            });
224
225            // Errata: The first read of the count is incorrect so we need to read it
226            // then wait for it to change.
227            Self::_wait_for_count_change(rtc);
228        }
229    }
230
231    /// Enables an RTC interrupt.
232    ///
233    /// # Safety
234    ///
235    /// Should be called only after setting the RTC mode using
236    /// [`set_mode`](RtcMode::set_mode).
237    #[inline]
238    fn enable_interrupt<I: RtcInterrupt>(rtc: &Rtc) {
239        I::enable(rtc);
240    }
241
242    /// Disables an RTC interrupt.
243    ///
244    /// # Safety
245    ///
246    /// Should be called only after setting the RTC mode using
247    /// [`set_mode`](RtcMode::set_mode).
248    #[inline]
249    fn disable_interrupt<I: RtcInterrupt>(rtc: &Rtc) {
250        I::disable(rtc);
251    }
252
253    /// Returns whether an RTC interrupt has been triggered.
254    ///
255    /// # Safety
256    ///
257    /// Should be called only after setting the RTC mode using
258    /// [`set_mode`](RtcMode::set_mode).
259    #[inline]
260    fn check_interrupt_flag<I: RtcInterrupt>(rtc: &Rtc) -> bool {
261        I::check_flag(rtc)
262    }
263
264    /// Clears an RTC interrupt flag so the ISR will not be called again
265    /// immediately.
266    ///
267    /// # Safety
268    ///
269    /// Should be called only after setting the RTC mode using
270    /// [`set_mode`](RtcMode::set_mode).
271    #[inline]
272    fn clear_interrupt_flag<I: RtcInterrupt>(rtc: &Rtc) {
273        I::clear_flag(rtc);
274    }
275
276    /// Waits for any register syncing to be completed, or returns immediately
277    /// if not currently syncing.
278    ///
279    /// # Safety
280    ///
281    /// Can be called any time.
282    #[inline]
283    fn sync(rtc: &Rtc) {
284        while Self::sync_busy(rtc) {}
285    }
286
287    /// Disables the RTC.
288    ///
289    /// # Safety
290    ///
291    /// Can be called any time.
292    #[inline]
293    #[hal_macro_helper]
294    fn disable(rtc: &Rtc) {
295        // NOTE: This register and field are the same in all modes.
296        // SYNC: Write
297        Self::sync(rtc);
298        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
299        rtc.mode0().ctrl().modify(|_, w| w.enable().clear_bit());
300        #[hal_cfg("rtc-d5x")]
301        rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit());
302    }
303
304    /// Enables the RTC.
305    ///
306    /// # Safety
307    ///
308    /// Can be called any time.
309    #[inline]
310    #[hal_macro_helper]
311    fn enable(rtc: &Rtc) {
312        // NOTE: This register and field are the same in all modes.
313        // SYNC: Write
314        Self::sync(rtc);
315
316        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
317        rtc.mode0().ctrl().modify(|_, w| w.enable().set_bit());
318        #[hal_cfg("rtc-d5x")]
319        rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit());
320    }
321
322    /// Waits until the COUNT register changes.
323    ///
324    /// Note that this may not necessarily be the next tick numerically due sync
325    /// delay.
326    ///
327    /// # Safety
328    ///
329    /// Should be called only after setting the RTC mode using
330    /// [`set_mode`](RtcMode::set_mode). This will halt forever if called when
331    /// the RTC is disabled.
332    #[inline]
333    fn _wait_for_count_change(rtc: &Rtc) -> Self::Count {
334        let mut last_count = Self::count(rtc);
335
336        loop {
337            let count = Self::count(rtc);
338
339            if count != last_count {
340                break count;
341            }
342
343            last_count = count;
344        }
345    }
346}
347
348/// Interface for using the RTC in MODE0 (32-bit COUNT)
349pub mod mode0 {
350    use super::*;
351
352    create_rtc_interrupt!(mode0, Compare0, cmp0);
353    #[cfg(feature = "rtic")]
354    #[hal_cfg("rtc-d5x")]
355    create_rtc_interrupt!(mode0, Compare1, cmp1);
356    #[cfg(feature = "rtic")]
357    #[hal_cfg("rtc-d5x")]
358    create_rtc_interrupt!(mode0, Overflow, ovf);
359
360    /// The RTC operating in MODE0 (32-bit COUNT)
361    pub struct RtcMode0;
362
363    impl RtcMode0 {
364        /// Sets or resets the match clear bit, which clears the counter when a
365        /// compare value matches.
366        ///
367        /// # Safety
368        ///
369        /// This should only be called when the RTC is disabled.
370        #[inline]
371        #[hal_macro_helper]
372        pub fn set_match_clear(rtc: &Rtc, enable: bool) {
373            // SYNC: None
374            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
375            rtc.mode0().ctrl().modify(|_, w| w.matchclr().bit(enable));
376            #[hal_cfg("rtc-d5x")]
377            rtc.mode0().ctrla().modify(|_, w| w.matchclr().bit(enable));
378        }
379    }
380
381    impl RtcMode for RtcMode0 {
382        type Count = u32;
383
384        #[inline]
385        #[hal_macro_helper]
386        fn set_mode(rtc: &Rtc) {
387            // NOTE: This register and field are the same in all modes.
388            // SYNC: None (for these bits)
389            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
390            rtc.mode0().ctrl().modify(|_, w| w.mode().count32());
391            #[hal_cfg("rtc-d5x")]
392            rtc.mode0().ctrla().modify(|_, w| w.mode().count32());
393        }
394
395        #[inline]
396        fn set_compare(rtc: &Rtc, number: usize, value: Self::Count) {
397            // SYNC: Write
398            Self::sync(rtc);
399            unsafe {
400                rtc.mode0().comp(number).write(|w| w.comp().bits(value));
401            }
402        }
403
404        #[inline]
405        #[cfg(feature = "rtic")]
406        fn get_compare(rtc: &Rtc, number: usize) -> Self::Count {
407            // SYNC: Write (we just read though)
408            rtc.mode0().comp(number).read().bits()
409        }
410
411        #[inline]
412        #[hal_macro_helper]
413        fn count(rtc: &Rtc) -> Self::Count {
414            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
415            {
416                // Request syncing of the COUNT register.
417                // SYNC: None
418                rtc.mode0().readreq().modify(|_, w| w.rreq().set_bit());
419            }
420
421            // SYNC: Read/Write
422            Self::sync(rtc);
423            rtc.mode0().count().read().bits()
424        }
425
426        #[inline]
427        fn set_count(rtc: &Rtc, count: Self::Count) {
428            // SYNC: Read/Write
429            Self::sync(rtc);
430            unsafe { rtc.mode0().count().write(|w| w.count().bits(count)) };
431        }
432    }
433}
434
435/// Interface for using the RTC in MODE1 (16-bit COUNT)
436#[hal_cfg(any("rtc-d11", "rtc-d21"))]
437#[cfg(feature = "rtic")]
438pub mod mode1 {
439    use super::*;
440
441    create_rtc_interrupt!(mode1, Compare0, cmp0);
442    #[cfg(feature = "rtic")]
443    create_rtc_interrupt!(mode1, Compare1, cmp1);
444    #[cfg(feature = "rtic")]
445    create_rtc_interrupt!(mode1, Overflow, ovf);
446
447    /// The RTC operating in MODE1 (16-bit COUNT)
448    pub struct RtcMode1;
449
450    impl RtcMode for RtcMode1 {
451        type Count = u16;
452
453        #[inline]
454        #[hal_macro_helper]
455        fn set_mode(rtc: &Rtc) {
456            // SYNC: Write
457            Self::sync(rtc);
458            // NOTE: This register and field are the same in all modes.
459            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
460            rtc.mode0().ctrl().modify(|_, w| w.mode().count16());
461            #[hal_cfg("rtc-d5x")]
462            rtc.mode0().ctrla().modify(|_, w| w.mode().count16());
463
464            // Set the mode 1 period
465            // SYNC: Write
466            Self::sync(rtc);
467            unsafe { rtc.mode1().per().write(|w| w.bits(0xFFFF)) };
468        }
469
470        #[inline]
471        fn set_compare(rtc: &Rtc, number: usize, value: Self::Count) {
472            // SYNC: Write
473            Self::sync(rtc);
474            unsafe { rtc.mode1().comp(number).write(|w| w.comp().bits(value)) };
475        }
476
477        #[inline]
478        #[cfg(feature = "rtic")]
479        fn get_compare(rtc: &Rtc, number: usize) -> Self::Count {
480            // SYNC: Write (we just read though)
481            rtc.mode1().comp(number).read().bits()
482        }
483
484        #[inline]
485        #[hal_macro_helper]
486        fn count(rtc: &Rtc) -> Self::Count {
487            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
488            {
489                // Request syncing of the COUNT register.
490                // SYNC: None
491                rtc.mode1().readreq().modify(|_, w| w.rreq().set_bit());
492            }
493
494            // SYNC: Read/Write
495            Self::sync(rtc);
496            rtc.mode1().count().read().bits()
497        }
498
499        #[inline]
500        fn set_count(rtc: &Rtc, count: Self::Count) {
501            // SYNC: Read/Write
502            Self::sync(rtc);
503            unsafe { rtc.mode1().count().write(|w| w.count().bits(count)) };
504        }
505    }
506}
507
508/// Interface for using the RTC in MODE2 (Clock/Calendar)
509pub mod mode2 {
510    use super::*;
511
512    // These actually aren't needed for anything right now
513    //create_rtc_interrupt!(mode2, Alarm0, alarm0);
514    //create_rtc_interrupt!(mode2, Alarm1, alarm1);
515
516    /// Datetime represents an RTC clock/calendar value.
517    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
518    pub struct Datetime {
519        pub seconds: u8,
520        pub minutes: u8,
521        pub hours: u8,
522        pub day: u8,
523        pub month: u8,
524        pub year: u8,
525    }
526
527    /// Macro to read from to the clock or alarm registers.
528    macro_rules! from_reg_datetime {
529        ($regr:ident) => {
530            impl From<pac::rtc::mode2::$regr::R> for Datetime {
531                fn from(clock: pac::rtc::mode2::$regr::R) -> Datetime {
532                    Datetime {
533                        seconds: clock.second().bits(),
534                        minutes: clock.minute().bits(),
535                        hours: clock.hour().bits(),
536                        day: clock.day().bits(),
537                        month: clock.month().bits(),
538                        year: clock.year().bits(),
539                    }
540                }
541            }
542        };
543    }
544
545    from_reg_datetime!(clock);
546    #[hal_cfg(any("rtc-d11", "rtc-d21"))]
547    from_reg_datetime!(alarm);
548    #[hal_cfg("rtc-d5x")]
549    from_reg_datetime!(alarm0);
550    #[hal_cfg("rtc-d5x")]
551    from_reg_datetime!(alarm1);
552
553    /// Macro to write to the clock or alarm registers.
554    macro_rules! write_datetime {
555        ($regw:ident, $time:ident) => {
556            unsafe {
557                $regw
558                    .second()
559                    .bits($time.seconds)
560                    .minute()
561                    .bits($time.minutes)
562                    .hour()
563                    .bits($time.hours)
564                    .day()
565                    .bits($time.day)
566                    .month()
567                    .bits($time.month)
568                    .year()
569                    .bits($time.year)
570            }
571        };
572    }
573
574    /// The RTC operating in MODE2 (Clock/Calendar)
575    pub struct RtcMode2;
576
577    impl RtcMode for RtcMode2 {
578        type Count = Datetime;
579
580        #[inline]
581        #[hal_macro_helper]
582        fn set_mode(rtc: &Rtc) {
583            // SYNC: Write
584            Self::sync(rtc);
585            // NOTE: This register and field are the same in all modes.
586            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
587            rtc.mode0().ctrl().modify(|_, w| w.mode().clock());
588            #[hal_cfg("rtc-d5x")]
589            rtc.mode0().ctrla().modify(|_, w| w.mode().clock());
590        }
591
592        #[inline]
593        #[hal_macro_helper]
594        fn set_compare(rtc: &Rtc, _number: usize, value: Self::Count) {
595            // SYNC: Write
596            Self::sync(rtc);
597
598            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
599            rtc.mode2().alarm(0).write(|w| write_datetime!(w, value));
600            #[hal_cfg("rtc-d5x")]
601            if _number == 0 {
602                rtc.mode2().alarm0().write(|w| write_datetime!(w, value));
603            } else {
604                rtc.mode2().alarm1().write(|w| write_datetime!(w, value));
605            }
606        }
607
608        #[inline]
609        #[hal_macro_helper]
610        #[cfg(feature = "rtic")]
611        fn get_compare(rtc: &Rtc, _number: usize) -> Self::Count {
612            // SYNC: Write (we just read though)
613            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
614            return rtc.mode2().alarm(0).read().into();
615            #[hal_cfg("rtc-d5x")]
616            if _number == 0 {
617                rtc.mode2().alarm0().read().into()
618            } else {
619                rtc.mode2().alarm1().read().into()
620            }
621        }
622
623        #[inline]
624        #[hal_macro_helper]
625        fn count(rtc: &Rtc) -> Self::Count {
626            #[hal_cfg(any("rtc-d11", "rtc-d21"))]
627            {
628                // Request syncing of the COUNT register.
629                // SYNC: None
630                rtc.mode2().readreq().modify(|_, w| w.rreq().set_bit());
631            }
632
633            // SYNC: Read/Write
634            Self::sync(rtc);
635            rtc.mode2().clock().read().into()
636        }
637
638        #[inline]
639        fn set_count(rtc: &Rtc, count: Self::Count) {
640            // SYNC: Read/Write
641            Self::sync(rtc);
642            rtc.mode2().clock().write(|w| write_datetime!(w, count));
643        }
644    }
645}