Simplify Systick Monotonic by integrating the TQ

This commit is contained in:
Emil Fresk 2023-02-02 21:00:41 +01:00
parent 1c2d9de9c0
commit 21e60427cf
3 changed files with 68 additions and 53 deletions

View file

@ -3,7 +3,7 @@
use super::Monotonic; use super::Monotonic;
pub use super::{TimeoutError, TimerQueue}; pub use super::{TimeoutError, TimerQueue};
use atomic_polyfill::{AtomicU32, Ordering}; use atomic_polyfill::{AtomicU32, Ordering};
use core::ops::Deref; use core::future::Future;
use cortex_m::peripheral::SYST; use cortex_m::peripheral::SYST;
use embedded_hal_async::delay::DelayUs; use embedded_hal_async::delay::DelayUs;
pub use fugit::ExtU32; pub use fugit::ExtU32;
@ -30,8 +30,7 @@ impl Systick {
/// `sysclk` and `TIMER_HZ`. /// `sysclk` and `TIMER_HZ`.
/// ///
/// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue.
#[must_use] pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) {
pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) -> Self {
// + TIMER_HZ / 2 provides round to nearest instead of round to 0. // + TIMER_HZ / 2 provides round to nearest instead of round to 0.
// - 1 as the counter range is inclusive [0, reload] // - 1 as the counter range is inclusive [0, reload]
let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
@ -45,7 +44,7 @@ impl Systick {
systick.enable_interrupt(); systick.enable_interrupt();
systick.enable_counter(); systick.enable_counter();
Systick {} SYSTICK_TIMER_QUEUE.initialize(Systick {});
} }
fn systick() -> SYST { fn systick() -> SYST {
@ -54,6 +53,44 @@ impl Systick {
} }
static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0);
static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new();
// 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.
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
SYSTICK_TIMER_QUEUE.delay_until(instant).await;
}
}
impl Monotonic for Systick { impl Monotonic for Systick {
type Instant = fugit::TimerInstantU32<TIMER_HZ>; type Instant = fugit::TimerInstantU32<TIMER_HZ>;
@ -92,49 +129,28 @@ impl Monotonic for Systick {
fn disable_timer() {} fn disable_timer() {}
} }
/// Timer queue wrapper to implement traits on impl DelayUs for Systick {
pub struct SystickTimerQueue(TimerQueue<Systick>);
impl SystickTimerQueue {
/// Create a new timer queue.
pub const fn new() -> Self {
Self(TimerQueue::new())
}
}
impl Deref for SystickTimerQueue {
type Target = TimerQueue<Systick>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DelayUs for SystickTimerQueue {
type Error = core::convert::Infallible; type Error = core::convert::Infallible;
async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
self.delay(us.micros()).await; SYSTICK_TIMER_QUEUE.delay(us.micros()).await;
Ok(()) Ok(())
} }
async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> {
self.delay(ms.millis()).await; SYSTICK_TIMER_QUEUE.delay(ms.millis()).await;
Ok(()) Ok(())
} }
} }
/// Register the Systick interrupt and crate a timer queue with a specific name and speed. /// Register the Systick interrupt for the monotonic.
#[macro_export] #[macro_export]
macro_rules! make_systick_timer_queue { macro_rules! make_systick_handler {
($timer_queue_name:ident) => { () => {
static $timer_queue_name: SystickTimerQueue = SystickTimerQueue::new();
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn SysTick() { unsafe extern "C" fn SysTick() {
$timer_queue_name.on_monotonic_interrupt(); Systick::__tq().on_monotonic_interrupt();
} }
}; };
} }

View file

