diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index 816bc24783..2a08694d19 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- nRF52xxx, nRF9160, nRF5340 Timer and RTC monotonics - Interrupt tokens for `Systick` and `rp2040` to make sure an interrupt handler exists ### Changed diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index c961c05997..503c22db86 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -23,8 +23,21 @@ fugit = { version = "0.3.6" } atomic-polyfill = "1" cfg-if = "1.0.0" cortex-m = { version = "0.7.6", optional = true } +critical-section = { version = "1", optional = true } + +# RP2040 rp2040-pac = { version = ">=0.2.0,<0.5", optional = true } +# nRF52 +nrf52810-pac = { version = "0.12.2", optional = true } +nrf52811-pac = { version = "0.12.2", optional = true } +nrf52832-pac = { version = "0.12.2", optional = true } +nrf52833-pac = { version = "0.12.2", optional = true } +nrf52840-pac = { version = "0.12.2", optional = true } +nrf5340-app-pac = { version = "0.12.2", optional = true } +nrf5340-net-pac = { version = "0.12.2", optional = true } +nrf9160-pac = { version = "0.12.2", optional = true } + [features] default = [] defmt = ["fugit/defmt"] @@ -36,3 +49,13 @@ systick-10khz = [] # Timer peripheral on the RP2040 rp2040 = ["dep:rp2040-pac"] + +# nRF Timers and RTC +nrf52810 = ["dep:nrf52810-pac", "dep:critical-section"] +nrf52811 = ["dep:nrf52811-pac", "dep:critical-section"] +nrf52832 = ["dep:nrf52832-pac", "dep:critical-section"] +nrf52833 = ["dep:nrf52833-pac", "dep:critical-section"] +nrf52840 = ["dep:nrf52840-pac", "dep:critical-section"] +nrf5340-app = ["dep:nrf5340-app-pac", "dep:critical-section"] +nrf5340-net = ["dep:nrf5340-net-pac", "dep:critical-section"] +nrf9160 = ["dep:nrf9160-pac", "dep:critical-section"] diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 6143fd0e5e..fab0b2dbd1 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -14,6 +14,18 @@ pub mod systick; #[cfg(feature = "rp2040")] pub mod rp2040; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", + feature = "nrf5340-app", + feature = "nrf5340-net", + feature = "nrf9160", +))] +pub mod nrf; + /// This marker is implemented on an interrupt token to enforce that the right tokens /// are given to the correct monotonic implementation. /// diff --git a/rtic-monotonics/src/nrf.rs b/rtic-monotonics/src/nrf.rs new file mode 100644 index 0000000000..0f6b97370d --- /dev/null +++ b/rtic-monotonics/src/nrf.rs @@ -0,0 +1,4 @@ +//! Monotonic implementations for the nRF series of MCUs. + +pub mod rtc; +pub mod timer; diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs new file mode 100644 index 0000000000..724c477c87 --- /dev/null +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -0,0 +1,232 @@ +//! RTIC Monotonic impl for the nRF RTCs. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::nrf::rtc::*; +//! +//! # async fn usage() { +//! # let rtc = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); +//! +//! // Start the monotonic +//! Rtc0::start(rtc, token); +//! +//! loop { +//! // Use the monotonic +//! Rtc0::delay(100.millis()).await; +//! } +//! # } +//! ``` + +#[cfg(feature = "nrf52810")] +use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[cfg(feature = "nrf52811")] +use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1}; +#[cfg(feature = "nrf52832")] +use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf52833")] +use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf52840")] +use nrf52840_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; +#[cfg(feature = "nrf5340-app")] +use nrf5340_app_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[cfg(feature = "nrf5340-net")] +use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +#[cfg(feature = "nrf9160")] +use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; + +use super::super::Monotonic; +pub use super::super::{TimeoutError, TimerQueue}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::future::Future; +pub use fugit::{self, ExtU64}; + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_rtc_interrupt { + ($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{ + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn $rtc() { + $crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt(); + } + + pub struct $rtc_token; + + unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {} + + $rtc_token + }}; +} + +/// Register the Rtc0 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_rtc0_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token) + }}; +} + +/// Register the Rtc1 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_rtc1_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token) + }}; +} + +/// Register the Rtc2 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_rtc2_monotonic_token { + () => {{ + $crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token) + }}; +} + +macro_rules! make_rtc { + ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident) => { + /// Monotonic timer queue implementation. + pub struct $mono_name; + + static $overflow: AtomicU32 = AtomicU32::new(0); + static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + + impl $mono_name { + /// Start the timer monotonic. + pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken) { + unsafe { rtc.prescaler.write(|w| w.bits(0)) }; + rtc.intenset.write(|w| w.compare0().set().ovrflw().set()); + rtc.evtenset.write(|w| w.compare0().set().ovrflw().set()); + + rtc.tasks_clear.write(|w| unsafe { w.bits(1) }); + rtc.tasks_start.write(|w| unsafe { w.bits(1) }); + + $tq.initialize(Self {}); + + unsafe { pac::NVIC::unmask(Interrupt::$rtc) }; + } + + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<$mono_name> { + &$tq + } + + /// Timeout at a specific time. + #[inline] + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + $tq.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + $tq.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + $tq.delay(duration).await; + } + + /// Delay to some specific time instant. + #[inline] + pub async fn delay_until(instant: ::Instant) { + $tq.delay_until(instant).await; + } + + #[inline(always)] + fn is_overflow() -> bool { + let rtc = unsafe { &*$rtc::PTR }; + rtc.events_ovrflw.read().bits() == 1 + } + } + + #[cfg(feature = "embedded-hal-async")] + impl embedded_hal_async::delay::DelayUs for $mono_name { + type Error = core::convert::Infallible; + + #[inline] + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + $tq.delay((us as u64).micros()).await; + Ok(()) + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + $tq.delay((ms as u64).millis()).await; + Ok(()) + } + } + + impl Monotonic for $mono_name { + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + type Instant = fugit::TimerInstantU64<32_768>; + type Duration = fugit::TimerDurationU64<32_768>; + + fn now() -> Self::Instant { + // In a critical section to not get a race between overflow updates and reading it + // and the flag here. + critical_section::with(|_| { + let rtc = unsafe { &*$rtc::PTR }; + let cnt = rtc.counter.read().bits() as u64; + let ovf = if Self::is_overflow() { + $overflow.load(Ordering::Relaxed) + 1 + } else { + $overflow.load(Ordering::Relaxed) + } as u64; + + Self::Instant::from_ticks((ovf << 24) | cnt) + }) + } + + fn on_interrupt() { + let rtc = unsafe { &*$rtc::PTR }; + if Self::is_overflow() { + $overflow.fetch_add(1, Ordering::SeqCst); + rtc.events_ovrflw.write(|w| unsafe { w.bits(0) }); + } + } + + // NOTE: To fix errata for RTC, if the release time is within 4 ticks + // we release as the RTC will not generate a compare interrupt... + fn should_dequeue_check(release_at: Self::Instant) -> bool { + Self::now() + ::Duration::from_ticks(4) >= release_at + } + + fn enable_timer() {} + + fn disable_timer() {} + + fn set_compare(instant: Self::Instant) { + let rtc = unsafe { &*$rtc::PTR }; + unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xffffff)) }; + } + + fn clear_compare_flag() { + let rtc = unsafe { &*$rtc::PTR }; + unsafe { rtc.events_compare[0].write(|w| w.bits(0)) }; + } + + fn pend_interrupt() { + pac::NVIC::pend(Interrupt::$rtc); + } + } + }; +} + +make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ); +make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ); diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs new file mode 100644 index 0000000000..ad490cc38e --- /dev/null +++ b/rtic-monotonics/src/nrf/timer.rs @@ -0,0 +1,259 @@ +//! Monotonic impl for the 32-bit timers of the nRF series. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::nrf::timer::*; +//! +//! # async fn usage() { +//! # let timer = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); +//! +//! // Start the monotonic +//! Timer0::start(timer, token); +//! +//! loop { +//! // Use the monotonic +//! Timer0::delay(100.millis()).await; +//! } +//! # } +//! ``` + +use super::super::Monotonic; +pub use super::super::{TimeoutError, TimerQueue}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::future::Future; +pub use fugit::{self, ExtU64}; + +#[cfg(feature = "nrf52810")] +use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[cfg(feature = "nrf52811")] +use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[cfg(feature = "nrf52832")] +use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf52833")] +use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf52840")] +use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[cfg(feature = "nrf5340-app")] +use nrf5340_app_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; +#[cfg(feature = "nrf5340-net")] +use nrf5340_net_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; +#[cfg(feature = "nrf9160")] +use nrf9160_pac::{ + self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +}; + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_nrf_timer_interrupt { + ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn $timer() { + $crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt(); + } + + pub struct $timer_token; + + unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {} + + $timer_token + }}; +} + +/// Register the Timer0 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer0_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) + }}; +} + +/// Register the Timer1 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer1_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) + }}; +} + +/// Register the Timer2 interrupt for the monotonic. +#[macro_export] +macro_rules! create_nrf_timer2_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) + }}; +} + +/// Register the Timer3 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_timer3_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) + }}; +} + +/// Register the Timer4 interrupt for the monotonic. +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +#[macro_export] +macro_rules! create_nrf_timer4_monotonic_token { + () => {{ + $crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) + }}; +} + +macro_rules! make_timer { + ($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident) => { + /// Monotonic timer queue implementation. + pub struct $mono_name; + + static $overflow: AtomicU32 = AtomicU32::new(0); + static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + + impl $mono_name { + /// Start the timer monotonic. + pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken) { + // 1 MHz + timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); + timer.bitmode.write(|w| w.bitmode()._32bit()); + timer + .intenset + .modify(|_, w| w.compare0().set().compare1().set()); + timer.cc[1].write(|w| unsafe { w.cc().bits(0) }); // Overflow + timer.tasks_clear.write(|w| unsafe { w.bits(1) }); + timer.tasks_start.write(|w| unsafe { w.bits(1) }); + + $tq.initialize(Self {}); + + timer.events_compare[0].write(|w| w); + timer.events_compare[1].write(|w| w); + + unsafe { pac::NVIC::unmask(Interrupt::$timer) }; + } + + /// Used to access the underlying timer queue + #[doc(hidden)] + pub fn __tq() -> &'static TimerQueue<$mono_name> { + &$tq + } + + /// Timeout at a specific time. + #[inline] + pub async fn timeout_at( + instant: ::Instant, + future: F, + ) -> Result { + $tq.timeout_at(instant, future).await + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + duration: ::Duration, + future: F, + ) -> Result { + $tq.timeout_after(duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(duration: ::Duration) { + $tq.delay(duration).await; + } + + /// Delay to some specific time instant. + #[inline] + pub async fn delay_until(instant: ::Instant) { + $tq.delay_until(instant).await; + } + } + + #[cfg(feature = "embedded-hal-async")] + impl embedded_hal_async::delay::DelayUs for $mono_name { + type Error = core::convert::Infallible; + + #[inline] + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + $tq.delay((us as u64).micros()).await; + Ok(()) + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + $tq.delay((ms as u64).millis()).await; + Ok(()) + } + } + + impl Monotonic for $mono_name { + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + type Instant = fugit::TimerInstantU64<1_000_000>; + type Duration = fugit::TimerDurationU64<1_000_000>; + + fn now() -> Self::Instant { + // In a critical section to not get a race between overflow updates and reading it + // and the flag here. + critical_section::with(|_| { + let timer = unsafe { &*$timer::PTR }; + timer.tasks_capture[2].write(|w| unsafe { w.bits(1) }); + + let unhandled_overflow = if timer.events_compare[1].read().bits() & 1 != 0 { + // The overflow has not been handled yet, so add an extra to the read overflow. + 1 + } else { + 0 + }; + + Self::Instant::from_ticks( + (unhandled_overflow + $overflow.load(Ordering::Relaxed) as u64) << 32 + | timer.cc[2].read().bits() as u64, + ) + }) + } + + fn on_interrupt() { + let timer = unsafe { &*$timer::PTR }; + + // If there is a compare match on channel 1, it is an overflow + if timer.events_compare[1].read().bits() & 1 != 0 { + timer.events_compare[1].write(|w| w); + $overflow.fetch_add(1, Ordering::SeqCst); + } + } + + fn enable_timer() {} + + fn disable_timer() {} + + fn set_compare(instant: Self::Instant) { + let timer = unsafe { &*$timer::PTR }; + timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); + } + + fn clear_compare_flag() { + let timer = unsafe { &*$timer::PTR }; + timer.events_compare[0].write(|w| w); + } + + fn pend_interrupt() { + pac::NVIC::pend(Interrupt::$timer); + } + } + }; +} + +make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); +make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); +make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ); +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ); diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 9d2f4f352c..5e880cd898 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -1,4 +1,25 @@ //! A monotonic implementation for RP2040's Timer peripheral. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::rp2040::*; +//! +//! # async fn usage() { +//! # let timer = unsafe { core::mem::transmute(()) }; +//! # let mut resets = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); +//! +//! // Start the monotonic +//! Timer::start(timer, &mut resets, token); +//! +//! loop { +//! // Use the monotonic +//! Timer::delay(100.millis()).await; +//! } +//! # } +//! ``` use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; @@ -41,6 +62,7 @@ impl Timer { } /// Timeout at a specific time. + #[inline] pub async fn timeout_at( instant: ::Instant, future: F, @@ -64,6 +86,7 @@ impl Timer { } /// Delay to some specific time instant. + #[inline] pub async fn delay_until(instant: ::Instant) { TIMER_QUEUE.delay_until(instant).await; } diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index f4345d434d..f1d49daf33 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -1,4 +1,25 @@ -//! ... +//! A monotonics based on Cortex-M SysTick. Note that this implementation is inefficient as it +//! ticks, and generates interrupts, at a constant rate. +//! +//! # Example +//! +//! ``` +//! use rtic_monotonics::systick::*; +//! +//! # async fn usage() { +//! # let systick = unsafe { core::mem::transmute(()) }; +//! // Generate the required token +//! let systick_token = rtic_monotonics::create_systick_token!(); +//! +//! // Start the monotonic +//! Systick::start(systick, 12_000_000, systick_token); +//! +//! loop { +//! // Use the monotonic +//! Systick::delay(100.millis()).await; +//! } +//! # } +//! ``` use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index d3a9d846ee..3a2fb9104a 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -9,6 +9,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- `should_dequeue` to the `Monotonic` trait to handle bugged timers + ### Changed ### Fixed diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 78b57a4f16..9bf148558a 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -131,7 +131,7 @@ impl TimerQueue { let head = self.queue.pop_if(|head| { release_at = Some(head.release_at); - let should_pop = Mono::now() >= head.release_at; + let should_pop = Mono::should_dequeue_check(head.release_at); head.was_poped.store(should_pop, Ordering::Relaxed); should_pop @@ -145,7 +145,7 @@ impl TimerQueue { Mono::enable_timer(); Mono::set_compare(instant); - if Mono::now() >= instant { + if Mono::should_dequeue_check(instant) { // The time for the next instant passed while handling it, // continue dequeueing continue; diff --git a/rtic-time/src/monotonic.rs b/rtic-time/src/monotonic.rs index 9b3742fa87..513cc07bb8 100644 --- a/rtic-time/src/monotonic.rs +++ b/rtic-time/src/monotonic.rs @@ -33,6 +33,13 @@ pub trait Monotonic { /// queue in RTIC checks this. fn set_compare(instant: Self::Instant); + /// Override for the dequeue check, override with timers that have bugs. + /// + /// E.g. nRF52 RTCs needs to be dequeued if the time is within 4 ticks. + fn should_dequeue_check(release_at: Self::Instant) -> bool { + ::now() >= release_at + } + /// Clear the compare interrupt flag. fn clear_compare_flag();