Migrating TI-RTOS examples to use FreeRTOS

There are significant project setting modifications required to migrate from a TI-RTOS example to one that supports FreeRTOS, such as modifying the toolchain, adding include paths, and managing predefined symbols. For this reason, the recommended approach to port an example from TI-RTOS to FreeRTOS is to create a new projectspec file. A projectspec, or project specification file, is used to create a new project based on predefined settings. For more information on projectspec files, see the ProjectSpecs in CCS page.

Most embedded applications are built the same way: a main loop that receives messages from the iCall or from the application itself. This “loop” is implemented and located inside the examples in the SDK. The developer can reference these examples meaning, the majority of the application code can be reused with slight modifications.

In this section, we will be using the code found in our examples as a guide to convert any project to FreeRTOS.

Important

If you are starting with an example that already enables FreeRTOS (i.e. if using Multi_Role imported from the SimpleLink Low Power F2 SDK), skip the section below and jump to Modify the application code to support FreeRTOS.

Modify the Project Properties to support FreeRTOS

The goal of this section is to inherit the project configuration settings from an existing example project that is already setup for FreeRTOS.

When modifying the projectspec as shown below, we inherit the example project’s toolChain, cgtVersion and all project’s compiler and linker settings. The majority of the changes below should be additions, i.e. adding your project files/content to the projectspec.

  1. Setup the new project folder. Create a new project folder inside the examples directory (located in {SDK_INSTALL_DIR}\examples\rtos\DEVICE_NAME\ble5stack\) by copying an example project folder and renaming it to a new name of your choice.

  2. Open the projectspec file inside the new project directory and rename the project by modifying the title and the name field.

  3. Modify the compilerBuildOptionsinclude paths to include the path to your custom project directory.

  4. Modify the linkerBuildOptions to add in any linker File Search Path if needed.

  5. Add any custom project files to the project using the <file path></file> tags. Use the existing example projectspec file as a template.

Modify the application code to support FreeRTOS

This section describes the changes to the application code that are necessary to support FreeRTOS. It is recommended to import a Project using FreeRTOS and search for the predefined symbol FREERTOS. This will reveal the modifications to the Project application that were done. The following steps are provided using the search mentioned in the previous section as a guide.

