FreeRTOS bad practices

Hi,

I am happy to have my first achievement with FreeRTOS. But I am worried that I am missing something rather important. I heard (and read) that FreeRTOS will “claim” the sys timer, or sys tick timer. I don’t know what that means exactly. Some clarity or link to proper explanation would be greatly appreciated.

What I have running now, is some a single created task, which toggles an output connected to a LED, and waits for 1 sec (using the HAL).

I thought that would get me into trouble, that I would not be able to use the hal, because “FreeRTOS already claimed sys tick”. That doesn’t seem to be the case. It works fine. But now I have the feeling that I am using it in the wrong way, and I am afraid on the long run weird issues will happen because of it.

Below the trivial code. I hope somebody can easy my mind (or explain me that indeed I am for misery on this path).

Thx a lot in advance!

#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx_hal.h"
#include "stm32f446xx.h"

static void BlinkLed( void * pvParameters )
{
    for( ;; )
    {
        GPIOB->ODR ^= GPIO_PIN_7;
        HAL_Delay(1000);
    }
}

int main()
{
    HAL_Init();

    __GPIOB_CLK_ENABLE();

    GPIO_InitTypeDef gpio;
    gpio.Pin = GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14;
    gpio.Mode = GPIO_MODE_OUTPUT_PP;
    gpio.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOB, &gpio);

    xTaskCreate( 
        BlinkLed,
        "Blink",
        200,
        NULL,
        1,
        NULL );

     xPortStartScheduler();

    while(1)
    {
    }
}

void SysTick_Handler(void)
{
    HAL_IncTick();
    HAL_SYSTICK_IRQHandler();
}

Does this also work with vTaskDelay(1000 / portTICK_PERIOD_MS)? Have you tried creating two tasks, each blinking an LED? If that’s not working, the timer logic is misconfigured.

Aha, nope neither of that works.

The timer setup and interrupt is what allows the FreeRTOS kernel to select a different task (“context switching”) and execute other stuff, regardless of what’s being currently executed, since the timer interrupt interrupts everything. You can see that clearly in e.g.

Usually FreeRTOS takes control of the SysTick but here you have two overlapping systems with the STM32 HAL doing its increase-tick thing in the SysTick too. That’s why it’s usually recommended to have FreeRTOS use a different timer (TIMx…) so that they can be configured differently (e.g. regarding tick frequency).

However if both frequencies are the same (e.g. a periodic 1 millisecond tick), then you can just call into the FreeRTOS systickhandler function in your Systick_Handler().

If you look at e.g. STM32FreeRTOS and the STM32Duino core, that’s how they do it, as an addition to everything else.

Core:

FreeRTOS:

You can do the same.

For some reason the Systick_Handler is called only once.

I can also see when I put a breakpoint in my task, that the freertos tick is increased exactly once.

I am calling the xPortSysTickHandler directly in my SysTick_Handler, but that seems to be the only relevant thing they do in the osSystickHandler (assuming it won’t hurt omitting the code preventing the tick gets increased before the scheduler started).

void SysTick_Handler(void)
{
    HAL_IncTick();
    HAL_SYSTICK_IRQHandler();

    xPortSysTickHandler();
}

But the tasks (slightly changed based on your suggestion to actually force a context switch and force to rely on freertos tick), also both run once. Once they stumble upon the wait code, nothing seems to happen anymore.

I also added some fault handler to check if I ended up in any infinite loop, but also that doesn’t seem to be the case.

Yesterday evening I thought I had to add a separate timer (I choose timer 6) to increase the system tick, so that freertos could rely on the SystemTick_Handler sololy. This morning after reading your comments, I realized I misunderstood. So if you will look into github, ignore the code timerman.c.

I pushed my example/test/trial to basprins/freertosexample (github.com) in case you are willing to have a look there.

Thanks so much for all the explanation so far by the way! Really helpful. I have the feeling I am starting to understand the foundations. It would be really nice if you could guide me through to the point where I can actually have some proof of life in freertos.

