mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-26 03:49:34 +01:00
Merge #721
721: Added nRF monotonics r=perlindgren a=korken89 Testing completed Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
This commit is contained in:
commit
72ae46083e
11 changed files with 652 additions and 5 deletions
|
@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- nRF52xxx, nRF9160, nRF5340 Timer and RTC monotonics
|
||||||
- Interrupt tokens for `Systick` and `rp2040` to make sure an interrupt handler exists
|
- Interrupt tokens for `Systick` and `rp2040` to make sure an interrupt handler exists
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -23,8 +23,21 @@ fugit = { version = "0.3.6" }
|
||||||
atomic-polyfill = "1"
|
atomic-polyfill = "1"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
cortex-m = { version = "0.7.6", optional = true }
|
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 }
|
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]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
defmt = ["fugit/defmt"]
|
defmt = ["fugit/defmt"]
|
||||||
|
@ -35,4 +48,14 @@ systick-100hz = []
|
||||||
systick-10khz = []
|
systick-10khz = []
|
||||||
|
|
||||||
# Timer peripheral on the RP2040
|
# Timer peripheral on the RP2040
|
||||||
rp2040 = ["dep:rp2040-pac"]
|
rp2040 = ["dep:cortex-m", "dep:rp2040-pac"]
|
||||||
|
|
||||||
|
# nRF Timers and RTC
|
||||||
|
nrf52810 = ["dep:cortex-m", "dep:nrf52810-pac", "dep:critical-section"]
|
||||||
|
nrf52811 = ["dep:cortex-m", "dep:nrf52811-pac", "dep:critical-section"]
|
||||||
|
nrf52832 = ["dep:cortex-m", "dep:nrf52832-pac", "dep:critical-section"]
|
||||||
|
nrf52833 = ["dep:cortex-m", "dep:nrf52833-pac", "dep:critical-section"]
|
||||||
|
nrf52840 = ["dep:cortex-m", "dep:nrf52840-pac", "dep:critical-section"]
|
||||||
|
nrf5340-app = ["dep:cortex-m", "dep:nrf5340-app-pac", "dep:critical-section"]
|
||||||
|
nrf5340-net = ["dep:cortex-m", "dep:nrf5340-net-pac", "dep:critical-section"]
|
||||||
|
nrf9160 = ["dep:cortex-m", "dep:nrf9160-pac", "dep:critical-section"]
|
||||||
|
|
|
@ -14,6 +14,54 @@ pub mod systick;
|
||||||
#[cfg(feature = "rp2040")]
|
#[cfg(feature = "rp2040")]
|
||||||
pub mod 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;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
|
||||||
|
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "rp2040",
|
||||||
|
feature = "nrf52810",
|
||||||
|
feature = "nrf52811",
|
||||||
|
feature = "nrf52832",
|
||||||
|
feature = "nrf52833",
|
||||||
|
feature = "nrf52840",
|
||||||
|
feature = "nrf5340-app",
|
||||||
|
feature = "nrf5340-net",
|
||||||
|
feature = "nrf9160",
|
||||||
|
))]
|
||||||
|
pub(crate) unsafe fn set_monotonic_prio(
|
||||||
|
prio_bits: u8,
|
||||||
|
interrupt: impl cortex_m::interrupt::InterruptNumber,
|
||||||
|
) {
|
||||||
|
extern "C" {
|
||||||
|
static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_prio = RTIC_ASYNC_MAX_LOGICAL_PRIO.max(1).min(1 << prio_bits);
|
||||||
|
|
||||||
|
let hw_prio = crate::cortex_logical2hw(max_prio, prio_bits);
|
||||||
|
|
||||||
|
// We take ownership of the entire IRQ and all settings to it, we only change settings
|
||||||
|
// for the IRQ we control.
|
||||||
|
// This will also compile-error in case the NVIC changes in size.
|
||||||
|
let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(());
|
||||||
|
|
||||||
|
nvic.set_priority(interrupt, hw_prio);
|
||||||
|
}
|
||||||
|
|
||||||
/// This marker is implemented on an interrupt token to enforce that the right tokens
|
/// This marker is implemented on an interrupt token to enforce that the right tokens
|
||||||
/// are given to the correct monotonic implementation.
|
/// are given to the correct monotonic implementation.
|
||||||
///
|
///
|
||||||
|
|
4
rtic-monotonics/src/nrf.rs
Normal file
4
rtic-monotonics/src/nrf.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//! Monotonic implementations for the nRF series of MCUs.
|
||||||
|
|
||||||
|
pub mod rtc;
|
||||||
|
pub mod timer;
|
241
rtic-monotonics/src/nrf/rtc.rs
Normal file
241
rtic-monotonics/src/nrf/rtc.rs
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
//! RTIC Monotonic impl for the nRF RTCs.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use rtic_monotonics::nrf::rtc::*;
|
||||||
|
//!
|
||||||
|
//! fn init() {
|
||||||
|
//! # // This is normally provided by the selected PAC
|
||||||
|
//! # 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);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn usage() {
|
||||||
|
//! 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<Self>) {
|
||||||
|
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 {});
|
||||||
|
|
||||||
|
// SAFETY: We take full ownership of the peripheral and interrupt vector,
|
||||||
|
// plus we are not using any external shared resources so we won't impact
|
||||||
|
// basepri/source masking based critical sections.
|
||||||
|
unsafe {
|
||||||
|
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc);
|
||||||
|
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<F: Future>(
|
||||||
|
instant: <Self as Monotonic>::Instant,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
$tq.timeout_at(instant, future).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeout after a specific duration.
|
||||||
|
#[inline]
|
||||||
|
pub async fn timeout_after<F: Future>(
|
||||||
|
duration: <Self as Monotonic>::Duration,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
$tq.timeout_after(duration, future).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay for some duration of time.
|
||||||
|
#[inline]
|
||||||
|
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||||
|
$tq.delay(duration).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay to some specific time instant.
|
||||||
|
#[inline]
|
||||||
|
pub async fn delay_until(instant: <Self as Monotonic>::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() + <Self as Monotonic>::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);
|
268
rtic-monotonics/src/nrf/timer.rs
Normal file
268
rtic-monotonics/src/nrf/timer.rs
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
//! Monotonic impl for the 32-bit timers of the nRF series.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use rtic_monotonics::nrf::timer::*;
|
||||||
|
//!
|
||||||
|
//! fn init() {
|
||||||
|
//! # // This is normally provided by the selected PAC
|
||||||
|
//! # 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);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn usage() {
|
||||||
|
//! 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<Self>) {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// SAFETY: We take full ownership of the peripheral and interrupt vector,
|
||||||
|
// plus we are not using any external shared resources so we won't impact
|
||||||
|
// basepri/source masking based critical sections.
|
||||||
|
unsafe {
|
||||||
|
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer);
|
||||||
|
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<F: Future>(
|
||||||
|
instant: <Self as Monotonic>::Instant,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
$tq.timeout_at(instant, future).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeout after a specific duration.
|
||||||
|
#[inline]
|
||||||
|
pub async fn timeout_after<F: Future>(
|
||||||
|
duration: <Self as Monotonic>::Duration,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
$tq.timeout_after(duration, future).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay for some duration of time.
|
||||||
|
#[inline]
|
||||||
|
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||||
|
$tq.delay(duration).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay to some specific time instant.
|
||||||
|
#[inline]
|
||||||
|
pub async fn delay_until(instant: <Self as Monotonic>::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);
|
|
@ -1,4 +1,28 @@
|
||||||
//! A monotonic implementation for RP2040's Timer peripheral.
|
//! A monotonic implementation for RP2040's Timer peripheral.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use rtic_monotonics::rp2040::*;
|
||||||
|
//!
|
||||||
|
//! fn init() {
|
||||||
|
//! # // This is normally provided by the selected PAC
|
||||||
|
//! # 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);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn usage() {
|
||||||
|
//! loop {
|
||||||
|
//! // Use the monotonic
|
||||||
|
//! Timer::delay(100.millis()).await;
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use super::Monotonic;
|
use super::Monotonic;
|
||||||
pub use super::{TimeoutError, TimerQueue};
|
pub use super::{TimeoutError, TimerQueue};
|
||||||
|
@ -22,7 +46,10 @@ impl Timer {
|
||||||
|
|
||||||
TIMER_QUEUE.initialize(Self {});
|
TIMER_QUEUE.initialize(Self {});
|
||||||
|
|
||||||
unsafe { NVIC::unmask(Interrupt::TIMER_IRQ_0) };
|
unsafe {
|
||||||
|
crate::set_monotonic_prio(rp2040_pac::NVIC_PRIO_BITS, Interrupt::TIMER_IRQ_0);
|
||||||
|
NVIC::unmask(Interrupt::TIMER_IRQ_0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timer() -> &'static timer::RegisterBlock {
|
fn timer() -> &'static timer::RegisterBlock {
|
||||||
|
@ -41,6 +68,7 @@ impl Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Timeout at a specific time.
|
/// Timeout at a specific time.
|
||||||
|
#[inline]
|
||||||
pub async fn timeout_at<F: Future>(
|
pub async fn timeout_at<F: Future>(
|
||||||
instant: <Self as Monotonic>::Instant,
|
instant: <Self as Monotonic>::Instant,
|
||||||
future: F,
|
future: F,
|
||||||
|
@ -64,6 +92,7 @@ impl Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delay to some specific time instant.
|
/// Delay to some specific time instant.
|
||||||
|
#[inline]
|
||||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||||
TIMER_QUEUE.delay_until(instant).await;
|
TIMER_QUEUE.delay_until(instant).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,28 @@
|
||||||
//! ...
|
//! 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::*;
|
||||||
|
//!
|
||||||
|
//! fn init() {
|
||||||
|
//! # // This is normally provided by the selected PAC
|
||||||
|
//! # 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);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn usage() {
|
||||||
|
//! loop {
|
||||||
|
//! // Use the monotonic
|
||||||
|
//! Systick::delay(100.millis()).await;
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use super::Monotonic;
|
use super::Monotonic;
|
||||||
pub use super::{TimeoutError, TimerQueue};
|
pub use super::{TimeoutError, TimerQueue};
|
||||||
|
|
|
@ -9,6 +9,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- `should_dequeue` to the `Monotonic` trait to handle bugged timers
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -131,7 +131,7 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
let head = self.queue.pop_if(|head| {
|
let head = self.queue.pop_if(|head| {
|
||||||
release_at = Some(head.release_at);
|
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);
|
head.was_poped.store(should_pop, Ordering::Relaxed);
|
||||||
|
|
||||||
should_pop
|
should_pop
|
||||||
|
@ -145,7 +145,7 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
Mono::enable_timer();
|
Mono::enable_timer();
|
||||||
Mono::set_compare(instant);
|
Mono::set_compare(instant);
|
||||||
|
|
||||||
if Mono::now() >= instant {
|
if Mono::should_dequeue_check(instant) {
|
||||||
// The time for the next instant passed while handling it,
|
// The time for the next instant passed while handling it,
|
||||||
// continue dequeueing
|
// continue dequeueing
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -33,6 +33,13 @@ pub trait Monotonic {
|
||||||
/// queue in RTIC checks this.
|
/// queue in RTIC checks this.
|
||||||
fn set_compare(instant: Self::Instant);
|
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 {
|
||||||
|
<Self as Monotonic>::now() >= release_at
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear the compare interrupt flag.
|
/// Clear the compare interrupt flag.
|
||||||
fn clear_compare_flag();
|
fn clear_compare_flag();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue