atsamd_hal/rtc/
mod.rs

1//! Real-time clock/counter
2use atsamd_hal_macros::hal_cfg;
3use fugit::ExtU32;
4
5use crate::ehal;
6use crate::ehal_02;
7use crate::pac;
8use crate::time::{Hertz, Nanoseconds};
9use crate::timer_traits::InterruptDrivenTimer;
10use crate::typelevel::Sealed;
11use core::convert::Infallible;
12use core::marker::PhantomData;
13use modes::{
14    RtcMode as _,
15    mode0::{Compare0, RtcMode0},
16    mode2::RtcMode2,
17};
18
19#[cfg(feature = "sdmmc")]
20use embedded_sdmmc::{TimeSource, Timestamp};
21
22mod modes;
23
24#[cfg(feature = "rtic")]
25pub mod rtic;
26
27// SAMx5x imports
28#[hal_cfg("rtc-d5x")]
29use crate::pac::{Mclk as Pm, rtc::mode0::ctrla::Prescalerselect};
30
31// SAMD11/SAMD21 imports
32#[hal_cfg(any("rtc-d11", "rtc-d21"))]
33use crate::pac::{Pm, rtc::mode0::ctrl::Prescalerselect};
34
35pub use modes::mode2::Datetime;
36
37/// RtcMode represents the mode of the RTC
38pub trait RtcMode: Sealed {}
39
40/// ClockMode represents the Clock/Alarm mode
41pub enum ClockMode {}
42
43impl RtcMode for ClockMode {}
44impl Sealed for ClockMode {}
45
46/// Count32Mode represents the 32-bit counter mode.
47///
48/// This is a free running
49/// count-up timer. When used in Periodic/CountDown mode with the embedded-hal
50/// trait(s), it resets to zero on compare and starts counting up again.
51pub enum Count32Mode {}
52
53impl RtcMode for Count32Mode {}
54impl Sealed for Count32Mode {}
55
56#[cfg(feature = "sdmmc")]
57impl From<Datetime> for Timestamp {
58    fn from(clock: Datetime) -> Timestamp {
59        Timestamp {
60            year_since_1970: clock.year,
61            zero_indexed_month: clock.month,
62            zero_indexed_day: clock.day,
63            hours: clock.hours,
64            minutes: clock.minutes,
65            seconds: clock.seconds,
66        }
67    }
68}
69
70/// Represents the RTC peripheral for either clock/calendar or timer mode.
71pub struct Rtc<Mode: RtcMode> {
72    rtc: pac::Rtc,
73    rtc_clock_freq: Hertz,
74    _mode: PhantomData<Mode>,
75}
76
77impl<Mode: RtcMode> Rtc<Mode> {
78    fn set_count32_mode(rtc: &pac::Rtc) {
79        RtcMode0::disable(rtc);
80        RtcMode0::reset(rtc);
81        RtcMode0::set_mode(rtc);
82        RtcMode0::start_and_initialize(rtc);
83    }
84
85    fn set_clock_mode(rtc: &pac::Rtc, rtc_clock_freq: Hertz) {
86        let divider = TimerParams::prescaler_from_divider(rtc_clock_freq.raw() as u64)
87            .unwrap_or_else(|| {
88                panic!("cannot use clock mode with an RTC clock rate of {rtc_clock_freq}")
89            });
90
91        RtcMode2::disable(rtc);
92        RtcMode2::reset(rtc);
93        RtcMode2::set_mode(rtc);
94        RtcMode2::set_prescaler(rtc, divider);
95        RtcMode2::start_and_initialize(rtc);
96    }
97
98    fn create(rtc: pac::Rtc, rtc_clock_freq: Hertz) -> Self {
99        Self {
100            rtc,
101            rtc_clock_freq,
102            _mode: PhantomData,
103        }
104    }
105
106    fn into_mode<M: RtcMode>(self) -> Rtc<M> {
107        Rtc::create(self.rtc, self.rtc_clock_freq)
108    }
109
110    /// Reconfigures the RTC for 32-bit counter mode with no prescaler (default
111    /// state after reset) and the counter initialized to zero and started.
112    pub fn into_count32_mode(self) -> Rtc<Count32Mode> {
113        Self::set_count32_mode(&self.rtc);
114        self.into_mode()
115    }
116
117    /// Reconfigures the peripheral for clock/calendar mode. Requires the source
118    /// clock to be running at 1024 Hz.
119    pub fn into_clock_mode(self) -> Rtc<ClockMode> {
120        Self::set_clock_mode(&self.rtc, self.rtc_clock_freq);
121        self.into_mode()
122    }
123
124    /// Releases the RTC resource
125    pub fn free(self) -> pac::Rtc {
126        self.rtc
127    }
128}
129
130impl Rtc<Count32Mode> {
131    /// Configures the RTC in 32-bit counter mode with no prescaler (default
132    /// state after reset) and the counter initialized to zero and started.
133    pub fn count32_mode(rtc: pac::Rtc, rtc_clock_freq: Hertz, pm: &mut Pm) -> Self {
134        RtcMode0::disable(&rtc);
135
136        // Enable the RTC clock.
137        // TODO: This will probably be done eventually using the clock v2 API instead.
138        // This should also allow automatic determination of the clock rate.
139        pm.apbamask().modify(|_, w| w.rtc_().set_bit());
140
141        Self::set_count32_mode(&rtc);
142
143        Self {
144            rtc,
145            rtc_clock_freq,
146            _mode: PhantomData,
147        }
148    }
149
150    /// Returns the internal counter value.
151    #[inline]
152    pub fn count32(&self) -> u32 {
153        RtcMode0::count(&self.rtc)
154    }
155
156    /// Sets the internal counter value.
157    #[inline]
158    pub fn set_count32(&mut self, count: u32) {
159        RtcMode0::disable(&self.rtc);
160        RtcMode0::set_count(&self.rtc, count);
161        RtcMode0::enable(&self.rtc);
162    }
163
164    /// This resets the internal counter and sets the prescaler to match the
165    /// provided timeout.
166    fn reset_and_set_prescaler(&mut self, divider: Prescalerselect) {
167        RtcMode0::disable(&self.rtc);
168        RtcMode0::reset(&self.rtc);
169        RtcMode0::set_mode(&self.rtc);
170        RtcMode0::set_prescaler(&self.rtc, divider);
171    }
172
173    /// This resets the internal counter, sets the prescaler to match the
174    /// provided timeout, and starts the counter. You should configure the
175    /// prescaler using the longest timeout you plan to measure.
176    pub fn reset_and_compute_prescaler<T: Into<Nanoseconds>>(&mut self, timeout: T) -> &Self {
177        let params = TimerParams::new_us(timeout, self.rtc_clock_freq);
178        let divider = params.divider;
179
180        self.reset_and_set_prescaler(divider);
181        RtcMode0::start_and_initialize(&self.rtc);
182
183        self
184    }
185}
186
187impl Rtc<ClockMode> {
188    pub fn clock_mode(rtc: pac::Rtc, rtc_clock_freq: Hertz, pm: &mut Pm) -> Self {
189        Rtc::count32_mode(rtc, rtc_clock_freq, pm).into_clock_mode()
190    }
191
192    /// Returns the current clock/calendar value.
193    pub fn current_time(&self) -> Datetime {
194        RtcMode2::count(&self.rtc)
195    }
196
197    /// Updates the current clock/calendar value.
198    pub fn set_time(&mut self, time: Datetime) {
199        RtcMode2::set_count(&self.rtc, time);
200    }
201}
202
203// --- Timer / Counter Functionality
204impl ehal_02::timer::Periodic for Rtc<Count32Mode> {}
205impl ehal_02::timer::CountDown for Rtc<Count32Mode> {
206    type Time = Nanoseconds;
207
208    /// Starts the timer and puts it in periodic mode in which the counter
209    /// counts up to the specified `timeout` and then resets repeatedly back
210    /// to zero.
211    fn start<T>(&mut self, timeout: T)
212    where
213        T: Into<Self::Time>,
214    {
215        <Self as InterruptDrivenTimer>::start(self, timeout);
216    }
217
218    fn wait(&mut self) -> nb::Result<(), void::Void> {
219        <Self as InterruptDrivenTimer>::wait(self).map_err(|e| e.map(|_| panic!()))
220    }
221}
222
223impl ehal::delay::DelayNs for Rtc<Count32Mode> {
224    fn delay_ns(&mut self, ns: u32) {
225        <Self as InterruptDrivenTimer>::start(self, ns.nanos());
226        // Note that this cannot error since the error type is `Infallible`
227        let _ = nb::block!(<Self as InterruptDrivenTimer>::wait(self));
228    }
229}
230
231impl InterruptDrivenTimer for Rtc<Count32Mode> {
232    /// Enable the interrupt generation for this hardware timer.
233    /// This method only sets the clock configuration to trigger
234    /// the interrupt; it does not configure the interrupt controller
235    /// or define an interrupt handler.
236    fn enable_interrupt(&mut self) {
237        RtcMode0::enable_interrupt::<Compare0>(&self.rtc);
238    }
239
240    /// Starts the timer and puts it in periodic mode in which the counter
241    /// counts up to the specified `timeout` and then resets repeatedly back
242    /// to zero.
243    fn start<T>(&mut self, timeout: T)
244    where
245        T: Into<Nanoseconds>,
246    {
247        let params = TimerParams::new_us(timeout, self.rtc_clock_freq);
248        let divider = params.divider;
249        let cycles = params.cycles;
250
251        // Reset and set prescaler, keeping the counter disabled
252        self.reset_and_set_prescaler(divider);
253        // Set the compare 0 value to the desired time
254        RtcMode0::set_compare(&self.rtc, 0, cycles);
255        // Clear on match for periodicity
256        RtcMode0::set_match_clear(&self.rtc, true);
257
258        // Start the counter
259        RtcMode0::start_and_initialize(&self.rtc);
260    }
261
262    fn wait(&mut self) -> nb::Result<(), Infallible> {
263        if RtcMode0::check_interrupt_flag::<Compare0>(&self.rtc) {
264            // Clear the flag
265            RtcMode0::clear_interrupt_flag::<Compare0>(&self.rtc);
266            Ok(())
267        } else {
268            Err(nb::Error::WouldBlock)
269        }
270    }
271
272    /// Disables interrupt generation for this hardware timer.
273    /// This method only sets the clock configuration to prevent
274    /// triggering the interrupt; it does not configure the interrupt
275    /// controller.
276    fn disable_interrupt(&mut self) {
277        RtcMode0::disable_interrupt::<Compare0>(&self.rtc);
278    }
279}
280
281#[cfg(feature = "sdmmc")]
282impl TimeSource for Rtc<ClockMode> {
283    fn get_timestamp(&self) -> Timestamp {
284        self.current_time().into()
285    }
286}
287
288/// Helper type for computing cycles and divider given frequency
289#[derive(Debug, Clone, Copy)]
290pub struct TimerParams {
291    pub divider: Prescalerselect,
292    pub cycles: u32,
293}
294
295impl TimerParams {
296    fn prescaler_from_divider(divider_value: u64) -> Option<Prescalerselect> {
297        match divider_value {
298            1 => Some(Prescalerselect::Div1),
299            2 => Some(Prescalerselect::Div2),
300            4 => Some(Prescalerselect::Div4),
301            8 => Some(Prescalerselect::Div8),
302            16 => Some(Prescalerselect::Div16),
303            32 => Some(Prescalerselect::Div32),
304            64 => Some(Prescalerselect::Div64),
305            128 => Some(Prescalerselect::Div128),
306            256 => Some(Prescalerselect::Div256),
307            512 => Some(Prescalerselect::Div512),
308            1024 => Some(Prescalerselect::Div1024),
309            _ => None,
310        }
311    }
312
313    /// calculates RTC timer parameters based on the input frequency-based
314    /// timeout.
315    pub fn new(timeout: impl Into<Hertz>, src_freq: impl Into<Hertz>) -> Self {
316        let timeout = timeout.into();
317        let src_freq = src_freq.into();
318        let ticks = src_freq.to_Hz() / timeout.to_Hz().max(1);
319        Self::new_from_ticks(ticks as u64)
320    }
321
322    /// calculates RTC timer parameters based on the input period-based timeout.
323    pub fn new_us(timeout: impl Into<Nanoseconds>, src_freq: impl Into<Hertz>) -> Self {
324        let timeout = timeout.into();
325        let src_freq = src_freq.into();
326        let ticks = timeout.to_nanos() as u64 * src_freq.to_Hz() as u64 / 1_000_000_000;
327        Self::new_from_ticks(ticks)
328    }
329
330    /// Common helper function that gets the best divider & calculates cycles
331    /// with that divider.
332    fn new_from_ticks(ticks: u64) -> Self {
333        let mut divider_value = ((ticks >> 16) + 1).next_power_of_two();
334        let divider = Self::prescaler_from_divider(divider_value).unwrap_or_else(|| {
335            divider_value = 1024;
336
337            Prescalerselect::Div1024
338        });
339
340        let cycles: u32 = (ticks / divider_value)
341            .try_into()
342            .expect("cannot achieve the timeout even with the maximum RTC prescaler");
343
344        TimerParams { divider, cycles }
345    }
346}