Search code examples
timerarmstm32interrupt

Systick Interrupt : concept and purpose


I am taking a beginners course on Stm32 and recently learned about Interrupts and also about the Systick.

I understand the concept of GPIO Interrupts. For example: a Button is pressed, that press is "detected" and a function is triggered.

However I don't understand the concept of a Systick-Interrupt and what it can do.

I used the Systick to create a delay function like this:

void systickDelayMs(int delay){
    //-------Configure Systick-------
    /* Reload with number of clocks per milisecond */
    SysTick->LOAD = SYSTICK_LOAD_VAL;
    /*Clear systick current value register*/
    SysTick->VAL = 0;
    /*Enable systick and select internal clock source */
    SysTick->CTRL = CTRL_ENABLE |  CTRL_CLKSRC;
    for(int i=0; i<delay ; i++){
        /*Wait until the countflag is set*/
        while((SysTick-> CTRL & CTRL_COUNTFLAG)== 0){}
    }

    SysTick->CTRL = 0;

}

Now what can a Systick Interrupt do that such a Systick delay function cannot?


Solution

  • For the most part, you can treat it like just another basic timer, it's just a part of the core and doesn't require activating clock for external peripheral, and it's standardized across the same cores, even if they are in other MCUs. Just like with any timer, you can do polling or interrupt. So, your question effectively boils down to "timer with polling vs timer with interrupt". Which is very close to "what is the use for interrupts in Timers".

    If you do polling, like in your delay implementation, your MCU is sitting in a while loop and waiting for something to happen to break out of that loop. Your MCU can't do anything in its main execution sequence - only an interrupt can take over.

    If you use interrupt approach, your program execution doesn't stop when you set up SysTick parameters and activate its interrupt. Your further code is executed immediately, but when certain set amount of time passes, an interrupt fires, so wherever your MCU was and whatever it was doing, it pauses it and does whatever interrupt function is (but if MCU already was handling some interrupt, only higher priority interrupt will be fired immediately). Similarly to basic timers, one of the most common uses for it is scheduling.

    Specifically for SysTick, it's common to have a 1ms interrupt and a variable associated with it (static - global for your SysTick.c, wrapped up into functions to return it). Every 1ms it gets incremented by 1 (32-bit variable will overflow after almost 50 days). One of the uses of that is, for example, that you can check the value of that variable to estimate passed time. Much like Arduino's millis(). Obviously, you are free to use it any way you want. It's your piece of hardware, and if your algorithm finds some other use for it, you're welcome to do it, although it's more common to leave SysTick ticking every 1ms, while custom scheduling is left for basic timers (e.g. TIM6/TIM7).

    Here is my implementation on registers on STM32F746, systick.c:

    #include "systick.h"
    
    volatile static uint32_t SYSTEM_MS; //49 days
    
    void systick_setup(uint32_t sys_freq) {
        SysTick->LOAD = (sys_freq / 1000U) - 1U; //1ms tick
        SysTick->VAL = 0x00; //explicitly set start value (undefined on reset)
        SysTick->CTRL |= (1U << SysTick_CTRL_TICKINT_Pos) | (1U << SysTick_CTRL_CLKSOURCE_Pos); //enable systick interrupt, source processor clock
        SCB->SHPR[8] = 0U; //set SysTick interrupt priority (default: 0, the highest)
        SysTick->CTRL |= (1U << SysTick_CTRL_ENABLE_Pos); //enable SysTick
    }
    
    void SysTick_Handler(void) {
        SYSTEM_MS++;
    }
    
    uint32_t millis(void){
        return SYSTEM_MS;
    }
    
    void system_msdelay(uint32_t delay_ms){
        uint32_t end = millis() + delay_ms;
        while(millis()!=end); //not <, because overflow
    }