Changing FreeRTOS Tick Timer – Systick Timer

Keywords: Embedded systems, ARM, FreeRTOS, Tick Timer,STM32F4

Code Link: Tutorial Source Code Github – Keil

This is the only freeRTOS tutorial in series containing bare-metal code. All subsequent tutorials are based on STM32 HAL libraries. The background reason behind this tutorial is that both HAL libraries and FreeRTOS use ARM Systick timer as a default timer (on ARM Cortex-M platform) for delays (in case of HAL libraries) and tick timer operations (in case of freertos). The conflict arises when HAL and freeRTOS don’t agree on same interrupt frequency (ticks per second). This tutorial is based on ARM cortex-M4F profile with port used for Keil compiler (RVDS).

Before going into actual implementation, let’s briefly discuss how freeRTOS implements tick timer and corresponding timer handler.

The FreeRTOS main kernel module is port.c which provides access to actual underlying hardware. port.c contains all the hardware dependent implementation. FreeRTOS uses void vPortSetupTimerInterrupt(void) function to setup and configure a tick timer which is used as reference clock for freeRTOS kernel to schedule tasks. For ARM cortex-m based Microcontrollers, the default tick timer used by freeRTOS kernel is called Systick Timer. The Systick timer is an oncore timer provided by ARM to facilitate OS functionality on Cortex-M profile cores.

Note: In some port.c files, the function is renamed to void prvSetupTimerInterrupt(void). For example the ARM_CM4_MPU port is using this function for implementing tick timer. According to FreeRTOS forum, there is no specific reason behind this illogical renaming. Anyhow in any case the point is to point out the main function that actually implement the timer.

If we go down into port.c file, we will see vPortSetupTimerInterrupt is defined similar in one the following ways.

Case-1:

#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0

   void vPortSetupTimerInterrupt( void )
   { 
     ..... 
   }

#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */

Case-2:

__weak void vPortSetupTimerInterrupt( void ) 
    { 
	  .....
    }

In the first case configOVERRIDE_DEFAULT_TICK_CONFIGURATION macro is used to add / remove the vPortSetupTimerInterrupt function from compilation. if the macro is set to any value other than 0, the function will be excluded from compilation and the compiler will throw an error if another implementation of vPortSetupTimerInterrupt function is not provided in the entire source code. This means (in the first case), if we want to give freeRTOS scheduler our own implementation of timer, we must have to set configOVERRIDE_DEFAULT_TICK_CONFIGURATION to some value other than 0 usually 1 in FreeRTOSConfig.h file.

In the second case the __weak keyword suggests that this function implementation is linked weakly to other module and if another implementation of the function is found, the function definition will be replaced by the new one thus skipping the implementation defined with __weak keyword. In second case all we have to do is to only provide a function that only configure our desired timer functionality.

NOTE: It is highly recommended NOT to edit port.c file. Use configOVERRIDE_DEFAULT_TICK_CONFIGURATION macro to remove the implementation from compilation. Also vPortSetupTimerInterrupt must not be defined or declared with internal linkage i.e. with static keyword. Its linkage must be kept global unless and until you have decided to make changes to the original port.c file or in cases where both the __weak and configOVERRIDE_DEFAULT_TICK_CONFIGURATION are missing like ARM_CM4_MPU port.c file.

The second thing to clarify is how to inform freeRTOS kernel about a tick/ time expiry / timer interrupt to reschedule/run tasks in queue. The freeRTOS uses the xPortSysTickHandler() function for this purpose. Actually in ARM cortex-M ports it’s not even a function, it’s a macro which is actually replaced by SysTick_Handler() function. SysTick_Hanler() is the default ISR for Systick timer, declared in Microcontroller startup(e.g. startup_stm32f407xx.s) file.

FreeRTOS configuration file (FreeRTOSConfig.h) uses the following marco to replace every found xPortSysTickHandler keyword with SysTick_Handler at preprocessor (first stage of compilation) time.

#define xPortSysTickHandler SysTick_Handler

This line must be comment out in FreeRTOSConfig.h file while configuring another timer to be used as a tick timer.

The final thing you must consider while changing timer is that the interrupt rate/tick rate of new timer must be same as defined by configTICK_RATE_HZ in FreeRTOSConfig.h file.

That’s all you need to know in order to change default Systick timer used by FreeRTOS kernel as tick timer. Let’s get into implementation.

Steps:

1. The first step is to create new project using Keil MDK v5. The following tutorial illustrates creating new project with Keil MDK-v5.

2. The next step is to define a function with name vPortSetupTimerInterrupt() with global visibility (default in C) in any of your C file. For convenience we will define it into main.c file. Just to keep things simple.

For illustration purpose we will use STM32f4-discovery Timer-2 as a replacement of tick timer. As the tick rate is 1000 as defined by configTICK_RATE_HZ MACRO, so the general-purpose timer i.e. timer-2 is configured to generate interrupt every 1ms i.e. frequency 1000.

Note: If you don’t know how to configure STM timers refer to the following tutorial for detail timers configuration.


