atsamd_hal/rtc/
mod.rs

1//! Real-time clock/counter
2use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
3use fugit::NanosDurationU32;
4
5use crate::ehal_02;
6use crate::pac;
7use crate::pac::rtc::{Mode0, Mode2};
8use crate::time::{Hertz, Nanoseconds};
9use crate::timer_traits::InterruptDrivenTimer;
10use crate::typelevel::Sealed;
11use core::convert::Infallible;
12use core::marker::PhantomData;
13
14#[cfg(feature = "sdmmc")]
15use embedded_sdmmc::{TimeSource, Timestamp};
16
17#[cfg(feature = "rtic")]
18mod modes;
19
20#[cfg(feature = "rtic")]
21pub mod rtic;
22
23// SAMx5x imports
24#[hal_cfg("rtc-d5x")]
25use crate::pac::{
26    rtc::mode0::ctrla::Prescalerselect, rtc::mode0::Ctrla as Mode0CtrlA,
27    rtc::mode2::Ctrla as Mode2CtrlA, Mclk as Pm,
28};
29
30// SAMD11/SAMD21 imports
31#[hal_cfg(any("rtc-d11", "rtc-d21"))]
32use crate::pac::{
33    rtc::mode0::ctrl::Prescalerselect, rtc::mode0::Ctrl as Mode0CtrlA,
34    rtc::mode2::Ctrl as Mode2CtrlA, Pm,
35};
36
37/// Datetime represents an RTC clock/calendar value.
38#[derive(Debug, Clone, Copy)]
39pub struct Datetime {
40    pub seconds: u8,
41    pub minutes: u8,
42    pub hours: u8,
43    pub day: u8,
44    pub month: u8,
45    pub year: u8,
46}
47
48type ClockR = crate::pac::rtc::mode2::clock::R;
49
50impl From<ClockR> for Datetime {
51    fn from(clock: ClockR) -> Datetime {
52        Datetime {
53            seconds: clock.second().bits(),
54            minutes: clock.minute().bits(),
55            hours: clock.hour().bits(),
56            day: clock.day().bits(),
57            month: clock.month().bits(),
58            year: clock.year().bits(),
59        }
60    }
61}
62
63/// RtcMode represents the mode of the RTC
64pub trait RtcMode: Sealed {}
65
66/// ClockMode represents the Clock/Alarm mode
67pub enum ClockMode {}
68
69impl RtcMode for ClockMode {}
70impl Sealed for ClockMode {}
71
72/// Count32Mode represents the 32-bit counter mode.
73///
74/// This is a free running
75/// count-up timer. When used in Periodic/CountDown mode with the embedded-hal
76/// trait(s), it resets to zero on compare and starts counting up again.
77pub enum Count32Mode {}
78
79impl RtcMode for Count32Mode {}
80impl Sealed for Count32Mode {}
81
82#[cfg(feature = "sdmmc")]
83impl From<Datetime> for Timestamp {
84    fn from(clock: Datetime) -> Timestamp {
85        Timestamp {
86            year_since_1970: clock.year,
87            zero_indexed_month: clock.month,
88            zero_indexed_day: clock.day,
89            hours: clock.hours,
90            minutes: clock.minutes,
91            seconds: clock.seconds,
92        }
93    }
94}
95
96/// Rtc represents the RTC peripheral for either clock/calendar or timer mode.
97pub struct Rtc<Mode: RtcMode> {
98    rtc: pac::Rtc,
99    rtc_clock_freq: Hertz,
100    _mode: PhantomData<Mode>,
101}
102
103impl<Mode: RtcMode> Rtc<Mode> {
104    // --- Helper Functions for M0 vs M4 targets
105    #[inline]
106    fn mode0(&self) -> &Mode0 {
107        self.rtc.mode0()
108    }
109
110    #[inline]
111    fn mode2(&self) -> &Mode2 {
112        self.rtc.mode2()
113    }
114
115    #[inline]
116    #[hal_macro_helper]
117    fn mode0_ctrla(&self) -> &Mode0CtrlA {
118        #[hal_cfg("rtc-d5x")]
119        return self.mode0().ctrla();
120        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
121        return self.mode0().ctrl();
122    }
123
124    #[inline]
125    #[hal_macro_helper]
126    fn mode2_ctrla(&self) -> &Mode2CtrlA {
127        #[hal_cfg("rtc-d5x")]
128        return self.mode2().ctrla();
129        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
130        return self.mode2().ctrl();
131    }
132
133    #[inline]
134    #[hal_macro_helper]
135    fn sync(&self) {
136        #[hal_cfg("rtc-d5x")]
137        while self.mode2().syncbusy().read().bits() != 0 {}
138        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
139        while self.mode2().status().read().syncbusy().bit_is_set() {}
140    }
141
142    #[inline]
143    fn reset(&mut self) {
144        self.mode0_ctrla().modify(|_, w| w.swrst().set_bit());
145        self.sync();
146    }
147
148    #[inline]
149    fn enable(&mut self, enable: bool) {
150        if enable {
151            self.mode0_ctrla().modify(|_, w| w.enable().set_bit());
152        } else {
153            self.mode0_ctrla().modify(|_, w| w.enable().clear_bit());
154        }
155        self.sync();
156    }
157
158    fn create(rtc: pac::Rtc, rtc_clock_freq: Hertz) -> Self {
159        Self {
160            rtc,
161            rtc_clock_freq,
162            _mode: PhantomData,
163        }
164    }
165
166    fn into_mode<M: RtcMode>(self) -> Rtc<M> {
167        Rtc::create(self.rtc, self.rtc_clock_freq)
168    }
169
170    /// Reonfigures the peripheral for 32bit counter mode.
171    #[hal_macro_helper]
172    pub fn into_count32_mode(mut self) -> Rtc<Count32Mode> {
173        self.enable(false);
174        self.sync();
175        self.mode0_ctrla().modify(|_, w| {
176            w.mode().count32() // enable mode2 (clock)
177            .matchclr().clear_bit()
178            .prescaler().div1() // No prescaler
179        });
180        self.sync();
181
182        // enable clock sync on SAMx5x
183        #[hal_cfg("rtc-d5x")]
184        {
185            self.mode2_ctrla().modify(|_, w| {
186                w.clocksync().set_bit() // synchronize the CLOCK register
187            });
188
189            self.sync();
190        }
191
192        self.enable(true);
193        self.into_mode()
194    }
195
196    /// Reconfigures the peripheral for clock/calendar mode. Requires the source
197    /// clock to be running at 1024 Hz.
198    #[hal_macro_helper]
199    pub fn into_clock_mode(mut self) -> Rtc<ClockMode> {
200        // The max divisor is 1024, so to get 1 Hz, we need a 1024 Hz source.
201        assert_eq!(
202            self.rtc_clock_freq.to_Hz(),
203            1024_u32,
204            "RTC clk not 1024 Hz!"
205        );
206
207        self.sync();
208        self.enable(false);
209        self.sync();
210        self.mode2_ctrla().modify(|_, w| {
211            w.mode().clock() // enable mode2 (clock)
212            .clkrep().clear_bit()
213            .matchclr().clear_bit()
214            .prescaler().div1024() // 1.024 kHz / 1024 = 1Hz
215        });
216
217        // enable clock sync on SAMx5x
218        #[hal_cfg("rtc-d5x")]
219        {
220            self.mode2_ctrla().modify(|_, w| {
221                w.clocksync().set_bit() // synchronize the CLOCK register
222            });
223
224            self.sync();
225        }
226
227        self.sync();
228        self.enable(true);
229        self.into_mode()
230    }
231
232    /// Releases the RTC resource
233    pub fn free(self) -> pac::Rtc {
234        self.rtc
235    }
236}
237
238impl Rtc<Count32Mode> {
239    /// Configures the RTC in 32-bit counter mode with no prescaler (default
240    /// state after reset) and the counter initialized to zero.
241    pub fn count32_mode(rtc: pac::Rtc, rtc_clock_freq: Hertz, pm: &mut Pm) -> Self {
242        pm.apbamask().modify(|_, w| w.rtc_().set_bit());
243
244        // TODO: This may not work properly because here the count sync bit is not set
245        // as it is in Self::into_count32_mode Maybe we can just call that to
246        // avoid code duplication
247
248        let mut new_rtc = Self {
249            rtc,
250            rtc_clock_freq,
251            _mode: PhantomData,
252        };
253
254        new_rtc.reset();
255        new_rtc.enable(true);
256        new_rtc
257    }
258
259    /// Returns the internal counter value.
260    #[inline]
261    #[hal_macro_helper]
262    pub fn count32(&self) -> u32 {
263        // synchronize this read on SAMD11/21. SAMx5x is automatically synchronized
264        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
265        {
266            self.mode0().readreq().modify(|_, w| w.rcont().set_bit());
267            self.sync();
268        }
269        self.mode0().count().read().bits()
270    }
271
272    /// Sets the internal counter value.
273    #[inline]
274    pub fn set_count32(&mut self, count: u32) {
275        self.sync();
276        self.enable(false);
277
278        self.sync();
279        self.mode0()
280            .count()
281            .write(|w| unsafe { w.count().bits(count) });
282
283        self.sync();
284        self.enable(true);
285    }
286
287    /// This resets the internal counter and sets the prescaler to match the
288    /// provided timeout. You should configure the prescaler using the longest
289    /// timeout you plan to measure.
290    pub fn reset_and_compute_prescaler<T: Into<<Self as ehal_02::timer::CountDown>::Time>>(
291        &mut self,
292        timeout: T,
293    ) -> &Self {
294        let params = TimerParams::new_us(timeout, self.rtc_clock_freq);
295        let divider = params.divider;
296
297        // Disable the timer while we reconfigure it
298        self.sync();
299        self.enable(false);
300
301        // Now that we have a clock routed to the peripheral, we
302        // can ask it to perform a reset.
303        self.sync();
304        self.reset();
305
306        while self.mode0_ctrla().read().swrst().bit_is_set() {}
307
308        self.mode0_ctrla().modify(|_, w| {
309            // set clock divider...
310            w.prescaler().variant(divider);
311            // and enable RTC.
312            w.enable().set_bit()
313        });
314        self
315    }
316}
317
318impl Rtc<ClockMode> {
319    pub fn clock_mode(rtc: pac::Rtc, rtc_clock_freq: Hertz, pm: &mut Pm) -> Self {
320        Rtc::count32_mode(rtc, rtc_clock_freq, pm).into_clock_mode()
321    }
322
323    /// Returns the current clock/calendar value.
324    #[hal_macro_helper]
325    pub fn current_time(&self) -> Datetime {
326        // synchronize this read on SAMD11/21. SAMx5x is automatically synchronized
327        #[hal_cfg(any("rtc-d11", "rtc-d21"))]
328        {
329            self.mode2().readreq().modify(|_, w| w.rcont().set_bit());
330            self.sync();
331        }
332        self.mode2().clock().read().into()
333    }
334
335    /// Updates the current clock/calendar value.
336    pub fn set_time(&mut self, time: Datetime) {
337        self.mode2().clock().write(|w| unsafe {
338            w.second()
339                .bits(time.seconds)
340                .minute()
341                .bits(time.minutes)
342                .hour()
343                .bits(time.hours)
344                .day()
345                .bits(time.day)
346                .month()
347                .bits(time.month)
348                .year()
349                .bits(time.year)
350        });
351        self.sync();
352    }
353}
354
355// --- Timer / Counter Functionality
356
357impl ehal_02::timer::Periodic for Rtc<Count32Mode> {}
358impl ehal_02::timer::CountDown for Rtc<Count32Mode> {
359    type Time = Nanoseconds;
360
361    fn start<T>(&mut self, timeout: T)
362    where
363        T: Into<Self::Time>,
364    {
365        <Self as InterruptDrivenTimer>::start(self, timeout);
366    }
367
368    fn wait(&mut self) -> nb::Result<(), void::Void> {
369        // Unwrapping an unreacheable error is totally OK
370        <Self as InterruptDrivenTimer>::wait(self).unwrap();
371        Ok(())
372    }
373}
374
375impl InterruptDrivenTimer for Rtc<Count32Mode> {
376    /// Enable the interrupt generation for this hardware timer.
377    /// This method only sets the clock configuration to trigger
378    /// the interrupt; it does not configure the interrupt controller
379    /// or define an interrupt handler.
380    fn enable_interrupt(&mut self) {
381        self.mode0().intenset().write(|w| w.cmp0().set_bit());
382    }
383
384    fn start<T>(&mut self, timeout: T)
385    where
386        T: Into<NanosDurationU32>,
387    {
388        let params = TimerParams::new_us(timeout, self.rtc_clock_freq);
389        let divider = params.divider;
390        let cycles = params.cycles;
391
392        // Disable the timer while we reconfigure it
393        self.enable(false);
394
395        // Now that we have a clock routed to the peripheral, we
396        // can ask it to perform a reset.
397        self.reset();
398        while self.mode0_ctrla().read().swrst().bit_is_set() {}
399
400        // set cycles to compare to...
401        self.mode0()
402            .comp(0)
403            .write(|w| unsafe { w.comp().bits(cycles) });
404        self.mode0_ctrla().modify(|_, w| {
405            // set clock divider...
406            w.prescaler().variant(divider);
407            // clear timer on match for periodicity...
408            w.matchclr().set_bit();
409            // and enable RTC.
410            w.enable().set_bit()
411        });
412    }
413
414    fn wait(&mut self) -> nb::Result<(), Infallible> {
415        if self.mode0().intflag().read().cmp0().bit_is_set() {
416            // Writing a 1 clears the flag
417            self.mode0().intflag().modify(|_, w| w.cmp0().set_bit());
418            Ok(())
419        } else {
420            Err(nb::Error::WouldBlock)
421        }
422    }
423
424    /// Disables interrupt generation for this hardware timer.
425    /// This method only sets the clock configuration to prevent
426    /// triggering the interrupt; it does not configure the interrupt
427    /// controller.
428    fn disable_interrupt(&mut self) {
429        self.mode0().intenclr().write(|w| w.cmp0().set_bit());
430    }
431}
432
433#[cfg(feature = "sdmmc")]
434impl TimeSource for Rtc<ClockMode> {
435    fn get_timestamp(&self) -> Timestamp {
436        self.current_time().into()
437    }
438}
439
440/// Helper type for computing cycles and divider given frequency
441#[derive(Debug, Clone, Copy)]
442pub struct TimerParams {
443    pub divider: Prescalerselect,
444    pub cycles: u32,
445}
446
447impl TimerParams {
448    /// calculates RTC timer paramters based on the input frequency-based
449    /// timeout.
450    pub fn new(timeout: impl Into<Hertz>, src_freq: impl Into<Hertz>) -> Self {
451        let timeout = timeout.into();
452        let src_freq = src_freq.into();
453        let ticks: u32 = src_freq.to_Hz() / timeout.to_Hz().max(1);
454        Self::new_from_ticks(ticks)
455    }
456
457    /// calculates RTC timer paramters based on the input period-based timeout.
458    pub fn new_us(timeout: impl Into<Nanoseconds>, src_freq: impl Into<Hertz>) -> Self {
459        let timeout = timeout.into();
460        let src_freq = src_freq.into();
461        let ticks: u32 =
462            (timeout.to_nanos() as u64 * src_freq.to_Hz() as u64 / 1_000_000_000_u64) as u32;
463        Self::new_from_ticks(ticks)
464    }
465
466    /// Common helper function that gets the best divider & calculates cycles
467    /// with that divider.
468    fn new_from_ticks(ticks: u32) -> Self {
469        let divider_value = ((ticks >> 16) + 1).next_power_of_two();
470        let divider = match divider_value {
471            1 => Prescalerselect::Div1,
472            2 => Prescalerselect::Div2,
473            4 => Prescalerselect::Div4,
474            8 => Prescalerselect::Div8,
475            16 => Prescalerselect::Div16,
476            32 => Prescalerselect::Div32,
477            64 => Prescalerselect::Div64,
478            128 => Prescalerselect::Div128,
479            256 => Prescalerselect::Div256,
480            512 => Prescalerselect::Div512,
481            1024 => Prescalerselect::Div1024,
482            _ => Prescalerselect::Div1024, /* would be nice to catch this at compile time
483                                            * (rust-lang/rust#51999) */
484        };
485
486        let cycles: u32 = ticks / divider_value;
487
488        TimerParams { divider, cycles }
489    }
490}