Using ARM Cortex-M SysTick Timer for Simple Delays

ARM Cortex-M based microcontrollers has an included system tick timer as part of the core. It’s generally used as the tick timer of a RTOS. Hence the name “SysTick”. It’s also relatively simple and lacks advanced features.

  • Only counts down
  • 24 bit
  • Auto-Reload register
  • Dedicated interrupt
  • Calibration value for interrupt period
  • Can use system clock or an external clock (*)
  • No input capture, no output compare, nothing fancy

(*) Implementation of SysTick timer somewhat varies between manufacturers. So make sure to check the documentation. In STM32F4 this external clock is the processor clock divided by 8. You can find more information in STM32F4 Programming Manual PM0214.

Here are the registers used to configure systick timer.

  • CTRL : configure systick timer, select clock source, enable interrupt, check countdown hit
  • LOAD : auto reload register, after hitting 0, count (VAL register) is reset to this value
  • VAL : current value register
  • CALIB : calibration register, only relevant for interrupts

Below is a function that delays by a given number of ticks.

void delay_ticks(unsigned ticks)
    SysTick->LOAD = ticks;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

    // COUNTFLAG is a bit that is set to 1 when counter reaches 0.
    // It's automatically cleared when read.
    while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
    SysTick->CTRL = 0;

By default the timer is configured to use system clock divided by 8. With a system clock of 84MHz, timer input is 10,5MHz which corresponds to a 0,095µs~=0,1µs tick period. For example calling this function with ticks=105 would result in a 10µs delay.

Let’s write functions that accepts microseconds and milliseconds as parameters.

static inline void delay_us(unsigned us)
    delay_ticks((us * (STM32_SYSCLK / 8)) / 1000000);

static inline void delay_ms(unsigned ms)
    delay_ticks((ms * (STM32_SYSCLK / 8)) / 1000);

Since these are very simple one liners, it makes sense to put them in a header file as inline functions so that compiler can optimize them, including the multiplication and divisions. Note that STM32_SYSCLK is a define for main system clock frequency, in my case 84000000. Its naming can change depending on the platform/libraries you are using.

Leave a Reply

Your email address will not be published. Required fields are marked *