Basic systick configuration on the STM32

By | February 2, 2016

The ARM cortex M4 and M3 processors all come with a systick timer that is part of the core. The other variants, such as the M0 may not have one.  This timer is very useful for producing the main system event clock. Here I will show you how to set it up on the STM32F4xx processors to generate an interrupt every millisecond.

A steady background tick event is great for all kinds of systems. It can look after user inputs, scheduling, sensor monitoring, real-time synchronisation and basic timing tasks. If you make the interval something simple and obvious, like every millisecond, you also have access to an easy way to generate delays or time events with millisecond accuracy.

The example here uses the STM32F407VG found on the STM32F4 Discovery board. The code makes use of the older Standard Peripheral Drivers. A version for the newer HAL drivers may follow later.

System Clock Frequency

By far the hardest part of the whole process is determining the actual clock frequency of your processor. Clock setup on the ARM cores is pretty involved. Perhaps the easiest way to do it is to use the tools from ST. Specifically, they have a spreadsheet that can work out all the settings for you and generate the system_stm32f4xx.c file that makes it all happen. In a typical standard project setup the startup code will call the SystemInit() function for you and, by the time your program gets control, the clocks are all set up and running.

You may think, at that stage, that you know what the clock frequency is but you should avoid referring to it by numerical value. Not only are magic numbers a Very Bad Thing in your code, if you make a mistake, the error can be hard to find.

Fortunately the peripheral drivers provide a handy way to get the current value of the HCLK and use it in your calculations without having to know what it is. The trick then is to proceed with the systick configuration and set up some test code that will tell you immediately if you have it right. For this purpose, I like to just flash an LED once per second. It is soon apparent if the calculations are wrong. Something like this should probably be the first thing you run on a new board. It is a blinky that tests the system clock speeds.

The CMSIS drivers provide a function, SysTick_Config(), that is used to set up the systick event. It takes a number of system clock ticks to be used as the period and initialises the system tick timer and its interrupt and leaves it running. Your code must implement a service routine called SysTick_Handler() which is called every time the systick event fires.

To get the current value of the system clock, there is a function provided by the peripheral drivers. RCC_GetClocksFreq() is given a pointer to a RCC_ClocksTypeDef structure. On return, one of the fields of that structure, HCLK_Frequency, has the number you need. All that is then needed is to divide the system frequency by the desired systick frequency to find the number of ticks to give to SysTick_Config(). Altogether it is just a few lines of code:

The SysTick Handler

In this service routine you can do whatever you like but it is best to keep in mind how much it will affect the performance of the rest of the code. This is the place for code that must run on a fixed schedule. The example below just increments a 32 bit counter at every tick. This counter is used to implement a millisecond resolution delay. Note that, if you are building a C++ file as I am in the example, you will have to declare the SysTick_Handler as a C function or the linker will ignore it causing much head scratching as you try to work out why nothing happens.

Here is the entire handler code for this example:

Not much to it really is there. Still, it is enough to let you implement a nice, accurate millisecond delay. Note that the ticks variable is declared as volatile so that there should be no problems with it being modified while being used elsewhere in the code. An unsigned 32 bit counter incremented every millisecond will not overflow for about 50 days.

The millis() function

Now you have a variable that increments automatically at a regular interval it is tempting to use it directly when measuring off time. However, it is probably better to write an accessor function that is more explicit in its purpose. Here, the function millis() just returns the tick count because there is a 1:1 correspondence between tick and milliseconds. In another system, you might want to run systick at some other frequency but still have millis() give you the number of milliseconds since reset. So millis() is just:

By making this function inline, you may gain some small performance improvement. Remember that inline is a hint not an instruction. The compiler can ignore it or may already have inlined such a simple function anyway.

The delay_ms() function

Finally, you can write a millisecond delay function. This is a little more complicated than you might think if you want to avoid possible problems with the tick count overflowing. If you never want to run the system for more than a couple of days, you can simplify this function but it is better to get it right in the first place.

