You are here

Fun with the TFT - Adding FreeRTOS | サイプレス セミコンダクタ

Fun with the TFT - Adding FreeRTOS

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).

Fun with the TFT - Adding FreeRTOS

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
#define configUSE_RECURSIVE_MUTEXES     1
#define configHEAP_ALLOCATION_SCHEME    (HEAP_ALLOCATION_TYPE2)

 

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:

 

COMPONENTS=EMWIN_OSNTS FREERTOS

 

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 );
    pri--;

    /* Turn on the OLED and set the background color */
    GUI_Init();
    GUI_SetBkColor( GUI_GRAY );
    GUI_Clear();

    /* 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 );
    __enable_irq();
    initRandomNumber( P10_7 );
    xTaskCreate( GUI_Init_Task, "GUI", configMINIMAL_STACK_SIZE*2, NULL, 5, NULL );
    vTaskStartScheduler();

}

 

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).

Blog: 

このサイトに掲示されているすべてのコンテンツと資料は、「そのままの状態」で提供されます。サイプレス セミコンダクタとその関連サプライヤは、これらの資料について、いかなる目的への適合性をも表明することはありません。また、これらの資料について、すべての保証や条件を放棄します。これには、暗示的な保証および条件、商用性、特定の目的への適合性、すべてのサードパーティの知的財産権に対する権利と非侵害などが含まれますが、これらに制限されることはありません。サイプレス セミコンダクタにより、明示または暗示にかかわらず、禁反言などによるライセンスは、付与されないものとします。このサイトに掲示されている情報の使用には、サードパーティまたはサイプレス セミコンダクタからのライセンスが必要となる場合があります。

このサイトのコンテンツには、特定のガイドラインや使用制限が含まれている場合があります。このサイトにおけるすべての掲示やコンテンツの使用は、サイトの利用規約に準じて行われるものとします。このコンテンツを使用するサードパーティは、制限やガイドラインに従い、このサイトの利用規約を遵守するものとします。サイプレス セミコンダクタとそのサプライヤは、コンテンツや資料、その製品、プログラム、サービスに対し、いつでも修正、削除、変更、改善、向上、その他の変更を加える権利を有します。また、いかなるコンテンツ、製品、プログラム、サービスを予告なく変更または閉鎖する権利を有します。