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.
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.Open the projectspec file inside the new project directory and rename the project by modifying the title and the name field.
Modify the compilerBuildOptionsinclude paths to include the path to your custom project directory.
Modify the linkerBuildOptions to add in any linker File Search Path if needed.
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.
Replace all the TI-RTOS calls with DPL/FreeRTOS/POSIX calls in your custom project file.
+#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
#ifndef SP_TASK_STACK_SIZE +#ifdef FREERTOS +#define SP_TASK_STACK_SIZE 2048 +#else #define SP_TASK_STACK_SIZE 1024 +#endif // FREERTOS
/********************************************************************* * 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
/********************************************************************* * 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
+#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
/********************************************************************* * 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
/********************************************************************* * @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 }
+#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.
/********************************************************************* * @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 }
/********************************************************************* * @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 {
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; }
// 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.
+// 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.Modify the .syscfg file of your custom project using a text editor to change the target rtos to freertos.
*/ -// @cliArgs --board /ti/boards/CC26X2R1_LAUNCHXL --rtos tirtos +// @cliArgs --board /ti/boards/CC26X2R1_LAUNCHXL --rtos freertos /* * Example.syscfg */