rtic_monotonics/
rp2040.rs

1//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral.
2//!
3//! Always runs at a fixed rate of 1 MHz.
4//!
5//! # Example
6//!
7//! ```
8//! use rtic_monotonics::rp2040::prelude::*;
9//!
10//! // Create the type `Mono`. It will manage the TIMER peripheral,
11//! // which is a 1 MHz, 64-bit timer.
12//! rp2040_timer_monotonic!(Mono);
13//!
14//! fn init() {
15//!     # // This is normally provided by the selected PAC
16//!     # let TIMER = unsafe { core::mem::transmute(()) };
17//!     # let mut RESETS = unsafe { core::mem::transmute(()) };
18//!     #
19//!     // Start the monotonic - passing ownership of an rp2040_pac object for
20//!     // TIMER0, and temporary access to one for the RESET peripheral.
21//!     Mono::start(TIMER, &mut RESETS);
22//! }
23//!
24//! async fn usage() {
25//!     loop {
26//!          // You can use the monotonic to get the time...
27//!          let timestamp = Mono::now();
28//!          // ...and you can use it to add a delay to this async function
29//!          Mono::delay(100.millis()).await;
30//!     }
31//! }
32//! ```
33
34/// Common definitions and traits for using the RP2040 timer monotonic
35pub mod prelude {
36    pub use crate::rp2040_timer_monotonic;
37
38    pub use crate::Monotonic;
39
40    pub use fugit::{self, ExtU64, ExtU64Ceil};
41}
42
43use crate::TimerQueueBackend;
44use rp2040_pac::{timer, Interrupt, NVIC};
45pub use rp2040_pac::{RESETS, TIMER};
46use rtic_time::timer_queue::TimerQueue;
47
48/// Timer implementing [`TimerQueueBackend`].
49pub struct TimerBackend;
50
51impl TimerBackend {
52    /// Starts the monotonic timer.
53    ///
54    /// **Do not use this function directly.**
55    ///
56    /// Use the prelude macros instead.
57    pub fn _start(timer: TIMER, resets: &RESETS) {
58        resets.reset().modify(|_, w| w.timer().clear_bit());
59        while resets.reset_done().read().timer().bit_is_clear() {}
60        timer.inte().modify(|_, w| w.alarm_0().bit(true));
61
62        TIMER_QUEUE.initialize(Self {});
63
64        unsafe {
65            crate::set_monotonic_prio(rp2040_pac::NVIC_PRIO_BITS, Interrupt::TIMER_IRQ_0);
66            NVIC::unmask(Interrupt::TIMER_IRQ_0);
67        }
68    }
69
70    fn timer() -> &'static timer::RegisterBlock {
71        unsafe { &*TIMER::ptr() }
72    }
73}
74
75static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
76
77impl TimerQueueBackend for TimerBackend {
78    type Ticks = u64;
79
80    fn now() -> Self::Ticks {
81        let timer = Self::timer();
82
83        let mut hi0 = timer.timerawh().read().bits();
84        loop {
85            let low = timer.timerawl().read().bits();
86            let hi1 = timer.timerawh().read().bits();
87            if hi0 == hi1 {
88                break (u64::from(hi0) << 32) | u64::from(low);
89            }
90            hi0 = hi1;
91        }
92    }
93
94    fn set_compare(instant: Self::Ticks) {
95        let now = Self::now();
96
97        const MAX: u64 = u32::MAX as u64;
98
99        // Since the timer may or may not overflow based on the requested compare val, we check
100        // how many ticks are left.
101        // `wrapping_sub` takes care of the u64 integer overflow special case.
102        let val = if instant.wrapping_sub(now) <= MAX {
103            instant & MAX
104        } else {
105            0
106        };
107
108        Self::timer()
109            .alarm0()
110            .write(|w| unsafe { w.bits(val as u32) });
111    }
112
113    fn clear_compare_flag() {
114        Self::timer().intr().modify(|_, w| w.alarm_0().bit(true));
115    }
116
117    fn pend_interrupt() {
118        rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0);
119    }
120
121    fn timer_queue() -> &'static TimerQueue<Self> {
122        &TIMER_QUEUE
123    }
124}
125
126/// Create an RP2040 timer based monotonic and register the necessary interrupt for it.
127///
128/// See [`crate::rp2040`] for more details.
129///
130/// # Arguments
131///
132/// * `name` - The name that the monotonic type will have.
133#[macro_export]
134macro_rules! rp2040_timer_monotonic {
135    ($name:ident) => {
136        /// A `Monotonic` based on the RP2040 Timer peripheral.
137        pub struct $name;
138
139        impl $name {
140            /// Starts the `Monotonic`.
141            ///
142            /// This method must be called only once.
143            pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) {
144                #[no_mangle]
145                #[allow(non_snake_case)]
146                unsafe extern "C" fn TIMER_IRQ_0() {
147                    use $crate::TimerQueueBackend;
148                    $crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt();
149                }
150
151                $crate::rp2040::TimerBackend::_start(timer, resets);
152            }
153        }
154
155        impl $crate::TimerQueueBasedMonotonic for $name {
156            type Backend = $crate::rp2040::TimerBackend;
157            type Instant = $crate::fugit::Instant<
158                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
159                1,
160                1_000_000,
161            >;
162            type Duration = $crate::fugit::Duration<
163                <Self::Backend as $crate::TimerQueueBackend>::Ticks,
164                1,
165                1_000_000,
166            >;
167        }
168
169        $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
170        $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
171    };
172}