You are here

Starter templates with FreeRTOS | Cypress Semiconductor

Starter templates with FreeRTOS

I promised to tell you about the FreeRTOS templates and, now is the time, so, here we go... In the last post I used the PioneerKitApp template. This time I'll try PioneerKitAppFreeRTOS. It's a bit of a mouthful.

PioneerKitAppFreeRTOS starter template

When the project is created it looks identical to last week's project except this one includes the FreeRTOS code and has an example FreeRTOSConfig.h in your source folder.

ModusToolbox application with Amazon FreeRTOS middleware

I am not going to go into the details of setting up the OS (that's maybe a topic for another day) but I will warn you that the example, IMHO, does not allocate enough SRAM for the OS to do anything interesting. Here is the line in the config file that configures the memory for OS resources like tasks and queues.

#define configTOTAL_HEAP_SIZE                   16384

 

The chip I am using has 288kB of RAM so I think I can allocate a little more than 16kB! The big consumers of RAM in the OS are task stacks and queues and the template has an, admittedly-generous, default stack of 1kB. If I create 5 or 6 tasks I find I run out of memory and the OS does not start. I could make the stacks smaller but I prefer to boost the available memory up a bit (and not worry that my tasks are using very little of the stack available to them).

#define configTOTAL_HEAP_SIZE                   65536

 

Moving on, you will see that the code in main.c is quite different from the other template. First of all, in main(), it creates a task and starts the FreeRTOS scheduler.

    BaseType_t retval;

    retval = xTaskCreate( blinky, BLINKY_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
    {
        vTaskStartScheduler();
    }

 

Notice how the code checks the return code from the create call because that is the easiest way to avoid out of memory start-up problems in the OS. I had no problems because I configured ooooooodles of RAM for my tasks, and I only have one! The task itself is implemented in a function called blinky() and has the following parameters.

/* Defines for a simple task */
#define BLINKY_NAME         ("Blinky")
#define BLINKY_STACK_SIZE   (1024)
#define BLINKY_PRIORITY     (5)
#define BLINKY_DELAY_TICKS  (500)

 

The two NULL arguments are unused options you can use when creating tasks - the first is a pointer to to an argument that gets passed into blinky() and the second is a pointer for the OS to return the created task's ID. I'll go into more detail on that later.

And here is the blinky() code. It's very complex (not) - toggle the pin and wait for a while!

void blinky( void * arg )
{
    (void)arg;
    for(;;)
    {
        /* Toggle the user LED periodically */
        Cy_GPIO_Inv( KIT_LED1_PORT, KIT_LED1_PIN );
        vTaskDelay( BLINKY_DELAY_TICKS );
    }
}

 

You can build and program the application to verify the OS running - the orange LED blinks at 1Hz (BLINKY_DELAY_TICKS is 500, and each tick is 1ms, so the LED is off for 500ms and on for 500ms). Let's extend that a little by blinking another LED. First, let's make a copy of blinky() and change the LED and frequency.

void blinky2( void * arg )
{
    (void)arg;
    for(;;)
    {
        /* Toggle the user LED periodically */
        Cy_GPIO_Inv( KIT_LED2_PORT, KIT_LED2_PIN );
        vTaskDelay( BLINKY_DELAY_TICKS / 3 );
    }
}

 

Then create the new task with a new name.

#define BLINKY2_NAME        ("Blinky2")

    retval = xTaskCreate( blinky, BLINKY_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
    retval = xTaskCreate( blinky2, BLINKY2_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL );

 

When I program the application the red LED blinks three times faster than the orange one.

I do not know about you but that copied code in blinky() and blinky2() drives me a bit nutty. Its the same code! I want to reuse it - for all the LEDs. The functions only differ in the pin they toggle and the time they go to sleep. Let's use that argument pointer I talked about above. First I create a struct to contain the arguments and five variables for each of the LEDs on the kit (red, orange, and the RGB).

struct led_t
{
    GPIO_PRT_Type* port;
    unsigned int   pin;
    int            rate;
};

const struct led_t LED1 = { KIT_LED1_PORT,  KIT_LED1_PIN,  300 };
const struct led_t LED2 = { KIT_LED2_PORT,  KIT_LED2_PIN,  600 };
const struct led_t LEDR = { KIT_RGB_R_PORT, KIT_RGB_R_PIN, 100 };
const struct led_t LEDG = { KIT_RGB_G_PORT, KIT_RGB_G_PIN, 150 };
const struct led_t LEDB = { KIT_RGB_B_PORT, KIT_RGB_B_PIN, 750 };

 

Then create more tasks and give each call the argument pointer.

    retval = xTaskCreate( blinky, "LED 1", BLINKY_STACK_SIZE, (void *)&LED1, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
        retval = xTaskCreate( blinky, "LED 2", BLINKY_STACK_SIZE, (void *)&LED2, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
        retval = xTaskCreate( blinky, "LED R", BLINKY_STACK_SIZE, (void *)&LEDR, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
        retval = xTaskCreate( blinky, "LED G", BLINKY_STACK_SIZE, (void *)&LEDG, BLINKY_PRIORITY, NULL );
    if( pdPASS == retval )
        retval = xTaskCreate( blinky, "LED B", BLINKY_STACK_SIZE, (void *)&LEDB, BLINKY_PRIORITY, NULL );

 

Lastly, I deleted blinky2() and modified blinky() to use the argument.

void blinky( void * arg )
{
    struct led_t *led = (struct led_t *)arg;
    for(;;)
    {
        /* Toggle the user LED periodically */
        Cy_GPIO_Inv( led->port, led->pin );
        vTaskDelay( led->rate );
    }
}

 

Now, when I program the kit, it twinkles like the lights in my Christmas tree (that I already miss terribly) and I did not have to write 5 almost-identical tasks to make it work. I attached the source code to this article for you, in case you want to play with the LEDs (note: I had to change the file extension to "txt" because, apparently, "c" files are dangerous!!!).

The point of all this is not really to create a Christmas tree replacement. Hopefully it's obvious that the template is a really nice way to get started on a multi-tasking application because it configures all the basic peripherals on the board, sets up the OS, and gives you a stub task (blinky) that you can modify and copy to make your own tasks (that do a little more than blink LEDs). Happy tasking!



 

ALL CONTENT AND MATERIALS ON THIS SITE ARE PROVIDED "AS IS". CYPRESS SEMICONDUCTOR AND ITS RESPECTIVE SUPPLIERS MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THESE MATERIALS FOR ANY PURPOSE AND DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THESE MATERIALS, INCLUDING BUT NOT LIMITED TO, ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT OF ANY THIRD PARTY INTELLECTUAL PROPERTY RIGHT. NO LICENSE, EITHER EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, IS GRANTED BY CYPRESS SEMICONDUCTOR. USE OF THE INFORMATION ON THIS SITE MAY REQUIRE A LICENSE FROM A THIRD PARTY, OR A LICENSE FROM CYPRESS SEMICONDUCTOR.

Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms and Conditions of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms and Conditions of this site. Cypress Semiconductor and its suppliers reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.