rtic_monotonics/nrf/
rtc.rs

1//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC).
2//!
3//! # Example
4//!
5//! ```
6//! use rtic_monotonics::nrf::rtc::prelude::*;
7//! nrf_rtc0_monotonic!(Mono);
8//!
9//! fn init() {
10//!     # // This is normally provided by the selected PAC
11//!     # let rtc = unsafe { core::mem::transmute(()) };
12//!     // Start the monotonic
13//!     Mono::start(rtc);
14//! }
15//!
16//! async fn usage() {
17//!     loop {
18//!          // Use the monotonic
19//!          let timestamp = Mono::now();
20//!          Mono::delay(100.millis()).await;
21//!     }
22//! }
23//! ```
24
25/// Common definitions and traits for using the nRF RTC monotonics
26pub mod prelude {
27    pub use crate::nrf_rtc0_monotonic;
28    pub use crate::nrf_rtc1_monotonic;
29    #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
30    pub use crate::nrf_rtc2_monotonic;
31
32    pub use crate::Monotonic;
33    pub use fugit::{self, ExtU64, ExtU64Ceil};
34}
35
36#[cfg(feature = "nrf52805")]
37#[doc(hidden)]
38pub use nrf52805_pac::{self as pac, RTC0, RTC1};
39#[cfg(feature = "nrf52810")]
40#[doc(hidden)]
41pub use nrf52810_pac::{self as pac, RTC0, RTC1};
42#[cfg(feature = "nrf52811")]
43#[doc(hidden)]
44pub use nrf52811_pac::{self as pac, RTC0, RTC1};
45#[cfg(feature = "nrf52832")]
46#[doc(hidden)]
47pub use nrf52832_pac::{self as pac, RTC0, RTC1, RTC2};
48#[cfg(feature = "nrf52833")]
49#[doc(hidden)]
50pub use nrf52833_pac::{self as pac, RTC0, RTC1, RTC2};
51#[cfg(feature = "nrf52840")]
52#[doc(hidden)]
53pub use nrf52840_pac::{self as pac, RTC0, RTC1, RTC2};
54#[cfg(feature = "nrf5340-app")]
55#[doc(hidden)]
56pub use nrf5340_app_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
57#[cfg(feature = "nrf5340-net")]
58#[doc(hidden)]
59pub use nrf5340_net_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
60#[cfg(feature = "nrf9160")]
61#[doc(hidden)]
62pub use nrf9160_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
63
64use portable_atomic::{AtomicU32, Ordering};
65use rtic_time::{
66    half_period_counter::calculate_now,
67    timer_queue::{TimerQueue, TimerQueueBackend},
68};
69
70#[doc(hidden)]
71#[macro_export]
72macro_rules! __internal_create_nrf_rtc_interrupt {
73    ($mono_backend:ident, $rtc:ident) => {
74        #[no_mangle]
75        #[allow(non_snake_case)]
76        unsafe extern "C" fn $rtc() {
77            use $crate::TimerQueueBackend;
78            $crate::nrf::rtc::$mono_backend::timer_queue().on_monotonic_interrupt();
79        }
80    };
81}
82
83#[doc(hidden)]
84#[macro_export]
85macro_rules! __internal_create_nrf_rtc_struct {
86    ($name:ident, $mono_backend:ident, $timer:ident) => {
87        /// A `Monotonic` based on the nRF RTC peripheral.
88        pub struct $name;
89
90        impl $name {
91            /// Starts the `Monotonic`.
92            ///
93            /// This method must be called only once.
94            pub fn start(rtc: $crate::nrf::rtc::$timer) {
95                $crate::__internal_create_nrf_rtc_interrupt!($mono_backend, $timer);
96
97                $crate::nrf::rtc::$mono_backend::_start(rtc);
98            }
99        }
100
101        impl $crate::TimerQueueBasedMonotonic for $name {
102            type Backend = $crate::nrf::rtc::$mono_backend;
103            type Instant = $crate::fugit::Instant<
104                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
105                1,
106                32_768,
107            >;
108            type Duration = $crate::fugit::Duration<
109                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
110                1,
111                32_768,
112            >;
113        }
114
115        $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
116        $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
117    };
118}
119
120/// Create an RTC0 based monotonic and register the RTC0 interrupt for it.
121///
122/// See [`crate::nrf::rtc`] for more details.
123#[macro_export]
124macro_rules! nrf_rtc0_monotonic {
125    ($name:ident) => {
126        $crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0);
127    };
128}
129
130/// Create an RTC1 based monotonic and register the RTC1 interrupt for it.
131///
132/// See [`crate::nrf::rtc`] for more details.
133#[macro_export]
134macro_rules! nrf_rtc1_monotonic {
135    ($name:ident) => {
136        $crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1);
137    };
138}
139
140/// Create an RTC2 based monotonic and register the RTC2 interrupt for it.
141///
142/// See [`crate::nrf::rtc`] for more details.
143#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
144#[cfg_attr(
145    docsrs,
146    doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
147)]
148#[macro_export]
149macro_rules! nrf_rtc2_monotonic {
150    ($name:ident) => {
151        $crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2);
152    };
153}
154
155struct TimerValueU24(u32);
156impl rtic_time::half_period_counter::TimerValue for TimerValueU24 {
157    const BITS: u32 = 24;
158}
159impl From<TimerValueU24> for u64 {
160    fn from(value: TimerValueU24) -> Self {
161        Self::from(value.0)
162    }
163}
164
165macro_rules! make_rtc {
166    ($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
167        /// RTC based [`TimerQueueBackend`].
168        $(
169            #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
170        )?
171        pub struct $backend_name;
172
173        static $overflow: AtomicU32 = AtomicU32::new(0);
174        static $tq: TimerQueue<$backend_name> = TimerQueue::new();
175
176        impl $backend_name {
177            /// Starts the timer.
178            ///
179            /// **Do not use this function directly.**
180            ///
181            /// Use the prelude macros instead.
182            pub fn _start(rtc: $rtc) {
183                unsafe { rtc.prescaler.write(|w| w.bits(0)) };
184
185                // Disable interrupts, as preparation
186                rtc.intenclr.write(|w| w
187                    .compare0().clear()
188                    .compare1().clear()
189                    .ovrflw().clear()
190                );
191
192                // Configure compare registers
193                rtc.cc[0].write(|w| unsafe { w.bits(0) }); // Dynamic wakeup
194                rtc.cc[1].write(|w| unsafe { w.bits(0x80_0000) }); // Half-period
195
196                // Timing critical, make sure we don't get interrupted
197                critical_section::with(|_|{
198                    // Reset the timer
199                    rtc.tasks_clear.write(|w| unsafe { w.bits(1) });
200                    rtc.tasks_start.write(|w| unsafe { w.bits(1) });
201
202                    // Clear pending events.
203                    // Should be close enough to the timer reset that we don't miss any events.
204                    rtc.events_ovrflw.write(|w| w);
205                    rtc.events_compare[0].write(|w| w);
206                    rtc.events_compare[1].write(|w| w);
207
208                    // Make sure overflow counter is synced with the timer value
209                    $overflow.store(0, Ordering::SeqCst);
210
211                    // Initialized the timer queue
212                    $tq.initialize(Self {});
213
214                    // Enable interrupts.
215                    // Should be close enough to the timer reset that we don't miss any events.
216                    rtc.intenset.write(|w| w
217                        .compare0().set()
218                        .compare1().set()
219                        .ovrflw().set()
220                    );
221                    rtc.evtenset.write(|w| w
222                        .compare0().set()
223                        .compare1().set()
224                        .ovrflw().set()
225                    );
226                });
227
228                // SAFETY: We take full ownership of the peripheral and interrupt vector,
229                // plus we are not using any external shared resources so we won't impact
230                // basepri/source masking based critical sections.
231                unsafe {
232                    crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc);
233                    pac::NVIC::unmask(pac::Interrupt::$rtc);
234                }
235            }
236        }
237
238        impl TimerQueueBackend for $backend_name {
239            type Ticks = u64;
240
241            fn now() -> Self::Ticks {
242                let rtc = unsafe { &*$rtc::PTR };
243                calculate_now(
244                    || $overflow.load(Ordering::Relaxed),
245                    || TimerValueU24(rtc.counter.read().bits())
246                )
247            }
248
249            fn on_interrupt() {
250                let rtc = unsafe { &*$rtc::PTR };
251                if rtc.events_ovrflw.read().bits() == 1 {
252                    rtc.events_ovrflw.write(|w| unsafe { w.bits(0) });
253                    let prev = $overflow.fetch_add(1, Ordering::Relaxed);
254                    assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!");
255                }
256                if rtc.events_compare[1].read().bits() == 1 {
257                    rtc.events_compare[1].write(|w| unsafe { w.bits(0) });
258                    let prev = $overflow.fetch_add(1, Ordering::Relaxed);
259                    assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
260                }
261            }
262
263            fn set_compare(mut instant: Self::Ticks) {
264                let rtc = unsafe { &*$rtc::PTR };
265
266                const MAX: u64 = 0xff_ffff;
267
268                // Disable interrupts because this section is timing critical.
269                // We rely on the fact that this entire section runs within one
270                // RTC clock tick. (which it will do easily if it doesn't get
271                // interrupted)
272                critical_section::with(|_|{
273                    let now = Self::now();
274                    // wrapping_sub deals with the u64 overflow corner case
275                    let diff = instant.wrapping_sub(now);
276                    let val = if diff <= MAX {
277                        // Now we know `instant` whill happen within one `MAX` time duration.
278
279                        // Errata: Timer interrupts don't fire if they are scheduled less than
280                        // two ticks in the future. Make it three, because the timer could
281                        // tick right now.
282                        if diff < 3 {
283                            instant = now.wrapping_add(3);
284                        }
285
286                        (instant & MAX) as u32
287                    } else {
288                        0
289                    };
290
291                    unsafe { rtc.cc[0].write(|w| w.bits(val)) };
292                });
293            }
294
295            fn clear_compare_flag() {
296                let rtc = unsafe { &*$rtc::PTR };
297                unsafe { rtc.events_compare[0].write(|w| w.bits(0)) };
298            }
299
300            fn pend_interrupt() {
301                pac::NVIC::pend(pac::Interrupt::$rtc);
302            }
303
304            fn timer_queue() -> &'static TimerQueue<Self> {
305                &$tq
306            }
307        }
308    };
309}
310
311make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
312make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
313#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
314make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));