Put all the systick code together

All the pieces are now ready. I will assume you can configure an LED to turn on and off. With all the previous parts in place, the main function of your program can be

I have a function to initialise the LEDs and macros to define the port and pin values. All being well, when the program is run on the target, a green LED will flash on and off at exactly one second intervals. If the intervals are not clearly 500ms each then there must be some misconfiguration of the system clocks. You will either have to delve into the system_stm32f4xx.c file or return to the clock configuration spreadsheet and generate a new version.

Full code

The complete code for this example looks like this:

And the hardware.h file is:


happy coding

12 thoughts on “Basic systick configuration on the STM32

  1. Pingback: TIM3 on the STM32 - an introduction - Micromouse Online

  2. Mark

    Very useful example. Clear and concise.

    However, the statement at the start that

    “The ARM cortex processors all come with a systick timer that is part of the core and so should be present on any ARM cortex.”

    Is not quite true. The SysTick timer is a given in the Cortex-M3 and Cortex-M4 (and probably others I’m missing) but optional in the Cortex-M0. The Nordic nRF51 family is an example of a Cortex-M0 device that is in current production which does not implement SysTick.

    See, for example, the Cortex-M0 Devices Generic User Guide, section 4.4, p. 4-21.

  3. Peter Harrison Post author

    Thank you for taking the time to point that out. I have amended the text accordingly.

  4. Mark

    You’re welcome.

    I must say I have found myself wishing your original assertion to be true. SysTick is such a useful thing to have.

  5. Roman Savrulin

    Thank you for example, but I see that if you set your delay near an overflow value you won’t get delay at all

  6. Peter Harrison Post author

    That can be a problem but at 1kHz, the timer will have no trouble with delays of well over a week.

  7. Clifford Slocombe

    Dealing with counter-roll over is trivial, and I am unconvinced that your “more complicated than you might think” delay_ms() is even correct, and the commented out “else” clause is unexplained. Why is it commented out? Why not just remove it? Or is the commenting out in fact an error?

    Always calculate and compare time intervals, not absolute time values:

    void delay_ms( uint32_t t )
    uint32_t start = millis() ;

    while( (uint32_t)millis() - start < t )
    // do nothing

  8. Peter Harrison Post author

    You are quite right, of course. Not even sure why I left it like that. I think the point is that the most obvious solution that one sees is wrog in that it will suffer from rollover.

    Thus my usual code is essentially the same as you suggest:

    void delay_ms(uint32_t t) {
    uint32_t elapsed;
    uint32_t start = millis();
    do {
    elapsed = millis() - start;
    } while (elapsed < t) ; }

    In this case, the elapsed time is calculated into a variable so that, when the debugger is used, I can see what the value is if necessary. Under even moderate optimisation, the expression should be calculated inline.

    Note that millis() is declared as uint32_t so it does not need a cast.

    The function as it stands is indeed broken and the formatting in wordpress made it even worse. I have now edited that and replaced the broken function.

    thank you for taking the time to read the article and get in touch.

  9. Arun kumar

    Hi, it’s very useful article but the issue i’m facing right now is the generated file throws an error like

    /* this is my output window

    compiling system_stm32f4xx.c…

    system_stm32f4xx.c(185): error: #147: declaration is incompatible with “const uint8_tAHBPrescTable[16]”
    (declared at line 76 of


    __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

    system_stm32f4xx.c(302): error: #20: identifier “HSI_VALUE” is undefined
    SystemCoreClock = HSI_VALUE;

    system_stm32f4xx.c(305): error: #20: identifier “HSE_VALUE” is undefined
    SystemCoreClock = HSE_VALUE;

    system_stm32f4xx.c(363): error: #20: identifier “HSE_STARTUP_TIMEOUT” is undefined
    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));


    so kindly help me out.

Leave a Reply

