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