2 releases

new 0.1.1 Mar 9, 2025
0.1.0 Mar 8, 2025

#471 in Hardware support

Apache-2.0

23KB
336 lines

64-bit SysTick timer for Cortex-M0

crate documentation Build

Implements a 64-bit SysTick based timer, that tracks overflows and provides as single monotonic 64-bit value at the desired resolution. The only dependencies are cortex-m and cortex-m-rt crates.

Optionally wraps this in an embassy-time-driver.

Example included for Qemu Cortex-M0

To run the demos with Qemu:

cargo runq --example basic_time

Embassy version:

cargo runq --example embassy_time

lib.rs:

Provides a SysTick based 64-bit timer implementation.

In addition, optionally wraps this into a basic Embassy time driver.

The timer is a standalone implementation that can be used from any Cortex-M0/M3/M4/M7 code.

Usage:

// Set up timer with 1ms resolution, reload at 100us, 8MHz clock
static INSTANCE : Timer = Timer::new(1_000, 799, 8_000_000);

#[cortex_m_rt::entry]
fn main() -> ! {
    // Configure and start SYST
    INSTANCE.start(&mut cortex_m::Peripherals::take().unwrap().SYST);
    // Get the current time in milliseconds
     let now = timer.now();
}

Call the timer from your Systick handler:

#[exception]
fn SysTick() {
    INSTANCE.systick_handler();
}

To reduce the frequency of overflow interrupts, you can use the maximum reload value:

let timer = Timer::new(1_000, 16_777_215, 48_000_000);

This generates an interrupt and reloads the timer every ~350ms, but the resolution is still 1ms


To use the Embassy driver, the setup needs to look as follows. First, create a static instance of the timer, passing in SysTick frequency and reload value. The constant <4> determines the number of concurrent wait tasks supported.

embassy_time_driver::time_driver_impl!(static DRIVER: SystickDriver<4>
  = SystickDriver::new(8_000_000, 7999));

Next, you must have a SysTick interrupt handler that calls the driver's systick_interrupt() method on its static instance.

#[exception]
fn SysTick() {
    DRIVER.systick_interrupt();
}

And in main, before using any timer calls, initialize the driver with the actual SysTick peripheral:

#[embassy_executor::main]
async fn main(_s: embassy_executor::Spawner) {
  let mut periph = Peripherals::take().unwrap();
  DRIVER.start(&mut periph.SYST);
  // .. can use Timer::now() etc.
}

Dependencies

~0.8–1.3MB
~21K SLoC