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}