The diffs below provide an overview of all the changes applied to the Project example (FreeRTOS + TICLANG) to support FreeRTOS. These changes should be applied to your custom example.

  1. Replace all the TI-RTOS calls with DPL/FreeRTOS/POSIX calls in your custom project file.

    Listing 12. FreeRTOS differences - INCLUDES
    +#ifdef FREERTOS
    +#include <FreeRTOS.h>
    +#include <task.h>
    +#include <mqueue.h>
    +#include <stdarg.h>
    +#else
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Queue.h>
    +#endif
    
    #if (!(defined FREERTOS) && !(defined __TI_COMPILER_VERSION__) && !(defined __GNUC__))
    #include <intrinsics.h>
    #endif
    
    Listing 13. FreeRTOS differences - STACK SIZE
    #ifndef SP_TASK_STACK_SIZE
    +#ifdef FREERTOS
    +#define SP_TASK_STACK_SIZE                   2048
    +#else
    #define SP_TASK_STACK_SIZE                   1024
    +#endif // FREERTOS
    
    Listing 14. FreeRTOS differences - Event queue and task variable
    /*********************************************************************
    * GLOBAL VARIABLES
    */
    +#ifdef FREERTOS
    +mqd_t g_EventsQueueID;
    +#endif
    extern UART2_Handle uart;
    extern UART2_Params uartParams;
    
    // Task configuration
    +#ifdef FREERTOS
    +typedef uint32_t * Task_Handle;
    +TaskHandle_t spTask = NULL;
    +#else
    Task_Struct spTask;
    +#endif
    
    +#ifndef FREERTOS
    #if defined __TI_COMPILER_VERSION__
    #pragma DATA_ALIGN(spTaskStack, 8)
    #else
    #pragma data_alignment=8
    #endif
    uint8_t spTaskStack[SP_TASK_STACK_SIZE];
    +#endif // !FREERTOS
    
    Listing 15. FreeRTOS differences - ICall_Sync
    /*********************************************************************
    * LOCAL VARIABLES
    */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Event globally used to post local events and pend on system and
    // local events.
    +#ifdef FREERTOS
    +ICall_SyncHandle syncEvent;
    +#else
    static ICall_SyncHandle syncEvent;
    +#endif
    
    Listing 16. FreeRTOS differences - Message Queue setup
    +#ifdef FREERTOS
    +/*Non blocking queue */
    +mqd_t g_POSIX_appMsgQueue;
    +#else
    // Queue object used for app messages
    static Queue_Struct appMsgQueue;
    static Queue_Handle appMsgQueueHandle;
    +#endif
    
    Listing 17. FreeRTOS differences - LOCAL FUNCTIONS
    /*********************************************************************
    * LOCAL FUNCTIONS
    */
    
    static void SimplePeripheral_init( void );
    +#ifdef FREERTOS
    +static void SimplePeripheral_taskFxn(void *a0);
    +#else // TIRTOS
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1);
    +#endif
    
    //..
    //..
    //..
    +#ifdef FREERTOS
    +static void SimplePeripheral_clockHandler(void * arg);
    +#else
    static void SimplePeripheral_clockHandler(UArg arg);
    +#endif
    
    Listing 18. FreeRTOS differences - SimplePeripheral_createTask
    /*********************************************************************
     * @fn      SimplePeripheral_createTask
     *
     * @brief   Task creation function for the Simple Peripheral.
     */
    void SimplePeripheral_createTask(void)
    {
    +#ifdef FREERTOS
    +  BaseType_t xReturned;
    +
    +  /* Create the task, storing the handle. */
    +  xReturned = xTaskCreate(
    +              SimplePeripheral_taskFxn,               /* Function that implements the task. */
    +              "SIMPLE_PERIPHERAL APP",                /* Text name for the task. */
    +              SP_TASK_STACK_SIZE / sizeof(uint32_t),  /* Stack size in words, not bytes. */
    +              ( void * ) NULL,                        /* Parameter passed into the task. */
    +              SP_TASK_PRIORITY,                       /* Priority at which the task is created. */
    +              &spTask );                              /* Used to pass out the created task's handle. */
    +
    +  if(xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY)
    +  {
    +    /* Creation of FreeRTOS task failed */
    +    while(1);
    +  }
    +#else
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = spTaskStack;
      taskParams.stackSize = SP_TASK_STACK_SIZE;
      taskParams.priority = SP_TASK_PRIORITY;
    
      Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL);
    +#endif
    }
    
    Listing 19. FreeRTOS differences - SimplePeripheral_init
    +#ifdef FREERTOS
    +  Util_constructQueue(&g_POSIX_appMsgQueue);
    +  Util_constructClock(&clkPeriodic,(void*) SimplePeripheral_clockHandler,
    +                                   SP_PERIODIC_EVT_PERIOD, 0, false,
    +                                   (uint32_t)&argPeriodic);
    +
    +#else
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueueHandle = Util_constructQueue(&appMsgQueue);
      // Create one-shot clock for internal periodic events.
      Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler,
                          SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic);
    +#endif
      /////
    

    Note

    Your application may not make use of a periodic timer like the one shown above. The above change should be performed to any clock constructed in the application using Util_constructClock() to avoid build issues.

    Listing 20. FreeRTOS differences - SimplePeripheral_taskFxn
    /*********************************************************************
     * @fn      SimplePeripheral_taskFxn
     *
     * @brief   Application task entry point for the Simple Peripheral.
     *
     * @param   a0, a1 - not used.
     */
    +#ifdef FREERTOS
    +static void SimplePeripheral_taskFxn(void *a0)
    +#else
    static void SimplePeripheral_taskFxn(UArg a0, UArg a1)
    +#endif
    {
      // Initialize application
      SimplePeripheral_init();
    
      // Application main loop
      for (;;)
      {
        uint32_t events;
    
        // Waits for an event to be posted associated with the calling thread.
        // Note that an event associated with a thread is posted when a
        // message is queued to the message receive queue of the thread
    +#ifdef FREERTOS
    + mq_receive(syncEvent, (char*)&events, sizeof(uint32_t), NULL);
    +#else
        events = Event_pend(syncEvent, Event_Id_NONE, SP_ALL_EVENTS,
                            ICALL_TIMEOUT_FOREVER);
    +#endif
        if (events)
        {
    
        //..
        //..
        //..
    
          // If RTOS queue is not empty, process app message.
          if (events & SP_QUEUE_EVT)
          {
    
    +#ifdef FREERTOS
    +        spEvt_t *pMsg;
    +         do
    +        {
    +          pMsg = (spEvt_t *)Util_dequeueMsg(g_POSIX_appMsgQueue);
    +          if (NULL != pMsg)
    +          {
    +            // Process message.
    +            SimplePeripheral_processAppMsg(pMsg);
    +
    +            // Free the space from the message.
    +            ICall_free(pMsg);
    +          }
    +          else
    +          {
    +            break;
    +          }
    +         } while(1);
    +#else
              while (!Queue_empty(appMsgQueueHandle))
              {
    
              spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
              if (pMsg)
              {
                // Process message.
                SimplePeripheral_processAppMsg(pMsg);
    
                // Free the space from the message.
                ICall_free(pMsg);
              }
            }
    +#endif
          }
    
    Listing 21. FreeRTOS differences - SimplePeripheral_clockHandler
    /*********************************************************************
     * @fn      SimplePeripheral_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     *
     * @return  None.
     */
    +#ifdef FREERTOS
    +static void SimplePeripheral_clockHandler(void * arg)
    +#else
    static void SimplePeripheral_clockHandler(UArg arg)
    +#endif
    {
    
    Listing 22. FreeRTOS differences - SimplePeripheral_enqueueMsg
          pMsg->pData = pData;
    
        // Enqueue the message.
    +#ifdef FREERTOS
    +    success = Util_enqueueMsg(g_POSIX_appMsgQueue, syncEvent, (uint8_t *)pMsg);
    +#else
        success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
    +#endif
      return (success) ? SUCCESS : FAILURE;
      }
    
    Listing 23. FreeRTOS differences - SimplePeripheral_addConn
            // Create a clock object and start
            connList[i].pUpdateClock
              = (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));
    
            if (connList[i].pUpdateClock)
            {
    +#ifdef FREERTOS
    +          Util_constructClock(connList[i].pUpdateClock,
    +                              (void*)SimplePeripheral_clockHandler,
    +                              SEND_PARAM_UPDATE_DELAY, 0, true,
    +                              (uint32_t) connList[i].pParamUpdateEventData);
    +#else
              Util_constructClock(connList[i].pUpdateClock,
                                  SimplePeripheral_clockHandler,
                                  SEND_PARAM_UPDATE_DELAY, 0, true,
                                  (UArg) (connList[i].pParamUpdateEventData));
    +#endif
            }
            else
    

    Note

    Make sure to apply the above change to any clock instances in your application as shown in the example code snippet below (which uses simple_central as our custom project). Take note of the scClockEventData_t structure used to pass in arguments.

    The example below shows the syntax required when passing in parameters to the Util_constructClock() function using FreeRTOS. Simply put, logical statements made inside the function call will result in a build issue.

    Listing 24. simple_central::SimpleCentral_StartRssi() - Clock construction
    +// Container to store information from clock expiration using a flexible array
    +// since data is not always needed
    +typedef struct
    +{
    +  uint8_t event;
    +  uint8_t data[];
    +} ClockEventData_t;
    
    
    +      if (connList[connIndex].pRssiClock)
    +   {
    +#ifdef FREERTOS
    +    ClockEventData_t locData;
    +    locData.event = (connIndex << 8) | SC_EVT_READ_RSSI;
    +
    +    // Create one-shot clock for RPA check event.
    +    Util_constructClock(connList[connIndex].pRssiClock,
    +                        (void*)SimpleCentral_clockHandler,
    +                        DEFAULT_RSSI_PERIOD, 0, true,
    +                        (void*)&locData);
    +#else
          Util_constructClock(connList[connIndex].pRssiClock,
                            SimpleCentral_clockHandler,
                            DEFAULT_RSSI_PERIOD, 0, true,
                            (connIndex << 8) | SC_EVT_READ_RSSI);
    +#endif
       }
    

    While searching for the macro FREERTOS in the project, you will find changes to other files (i.e. util.c/h, icall_user_config.c, etc). These changes do not need to be applied to the new example because these files are inherited directly from the SDK.

  2. Modify the .syscfg file of your custom project using a text editor to change the target rtos to freertos.

    Listing 25. freertos differences - SysConfig
    */
    -// @cliArgs --board /ti/boards/CC26X2R1_LAUNCHXL --rtos tirtos
    +// @cliArgs --board /ti/boards/CC26X2R1_LAUNCHXL --rtos freertos
    
    /*
    *
    Example.syscfg
    */