Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob

Fun with the TFT - Adding FreeRTOS

lock attach
Attachments are accessible only for community members.

Fun with the TFT - Adding FreeRTOS

50 sign-ins 10 solutions authored 5 solutions authored

As promised, I am going to add FreeRTOS to the bouncing ball application. Why? So I can explain how to do it and create multiple bouncy ball tasks! Also, I get to leave you guys with a teaser question for next time!

Start by opening the Library Manager and adding both the freertos and abstraction-rtos libraries, as shown. You need the abstraction because the Segger emWin port (in the CY8CKIT-028-TFT library) is built to be able to run on any supported kernel, such as FreeRTOS and RTX (inside Mbed OS).


That's easy enough. Now we need to configure the RTOS. Start by copying the example FreeRTOSConfig.h file from libs/freertos/Source/portable to your top-level folder. If you do not make a copy then the build system will pick up the default version, which is fine in many cases, but I need to make a few changes. In your copy of the file hunt down these definitions and check/change them to the values below.


#define configUSE_MUTEXES               1




The mutex choices are an emWin requirement when using FreeRTOS. If you forget to set those values the application will fail to build because the abstraction-rtos requires them (the error is "cyabs_rtos_freertos.c:280: undefined reference to `xSemaphoreCreateRecursiveMutex'").

The heap choice is a means of choosing how memory is allocated and freed when you create and delete resources like tasks and semaphores. The default is type 1, which does not support freeing at all. That sounds a little dumb but, in reality, many applications just grab the memory and never free it so this is a memory optimization. My application is going to create and delete a task and so I chose a slightly more sophisticated scheme.

To complete the RTOS configuration, delete this line so you do not get swamped with warnings.


#warning This is a template. Copy this file to your project and remove this line. Refer to FreeRTOS README.md for usage details.


That's sorted out the RTOS. Now let's tell emWin to use it. Open the Makefile and find the COMPONENTS line. Change the EMWIN to use OS mode, instead of NOS, and tell the abstraction-rtos to support FreeRTOS. it should look like this:




Now open main.c so we can turn the bounce() function into a task. Add two include files for the OS near the top of the file.


#include "FreeRTOS.h"

#include "task.h"


Now scroll down and change the bounce() function argument to match the FreeRTOS definition of TaskFunction_t, which is a void function accepting a void* argument. The original looks like this:


void bounce( ball_t* ball )


It already returns a void and so, to make it a FreeRTOS task, just change the argument like this:


void bounce( void* arg )


When I create the task I shall pass in a pointer to the ball configuration as an argument and so the first line of the new function should create a new ball pointer variable.


    ball_t* ball = (ball_t*) arg;


The last change to my task is to use the FreeRTOS delay function, which does not hog the CPU like Cy_SysLib_Delay(). The good news is that the FreeRTOS configuration uses a 1ms tick, so the speed calculation does not change, only the function name.


    vTaskDelay( speed );


Still with me? I hope so, but the final main.c file is attached, just in case.

When used in OS mode the emWin REQUIRES all calls to come from a task (i.e. after the OS is started) so we need to create a new task to initialize the GUI and start the bouncing ball tasks. You will not have a good time if you try to call GUI_Init() from main(). Create a new function above main() – I called mine GUI_Init_Task() because it is a task that initializes the GUI. I can be quite literal sometimes!


void GUI_Init_Task( void* arg )


    TaskHandle_t thisTask = xTaskGetCurrentTaskHandle();

    unsigned int pri = uxTaskPriorityGet( thisTask );



    /* Turn on the OLED and set the background color */


    GUI_SetBkColor( GUI_GRAY );



    /* Define the ball initial conditions */

    static ball_t b1 = { GUI_RED, 3, 45, +1 /*RIGHT*/, -3 /*UP*/ };


    xTaskCreate( bounce, "b1", configMINIMAL_STACK_SIZE*2, &b1, pri, NULL );


    vTaskDelete( thisTask );



What is going on here? First of all I am getting the task handle from the OS, then I am using it to get the priority of the task (which will get set when I create it in a moment). Then I decrement that value because I want to create a lot of bounce tasks at a lower priority than this task. This means GUI_Init_Task() will create them all but not let them run until it is good and ready!

Then, I initialize the GUI with three lines code ( GUI_* calls) that I just moved from main().

Also stolen from main() is the ball definition from main(). Instead of calling the bounce function directly, though, I create a task.


    xTaskCreate( bounce, "b1", configMINIMAL_STACK_SIZE*2, &b1, pri, NULL );


Let’s go through all those arguments for creating the task. The first one is the bounce() function. Second is the name, which can prove handy when debugging. Next is the amount of heap I want to get allocated for the bounce function stack – I chose 2x the absolute minimum (that's not a lot but this is a pretty simple task). The fourth argument is the cool part - I am passing in a pointer to the ball, which will get passed into the bounce() function when it starts. Then I specify the priority that was calculated above. The last argument is a pointer to get the task handle but I just pass in NULL because I do not need it.

So now I have a new task waiting to run. How to "release the hound"? I just delete the current task and the new one will automatically get scheduled.


    vTaskDelete( thisTask );


The vTaskdelete() call frees the memory allocated to GUI_Init_Task and so his is why I needed the upgraded heap option in the config file.

We're close now... just need to create our temporary task and start the OS. Here is the code for that (note that it still sets up the random number seed with the ADC):


int main( void )


    CY_ASSERT( cybsp_init() == CY_RSLT_SUCCESS );



    initRandomNumber( P10_7 );


    xTaskCreate( GUI_Init_Task, "GUI", configMINIMAL_STACK_SIZE*2, NULL, 5, NULL );





Cool, now build and program the board. It's... just the same as the non-OS version! So, before you start making effigies of me and sticking pins into my eyes for wasting your time, let's do something that would have been difficult before... add another ball or two or three.

In the GUI_Init_Task() function all you have to do is copy the ball variable (b1), rename it to b2, and change some of the arguments. Then copy the task creation line to start the task. Just change the name of the task to "b2" and the task argument from &b1 to &b2.


    static ball_t b1 = { GUI_RED,  3, 45, +1 /*RIGHT*/, -3 /*UP*/   };

    static ball_t b2 = { GUI_BLUE, 5, 35, -2 /*LEFT*/,  +1 /*DOWN*/ };


    xTaskCreate( bounce, "b1", configMINIMAL_STACK_SIZE*2, &b1, pri, NULL );

    xTaskCreate( bounce, "b2", configMINIMAL_STACK_SIZE*2, &b2, pri, NULL );


Build and program the kit and you have two balls starting at different positions, of different sizes, speeds, and directions (depending upon what you changed in the variable definition).

I think this is a lot of fun. You can now create half a dozen balls if you wish. It's a nice way of using the same task function multiple times, on different data, with just two extra lines of code.

Go on, have a play with the speed and radius of the balls. I left a defect in the application that I am going to fix next time. Can you find it? You should see it with lots of fast balls bouncing around (Hint: change the background color of the screen in GUI_Init_Task() to BLACK so it is easier to see what happens when the balls cross paths).