How to use Systick to achieve microsecond (us) level delay in STM32?
Hedy

Hedy @carolineee

About: Publish some interesting electronic articles

Joined:
Dec 18, 2023

How to use Systick to achieve microsecond (us) level delay in STM32?

Publish Date: Aug 11
0 0

SysTick is a built-in timer in ARM Cortex-M cores that can provide precise timing for delay functions. Here's how to implement microsecond-level delays using SysTick on STM32.

1. SysTick Configuration
Basic Setup (Without Interrupt)

c
#include "stm32fxxx.h"  // Replace with your specific header

void SysTick_Init(void) {
    // Configure SysTick to count at CPU clock speed
    SysTick->LOAD = (SystemCoreClock / 1000000) - 1;  // 1µs per tick
    SysTick->VAL = 0;                                 // Clear current value
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk |        // Enable SysTick
                    SysTick_CTRL_CLKSOURCE_Msk;       // Use processor clock
}

void delay_us(uint32_t microseconds) {
    SysTick->VAL = 0;  // Reset counter
    while (microseconds--) {
        while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // Wait for tick
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage Example

c
int main(void) {
    HAL_Init();
    SystemClock_Config();
    SysTick_Init();

    while (1) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);  // Toggle LED
        delay_us(500);                          // 500µs delay
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Higher Precision with 24-bit Counter
SysTick is a 24-bit down-counter, so maximum delay per call is:

  • At 168MHz: (2²⁴-1) / 168 ≈ 99.9ms
  • For longer delays, use a loop.

Optimized Delay Function

c
void delay_us(uint32_t us) {
    uint32_t start = SysTick->VAL;
    uint32_t ticks = us * (SystemCoreClock / 1000000);
    while (1) {
        uint32_t now = SysTick->VAL;
        if (now < start) {  // Handle counter wrap-around
            if (start - now >= ticks) break;
        } else {
            if ((SysTick->LOAD - now) + start >= ticks) break;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Using SysTick Interrupt (For Long Delays)
For delays >100ms, use an interrupt-based approach.

Interrupt Setup

c
volatile uint32_t us_delay;

void SysTick_Handler(void) {
    if (us_delay) us_delay--;
}

void delay_us(uint32_t us) {
    us_delay = us;
    while (us_delay);
}
Enter fullscreen mode Exit fullscreen mode

Initialization

c
void SysTick_Init(void) {
    SysTick_Config(SystemCoreClock / 1000000);  // 1µs interrupt
    NVIC_SetPriority(SysTick_IRQn, 0);          // Highest priority
}
Enter fullscreen mode Exit fullscreen mode

4. Calibration for Accuracy
SysTick's actual resolution depends on:

  • CPU clock speed (SystemCoreClock).
  • Prescaler settings.

Calibration Steps

  1. Measure a known delay (e.g., GPIO toggle) with an oscilloscope.
  2. Adjust the SystemCoreClock / 1000000 divisor if needed.

5. Comparison with Other Methods

6. Troubleshooting

7. Advanced: Combining with DWT (Data Watchpoint Trace)
For even higher precision (nanosecond-level), use the DWT Cycle Counter:

c
#define DWT_CYCCNT   *(volatile uint32_t *)0xE0001004

void DWT_Init(void) {
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

void delay_us(uint32_t us) {
    uint32_t start = DWT->CYCCNT;
    uint32_t cycles = us * (SystemCoreClock / 1000000);
    while ((DWT->CYCCNT - start) < cycles);
}
Enter fullscreen mode Exit fullscreen mode

Final Recommendation

  • For µs delays: Use SysTick polling (simple and accurate).
  • For ns delays: Use DWT cycle counter (if available).
  • For long delays: Use SysTick interrupts.

Comments 0 total

    Add comment