tudelft_quadrupel/
time.rs

1use core::arch::asm;
2use core::ops::Sub;
3use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
4use core::time::Duration;
5use nrf51_pac::interrupt;
6
7use crate::mutex::Mutex;
8use crate::once_cell::OnceCell;
9/// Delay for a number of CPU cycles. Very inaccurate
10/// and hard to convert to an exact number of seconds
11pub use cortex_m::asm::delay as assembly_delay;
12use cortex_m::peripheral::NVIC;
13use nrf51_hal::rtc::{RtcCompareReg, RtcInterrupt};
14use nrf51_hal::Rtc;
15use nrf51_pac::RTC0;
16
17/// A moment in time
18#[derive(Debug, Copy, Clone)]
19pub struct Instant {
20    time: u64,
21}
22
23impl Instant {
24    /// Return the current instant, i.e. the current time
25    #[must_use]
26    pub fn now() -> Self {
27        Self {
28            time: get_time_ns(),
29        }
30    }
31
32    /// Get the [`Duration`] since a previous instant. This function panics if this instant was *before* the other instant.
33    ///
34    /// Note: `Instant` also implements `Sub`, so you can use the minus operator instead of this function.
35    ///
36    /// # Panics
37    /// when the `other` duration is actually in the future
38    #[must_use]
39    pub fn duration_since(self, other: Self) -> Duration {
40        assert!(self.time >= other.time);
41        Duration::from_nanos(self.time - other.time)
42    }
43
44    /// Adds a duration to this instant, producing a new instant in the future
45    #[must_use]
46    pub fn add_duration(self, d: Duration) -> Self {
47        Self {
48            time: self.time + d.as_nanos() as u64,
49        }
50    }
51
52    /// Check if this `Instant` is later than another `Instant`.
53    ///
54    /// Note: `Instant` also implements `Ord`, so you can use the comparison operators instead of this function.
55    #[must_use]
56    pub fn is_later_than(self, other: Self) -> bool {
57        self.time > other.time
58    }
59
60    /// Returns how many nanoseconds passed between when the timers started
61    /// and the current time. This is essentially what an `Instant` inherently represents.
62    #[must_use]
63    pub fn ns_since_start(&self) -> u64 {
64        self.time
65    }
66}
67
68impl Sub<Self> for Instant {
69    type Output = Duration;
70
71    /// Get the [`Duration`] since a previous instant (rhs)
72    fn sub(self, rhs: Self) -> Self::Output {
73        self.duration_since(rhs)
74    }
75}
76
77impl Eq for Instant {}
78
79impl PartialEq<Self> for Instant {
80    fn eq(&self, other: &Self) -> bool {
81        self.time == other.time
82    }
83}
84
85impl PartialOrd<Self> for Instant {
86    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
87        Some(self.cmp(other))
88    }
89}
90
91impl Ord for Instant {
92    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
93        self.time.cmp(&other.time)
94    }
95}
96
97// RTC0 is used for measuring absolute instants
98static RTC: Mutex<OnceCell<Rtc<RTC0>>> = Mutex::new(OnceCell::uninitialized());
99
100/// take the highest prescaler
101/// NOTE: change the period below when changing the prescaler
102const PRESCALER: u32 = 0;
103/// giving a period of this many nanoseconds
104const PERIOD: u64 = 30517;
105/// the largest value of the rtc counter before it overflows
106const COUNTER_MAX: u32 = 1 << 24;
107
108/// is set to true when the timer interrupt has gone off.
109/// Used to wait on the timer interrupt in [`wait_for_next_tick`]
110static TIMER_FLAG: AtomicBool = AtomicBool::new(false);
111
112/// Global time in magic timer units ([`PERIOD`]) since timers started
113/// SAFETY: only changed within timer interrupt. Safe to read at all times
114static GLOBAL_TIME: Mutex<u64> = Mutex::new(0);
115
116/// what was the counter before, so we can find the difference with what it's now.
117static PREV_COUNTER: AtomicU32 = AtomicU32::new(0);
118
119/// the number of counts until the interrupt should fire again
120static COUNTER_PERIOD: AtomicU32 = AtomicU32::new(0);
121
122pub(crate) fn initialize(clock_instance: RTC0, nvic: &mut NVIC) {
123    RTC.modify(|rtc| {
124        rtc.initialize(Rtc::new(clock_instance, PRESCALER).unwrap());
125        rtc.enable_event(RtcInterrupt::Compare0);
126        rtc.enable_interrupt(RtcInterrupt::Compare0, Some(nvic));
127        rtc.enable_counter();
128    });
129}
130
131// get the current global time in nanoseconds. The precision is not necessarily in single nanoseconds
132fn get_time_ns() -> u64 {
133    GLOBAL_TIME.modify(|global_time| {
134        let counter = RTC.modify(|counter| counter.get_counter());
135
136        // the previous state of the clock, when the global_time was last updated
137        let prev_counter = PREV_COUNTER.load(Ordering::SeqCst);
138
139        // take the global time, and add to that how much time has passed since the last interrupt
140        (*global_time + u64::from(counter_diff(prev_counter, counter))) * PERIOD
141    })
142}
143
144/// neatly calculates a difference in clockcycles between two rtc counter values
145/// essentially a subtraction modulo `COUNTER_MAX`
146fn counter_diff(prev: u32, curr: u32) -> u32 {
147    if curr < prev {
148        // what's left to go until the max
149        // plus what we've done since 0
150        (COUNTER_MAX - prev) + curr
151    } else {
152        curr - prev
153    }
154}
155
156#[interrupt]
157unsafe fn RTC0() {
158    // SAFETY: we're in an interrupt so this code cannot be run concurrently anyway
159    let rtc = RTC.no_critical_section_lock_mut();
160    // SAFETY: we're in an interrupt so this code cannot be run concurrently anyway
161    let global_time = GLOBAL_TIME.no_critical_section_lock_mut();
162
163    if rtc.is_event_triggered(RtcInterrupt::Compare0) {
164        let counter = rtc.get_counter();
165        let prev_counter = PREV_COUNTER.load(Ordering::SeqCst);
166
167        *global_time += u64::from(counter_diff(prev_counter, counter));
168        PREV_COUNTER.store(counter, Ordering::SeqCst);
169
170        let mut new_counter = counter + COUNTER_PERIOD.load(Ordering::SeqCst);
171        if new_counter >= COUNTER_MAX {
172            new_counter -= COUNTER_MAX;
173        }
174
175        rtc.set_compare(RtcCompareReg::Compare0, new_counter)
176            .unwrap();
177        rtc.reset_event(RtcInterrupt::Compare0);
178        TIMER_FLAG.store(true, Ordering::SeqCst);
179    }
180}
181
182/// Wait for the next interrupt configured by `set_interrupt_frequency`.
183pub fn wait_for_next_tick() {
184    if RTC.modify(|rtc| {
185        if rtc.is_event_triggered(RtcInterrupt::Compare0) {
186            // the compare register has already triggered
187            TIMER_FLAG.store(false, Ordering::SeqCst);
188            true
189        } else {
190            false
191        }
192    }) {
193        return;
194    }
195
196    while !TIMER_FLAG.load(Ordering::SeqCst) {
197        cortex_m::asm::wfi();
198    }
199    TIMER_FLAG.store(false, Ordering::SeqCst);
200}
201
202/// Set this timer to interrupt at the given frequency.
203/// The next interrupt will be after 1/hz seconds.
204///
205/// Call this before using any other time-related functions.
206#[allow(clippy::missing_panics_doc)]
207pub fn set_tick_frequency(hz: u64) {
208    RTC.modify(|rtc| {
209        let counter_setting = (1_000_000_000 / hz) / PERIOD;
210        // this can't actually happen, since it'd need hz < 1
211        debug_assert!(counter_setting < (1 << 24), "counter period should be less than 1<<24 (roughly 6 minutes with the default PRESCALER settings)");
212
213        COUNTER_PERIOD.store(counter_setting as u32, Ordering::SeqCst);
214
215        rtc.set_compare(RtcCompareReg::Compare0, counter_setting as u32)
216            .unwrap();
217        rtc.clear_counter();
218        PREV_COUNTER.store(0, Ordering::SeqCst);
219    });
220
221    // Give the counter time to clear (Section 19.1.8 of the NRF51 reference manual V3)
222    delay_us_assembly(500);
223}
224
225/// Delay the program for a time using assembly instructions.
226/// Testing shows this overshoots by ~5%, which is the closest that is possible without undershooting.
227#[allow(unused_assignments)]
228pub fn delay_us_assembly(mut number_of_us: u32) {
229    unsafe {
230        asm!(
231        "1:",
232        "subs {}, #1",
233        "nop",
234        "nop",
235        "nop",
236        "nop",
237        "nop",
238        "nop",
239        "nop",
240        "nop",
241        "nop",
242        "nop",
243        "nop",
244        "nop",
245        "bne 1b",
246        inout(reg) number_of_us,
247        options(nomem, nostack)
248        );
249    }
250}
251
252/// Delay the program for a time using assembly instructions.
253/// Testing shows this overshoots by ~5%, which is the closest that is possible without undershooting.
254pub fn delay_ms_assembly(number_of_ms: u32) {
255    for _ in 0..number_of_ms {
256        delay_us_assembly(999);
257    }
258}