mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-25 03:19: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
|
||||
|
||||
- nRF52xxx, nRF9160, nRF5340 Timer and RTC monotonics
|
||||
- Interrupt tokens for `Systick` and `rp2040` to make sure an interrupt handler exists
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -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"]
|
||||
|
@ -35,4 +48,14 @@ systick-100hz = []
|
|||
systick-10khz = []
|
||||
|
||||
# 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")]
|
||||
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
|
||||
/// 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.
|
||||
//!
|
||||
//! # 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;
|
||||
pub use super::{TimeoutError, TimerQueue};
|
||||
|
@ -22,7 +46,10 @@ impl Timer {
|
|||
|
||||
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 {
|
||||
|
@ -41,6 +68,7 @@ impl Timer {
|
|||
}
|
||||
|
||||
/// Timeout at a specific time.
|
||||
#[inline]
|
||||
pub async fn timeout_at<F: Future>(
|
||||
instant: <Self as Monotonic>::Instant,
|
||||
future: F,
|
||||
|
@ -64,6 +92,7 @@ impl Timer {
|
|||
}
|
||||
|
||||
/// Delay to some specific time instant.
|
||||
#[inline]
|
||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||
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;
|
||||
pub use super::{TimeoutError, TimerQueue};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -131,7 +131,7 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
|||
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<Mono: Monotonic> TimerQueue<Mono> {
|
|||
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;
|
||||
|
|
|
@ -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 {
|
||||
<Self as Monotonic>::now() >= release_at
|
||||
}
|
||||
|
||||
/// Clear the compare interrupt flag.
|
||||
fn clear_compare_flag();
|
||||
|
||||
|
|
Loading…
Reference in a new issue