/*********************************************
    
    From FreeRTOSConfig.h
    configTICK_RATE_HZ = 1000
    So Timer-2 needs to be configured to
    generate 1msec delay that matches
    configTICK_RATE_HZ rate.
 *********************************************/

 void vPortSetupTimerInterrupt (void) {
        
    /*****************************************
        Timers Configuration
     *****************************************/
    
    /* 
        From STM32F407 datasheet, Timer2 is clocked from
        APB1 bus (42Mhz max). In default configuration
        Timer-2 is receiving 16Mhz (HSI) bus clock.
    */
    
    /* Enable clock to Timer-2 on AHB-1 Bus */
    __setbit(RCC->APB1ENR, 0U);

    /*
        Divide the timer-2 input frequency (16Mhz)
        by a factor of 1000 (16,000,000/1,000 = 16,000 = 16Khz) 
    */
    TIM2->PSC = 1000;
    
    #if (UP_COUNTER)
     /* Up-Counter mode*/
     __clearbit(TIM2->CR1, 4U);
    #else
     /* Down Counter*/
     __clearbit(TIM2->CR1, 4U);
    #endif

    /*
        As configTICK_RATE_HZ = 1000, so tick timer
        need to generate interrupt at the rate of 1000/sec (1msec delay).
        As the input frequency is 16khz so the total
        counts required for 1msec delay:
        
        total counts = 1msec * f
                     =  0.001 * 16,000
                     = 16
    */
    TIM2->ARR = 16;

    /*
        Enable timer-2 Update interrupt to
        generate interrupt on counter overflow/underflow
    */
    __setbit(TIM2->DIER, 0U);

    /*
        Timer-2 interrupt is received on IRQ-6 on NVIC
        so enable IRQ-6 on NVIC for interrupt detection
    */
     NVIC_EnableIRQ(TIM2_IRQn);
     
     /* Start Timer-2 */
     __setbit(TIM2->CR1, 0U);
}

3. Set the priority of tick timer (timer-2 in this case) to lowest priority available. In case of STM32f4xx, the lowest priority available is 15. See the following tutorial for better understanding of ARM Interrupt priorities.

NVIC_SetPriorityGrouping(0U);
    
/* Tick timer should have least priority */
NVIC_SetPriority(TIM2_IRQn,0xff);	

4. Next, go to port.c file and locate the vPortSetupTimerInterrupt function. If it is defined like shown in Case-1 mentioned above, set configOVERRIDE_DEFAULT_TICK_CONFIGURATION to 1 if already set to 0 or simple add the following code to your FreeRTOSConfig.h file.

#define   configOVERRIDE_DEFAULT_TICK_CONFIGURATION     1

Otherwise if the it is define with __weak keyword, like in Case-2, then just skip this step.

5. Next comment out the following line in FreeRTOSConfig.h file

//#define xPortSysTickHandler SysTick_Handler

6. Implement the timer ISR to be called on every timer interrupt. In our case as we are using Timer-2 so the default timer ISR in Keil is TIM2_IRQHandler. The ISR should do two things. First it should clear the interrupt flag so that next interrupt is generated. This is the first step that must be done in timer ISR to reduce ticks latency. Next is to call xPortSysTickHandler() function in timer ISR to inform scheduler for a timer tick.

void TIM2_IRQHandler (void) {
    
    /* clear timer interrupt */
    __clearbit(TIM2->SR, 0U);
    
    /* call the FreeRTOS kernel for a tick update*/
    xPortSysTickHandler();
}

7. Add the extern void xPortSysTickHandler(void), declaration to your ISR file to avoid implicit declaration compiler warning.

8. This is the most undesirable step (in my opinion) in FreeRTOS tick timer configuration as this requires editing to port.c file. I wonder why Real Time Engineering Group didn’t make a way around it. This step is all about setting default tick timer i.e. Systick Timer priority. Though this step can be omitted as we don’t use Systick as tick timer any more still let’s exclude its priority configuration from source code so that any other library etc. that uses Systick can get expected behavior. It would be much better the code be excluded from compilation using the same macro as used to disable vPortSetupTimerInterrupt i.e. configOVERRIDE_DEFAULT_TICK_CONFIGURATION.

Anyhow, the last step is to comment out the line in port.c file that assigns priority (lowest priority) to Systick Timer. The Following code shows the commented out line. The better approach will be reassign default/desirable priority to Systick in user code, once the FreeRTOS scheduler is called/started.

/* Make PendSV and SysTick the lowest priority interrupts. */
//portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI

That’s it… Congratulation, you have successfully changed the FreeRTOS kernel default timer.

Note: For demonstration purpose we have implemented two tasks that blinks LEDs for demonstration purpose. The complete code can be found in the given github link at the start of the tutorial. The supporting tutorials for configuring STM GPIOs and FreeRTOS tasks creation can be found in the following links.

Video Demonstration:

References:

[1] – Reference manual-RM0090

[1] – FreeRTOS official website



87 thoughts on “Changing FreeRTOS Tick Timer – Systick Timer”

Leave a Reply

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