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