@ -11,7 +11,7 @@ mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtic_monotonics::systick_monotonic::*; use rtic_monotonics::systick_monotonic::*;
rtic_monotonics::make_systick_timer_queue!(TIMER); rtic_monotonics::make_systick_handler!();
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -23,8 +23,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init"); hprintln!("init");
let systick = Systick::start(cx.core.SYST, 12_000_000); Systick::start(cx.core.SYST, 12_000_000);
TIMER.initialize(systick);
foo::spawn().ok(); foo::spawn().ok();
bar::spawn().ok(); bar::spawn().ok();
@ -45,21 +44,21 @@ mod app {
#[task] #[task]
async fn foo(_cx: foo::Context) { async fn foo(_cx: foo::Context) {
hprintln!("hello from foo"); hprintln!("hello from foo");
TIMER.delay(100.millis()).await; Systick::delay(100.millis()).await;
hprintln!("bye from foo"); hprintln!("bye from foo");
} }
#[task] #[task]
async fn bar(_cx: bar::Context) { async fn bar(_cx: bar::Context) {
hprintln!("hello from bar"); hprintln!("hello from bar");
TIMER.delay(200.millis()).await; Systick::delay(200.millis()).await;
hprintln!("bye from bar"); hprintln!("bye from bar");
} }
#[task] #[task]
async fn baz(_cx: baz::Context) { async fn baz(_cx: baz::Context) {
hprintln!("hello from baz"); hprintln!("hello from baz");
TIMER.delay(300.millis()).await; Systick::delay(300.millis()).await;
hprintln!("bye from baz"); hprintln!("bye from baz");
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);

View file

@ -12,8 +12,9 @@ use rtic_monotonics::systick_monotonic::*;
mod app { mod app {
use super::*; use super::*;
use futures::{future::FutureExt, select_biased}; use futures::{future::FutureExt, select_biased};
use rtic_monotonics::Monotonic;
rtic_monotonics::make_systick_timer_queue!(TIMER); rtic_monotonics::make_systick_handler!();
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -25,8 +26,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init"); hprintln!("init");
let systick = Systick::start(cx.core.SYST, 12_000_000); Systick::start(cx.core.SYST, 12_000_000);
TIMER.initialize(systick);
foo::spawn().ok(); foo::spawn().ok();
@ -37,37 +37,37 @@ mod app {
async fn foo(_cx: foo::Context) { async fn foo(_cx: foo::Context) {
// Call hal with short relative timeout using `select_biased` // Call hal with short relative timeout using `select_biased`
select_biased! { select_biased! {
v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), v = hal_get(1).fuse() => hprintln!("hal returned {}", v),
_ = TIMER.delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first _ = Systick::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first
} }
// Call hal with long relative timeout using `select_biased` // Call hal with long relative timeout using `select_biased`
select_biased! { select_biased! {
v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), // hal finish first v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first
_ = TIMER.delay(1000.millis()).fuse() => hprintln!("timeout", ), _ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ),
} }
// Call hal with long relative timeout using monotonic `timeout_after` // Call hal with long relative timeout using monotonic `timeout_after`
match TIMER.timeout_after(1000.millis(), hal_get(&TIMER, 1)).await { match Systick::timeout_after(1000.millis(), hal_get(1)).await {
Ok(v) => hprintln!("hal returned {}", v), Ok(v) => hprintln!("hal returned {}", v),
_ => hprintln!("timeout"), _ => hprintln!("timeout"),
} }
// get the current time instance // get the current time instance
let mut instant = TIMER.now(); let mut instant = Systick::now();
// do this 3 times // do this 3 times
for n in 0..3 { for n in 0..3 {
// exact point in time without drift // exact point in time without drift
instant += 1000.millis(); instant += 1000.millis();
TIMER.delay_until(instant).await; Systick::delay_until(instant).await;
// exact point it time for timeout // exact point it time for timeout
let timeout = instant + 500.millis(); let timeout = instant + 500.millis();
hprintln!("now is {:?}, timeout at {:?}", TIMER.now(), timeout); hprintln!("now is {:?}, timeout at {:?}", Systick::now(), timeout);
match TIMER.timeout_at(timeout, hal_get(&TIMER, n)).await { match Systick::timeout_at(timeout, hal_get(n)).await {
Ok(v) => hprintln!("hal returned {} at time {:?}", v, TIMER.now()), Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()),
_ => hprintln!("timeout"), _ => hprintln!("timeout"),
} }
} }
@ -77,11 +77,11 @@ mod app {
} }
// Emulate some hal // Emulate some hal
async fn hal_get(timer: &'static SystickTimerQueue, n: u32) -> u32 { async fn hal_get(n: u32) -> u32 {
// emulate some delay time dependent on n // emulate some delay time dependent on n
let d = 350.millis() + n * 100.millis(); let d = 350.millis() + n * 100.millis();
hprintln!("the hal takes a duration of {:?}", d); hprintln!("the hal takes a duration of {:?}", d);
timer.delay(d).await; Systick::delay(d).await;
// emulate some return value // emulate some return value
5 5
} }