use core::{ cmp::{self, Ordering}, convert::TryInto, mem, ops::Sub, }; use cortex_m::peripheral::{SCB, SYST}; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; use crate::Monotonic; pub struct TimerQueue(pub BinaryHeap, N, Min>) where M: Monotonic, ::Output: TryInto, N: ArrayLength>, T: Copy; impl TimerQueue where M: Monotonic, ::Output: TryInto, N: ArrayLength>, T: Copy, { #[inline] pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady) { let mut is_empty = true; if self .0 .peek() .map(|head| { is_empty = false; nr.instant < head.instant }) .unwrap_or(true) { if is_empty { mem::transmute::<_, SYST>(()).enable_interrupt(); } // set SysTick pending SCB::set_pendst(); } self.0.push_unchecked(nr); } #[inline] pub fn dequeue(&mut self) -> Option<(T, u8)> { unsafe { if let Some(instant) = self.0.peek().map(|p| p.instant) { let now = M::now(); if instant < now { // task became ready let nr = self.0.pop_unchecked(); Some((nr.task, nr.index)) } else { // set a new timeout const MAX: u32 = 0x00ffffff; let dur = match (instant - now) .try_into() .ok() .and_then(|x| x.checked_mul(M::ratio())) { None => MAX, Some(x) => cmp::min(MAX, x), }; mem::transmute::<_, SYST>(()).set_reload(dur); // start counting down from the new reload mem::transmute::<_, SYST>(()).clear_current(); None } } else { // the queue is empty mem::transmute::<_, SYST>(()).disable_interrupt(); None } } } } pub struct NotReady where T: Copy, M: Monotonic, ::Output: TryInto, { pub index: u8, pub instant: M::Instant, pub task: T, } impl Eq for NotReady where T: Copy, M: Monotonic, ::Output: TryInto, { } impl Ord for NotReady where T: Copy, M: Monotonic, ::Output: TryInto, { fn cmp(&self, other: &Self) -> Ordering { self.instant.cmp(&other.instant) } } impl PartialEq for NotReady where T: Copy, M: Monotonic, ::Output: TryInto, { fn eq(&self, other: &Self) -> bool { self.instant == other.instant } } impl PartialOrd for NotReady where T: Copy, M: Monotonic, ::Output: TryInto, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) } }