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

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!