Skip to main content

atsam4_hal/
rtt.rs

1use crate::hal::timer::{CountDown, Periodic};
2use crate::pac::RTT;
3use fugit::{ExtU32, TimerDurationU32 as TimerDuration};
4use void::Void;
5
6/// RTT (Real-time Timer) can be configured in one of
7/// two ways:
8/// 1. Use 32.768 kHz (/w 16-bit prescaler) input clock
9///    to expire a 32-bit counter. The prescaler has an additional
10///    interrupt that can be triggered on incrementing.
11///    input clock to expire a 32-bit counter.
12/// 2. Use 1 Hz RC clock, 16-bit prescaler is ignored and can be used
13///    separately. This requires the RTC module is setup and enabled.
14///
15/// (1) is independent of (2), except that the 16-bit prescaler is shared.
16const SLCK_FREQ: u32 = 32_768;
17pub struct RealTimeTimer<const PRESCALER: usize, const RTC1HZ: bool> {
18    rtt: RTT,
19}
20
21impl<const PRESCALER: usize, const RTC1HZ: bool> Periodic for RealTimeTimer<PRESCALER, RTC1HZ> {}
22impl<const PRESCALER: usize, const RTC1HZ: bool> CountDown for RealTimeTimer<PRESCALER, RTC1HZ> {
23    // Create a frequency base using the prescaler
24    type Time = TimerDuration<SLCK_FREQ>;
25
26    fn start<T>(&mut self, timeout: T)
27    where
28        T: Into<Self::Time>,
29    {
30        // Disable timer during configuration
31        self.rtt.mr.modify(|_, w| w.rttdis().set_bit());
32
33        // Check if ALMIEN is set (need to disable, then re-enable)
34        let rtt_mr = self.rtt.mr.read();
35        let almien = rtt_mr.almien().bit_is_set();
36        let rttincien = rtt_mr.rttincien().bit_is_set();
37        let timeout: TimerDuration<SLCK_FREQ> = timeout.into();
38
39        // Calculate the prescaler period
40        let period: Self::Time = if RTC1HZ {
41            // When using RTC1HZ, PRESCALER must be set to 32768
42            assert_eq!(
43                PRESCALER,
44                (u16::MAX / 2) as usize,
45                "Prescaler must be set to 32768 for RTC1HZ"
46            );
47            1.secs()
48        } else {
49            let slck_duration: TimerDuration<SLCK_FREQ> = TimerDuration::from_ticks(1);
50            match PRESCALER {
51                0 => slck_duration * 2_u32.pow(16),
52                1 | 2 => {
53                    panic!("Invalid prescaler");
54                }
55                _ => slck_duration * PRESCALER as u32,
56            }
57        };
58
59        // Determine alarm value
60        let alarmv = timeout / period;
61        defmt::trace!(
62            "RTT: timeout:{:?} period:{:?} alarmv:{:?}",
63            timeout,
64            period,
65            alarmv
66        );
67
68        // ALMIEN must be disabled when setting a new alarm value
69        if almien {
70            self.disable_alarm_interrupt();
71        }
72        if rttincien {
73            self.disable_prescaler_interrupt();
74        }
75
76        // The alarm value is always alarmv - 1 as RTT_AR is set
77        // to 0xFFFF_FFFF on reset
78        self.rtt.ar.write(|w| unsafe { w.almv().bits(alarmv) });
79
80        // Re-enable ALMIEN if it was enabled
81        if almien {
82            self.enable_alarm_interrupt();
83        }
84        if rttincien {
85            self.enable_prescaler_interrupt();
86        }
87
88        // Start timer, making sure to start fresh
89        // NOTE: This seems to behave better as two calls when prescaler is set to 3
90        self.rtt.mr.modify(|_, w| w.rttdis().clear_bit());
91        self.rtt.mr.modify(|_, w| w.rttrst().set_bit());
92    }
93
94    /// Waits on the 32-bit register alarm flag (ALMS)
95    fn wait(&mut self) -> nb::Result<(), Void> {
96        // Reading clears the flag, so store it for analysis
97        // Double-reading can cause interesting issues where the module
98        // doesn't reset the timer correctly.
99        let rtt_sr = self.rtt.sr.read();
100
101        // Reading clears the flag
102        if rtt_sr.alms().bit_is_set() {
103            // Reset the timer (to ensure we're periodic)
104            self.rtt.mr.modify(|_, w| w.rttrst().set_bit());
105            Ok(())
106        } else {
107            Err(nb::Error::WouldBlock)
108        }
109    }
110}
111
112impl<const PRESCALER: usize, const RTC1HZ: bool> RealTimeTimer<PRESCALER, RTC1HZ> {
113    /// RTT is simple to initialize as it requires no other setup.
114    /// (with the exception of using a 32.768 kHz crystal).
115    /// Both the internal RC counters (32.768 kHz and 1 Hz) require
116    /// no setup.
117    ///
118    /// If prescaler is equal to zero, the prescaler period
119    /// is equal to 2^16 * SCLK period. If not, the prescaler period
120    /// is equal to us_prescaler * SCLK period.
121    /// 0         - 2^16 * SCLK
122    /// 1, 2      - Forbidden
123    /// Otherwise - RTPRES * SLCK
124    /// 3 => 32.768 kHz / 3 = 10.92267 kHz (91552.706 ns)
125    /// This means our minimum unit of time is ~92 us.
126    ///
127    /// The maximum amount of time using the minimum unit of time:
128    /// 91552.706 ns * 2^32 = 3.932159E14
129    ///  393215.9     seconds
130    ///    6553.598   minutes
131    ///     109.2266  hours
132    ///       4.55111 days
133    ///
134    /// If the RTC1HZ is enabled, a 1 Hz signal is used for the 32-bit
135    /// alarm. The prescaler is still active and can be triggered from
136    /// the prescaler increment interrupt.
137    /// This is a calibrated source and is optimized for 1 Hz (if you don't have
138    /// a physical 32.768 Hz crystal).
139    ///
140    /// ```rust
141    /// const PRESCALER: usize = 3;
142    /// let mut rtt = RealTimeTimer::<PRESCALER, false>::new(peripherals.RTT);
143    /// // Set Wait for 1 second
144    /// rtt.start(1_000_000u32.micros());
145    /// // Wait for 1 second
146    /// while !rtt.wait().is_ok() {}
147    /// // Wait for 1 second again
148    /// while !rtt.wait().is_ok() {}
149    /// ```
150    pub fn new(rtt: RTT) -> Self {
151        // Compile-time check to make sure the prescaler is not set to 1 or 2
152        crate::sealed::not_one_or_two::<PRESCALER>();
153
154        // Compile-time check to make sure prescalar is u16
155        crate::sealed::smaller_than_or_eq::<PRESCALER, { u16::MAX as usize }>();
156
157        // Disable timer while reconfiguring and prescaler interrupt before setting RTPRES
158        rtt.mr
159            .modify(|_, w| w.rttdis().set_bit().rttincien().clear_bit());
160
161        // Set the prescalar, rtc1hz and reset the prescaler
162        // NOTE: rtc1hz is write-only on some MCUs
163        rtt.mr.modify(|_, w| unsafe {
164            w.rtpres()
165                .bits(PRESCALER as u16)
166                .rtc1hz()
167                .bit(RTC1HZ)
168                .rttrst()
169                .set_bit()
170        });
171
172        Self { rtt }
173    }
174
175    /// Enable the interrupt generation for the 32-bit register
176    /// alarm. This method only sets the clock configuration to
177    /// trigger the interrupt; it does not configure the interrupt
178    /// controller or define an interrupt handler.
179    pub fn enable_alarm_interrupt(&mut self) {
180        self.rtt.mr.modify(|_, w| w.almien().set_bit());
181    }
182
183    /// Enable the interrupt generation for the 16-bit prescaler
184    /// overflow. This method only sets the clock configuration to
185    /// trigger the interrupt; it does not configure the interrupt
186    /// controller or define an interrupt handler.
187    pub fn enable_prescaler_interrupt(&mut self) {
188        self.rtt.mr.modify(|_, w| w.rttincien().set_bit());
189    }
190
191    /// Disables interrupt generation for the 32-bit register alarm.
192    /// This method only sets the clock configuration to prevent
193    /// triggering the interrupt; it does not configure the interrupt
194    /// controller.
195    pub fn disable_alarm_interrupt(&mut self) {
196        self.rtt.mr.modify(|_, w| w.almien().clear_bit());
197    }
198
199    /// Disables interrupt generation for the 16-bit prescaler overflow.
200    /// This method only sets the clock configuration to prevent
201    /// triggering the interrupt; it does not configure the interrupt
202    /// controller.
203    pub fn disable_prescaler_interrupt(&mut self) {
204        self.rtt.mr.modify(|_, w| w.rttincien().clear_bit());
205    }
206
207    /// Clear interrupt status
208    /// This will clear both prescaler and alarm interrupts
209    pub fn clear_interrupt_flags(&mut self) {
210        let _rtt_sr = self.rtt.sr.read();
211
212        // Reset the timer (to ensure we're periodic)
213        self.rtt.mr.modify(|_, w| w.rttrst().set_bit());
214    }
215}