Integrating FreeRTOS and STM HAL Libraries

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

Code Link: Tutorial Source Code Github – Keil

By the time i was writing FreeRTOS tutorials, I thought of making FreeRTOS tutorial (on STM32F4 Platform) by integrating FreeRTOS with Standard Peripheral Libraries provided by ST Microelectronics as part of their STMxx Microcontroller series. While downloading Standard Peripheral Library, there was an indication of cutting future MCUs support in Standard Peripheral Libraries. The ST Microelectronics itself is pushing hard the new HAL libraries to be considered for programming STM Microcontrollers. So in short I decided to switch to new HAL Libraries.

While creating simple tasks, that blinks LEDs, using FreeRTOS and HAL libraries, i found that both the FreeRTOS and HAL libraries uses ARM on Core 24-bits count down Timer called SysTick timer. FreeRTOS uses SysTick as tick timer on ARM Cortex-M cores for scheduling tasks while HAL libraries use Systick for generating fixed delay of 1msec (or multiple of 1msec) for various APIs operations.

The time requirements of both FreeRTOS and HAL Libraries converges when both freeRTOS Tick Timer and HAL Libraries delay timer needs to tick at the same rate of 1msec. In HAL libraries Systick interrupt is fixed at 1msec while in case of FreeRTOS, tick timer interrupt rate is not fixed and configurable via configTICK_RATE_HZ. So ultimately at some point there may be a conflict of interests for different tick rates and same timer may not be used to serve both. Thats the point where you have to consider to use separate independent timers to serve each. This tutorial is all about resolving this point of conflict. Let’s get started.

Steps:

1. The first step is to create a minimal project based on FreeRTOS and STM HAL Libraries. This is relatively easy step, still if you are stuck at some point, you can refer to the the following tutorial or use the example project provided with this tutorial at github.

2. Next is to initiate HAL libraries to be used ahead.

    /*
        initialize HAL Library. This step is mandatory
        for HAL libraries to work properly. It configures
        SysTick etc. for its internal configurations.
    */
    HAL_Init();

Once the HAL_Init() gets executed on STM32F4 MCU, the MCU exists in the following state:

  *    Configure the Flash prefetch, instruction and Data caches.
  *    Configures the SysTick to generate an interrupt each 1 millisecond,
  *    which is clocked by the HSI (at this stage, the clock is not yet
  *    configured and thus the system is running from the internal HSI at 16 MHz).
  *    Set NVIC Group Priority to 4.

                             HAL Library Documentation

3. As documented, after HAL_Init, NVIC is set to Priority Group-4. FreeRTOS highly recommend to set the priority grouping to only 0 i.e. preempt priorities only. For further information on ARM Cortex-M priority and Priority Grouping, refer to the following tutorial.

So in accordance to FreeRTOS recommendation, let’s set the priority grouping to 0.

    /*
        HAL_Init() sets the default priority grouping
        to 4U (3-premption, 1-sub priority). FreeRTOS 
        highly recommend to set the priority grouping 
        to only 0 i.e. preempt priorities only.
        
        webLink: https://www.freertos.org/RTOS-Cortex-M3-M4.html
    */
    NVIC_SetPriorityGrouping(0U);

4. The next step is to decide and configure a general purpose timer (other than Systick Timer) to be used as a tick timer. In our case we have decided to use STM32 general purpose Timer-2 to be configured for FreeRTOS tick timer. The Tick timer configuration needs to use FreeRTOS specific APIs to be called by FreeRTOS kernel scheduler to start timer and catch its interrupt/tick. The following tutorial explains this very well. we will not repeat the same process here again. The main difference between the previous tutorial code and this is the use of HAL libraries. In this tutorial we will configure timer using STM HAL libraries.