I see. I’ve slightly adapted the pins for my STM32F407ZG board, verified the blinky-LED was running fine without RTOS, and then started debugging the RTOS version. I noticed:

  • the first task function is reached and executed. When the vTaskDelay() is executed, it goes into the second task function (as expected, since the task has nothing more to do, a context switch is done to a task that does have something to do)
  • the second task function is executed once and goes in to the delay function
  • then execution hangs up.
  • When then pausing execution, it was stuck in the PendSV_Handler() interrupt while doing vTaskSwitchContext().

The line

0x08001734: fe e7           	b.n	0x8001734 

is a infinite loop in the since the branch (b.n = “branch near”) points back to its own address.

The code belonged to the line

        /* Select a new task to run using either the generic C or port
         * optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
        traceTASK_SWITCHED_IN();

That macro evaluates to

https://github.com/basprins/freertosexample/blob/b29d0847507e8126e5a1e222e363d3548b78940e/lib/FreeRTOS/Source/tasks.c#L134-L149

with further

https://github.com/basprins/freertosexample/blob/b29d0847507e8126e5a1e222e363d3548b78940e/include/FreeRTOSConfig.h#L153-L155

Here FreeRTOS is stepping through the list of pxReadTasksLists, selecting the next task.

However, if it is stuck there, that means that it did have no other task to run and has hit the configASSERT() infinite loop on error case.

This should however be impossible – if no user task can be run anymore, there is the special “Idle” task that gets run in its place. It being stuck there is an indication of the idle task not being correctly in the task list, or it may have never been created in the first place.

Starting the idle task is done in one of the start-up functions.

You write in your code

https://github.com/basprins/freertosexample/blob/b29d0847507e8126e5a1e222e363d3548b78940e/src/main.c#L63-L64

However when looking at for example STM32FreeRTOS code, it does…

vTaskStartScheduler(), not xPortStartScheduler().

And what does vTaskStartScheduler() do more?

Start the idle task :slight_smile:

So, since that is not being done in your firmware, the scheduler is stuck now, having no task to run at all.

If I correct it to do

vTaskStartScheduler()

however, I get an error about two missing functions

.pio\build\nucleo_f446ze\liba30\FreeRTOS\Source\tasks.o: In function `vTaskStartScheduler':
tasks.c:(.text.vTaskStartScheduler+0x10): undefined reference to `vApplicationGetIdleTaskMemory'
.pio\build\nucleo_f446ze\liba30\FreeRTOS\Source\timers.o: In function `xTimerCreateTimerTask':
timers.c:(.text.xTimerCreateTimerTask+0x1a): undefined reference to `vApplicationGetTimerTaskMemory'

That is vApplicationGetTimerTaskMemory() and vApplicationGetIdleTaskMemory().

They way you’ve configured FreeRTOS right now is that it does static allocation and timers turned on, but then you must provide the two function implementations per documentation

/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
application must provide an implementation of vApplicationGetTimerTaskMemory()
to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
                                   StackType_t **ppxTimerTaskStackBuffer,
                                  uint32_t *pulTimerTaskStackSize )

You can of course also not provide the implementation but then the static allocation option must be turned off in the FreeRTOSConfig.h.

I’ve decided for my test to just add the implementation.

And alas, when I did so, the task switching worked! The application was no longer stuck in the in the task switch because the idle task was properly created by calling vTaskStartScheduler(), and some missing implementations have been added.

I’ll provide a PR shortly. PR is provided in https://github.com/basprins/freertosexample/pull/1

1 Like

Yes!!! Thank you so much!! :smile:

That’s a great tip between the lines. I will remember that, this would have helped me many times before already where I was looking for some silly bug I caused. Never came to my mind that pausing the debugger would point me to the inf loop (instead,… i was putting breakpoints in my own inf loops to try to find where the mcu got stuck… )

And thx a lot for taking all the time to show me how you drilled down to the actual root cause. Very insightful!

Not sure what the benefit / drawbacks are for this yet. I’ll have to read up on that.

I am now at the point where I can start to move my app in the “FreeRTOS” harness piece by piece and probably run into tons of issues, but that’s fine. I am really really glad you fixed the problem so that I have this simple blink thing working to start experimenting and learning.

I just tested if I also can use HAL_delay(), that also works fine. I guess it’s not the most advisable thing to use as it consumes processing power for no reason. So I better stick with vTaskDelay (assuming the overhead of the scheduler is neglectable in 99.99% of the cases).

For the last time: thanks!! :smile: