aboutsummaryrefslogtreecommitdiff
path: root/rtic-monotonics/src/systick.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-monotonics/src/systick.rs')
-rw-r--r--rtic-monotonics/src/systick.rs236
1 files changed, 103 insertions, 133 deletions
diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs
index 9bd056c..cc6ea3e 100644
--- a/rtic-monotonics/src/systick.rs
+++ b/rtic-monotonics/src/systick.rs
@@ -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 implementing [`Monotonic`] which runs at 1 kHz, 100Hz or 10 kHz.
-pub struct Systick;
+/// Systick based [`TimerQueueBackend`].
+pub struct SystickBackend;
-impl Systick {
- /// Start a `Monotonic` based on SysTick.
- ///
- /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
- /// the clock generation function of the used HAL.
+impl SystickBackend {
+ /// Starts the monotonic timer.
///
- /// Notice that the actual rate of the timer is a best approximation based on the given
- /// `sysclk` and `TIMER_HZ`.
+ /// **Do not use this function directly.**
///
- /// 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;
-
- unsafe impl $crate::InterruptToken<$crate::systick::Systick> for 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 },
+ >;
+ }
- SystickToken
- }};
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}