/*********************************************
    
    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) {

    /* 
        Enable clock to Timer-2 
        
        NOTE: This is lagacy Macro. The better approach is the
        use of HAL_TIM_ConfigClockSource function.
    */
    __HAL_RCC_TIM2_CLK_ENABLE();
    
    /*
        From STM32F407 datasheet, Timer2 is clocked from
        APB1 bus (42Mhz max). In default configuration
        Timer-2 is receiving 16Mhz (HSI) bus clock.
    */        
    
    /***************************************************
                   Timer-2 Configuration:
    ****************************************************/
    
    /* Select Timer-2 for further configuration */
    TIM_InitStruct.Instance = TIM2;
    
    /*
        Divide the timer-2 input frequency (16Mhz)
        by a factor of 1000 (16,000,000/1,000 = 16,000 = 16Khz) 
    */
    TIM_InitStruct.Init.Prescaler   = 1000;
    
    #if (UP_COUNTER)
     /* Up-Counter mode*/
     TIM_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
    #else 
      TIM_InitStruct.Init.CounterMode = TIM_COUNTERMODE_DOWN;        
    #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
    */
    TIM_InitStruct.Init.Period = 16;
        
    /*
        Finally initialize Timer-2
    */
    while (HAL_TIM_Base_Init(&TIM_InitStruct)!= HAL_OK);

    /* Enable interrupt at IRQ-Level */
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
    
    /* 
        Tick timer should have least priority
        In STM32F4, the lowest Prioirty is 0xf.
    */
    NVIC_SetPriority(TIM2_IRQn,0x0fU);
    
    /*
        Start Timer and enable timer-2 IRQ interrupt
    */
    HAL_TIM_Base_Start_IT(&TIM_InitStruct);
    
}

5. 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. The following code snippet shows Timer-2 ISR implemented in HAL stm32f4xx_it.c file.

void TIM2_IRQHandler (void) {
    
    /* clear timer interrupt */
    __HAL_TIM_CLEAR_IT(&TIM_InitStruct, TIM_IT_UPDATE);

    /* call the FreeRTOS kernel for a tick update*/
    xPortSysTickHandler();
}

6. For demonstration purpose we will configure GPIOs connected (to which LEDs are connected on STM32F4-Discovery) and create two simple tasks, that will blink the LEDs. The detail of creating new tasks will be covered in other tutorials. For here just copy and ignore the details.

/*
    Configure onboard LEDs as output
*/
static void configureLEDs(void);

/*
    Simple tasks to blink LEDs.
*/
void myTask1( void *pvParameters );
void myTask2( void *pvParameters );


void myTask1( void *pvParameters ) {
    volatile unsigned int i = 0;
    
    for (;;) {
        
        /* Toggle GPIOD Pin#14 --> RED LED on STM32F407-Discovery */
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);

        for (i = 0 ; i<60000; i++);
    }
}

void myTask2( void *pvParameters ) {
    volatile unsigned int i = 0;
    
    for (;;) {
        
        /* Toggle GPIOD Pin#15 --> BLUE LED on STM32F407-Discovery */
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15);
        
        for (i = 0 ; i<80000; i++);
    }
}

static void configureLEDs(void) {

    GPIO_InitTypeDef GPIO_InitStruct;
    
    /* Enable clock to GPIO-D */
    __HAL_RCC_GPIOD_CLK_ENABLE();
    
    /* Set GPIOD Pin#14, Pin#15 Parameters */
    GPIO_InitStruct.Pin     = GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_InitStruct.Mode    = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull    = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_LOW;
    
    /* Init GPIOD Pin#14,15 */
    HAL_GPIO_Init(GPIOD, &amp;GPIO_InitStruct);
}

/*****************************
    Tasks creation in main 
*****************************/
xTaskCreate( myTask1, "Task1", 50, NULL, 1, NULL);
xTaskCreate( myTask2, "Task2", 50, NULL, 1, NULL);

7. Now everything is set and in place, its time to start FreeRTOS scheduler.

/*
    Start FreeRTOS Kernel Scheduler
*/
vTaskStartScheduler();

That’s pretty much all the configuration you needed. Now grab the source code with link given a the start of this tutorial, match it with the steps, build it in Keil v5, burn it to STM32f4-discovery board and enjoy the beautiful blinking LEDs.

Video Demonstration:

References:

[1] – Reference manual-RM0090

[2] – FreeRTOS official website



17 thoughts on “Integrating FreeRTOS and STM HAL Libraries”

Leave a Reply

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