mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
Monotonic rewrite (#874)
* Rework timer_queue and monotonic architecture Goals: * make Monotonic purely internal * make Monotonic purely tick passed, no fugit involved * create a wrapper struct in the user's code via a macro that then converts the "now" from the tick based monotonic to a fugit based timestamp We need to proxy the delay functions of the timer queue anyway, so we could simply perform the conversion in those proxy functions. * Update cargo.lock * Update readme of rtic-time * CI: ESP32: Redact esp_image: Too volatile * Fixup: Changelog double entry rebase mistake --------- Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
This commit is contained in:
parent
e4cc5fd17b
commit
8c23e178f3
54 changed files with 2637 additions and 1676 deletions
|
|
@ -5,10 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
||||
|
||||
## Unreleased
|
||||
## Unreleased - v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Rework all timers based on `rtic-time 2.0.0`
|
||||
- Most timer tick rates are now configurable
|
||||
- Tweak `build.rs` to avoid warnings in Nightly 1.78+
|
||||
- Removed unused `rust-toolchain.toml`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rtic-monotonics"
|
||||
version = "1.5.0"
|
||||
version = "2.0.0"
|
||||
|
||||
edition = "2021"
|
||||
authors = [
|
||||
|
|
@ -24,13 +24,17 @@ features = [
|
|||
"imxrt_gpt1",
|
||||
"imxrt_gpt2",
|
||||
"imxrt-ral/imxrt1011",
|
||||
"stm32h725ag",
|
||||
"stm32_tim2",
|
||||
"stm32_tim3",
|
||||
"stm32_tim4",
|
||||
"stm32_tim5",
|
||||
"stm32_tim15",
|
||||
]
|
||||
rustdoc-flags = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rtic-time = { version = "1.1.0", path = "../rtic-time" }
|
||||
embedded-hal = { version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0", optional = true }
|
||||
rtic-time = { version = "2.0.0", path = "../rtic-time" }
|
||||
fugit = { version = "0.3.6" }
|
||||
atomic-polyfill = "1"
|
||||
cfg-if = "1.0.0"
|
||||
|
|
@ -69,8 +73,6 @@ defmt = ["fugit/defmt"]
|
|||
|
||||
# Systick on Cortex-M, default 1 kHz
|
||||
cortex-m-systick = ["dep:cortex-m"]
|
||||
systick-100hz = []
|
||||
systick-10khz = []
|
||||
# Use 64-bit wide backing storage for the Instant
|
||||
systick-64bit = []
|
||||
|
||||
|
|
@ -99,7 +101,6 @@ stm32_tim2 = []
|
|||
stm32_tim3 = []
|
||||
stm32_tim4 = []
|
||||
stm32_tim5 = []
|
||||
stm32_tim12 = []
|
||||
stm32_tim15 = []
|
||||
|
||||
stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"]
|
||||
|
|
|
|||
|
|
@ -1,112 +1,158 @@
|
|||
//! [`Monotonic`] implementations for i.MX RT's GPT peripherals.
|
||||
//! [`Monotonic`](rtic_time::Monotonic) implementations for i.MX RT's GPT peripherals.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::imxrt::*;
|
||||
//! use rtic_monotonics::imxrt::Gpt1 as Mono;
|
||||
//! use rtic_monotonics::imxrt::prelude::*;
|
||||
//! imxrt_gpt1_monotonic!(Mono, 1_000_000);
|
||||
//!
|
||||
//! fn init() {
|
||||
//! // Obtain ownership of the timer register block
|
||||
//! // Obtain ownership of the timer register block.
|
||||
//! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() };
|
||||
//!
|
||||
//! // Configure the timer clock source and determine its tick rate
|
||||
//! let timer_tickrate_hz = 1_000_000;
|
||||
//!
|
||||
//! // Generate timer token to ensure correct timer interrupt handler is used
|
||||
//! let token = rtic_monotonics::create_imxrt_gpt1_token!();
|
||||
//! // Configure the timer tick rate as specified earlier
|
||||
//! todo!("Configure the gpt1 peripheral to a tick rate of 1_000_000");
|
||||
//!
|
||||
//! // Start the monotonic
|
||||
//! Mono::start(timer_tickrate_hz, gpt1, token);
|
||||
//! Mono::start(gpt1);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! let timestamp = Mono::now().ticks();
|
||||
//! let timestamp = Mono::now();
|
||||
//! Mono::delay(100.millis()).await;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
use rtic_time::{
|
||||
half_period_counter::calculate_now,
|
||||
timer_queue::{TimerQueue, TimerQueueBackend},
|
||||
};
|
||||
|
||||
use imxrt_ral as ral;
|
||||
pub use imxrt_ral as ral;
|
||||
|
||||
const TIMER_HZ: u32 = 1_000_000;
|
||||
/// Common definitions and traits for using the i.MX RT monotonics
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "imxrt_gpt1")]
|
||||
pub use crate::imxrt_gpt1_monotonic;
|
||||
#[cfg(feature = "imxrt_gpt2")]
|
||||
pub use crate::imxrt_gpt2_monotonic;
|
||||
|
||||
pub use crate::Monotonic;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_imxrt_timer_interrupt {
|
||||
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{
|
||||
($mono_backend:ident, $timer:ident) => {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $timer() {
|
||||
$crate::imxrt::$mono_timer::__tq().on_monotonic_interrupt();
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::imxrt::$mono_backend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_imxrt_timer_struct {
|
||||
($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
|
||||
/// A `Monotonic` based on the GPT peripheral.
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(gpt: $crate::imxrt::ral::gpt::$timer) {
|
||||
$crate::__internal_create_imxrt_timer_interrupt!($mono_backend, $timer);
|
||||
|
||||
$crate::imxrt::$mono_backend::_start(gpt);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct $timer_token;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::imxrt::$mono_backend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::imxrt::$mono_timer> for $timer_token {}
|
||||
|
||||
$timer_token
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the GPT1 interrupt for the monotonic.
|
||||
/// Create a GPT1 based monotonic and register the GPT1 interrupt for it.
|
||||
///
|
||||
/// See [`crate::imxrt`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility
|
||||
/// to configure the peripheral to the given frequency before starting the
|
||||
/// monotonic.
|
||||
#[cfg(feature = "imxrt_gpt1")]
|
||||
#[macro_export]
|
||||
macro_rules! create_imxrt_gpt1_token {
|
||||
() => {{
|
||||
$crate::__internal_create_imxrt_timer_interrupt!(Gpt1, GPT1, Gpt1Token)
|
||||
}};
|
||||
macro_rules! imxrt_gpt1_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_imxrt_timer_struct!($name, Gpt1Backend, GPT1, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the GPT2 interrupt for the monotonic.
|
||||
/// Create a GPT2 based monotonic and register the GPT2 interrupt for it.
|
||||
///
|
||||
/// See [`crate::imxrt`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility
|
||||
/// to configure the peripheral to the given frequency before starting the
|
||||
/// monotonic.
|
||||
#[cfg(feature = "imxrt_gpt2")]
|
||||
#[macro_export]
|
||||
macro_rules! create_imxrt_gpt2_token {
|
||||
() => {{
|
||||
$crate::__internal_create_imxrt_timer_interrupt!(Gpt2, GPT2, Gpt2Token)
|
||||
}};
|
||||
macro_rules! imxrt_gpt2_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_imxrt_timer_struct!($name, Gpt2Backend, GPT2, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! make_timer {
|
||||
($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Timer implementing [`Monotonic`] which runs at 1 MHz.
|
||||
($mono_name:ident, $backend_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// GPT based [`TimerQueueBackend`].
|
||||
$(
|
||||
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
|
||||
)?
|
||||
|
||||
pub struct $mono_name;
|
||||
pub struct $backend_name;
|
||||
|
||||
use ral::gpt::$timer;
|
||||
|
||||
/// Number of 2^31 periods elapsed since boot.
|
||||
static $period: AtomicU32 = AtomicU32::new(0);
|
||||
static $tq: TimerQueue<$mono_name> = TimerQueue::new();
|
||||
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
|
||||
|
||||
impl $mono_name {
|
||||
/// Starts the monotonic timer.
|
||||
impl $backend_name {
|
||||
/// Starts the timer.
|
||||
///
|
||||
/// - `tick_freq_hz`: The tick frequency of the given timer.
|
||||
/// - `gpt`: The GPT timer register block instance.
|
||||
/// - `_interrupt_token`: Required for correct timer interrupt handling.
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(tick_freq_hz: u32, gpt: $timer, _interrupt_token: impl crate::InterruptToken<Self>) {
|
||||
// Find a prescaler that creates our desired tick frequency
|
||||
let previous_prescaler = ral::read_reg!(ral::gpt, gpt, PR, PRESCALER) + 1;
|
||||
let previous_clock_freq = tick_freq_hz * previous_prescaler;
|
||||
assert!((previous_clock_freq % TIMER_HZ) == 0,
|
||||
"Unable to find a fitting prescaler value!\n Input: {}/{}\n Desired: {}",
|
||||
previous_clock_freq, previous_prescaler, TIMER_HZ);
|
||||
let prescaler = previous_clock_freq / TIMER_HZ;
|
||||
assert!(prescaler > 0);
|
||||
assert!(prescaler <= 4096);
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(gpt: $timer) {
|
||||
|
||||
// Disable the timer.
|
||||
ral::modify_reg!(ral::gpt, gpt, CR, EN: 0);
|
||||
|
|
@ -122,11 +168,6 @@ macro_rules! make_timer {
|
|||
// Reset period
|
||||
$period.store(0, Ordering::SeqCst);
|
||||
|
||||
// Prescaler
|
||||
ral::modify_reg!(ral::gpt, gpt, PR,
|
||||
PRESCALER: (prescaler - 1), // Scale to our desired clock rate
|
||||
);
|
||||
|
||||
// Enable interrupts
|
||||
ral::write_reg!(ral::gpt, gpt, IR,
|
||||
ROVIE: 1, // Rollover interrupt
|
||||
|
|
@ -150,7 +191,6 @@ macro_rules! make_timer {
|
|||
ENMOD: 0, // Keep state when disabled
|
||||
);
|
||||
|
||||
|
||||
// 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.
|
||||
|
|
@ -159,65 +199,21 @@ macro_rules! make_timer {
|
|||
cortex_m::peripheral::NVIC::unmask(ral::Interrupt::$timer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to access the underlying timer queue
|
||||
#[doc(hidden)]
|
||||
pub fn __tq() -> &'static TimerQueue<$mono_name> {
|
||||
&$tq
|
||||
}
|
||||
|
||||
/// Delay for some duration of time.
|
||||
#[inline]
|
||||
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||
$tq.delay(duration).await;
|
||||
}
|
||||
|
||||
/// Timeout at a specific time.
|
||||
pub async fn timeout_at<F: core::future::Future>(
|
||||
instant: <Self as rtic_time::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: core::future::Future>(
|
||||
duration: <Self as Monotonic>::Duration,
|
||||
future: F,
|
||||
) -> Result<F::Output, TimeoutError> {
|
||||
$tq.timeout_after(duration, future).await
|
||||
}
|
||||
|
||||
/// Delay to some specific time instant.
|
||||
#[inline]
|
||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||
$tq.delay_until(instant).await;
|
||||
}
|
||||
}
|
||||
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
|
||||
impl TimerQueueBackend for $backend_name {
|
||||
type Ticks = u64;
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
|
||||
|
||||
impl Monotonic for $mono_name {
|
||||
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
|
||||
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
|
||||
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
fn now() -> Self::Ticks {
|
||||
let gpt = unsafe{ $timer::instance() };
|
||||
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
calculate_now(
|
||||
|| $period.load(Ordering::Relaxed),
|
||||
|| ral::read_reg!(ral::gpt, gpt, CNT)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
fn set_compare(instant: Self::Ticks) {
|
||||
let gpt = unsafe{ $timer::instance() };
|
||||
|
||||
// Set the timer regardless of whether it is multiple periods in the future,
|
||||
|
|
@ -225,8 +221,7 @@ macro_rules! make_timer {
|
|||
// The worst thing that can happen is a spurious wakeup, and with a timer
|
||||
// period of half an hour, this is hardly a problem.
|
||||
|
||||
let ticks = instant.duration_since_epoch().ticks();
|
||||
let ticks_wrapped = ticks as u32;
|
||||
let ticks_wrapped = instant as u32;
|
||||
|
||||
ral::write_reg!(ral::gpt, gpt, OCR[1], ticks_wrapped);
|
||||
}
|
||||
|
|
@ -257,12 +252,16 @@ macro_rules! make_timer {
|
|||
assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
|
||||
}
|
||||
}
|
||||
|
||||
fn timer_queue() -> &'static TimerQueue<Self> {
|
||||
&$tq
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "imxrt_gpt1")]
|
||||
make_timer!(Gpt1, GPT1, GPT1_HALFPERIODS, GPT1_TQ);
|
||||
make_timer!(Gpt1, Gpt1Backend, GPT1, GPT1_HALFPERIODS, GPT1_TQ);
|
||||
|
||||
#[cfg(feature = "imxrt_gpt2")]
|
||||
make_timer!(Gpt2, GPT2, GPT2_HALFPERIODS, GPT2_TQ);
|
||||
make_timer!(Gpt2, Gpt2Backend, GPT2, GPT2_HALFPERIODS, GPT2_TQ);
|
||||
|
|
|
|||
|
|
@ -21,14 +21,18 @@
|
|||
//! `Available on crate features X only` tag are available on any `nrf52*` feature.
|
||||
//!
|
||||
// To build these docs correctly:
|
||||
// RUSTFLAGS="--cfg docsrs" cargo doc --featuers cortex-m-systick,rp2040,nrf52840
|
||||
// RUSTFLAGS="--cfg docsrs" cargo +nightly doc --features thumbv7-backend,cortex-m-systick,rp2040,nrf52840,imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1011,stm32h725ag,stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15
|
||||
|
||||
#![no_std]
|
||||
#![deny(missing_docs)]
|
||||
#![allow(incomplete_features)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub use rtic_time::{Monotonic, TimeoutError, TimerQueue};
|
||||
pub use fugit;
|
||||
pub use rtic_time::{
|
||||
self, monotonic::TimerQueueBasedMonotonic, timer_queue::TimerQueueBackend, Monotonic,
|
||||
TimeoutError,
|
||||
};
|
||||
|
||||
#[cfg(feature = "cortex-m-systick")]
|
||||
pub mod systick;
|
||||
|
|
@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio(
|
|||
|
||||
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.
|
||||
///
|
||||
/// This trait is implemented by this crate and not intended for user implementation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is only safely implemented by this crate.
|
||||
pub unsafe trait InterruptToken<Periperhal> {}
|
||||
|
|
|
|||
|
|
@ -1,96 +1,152 @@
|
|||
//! [`Monotonic`] implementation for the nRF Real Time Clocks (RTC).
|
||||
//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC).
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::nrf::rtc::*;
|
||||
//! use rtic_monotonics::nrf::rtc::prelude::*;
|
||||
//! nrf_rtc0_monotonic!(Mono);
|
||||
//!
|
||||
//! 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);
|
||||
//! Mono::start(rtc);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! Rtc0::delay(100.millis()).await;
|
||||
//! let timestamp = Mono::now();
|
||||
//! Mono::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};
|
||||
/// Common definitions and traits for using the nRF RTC monotonics
|
||||
pub mod prelude {
|
||||
pub use crate::nrf_rtc0_monotonic;
|
||||
pub use crate::nrf_rtc1_monotonic;
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub use crate::nrf_rtc2_monotonic;
|
||||
|
||||
pub use crate::Monotonic;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
}
|
||||
|
||||
#[cfg(feature = "nrf52810")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf52810_pac::{self as pac, RTC0, RTC1};
|
||||
#[cfg(feature = "nrf52811")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf52811_pac::{self as pac, RTC0, RTC1};
|
||||
#[cfg(feature = "nrf52832")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf52832_pac::{self as pac, RTC0, RTC1, RTC2};
|
||||
#[cfg(feature = "nrf52833")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf52833_pac::{self as pac, RTC0, RTC1, RTC2};
|
||||
#[cfg(feature = "nrf52840")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf52840_pac::{self as pac, RTC0, RTC1, RTC2};
|
||||
#[cfg(feature = "nrf5340-app")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf5340_app_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
|
||||
#[cfg(feature = "nrf5340-net")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf5340_net_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
|
||||
#[cfg(feature = "nrf9160")]
|
||||
#[doc(hidden)]
|
||||
pub use nrf9160_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
use core::future::Future;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
use rtic_time::{
|
||||
half_period_counter::calculate_now,
|
||||
timer_queue::{TimerQueue, TimerQueueBackend},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_nrf_rtc_interrupt {
|
||||
($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{
|
||||
($mono_backend:ident, $rtc:ident) => {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $rtc() {
|
||||
$crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt();
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::nrf::rtc::$mono_backend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_nrf_rtc_struct {
|
||||
($name:ident, $mono_backend:ident, $timer:ident) => {
|
||||
/// A `Monotonic` based on the nRF RTC peripheral.
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(rtc: $crate::nrf::rtc::$timer) {
|
||||
$crate::__internal_create_nrf_rtc_interrupt!($mono_backend, $timer);
|
||||
|
||||
$crate::nrf::rtc::$mono_backend::_start(rtc);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct $rtc_token;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::nrf::rtc::$mono_backend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
32_768,
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
32_768,
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {}
|
||||
|
||||
$rtc_token
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Rtc0 interrupt for the monotonic.
|
||||
/// Create an RTC0 based monotonic and register the RTC0 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::rtc`] for more details.
|
||||
#[macro_export]
|
||||
macro_rules! create_nrf_rtc0_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token)
|
||||
}};
|
||||
macro_rules! nrf_rtc0_monotonic {
|
||||
($name:ident) => {
|
||||
$crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Rtc1 interrupt for the monotonic.
|
||||
/// Create an RTC1 based monotonic and register the RTC1 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::rtc`] for more details.
|
||||
#[macro_export]
|
||||
macro_rules! create_nrf_rtc1_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token)
|
||||
}};
|
||||
macro_rules! nrf_rtc1_monotonic {
|
||||
($name:ident) => {
|
||||
$crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Rtc2 interrupt for the monotonic.
|
||||
/// Create an RTC2 based monotonic and register the RTC2 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::rtc`] for more details.
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(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! nrf_rtc2_monotonic {
|
||||
($name:ident) => {
|
||||
$crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2);
|
||||
};
|
||||
}
|
||||
|
||||
struct TimerValueU24(u32);
|
||||
|
|
@ -104,19 +160,23 @@ impl From<TimerValueU24> for u64 {
|
|||
}
|
||||
|
||||
macro_rules! make_rtc {
|
||||
($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Monotonic timer queue implementation.
|
||||
($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// RTC based [`TimerQueueBackend`].
|
||||
$(
|
||||
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
|
||||
)?
|
||||
pub struct $mono_name;
|
||||
pub struct $backend_name;
|
||||
|
||||
static $overflow: AtomicU32 = AtomicU32::new(0);
|
||||
static $tq: TimerQueue<$mono_name> = TimerQueue::new();
|
||||
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
|
||||
|
||||
impl $mono_name {
|
||||
/// Start the timer monotonic.
|
||||
pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) {
|
||||
impl $backend_name {
|
||||
/// Starts the timer.
|
||||
///
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(rtc: $rtc) {
|
||||
unsafe { rtc.prescaler.write(|w| w.bits(0)) };
|
||||
|
||||
// Disable interrupts, as preparation
|
||||
|
|
@ -166,67 +226,21 @@ macro_rules! make_rtc {
|
|||
// 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);
|
||||
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc);
|
||||
pac::NVIC::unmask(pac::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;
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerQueueBackend for $backend_name {
|
||||
type Ticks = u64;
|
||||
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
|
||||
|
||||
impl Monotonic for $mono_name {
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
type Instant = fugit::TimerInstantU64<32_768>;
|
||||
type Duration = fugit::TimerDurationU64<32_768>;
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
fn now() -> Self::Ticks {
|
||||
let rtc = unsafe { &*$rtc::PTR };
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
calculate_now(
|
||||
|| $overflow.load(Ordering::Relaxed),
|
||||
|| TimerValueU24(rtc.counter.read().bits())
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn on_interrupt() {
|
||||
|
|
@ -243,28 +257,35 @@ macro_rules! make_rtc {
|
|||
}
|
||||
}
|
||||
|
||||
fn enable_timer() {}
|
||||
|
||||
fn disable_timer() {}
|
||||
|
||||
fn set_compare(mut instant: Self::Instant) {
|
||||
fn set_compare(mut instant: Self::Ticks) {
|
||||
let rtc = unsafe { &*$rtc::PTR };
|
||||
|
||||
const MAX: u64 = 0xff_ffff;
|
||||
|
||||
// Disable interrupts because this section is timing critical.
|
||||
// We rely on the fact that this entire section runs within one
|
||||
// RTC clock tick. (which it will do easily if it doesn't get
|
||||
// interrupted)
|
||||
critical_section::with(|_|{
|
||||
let now = Self::now();
|
||||
if let Some(diff) = instant.checked_duration_since(now) {
|
||||
// wrapping_sub deals with the u64 overflow corner case
|
||||
let diff = instant.wrapping_sub(now);
|
||||
let val = if diff <= MAX {
|
||||
// Now we know `instant` whill happen within one `MAX` time duration.
|
||||
|
||||
// Errata: Timer interrupts don't fire if they are scheduled less than
|
||||
// two ticks in the future. Make it three, because the timer could
|
||||
// tick right now.
|
||||
if diff.ticks() < 3 {
|
||||
instant = Self::Instant::from_ticks(now.ticks().wrapping_add(3));
|
||||
if diff < 3 {
|
||||
instant = now.wrapping_add(3);
|
||||
}
|
||||
unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) };
|
||||
}
|
||||
|
||||
(instant & MAX) as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
unsafe { rtc.cc[0].write(|w| w.bits(val)) };
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -274,13 +295,17 @@ macro_rules! make_rtc {
|
|||
}
|
||||
|
||||
fn pend_interrupt() {
|
||||
pac::NVIC::pend(Interrupt::$rtc);
|
||||
pac::NVIC::pend(pac::Interrupt::$rtc);
|
||||
}
|
||||
|
||||
fn timer_queue() -> &'static TimerQueue<Self> {
|
||||
&$tq
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
|
||||
make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
|
||||
make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
|
||||
make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! [`Monotonic`] impl for the 32-bit timers of the nRF series.
|
||||
//! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series.
|
||||
//!
|
||||
//! Not all timers are available on all parts. Ensure that only the available
|
||||
//! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`.
|
||||
|
|
@ -6,139 +6,217 @@
|
|||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::nrf::timer::*;
|
||||
//! use rtic_monotonics::nrf::timer::prelude::*;
|
||||
//! nrf_timer0_monotonic!(Mono);
|
||||
//!
|
||||
//! 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);
|
||||
//! Mono::start(timer);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! Timer0::delay(100.millis()).await;
|
||||
//! let timestamp = Mono::now();
|
||||
//! Mono::delay(100.millis()).await;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
use core::future::Future;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
/// Common definitions and traits for using the nRF Timer monotonics
|
||||
pub mod prelude {
|
||||
pub use crate::nrf_timer0_monotonic;
|
||||
pub use crate::nrf_timer1_monotonic;
|
||||
pub use crate::nrf_timer2_monotonic;
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub use crate::nrf_timer3_monotonic;
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub use crate::nrf_timer4_monotonic;
|
||||
|
||||
pub use crate::Monotonic;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
}
|
||||
|
||||
#[cfg(feature = "nrf52810")]
|
||||
use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2};
|
||||
#[doc(hidden)]
|
||||
pub use nrf52810_pac::{self as pac, TIMER0, TIMER1, TIMER2};
|
||||
#[cfg(feature = "nrf52811")]
|
||||
use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2};
|
||||
#[doc(hidden)]
|
||||
pub use nrf52811_pac::{self as pac, TIMER0, TIMER1, TIMER2};
|
||||
#[cfg(feature = "nrf52832")]
|
||||
use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
|
||||
#[doc(hidden)]
|
||||
pub use nrf52832_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
|
||||
#[cfg(feature = "nrf52833")]
|
||||
use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
|
||||
#[doc(hidden)]
|
||||
pub use nrf52833_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
|
||||
#[cfg(feature = "nrf52840")]
|
||||
use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
|
||||
#[doc(hidden)]
|
||||
pub use nrf52840_pac::{self as pac, 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,
|
||||
#[doc(hidden)]
|
||||
pub use nrf5340_app_pac::{
|
||||
self as pac, 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,
|
||||
#[doc(hidden)]
|
||||
pub use nrf5340_net_pac::{
|
||||
self as pac, 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)]
|
||||
pub use nrf9160_pac::{self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2};
|
||||
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
use rtic_time::{
|
||||
half_period_counter::calculate_now,
|
||||
timer_queue::{TimerQueue, TimerQueueBackend},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_nrf_timer_interrupt {
|
||||
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{
|
||||
($mono_backend:ident, $timer:ident) => {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $timer() {
|
||||
$crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt();
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::nrf::timer::$mono_backend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_nrf_timer_struct {
|
||||
($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
|
||||
/// A `Monotonic` based on the nRF Timer peripheral.
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(timer: $crate::nrf::timer::$timer) {
|
||||
$crate::__internal_create_nrf_timer_interrupt!($mono_backend, $timer);
|
||||
|
||||
const PRESCALER: u8 = match $tick_rate_hz {
|
||||
16_000_000 => 0,
|
||||
8_000_000 => 1,
|
||||
4_000_000 => 2,
|
||||
2_000_000 => 3,
|
||||
1_000_000 => 4,
|
||||
500_000 => 5,
|
||||
250_000 => 6,
|
||||
125_000 => 7,
|
||||
62_500 => 8,
|
||||
31_250 => 9,
|
||||
_ => panic!("Timer cannot run at desired tick rate!"),
|
||||
};
|
||||
|
||||
$crate::nrf::timer::$mono_backend::_start(timer, PRESCALER);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct $timer_token;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::nrf::timer::$mono_backend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {}
|
||||
|
||||
$timer_token
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Timer0 interrupt for the monotonic.
|
||||
/// Create an Timer0 based monotonic and register the TIMER0 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::timer`] for more details.
|
||||
#[macro_export]
|
||||
macro_rules! create_nrf_timer0_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token)
|
||||
}};
|
||||
macro_rules! nrf_timer0_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_nrf_timer_struct!($name, Timer0Backend, TIMER0, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Timer1 interrupt for the monotonic.
|
||||
/// Create an Timer1 based monotonic and register the TIMER1 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::timer`] for more details.
|
||||
#[macro_export]
|
||||
macro_rules! create_nrf_timer1_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token)
|
||||
}};
|
||||
macro_rules! nrf_timer1_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_nrf_timer_struct!($name, Timer1Backend, TIMER1, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Timer2 interrupt for the monotonic.
|
||||
/// Create an Timer2 based monotonic and register the TIMER2 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::timer`] for more details.
|
||||
#[macro_export]
|
||||
macro_rules! create_nrf_timer2_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token)
|
||||
}};
|
||||
macro_rules! nrf_timer2_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_nrf_timer_struct!($name, Timer2Backend, TIMER2, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Timer3 interrupt for the monotonic.
|
||||
/// Create an Timer3 based monotonic and register the TIMER3 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::timer`] for more details.
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
|
||||
)]
|
||||
#[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)
|
||||
}};
|
||||
macro_rules! nrf_timer3_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_nrf_timer_struct!($name, Timer3Backend, TIMER3, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register the Timer4 interrupt for the monotonic.
|
||||
/// Create an Timer4 based monotonic and register the TIMER4 interrupt for it.
|
||||
///
|
||||
/// See [`crate::nrf::timer`] for more details.
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
|
||||
)]
|
||||
#[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! nrf_timer4_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! make_timer {
|
||||
($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Monotonic timer queue implementation.
|
||||
($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Timer peripheral based [`TimerQueueBackend`].
|
||||
$(
|
||||
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
|
||||
)?
|
||||
pub struct $mono_name;
|
||||
pub struct $backend_name;
|
||||
|
||||
static $overflow: AtomicU32 = AtomicU32::new(0);
|
||||
static $tq: TimerQueue<$mono_name> = TimerQueue::new();
|
||||
static $tq: TimerQueue<$backend_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) });
|
||||
impl $backend_name {
|
||||
/// Starts the timer.
|
||||
///
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(timer: $timer, prescaler: u8) {
|
||||
timer.prescaler.write(|w| unsafe { w.prescaler().bits(prescaler) });
|
||||
timer.bitmode.write(|w| w.bitmode()._32bit());
|
||||
|
||||
// Disable interrupts, as preparation
|
||||
|
|
@ -184,70 +262,25 @@ macro_rules! make_timer {
|
|||
// 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);
|
||||
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer);
|
||||
pac::NVIC::unmask(pac::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;
|
||||
}
|
||||
}
|
||||
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
|
||||
impl TimerQueueBackend for $backend_name {
|
||||
type Ticks = u64;
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
|
||||
|
||||
impl Monotonic for $mono_name {
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
type Instant = fugit::TimerInstantU64<1_000_000>;
|
||||
type Duration = fugit::TimerDurationU64<1_000_000>;
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
fn now() -> Self::Ticks {
|
||||
let timer = unsafe { &*$timer::PTR };
|
||||
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
calculate_now(
|
||||
|| $overflow.load(Ordering::Relaxed),
|
||||
|| {
|
||||
timer.tasks_capture[3].write(|w| unsafe { w.bits(1) });
|
||||
timer.cc[3].read().bits()
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn on_interrupt() {
|
||||
|
|
@ -268,9 +301,9 @@ macro_rules! make_timer {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
fn set_compare(instant: Self::Ticks) {
|
||||
let timer = unsafe { &*$timer::PTR };
|
||||
timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) });
|
||||
timer.cc[0].write(|w| unsafe { w.cc().bits(instant as u32) });
|
||||
}
|
||||
|
||||
fn clear_compare_flag() {
|
||||
|
|
@ -279,16 +312,20 @@ macro_rules! make_timer {
|
|||
}
|
||||
|
||||
fn pend_interrupt() {
|
||||
pac::NVIC::pend(Interrupt::$timer);
|
||||
pac::NVIC::pend(pac::Interrupt::$timer);
|
||||
}
|
||||
|
||||
fn timer_queue() -> &'static TimerQueue<$backend_name> {
|
||||
&$tq
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
|
||||
make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
|
||||
make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
|
||||
make_timer!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
|
||||
make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
|
||||
make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
make_timer!(Timer3Backend, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
make_timer!(Timer4Backend, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
|
||||
|
|
|
|||
|
|
@ -1,46 +1,56 @@
|
|||
//! [`Monotonic`] implementation for RP2040's Timer peripheral.
|
||||
//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral.
|
||||
//!
|
||||
//! Always runs at a fixed rate of 1 MHz.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::rp2040::*;
|
||||
//! use rtic_monotonics::rp2040::prelude::*;
|
||||
//!
|
||||
//! rp2040_timer_monotonic!(Mono);
|
||||
//!
|
||||
//! 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);
|
||||
//! Mono::start(timer, &mut resets);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! Timer::delay(100.millis()).await;
|
||||
//! let timestamp = Mono::now();
|
||||
//! Mono::delay(100.millis()).await;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use super::Monotonic;
|
||||
/// Common definitions and traits for using the RP2040 timer monotonic
|
||||
pub mod prelude {
|
||||
pub use crate::rp2040_timer_monotonic;
|
||||
|
||||
pub use super::{TimeoutError, TimerQueue};
|
||||
use core::future::Future;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER};
|
||||
pub use crate::Monotonic;
|
||||
|
||||
/// Timer implementing [`Monotonic`] which runs at 1 MHz.
|
||||
pub struct Timer;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Start a `Monotonic` based on RP2040's Timer.
|
||||
pub fn start(
|
||||
timer: TIMER,
|
||||
resets: &RESETS,
|
||||
_interrupt_token: impl crate::InterruptToken<Self>,
|
||||
) {
|
||||
use crate::TimerQueueBackend;
|
||||
use rp2040_pac::{timer, Interrupt, NVIC};
|
||||
pub use rp2040_pac::{RESETS, TIMER};
|
||||
use rtic_time::timer_queue::TimerQueue;
|
||||
|
||||
/// Timer implementing [`TimerQueueBackend`].
|
||||
pub struct TimerBackend;
|
||||
|
||||
impl TimerBackend {
|
||||
/// Starts the monotonic timer.
|
||||
///
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(timer: TIMER, resets: &RESETS) {
|
||||
resets.reset.modify(|_, w| w.timer().clear_bit());
|
||||
while resets.reset_done.read().timer().bit_is_clear() {}
|
||||
timer.inte.modify(|_, w| w.alarm_0().bit(true));
|
||||
|
|
@ -58,55 +68,12 @@ impl Timer {
|
|||
}
|
||||
}
|
||||
|
||||
static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new();
|
||||
static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
|
||||
|
||||
// Forward timerqueue interface
|
||||
impl Timer {
|
||||
/// Used to access the underlying timer queue
|
||||
#[doc(hidden)]
|
||||
pub fn __tq() -> &'static TimerQueue<Timer> {
|
||||
&TIMER_QUEUE
|
||||
}
|
||||
impl TimerQueueBackend for TimerBackend {
|
||||
type Ticks = u64;
|
||||
|
||||
/// Timeout at a specific time.
|
||||
#[inline]
|
||||
pub async fn timeout_at<F: Future>(
|
||||
instant: <Self as Monotonic>::Instant,
|
||||
future: F,
|
||||
) -> Result<F::Output, TimeoutError> {
|
||||
TIMER_QUEUE.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> {
|
||||
TIMER_QUEUE.timeout_after(duration, future).await
|
||||
}
|
||||
|
||||
/// Delay for some duration of time.
|
||||
#[inline]
|
||||
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||
TIMER_QUEUE.delay(duration).await;
|
||||
}
|
||||
|
||||
/// Delay to some specific time instant.
|
||||
#[inline]
|
||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||
TIMER_QUEUE.delay_until(instant).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Monotonic for Timer {
|
||||
type Instant = fugit::TimerInstantU64<1_000_000>;
|
||||
type Duration = fugit::TimerDurationU64<1_000_000>;
|
||||
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
fn now() -> Self::Ticks {
|
||||
let timer = Self::timer();
|
||||
|
||||
let mut hi0 = timer.timerawh.read().bits();
|
||||
|
|
@ -114,22 +81,24 @@ impl Monotonic for Timer {
|
|||
let low = timer.timerawl.read().bits();
|
||||
let hi1 = timer.timerawh.read().bits();
|
||||
if hi0 == hi1 {
|
||||
break Self::Instant::from_ticks((u64::from(hi0) << 32) | u64::from(low));
|
||||
break ((u64::from(hi0) << 32) | u64::from(low));
|
||||
}
|
||||
hi0 = hi1;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
fn set_compare(instant: Self::Ticks) {
|
||||
let now = Self::now();
|
||||
|
||||
let max = u32::MAX as u64;
|
||||
const MAX: u64 = u32::MAX as u64;
|
||||
|
||||
// Since the timer may or may not overflow based on the requested compare val, we check
|
||||
// how many ticks are left.
|
||||
let val = match instant.checked_duration_since(now) {
|
||||
Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow
|
||||
_ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts
|
||||
// `wrapping_sup` takes care of the u64 integer overflow special case.
|
||||
let val = if instant.wrapping_sub(now) <= MAX {
|
||||
instant & MAX
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Self::timer()
|
||||
|
|
@ -145,32 +114,55 @@ impl Monotonic for Timer {
|
|||
rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0);
|
||||
}
|
||||
|
||||
fn on_interrupt() {}
|
||||
|
||||
fn enable_timer() {}
|
||||
|
||||
fn disable_timer() {}
|
||||
fn timer_queue() -> &'static TimerQueue<Self> {
|
||||
&TIMER_QUEUE
|
||||
}
|
||||
}
|
||||
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!(Timer);
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer);
|
||||
|
||||
/// Register the Timer interrupt for the monotonic.
|
||||
/// Create an RP2040 timer based monotonic and register the necessary interrupt for it.
|
||||
///
|
||||
/// See [`crate::rp2040`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
#[macro_export]
|
||||
macro_rules! create_rp2040_monotonic_token {
|
||||
() => {{
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn TIMER_IRQ_0() {
|
||||
$crate::rp2040::Timer::__tq().on_monotonic_interrupt();
|
||||
macro_rules! rp2040_timer_monotonic {
|
||||
($name:ident) => {
|
||||
/// A `Monotonic` based on the RP2040 Timer peripheral.
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn TIMER_IRQ_0() {
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
|
||||
$crate::rp2040::TimerBackend::_start(timer, resets);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rp2040Token;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::rp2040::TimerBackend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
1_000_000,
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
1_000_000,
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::rp2040::Timer> for Rp2040Token {}
|
||||
|
||||
Rp2040Token
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! [`Monotonic`] impl for the STM32.
|
||||
//! [`Monotonic`](rtic_time::Monotonic) implementations for STM32 chips.
|
||||
//!
|
||||
//! Not all timers are available on all parts. Ensure that only available
|
||||
//! timers are exposed by having the correct `stm32*` feature enabled for `rtic-monotonics`.
|
||||
|
|
@ -6,38 +6,56 @@
|
|||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::stm32::*;
|
||||
//! use rtic_monotonics::stm32::Tim2 as Mono;
|
||||
//! use rtic_monotonics::Monotonic;
|
||||
//! use embassy_stm32::peripherals::TIM2;
|
||||
//! use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
//! use rtic_monotonics::stm32::prelude::*;
|
||||
//!
|
||||
//! // Define the monotonic and set it to 1MHz tick rate
|
||||
//! stm32_tim2_monotonic!(Mono, 1_000_000);
|
||||
//!
|
||||
//! fn init() {
|
||||
//! // Generate timer token to ensure correct timer interrupt handler is used.
|
||||
//! let token = rtic_monotonics::create_stm32_tim2_monotonic_token!();
|
||||
//!
|
||||
//! // If using `embassy-stm32` HAL, timer clock can be read out like this:
|
||||
//! let timer_clock_hz = TIM2::frequency();
|
||||
//! let timer_clock_hz = embassy_stm32::peripherals::TIM2::frequency();
|
||||
//! // Or define it manually if you are using other HAL or know correct frequency:
|
||||
//! let timer_clock_hz = 64_000_000;
|
||||
//!
|
||||
//! // Start the monotonic
|
||||
//! Mono::start(timer_clock_hz, token);
|
||||
//! Mono::start(timer_clock_hz);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! let timestamp = Mono::now().ticks();
|
||||
//! let timestamp = Mono::now();
|
||||
//! Mono::delay(100.millis()).await;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
/// Common definitions and traits for using the STM32 monotonics
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "stm32_tim2")]
|
||||
pub use crate::stm32_tim2_monotonic;
|
||||
|
||||
#[cfg(feature = "stm32_tim3")]
|
||||
pub use crate::stm32_tim3_monotonic;
|
||||
|
||||
#[cfg(feature = "stm32_tim4")]
|
||||
pub use crate::stm32_tim4_monotonic;
|
||||
|
||||
#[cfg(feature = "stm32_tim5")]
|
||||
pub use crate::stm32_tim5_monotonic;
|
||||
|
||||
#[cfg(feature = "stm32_tim15")]
|
||||
pub use crate::stm32_tim15_monotonic;
|
||||
|
||||
pub use crate::Monotonic;
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
}
|
||||
|
||||
use atomic_polyfill::{AtomicU64, Ordering};
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
use rtic_time::{
|
||||
half_period_counter::calculate_now,
|
||||
timer_queue::{TimerQueue, TimerQueueBackend},
|
||||
};
|
||||
use stm32_metapac as pac;
|
||||
|
||||
mod _generated {
|
||||
|
|
@ -48,116 +66,180 @@ mod _generated {
|
|||
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
|
||||
}
|
||||
|
||||
const TIMER_HZ: u32 = 1_000_000;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_stm32_timer_interrupt {
|
||||
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{
|
||||
($mono_backend:ident, $interrupt_name:ident) => {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $timer() {
|
||||
$crate::stm32::$mono_timer::__tq().on_monotonic_interrupt();
|
||||
unsafe extern "C" fn $interrupt_name() {
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __internal_create_stm32_timer_struct {
|
||||
($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// - `tim_clock_hz`: `TIMx` peripheral clock frequency.
|
||||
///
|
||||
/// Panics if it is impossible to achieve the desired monotonic tick rate based
|
||||
/// on the given `tim_clock_hz` parameter. If that happens, adjust the desired monotonic tick rate.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(tim_clock_hz: u32) {
|
||||
$crate::__internal_create_stm32_timer_interrupt!($mono_backend, $timer);
|
||||
|
||||
$crate::stm32::$mono_backend::_start(tim_clock_hz, $tick_rate_hz);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct $timer_token;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::stm32::$mono_backend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {}
|
||||
|
||||
$timer_token
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register TIM2 interrupt for the monotonic.
|
||||
/// Create a TIM2 based monotonic and register the TIM2 interrupt for it.
|
||||
///
|
||||
/// See [`crate::stm32`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
///
|
||||
#[cfg(feature = "stm32_tim2")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim2_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim2, TIM2, Tim2Token)
|
||||
}};
|
||||
macro_rules! stm32_tim2_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_stm32_timer_struct!($name, Tim2Backend, TIM2, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register TIM3 interrupt for the monotonic.
|
||||
/// Create a TIM3 based monotonic and register the TIM3 interrupt for it.
|
||||
///
|
||||
/// See [`crate::stm32`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
///
|
||||
#[cfg(feature = "stm32_tim3")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim3_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim3, TIM3, Tim3Token)
|
||||
}};
|
||||
macro_rules! stm32_tim3_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_stm32_timer_struct!($name, Tim3Backend, TIM3, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register TIM4 interrupt for the monotonic.
|
||||
/// Create a TIM4 based monotonic and register the TIM4 interrupt for it.
|
||||
///
|
||||
/// See [`crate::stm32`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
///
|
||||
#[cfg(feature = "stm32_tim4")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim4_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim4, TIM4, Tim4Token)
|
||||
}};
|
||||
macro_rules! stm32_tim4_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_stm32_timer_struct!($name, Tim4Backend, TIM4, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register TIM5 interrupt for the monotonic.
|
||||
/// Create a TIM5 based monotonic and register the TIM5 interrupt for it.
|
||||
///
|
||||
/// See [`crate::stm32`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
///
|
||||
#[cfg(feature = "stm32_tim5")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim5_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim5, TIM5, Tim5Token)
|
||||
}};
|
||||
macro_rules! stm32_tim5_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
/// Register TIM12 interrupt for the monotonic.
|
||||
#[cfg(feature = "stm32_tim12")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim12_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim12, TIM12, Tim12Token)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Register TIM15 interrupt for the monotonic.
|
||||
/// Create a TIM15 based monotonic and register the TIM15 interrupt for it.
|
||||
///
|
||||
/// See [`crate::stm32`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
///
|
||||
#[cfg(feature = "stm32_tim15")]
|
||||
#[macro_export]
|
||||
macro_rules! create_stm32_tim15_monotonic_token {
|
||||
() => {{
|
||||
$crate::__internal_create_stm32_timer_interrupt!(Tim15, TIM15, Tim15Token)
|
||||
}};
|
||||
macro_rules! stm32_tim15_monotonic {
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
$crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! make_timer {
|
||||
($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Monotonic timer queue implementation.
|
||||
($backend_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Monotonic timer backend implementation.
|
||||
$(
|
||||
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
|
||||
)?
|
||||
|
||||
pub struct $mono_name;
|
||||
pub struct $backend_name;
|
||||
|
||||
use pac::$timer;
|
||||
|
||||
static $overflow: AtomicU64 = AtomicU64::new(0);
|
||||
static $tq: TimerQueue<$mono_name> = TimerQueue::new();
|
||||
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
|
||||
|
||||
impl $mono_name {
|
||||
/// Starts the monotonic timer.
|
||||
impl $backend_name {
|
||||
/// Starts the timer.
|
||||
///
|
||||
/// - `tim_clock_hz`: `TIMx` peripheral clock frequency.
|
||||
/// - `_interrupt_token`: Required for correct timer interrupt handling.
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(tim_clock_hz: u32, _interrupt_token: impl crate::InterruptToken<Self>) {
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
|
||||
_generated::$timer::enable();
|
||||
_generated::$timer::reset();
|
||||
|
||||
$timer.cr1().modify(|r| r.set_cen(false));
|
||||
|
||||
assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!");
|
||||
let psc = tim_clock_hz / TIMER_HZ - 1;
|
||||
assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
|
||||
let psc = tim_clock_hz / timer_hz - 1;
|
||||
$timer.psc().write(|r| r.set_psc(psc as u16));
|
||||
|
||||
// Enable full-period interrupt.
|
||||
$timer.dier().modify(|r| r.set_uie(true));
|
||||
|
||||
// Configure and enable half-period interrupt
|
||||
$timer.ccr(2).write(|r| r.set_ccr($bits::MAX - ($bits::MAX >> 1)));
|
||||
$timer.ccr(2).write(|r| r.set_ccr(($bits::MAX - ($bits::MAX >> 1)).into()));
|
||||
$timer.dier().modify(|r| r.set_ccie(2, true));
|
||||
|
||||
// Trigger an update event to load the prescaler value to the clock.
|
||||
|
|
@ -183,73 +265,31 @@ macro_rules! make_timer {
|
|||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to access the underlying timer queue
|
||||
#[doc(hidden)]
|
||||
pub fn __tq() -> &'static TimerQueue<$mono_name> {
|
||||
&$tq
|
||||
}
|
||||
|
||||
/// Delay for some duration of time.
|
||||
#[inline]
|
||||
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||
$tq.delay(duration).await;
|
||||
}
|
||||
|
||||
/// Timeout at a specific time.
|
||||
pub async fn timeout_at<F: core::future::Future>(
|
||||
instant: <Self as rtic_time::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: core::future::Future>(
|
||||
duration: <Self as Monotonic>::Duration,
|
||||
future: F,
|
||||
) -> Result<F::Output, TimeoutError> {
|
||||
$tq.timeout_after(duration, future).await
|
||||
}
|
||||
|
||||
/// Delay to some specific time instant.
|
||||
#[inline]
|
||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||
$tq.delay_until(instant).await;
|
||||
}
|
||||
}
|
||||
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
|
||||
impl TimerQueueBackend for $backend_name {
|
||||
type Ticks = u64;
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
|
||||
|
||||
impl Monotonic for $mono_name {
|
||||
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
|
||||
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
|
||||
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
fn now() -> Self::Ticks {
|
||||
calculate_now(
|
||||
|| $overflow.load(Ordering::Relaxed),
|
||||
|| $timer.cnt().read().cnt()
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
fn set_compare(instant: Self::Ticks) {
|
||||
let now = Self::now();
|
||||
|
||||
// Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left.
|
||||
let val = match instant.checked_duration_since(now) {
|
||||
None => 0, // In the past
|
||||
Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow
|
||||
Some(_x) => 0, // Will overflow
|
||||
// `wrapping_sup` takes care of the u64 integer overflow special case.
|
||||
let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) {
|
||||
instant as $bits
|
||||
} else {
|
||||
// In the past or will overflow
|
||||
0
|
||||
};
|
||||
|
||||
$timer.ccr(1).write(|r| r.set_ccr(val));
|
||||
$timer.ccr(1).write(|r| r.set_ccr(val.into()));
|
||||
}
|
||||
|
||||
fn clear_compare_flag() {
|
||||
|
|
@ -282,24 +322,25 @@ macro_rules! make_timer {
|
|||
assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
|
||||
}
|
||||
}
|
||||
|
||||
fn timer_queue() -> &'static TimerQueue<$backend_name> {
|
||||
&$tq
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32_tim2")]
|
||||
make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
|
||||
make_timer!(Tim2Backend, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
|
||||
|
||||
#[cfg(feature = "stm32_tim3")]
|
||||
make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
|
||||
make_timer!(Tim3Backend, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
|
||||
|
||||
#[cfg(feature = "stm32_tim4")]
|
||||
make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
|
||||
make_timer!(Tim4Backend, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
|
||||
|
||||
#[cfg(feature = "stm32_tim5")]
|
||||
make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
|
||||
|
||||
#[cfg(feature = "stm32_tim12")]
|
||||
make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ);
|
||||
make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
|
||||
|
||||
#[cfg(feature = "stm32_tim15")]
|
||||
make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);
|
||||
make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);
|
||||
|
|
|
|||
|
|
@ -1,93 +1,79 @@
|
|||
//! [`Monotonic`] based on Cortex-M SysTick. Note: this implementation is inefficient as it
|
||||
//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick.
|
||||
//! Note: this implementation is inefficient as it
|
||||
//! ticks and generates interrupts at a constant rate.
|
||||
//!
|
||||
//! Currently, the following tick rates are supported:
|
||||
//!
|
||||
//! | Feature | Tick rate | Precision |
|
||||
//! |:----------------:|----------:|----------:|
|
||||
//! | (none / default) | 1 kHz | 1 ms |
|
||||
//! | systick-100hz | 100 Hz | 10 ms |
|
||||
//! | systick-10khz | 10 kHz | 0.1 ms |
|
||||
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use rtic_monotonics::systick::*;
|
||||
//! use rtic_monotonics::systick::prelude::*;
|
||||
//! systick_monotonic!(Mono, 1_000);
|
||||
//!
|
||||
//! 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);
|
||||
//! Mono::start(systick, 12_000_000);
|
||||
//! }
|
||||
//!
|
||||
//! async fn usage() {
|
||||
//! loop {
|
||||
//! // Use the monotonic
|
||||
//! let timestamp = Mono::now();
|
||||
//! Systick::delay(100.millis()).await;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use super::Monotonic;
|
||||
pub use super::{TimeoutError, TimerQueue};
|
||||
/// Common definitions and traits for using the systick monotonic
|
||||
pub mod prelude {
|
||||
pub use crate::systick_monotonic;
|
||||
|
||||
pub use crate::Monotonic;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "systick-64bit")] {
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
} else {
|
||||
pub use fugit::{self, ExtU32, ExtU32Ceil};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use cortex_m::peripheral::SYST;
|
||||
|
||||
use atomic_polyfill::Ordering;
|
||||
use core::future::Future;
|
||||
use cortex_m::peripheral::SYST;
|
||||
pub use fugit;
|
||||
use rtic_time::timer_queue::TimerQueue;
|
||||
|
||||
use crate::TimerQueueBackend;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "systick-64bit")] {
|
||||
pub use fugit::{ExtU64, ExtU64Ceil};
|
||||
use atomic_polyfill::AtomicU64;
|
||||
static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0);
|
||||
} else {
|
||||
pub use fugit::{ExtU32, ExtU32Ceil};
|
||||
use atomic_polyfill::AtomicU32;
|
||||
static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0);
|
||||
}
|
||||
}
|
||||
static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new();
|
||||
|
||||
// Features should be additive, here systick-100hz gets picked if both
|
||||
// `systick-100hz` and `systick-10khz` are enabled.
|
||||
static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new();
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "systick-100hz")]
|
||||
{
|
||||
const TIMER_HZ: u32 = 100;
|
||||
} else if #[cfg(feature = "systick-10khz")]
|
||||
{
|
||||
const TIMER_HZ: u32 = 10_000;
|
||||
} else {
|
||||
// Default case is 1 kHz
|
||||
const TIMER_HZ: u32 = 1_000;
|
||||
}
|
||||
}
|
||||
/// Systick based [`TimerQueueBackend`].
|
||||
pub struct SystickBackend;
|
||||
|
||||
/// Systick implementing [`Monotonic`] which runs at 1 kHz, 100Hz or 10 kHz.
|
||||
pub struct Systick;
|
||||
|
||||
impl Systick {
|
||||
/// Start a `Monotonic` based on SysTick.
|
||||
impl SystickBackend {
|
||||
/// Starts the monotonic timer.
|
||||
///
|
||||
/// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
|
||||
/// the clock generation function of the used HAL.
|
||||
/// **Do not use this function directly.**
|
||||
///
|
||||
/// Notice that the actual rate of the timer is a best approximation based on the given
|
||||
/// `sysclk` and `TIMER_HZ`.
|
||||
///
|
||||
/// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue.
|
||||
pub fn start(
|
||||
mut systick: cortex_m::peripheral::SYST,
|
||||
sysclk: u32,
|
||||
_interrupt_token: impl crate::InterruptToken<Self>,
|
||||
) {
|
||||
// + TIMER_HZ / 2 provides round to nearest instead of round to 0.
|
||||
// - 1 as the counter range is inclusive [0, reload]
|
||||
let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
|
||||
/// Use the prelude macros instead.
|
||||
pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) {
|
||||
assert!(
|
||||
(sysclk % timer_hz) == 0,
|
||||
"timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency."
|
||||
);
|
||||
let reload = sysclk / timer_hz - 1;
|
||||
|
||||
assert!(reload <= 0x00ff_ffff);
|
||||
assert!(reload > 0);
|
||||
|
|
@ -98,7 +84,7 @@ impl Systick {
|
|||
systick.enable_interrupt();
|
||||
systick.enable_counter();
|
||||
|
||||
SYSTICK_TIMER_QUEUE.initialize(Systick {});
|
||||
SYSTICK_TIMER_QUEUE.initialize(SystickBackend {});
|
||||
}
|
||||
|
||||
fn systick() -> SYST {
|
||||
|
|
@ -106,67 +92,24 @@ impl Systick {
|
|||
}
|
||||
}
|
||||
|
||||
// Forward timerqueue interface
|
||||
impl Systick {
|
||||
/// Used to access the underlying timer queue
|
||||
#[doc(hidden)]
|
||||
pub fn __tq() -> &'static TimerQueue<Systick> {
|
||||
&SYSTICK_TIMER_QUEUE
|
||||
}
|
||||
|
||||
/// Timeout at a specific time.
|
||||
pub async fn timeout_at<F: Future>(
|
||||
instant: <Self as Monotonic>::Instant,
|
||||
future: F,
|
||||
) -> Result<F::Output, TimeoutError> {
|
||||
SYSTICK_TIMER_QUEUE.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> {
|
||||
SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await
|
||||
}
|
||||
|
||||
/// Delay for some duration of time.
|
||||
#[inline]
|
||||
pub async fn delay(duration: <Self as Monotonic>::Duration) {
|
||||
SYSTICK_TIMER_QUEUE.delay(duration).await;
|
||||
}
|
||||
|
||||
/// Delay to some specific time instant.
|
||||
#[inline]
|
||||
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
|
||||
SYSTICK_TIMER_QUEUE.delay_until(instant).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Monotonic for Systick {
|
||||
impl TimerQueueBackend for SystickBackend {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "systick-64bit")] {
|
||||
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
|
||||
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
|
||||
type Ticks = u64;
|
||||
} else {
|
||||
type Instant = fugit::TimerInstantU32<TIMER_HZ>;
|
||||
type Duration = fugit::TimerDurationU32<TIMER_HZ>;
|
||||
type Ticks = u32;
|
||||
}
|
||||
}
|
||||
|
||||
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
|
||||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
fn now() -> Self::Ticks {
|
||||
if Self::systick().has_wrapped() {
|
||||
SYSTICK_CNT.fetch_add(1, Ordering::AcqRel);
|
||||
}
|
||||
|
||||
Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed))
|
||||
SYSTICK_CNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_compare(_: Self::Instant) {
|
||||
fn set_compare(_: Self::Ticks) {
|
||||
// No need to do something here, we get interrupts anyway.
|
||||
}
|
||||
|
||||
|
|
@ -184,39 +127,66 @@ impl Monotonic for Systick {
|
|||
}
|
||||
}
|
||||
|
||||
fn enable_timer() {}
|
||||
|
||||
fn disable_timer() {}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "systick-64bit")] {
|
||||
rtic_time::embedded_hal_delay_impl_fugit64!(Systick);
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit64!(Systick);
|
||||
} else {
|
||||
rtic_time::embedded_hal_delay_impl_fugit32!(Systick);
|
||||
|
||||
#[cfg(feature = "embedded-hal-async")]
|
||||
rtic_time::embedded_hal_async_delay_impl_fugit32!(Systick);
|
||||
fn timer_queue() -> &'static TimerQueue<Self> {
|
||||
&SYSTICK_TIMER_QUEUE
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the Systick interrupt for the monotonic.
|
||||
/// Create a Systick based monotonic and register the Systick interrupt for it.
|
||||
///
|
||||
/// See [`crate::systick`] for more details.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name that the monotonic type will have.
|
||||
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
|
||||
/// Can be omitted; defaults to 1kHz.
|
||||
#[macro_export]
|
||||
macro_rules! create_systick_token {
|
||||
() => {{
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn SysTick() {
|
||||
$crate::systick::Systick::__tq().on_monotonic_interrupt();
|
||||
macro_rules! systick_monotonic {
|
||||
($name:ident) => {
|
||||
$crate::systick_monotonic($name, 1_000);
|
||||
};
|
||||
($name:ident, $tick_rate_hz:expr) => {
|
||||
/// A `Monotonic` based on SysTick.
|
||||
struct $name;
|
||||
|
||||
impl $name {
|
||||
/// Starts the `Monotonic`.
|
||||
///
|
||||
/// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
|
||||
/// the clock generation function of the used HAL.
|
||||
///
|
||||
/// Panics if it is impossible to achieve the desired monotonic tick rate based
|
||||
/// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate.
|
||||
///
|
||||
/// This method must be called only once.
|
||||
pub fn start(systick: $crate::systick::SYST, sysclk: u32) {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn SysTick() {
|
||||
use $crate::TimerQueueBackend;
|
||||
$crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt();
|
||||
}
|
||||
|
||||
$crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SystickToken;
|
||||
impl $crate::TimerQueueBasedMonotonic for $name {
|
||||
type Backend = $crate::systick::SystickBackend;
|
||||
type Instant = $crate::fugit::Instant<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
type Duration = $crate::fugit::Duration<
|
||||
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
|
||||
1,
|
||||
{ $tick_rate_hz },
|
||||
>;
|
||||
}
|
||||
|
||||
unsafe impl $crate::InterruptToken<$crate::systick::Systick> for SystickToken {}
|
||||
|
||||
SystickToken
|
||||
}};
|
||||
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
|
||||
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue