mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-18 22:05:37 +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
|
|
@ -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