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:
Finomnis 2024-04-11 00:00:38 +02:00 committed by GitHub
parent e4cc5fd17b
commit 8c23e178f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 2637 additions and 1676 deletions

View file

@ -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`

View file

@ -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"]

View file

@ -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);

View file

@ -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> {}

View file

@ -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")));

View file

@ -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")));

View file

@ -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);
};
}

View file

@ -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);

View file

@ -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);
};
}