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. In this section, we will be using the multi_role
example as a guide to
convert the simple_central
project to FreeRTOS.
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 multi_role
example so, the developer can
take the example from there. This means that the majority of the application
code can be reused with slight modifications.
In this section, we will be using the multi_role
FreeRTOS example as a guide
to convert the simple_central
project to FreeRTOS. The same can be used to
convert any BLE application into FreeRTOS + GCC.
Important
If you are starting with an example that already enables FreeRTOS (i.e. if
using multi_role
imported from SimpleLink CC13xx/CC26xx 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.
Setup the new project folders. Create a freertos folder inside the
simple_central
project directory located in:{SDK_INSTALL_DIR}\examples\rtos\CC26X2R1_LAUNCHXL\ble5stack\simple_central
Create a new projectspec file. Copy the gcc folder from
multi_role/freertos/gcc
tosimple_central/freertos/gcc
and rename the projectspec frommulti_role_CC26X2R1_LAUNCHXL_freertos_gcc.projectspec
tosimple_central_CC26X2R1_LAUNCHXL_freertos_gcc.projectspec
.Ensure the linker file
CC26X2R1_LAUNCHXL_FREERTOS.lds
is copied insidesimple_central/freertos/gcc
if not already done.Update
simple_central_CC26X2R1_LAUNCHXL_freertos_gcc.projectspec
, find and replace allmulti_role
withsimple_central
.By comparing the projectspecs, there will be some files that will need to be added or removed.
simple_central_CC26X2R1_LAUNCHXL_freertos_gcc.projectspec
new (FreeRTOS + gcc)simple_central_CC26X2R1_LAUNCHXL_tirtos_ccs.projectspec
legacy (TI-RTOS + CCS)
The following diff below shows files that are used by
multi_role
for its peripheral service declarations. Since we are using thesimple_central
example, these are not needed and can be removed.-<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/profiles/dev_info/cc26xx/devinfoservice.c" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/profiles/dev_info/devinfoservice.h" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/inc/gatt_profile_uuid.h" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/host/gatt_uuid.c" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/inc/gatt_uuid.h" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/host/gattservapp_util.c" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/profiles/simple_profile/cc26xx/simple_gatt_profile.c" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file> -<file path="${COM_TI_SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}/source/ti/ble5stack/profiles/simple_profile/simple_gatt_profile.h" openOnCreation="false" excludeFromBuild="false" action="link" targetDirectory="Profiles"> -</file>
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 the multi_role
FreeRTOS project and search for the predefined symbol FREERTOS
. This will
reveal the modifications to the multi_role
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 differences and changes between
the legacy (TI-RTOS + CCS) multi_role
example and the new (FreeRTOS + gcc)
multi_role
example. These changes should be applied to the corresponding
simple_central
example.
Replace all the TI-RTOS calls with DPL/FreeRTOS/POSIX calls in
simple_central.c
.+#ifdef FREERTOS +#include <FreeRTOS.h> +#include <task.h> +//#include <pthread.h> +#include <mqueue.h> +#else #include <string.h> #include <ti/sysbios/knl/Clock.h> #include <ti/sysbios/knl/Event.h> #include <ti/sysbios/knl/Queue.h> #include <ti/sysbios/knl/Task.h> +#endif #ifdef FREERTOS #include <stdarg.h> #endif #include <ti/display/Display.h> +#if (!(defined FREERTOS) && !(defined __TI_COMPILER_VERSION__)) && !(defined(__clang__)) #include <intrinsics.h> #endif
#ifndef MR_TASK_STACK_SIZE +#ifdef FREERTOS +#define MR_TASK_STACK_SIZE 2048 +#else #define MR_TASK_STACK_SIZE 1024 +#endif
/********************************************************************* * GLOBAL VARIABLES */ +#ifdef FREERTOS +mqd_t g_EventsQueueID; +#endif
/********************************************************************* * 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 appMsg; static Queue_Handle appMsgQueue; +#endif // Task configuration +#ifdef FREERTOS +typedef uint32_t * Task_Handle; +TaskHandle_t mrTask = NULL; +#else Task_Struct mrTask; +#endif +#ifndef FREERTOS #if defined __TI_COMPILER_VERSION__ #pragma DATA_ALIGN(mrTaskStack, 8) +#else +#pragma data_alignment=8 +#endif +#ifndef FREERTOS uint8_t mrTaskStack[MR_TASK_STACK_SIZE]; +#endif +#endif
/********************************************************************* * LOCAL FUNCTIONS */ static void multi_role_init(void); static void multi_role_scanInit(void); static void multi_role_advertInit(void); +#ifdef FREERTOS +static void multi_role_taskFxn(void *a0); +#else//TI_RTOS static void multi_role_taskFxn(UArg a0, UArg a1); +#endif //.. //.. //.. +#ifdef FREERTOS +static void multi_role_clockHandler(void * arg); +#else static void multi_role_clockHandler(UArg arg); +#endif
/********************************************************************* * @fn multi_role_createTask * * @brief Task creation function for multi_role. * * @param None. * * @return None. */ void multi_role_createTask(void) { +#ifdef FREERTOS + BaseType_t xReturned; + + /* Create the task, storing the handle. */ + xReturned = xTaskCreate( + multi_role_taskFxn, /* Function that implements the task. */ + "MULTI_ROLE_APP", /* Text name for the task. */ + MR_TASK_STACK_SIZE / sizeof(uint32_t), /* Stack size in words, not bytes. */ + ( void * ) NULL, /* Parameter passed into the task. */ + MR_TASK_PRIORITY, /* Priority at which the task is created. */ + &mrTask ); /* 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 = mrTaskStack; taskParams.stackSize = MR_TASK_STACK_SIZE; taskParams.priority = MR_TASK_PRIORITY; Task_construct(&mrTask, multi_role_taskFxn, &taskParams, NULL); +#endif }
+#ifdef FREERTOS + Util_constructQueue(&g_POSIX_appMsgQueue); +#else // Create an RTOS queue for message from profile to be sent to app. appMsgQueue = Util_constructQueue(&appMsg); +#endif // Create one-shot clock for internal periodic events. +#ifdef FREERTOS + Util_constructClock(&clkPeriodic,(void*) multi_role_clockHandler, + MR_PERIODIC_EVT_PERIOD, 0, false, + (void*)&periodicUpdateData); +#else Util_constructClock(&clkPeriodic, multi_role_clockHandler, MR_PERIODIC_EVT_PERIOD, 0, false, (UArg)&periodicUpdateData); +#endif
Note
simple_central
does 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 multi_role_taskFxn * * @brief Application task entry point for the multi_role. * * @param a0, a1 - not used. * * @return None. */ +#ifdef FREERTOS +static void multi_role_taskFxn(void* a0) +#else static void multi_role_taskFxn(UArg a0, UArg a1) +#endif { // Initialize application multi_role_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, MR_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); // event_31 + event_30 +#endif if (events) //.. //.. //.. // If RTOS queue is not empty, process app message. if (events & MR_QUEUE_EVT) { +#ifdef FREERTOS + + mrEvt_t *pMsg; + do { + pMsg = (mrEvt_t *)Util_dequeueMsg(g_POSIX_appMsgQueue); + if (NULL != pMsg) + { + // Process message. + multi_role_processAppMsg(pMsg); + + // Free the space from the message. + ICall_free(pMsg); + } + else + { + break; + } + }while(1); +#else while (!Queue_empty(appMsgQueue)) { mrEvt_t *pMsg = (mrEvt_t *)Util_dequeueMsg(appMsgQueue); if (pMsg) { // Process message. multi_role_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } } +#endif }
if (addrMode > ADDRMODE_RANDOM) { multi_role_updateRPA(); +#ifdef FREERTOS + // Create one-shot clock for RPA check event. + Util_constructClock(&clkRpaRead, (void*)multi_role_clockHandler, + READ_RPA_PERIOD, 0, true, + (void*) &argRpaRead); + +#else // Create one-shot clock for RPA check event. Util_constructClock(&clkRpaRead, multi_role_clockHandler, READ_RPA_PERIOD, 0, true, (UArg) &argRpaRead); +#endif } }
pMsg->pData = pData; // Enqueue the message. +#ifdef FREERTOS + success = Util_enqueueMsg(g_POSIX_appMsgQueue, syncEvent, (uint8_t *)pMsg); +#else success = Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg); +#endif return (success) ? SUCCESS : FAILURE; }
/********************************************************************* * @fn multi_role_clockHandler * * @brief Handler function for clock timeouts. * * @param arg - event type * * @return None. */ +#ifdef FREERTOS +static void multi_role_clockHandler(void * arg) +#else static void multi_role_clockHandler(UArg arg) +#endif {
/********************************************************************* * @fn multi_role_handleKeys * * @brief Handles all key events for this device. * * @param keys - bit field for key events. Valid entries: * HAL_KEY_SW_2 * HAL_KEY_SW_1 * * @return none */ static void multi_role_handleKeys(uint8_t keys) { +#ifndef FREERTOS uint32_t rtnVal = 0; +#endif if (keys & KEY_LEFT) { +#ifndef FREERTOS // Check if the key is still pressed if (PIN_getInputValue(CONFIG_GPIO_BTN1) == 0) { +#endif tbm_buttonLeft(); +#ifndef FREERTOS } +#endif } else if (keys & KEY_RIGHT) { // Check if the key is still pressed +#ifndef FREERTOS rtnVal = PIN_getInputValue(CONFIG_GPIO_BTN2); if (rtnVal == 0) { +#endif tbm_buttonRight(); +#ifndef FREERTOS } +#endif } }
// 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*)multi_role_clockHandler, + SEND_PARAM_UPDATE_DELAY, 0, true, + (void*) connList[i].pParamUpdateEventData); + +#else Util_constructClock(connList[i].pUpdateClock, multi_role_clockHandler, SEND_PARAM_UPDATE_DELAY, 0, true, (UArg) connList[i].pParamUpdateEventData); +#endif } else {
Note
There are other clocks (i.e. pRssiClock) defined in the
simple_central
project. Make sure to apply the above change to those instances as shown in the code snippet below, take note of the new scClockEventData_t structure used to pass in arguments. This can be taken frommulti_role.c
file and renamed.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.
if (connList[connIndex].pRssiClock) { +#ifdef FREERTOS + scClockEventData_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 main_freertos.c
#include "bcomdef.h" -#include "multi_role.h" +#include "simple_central.h"
/* Kick off application - Priority 1 */ -multi_role_createTask(); +SimpleCentral_createTask();
Modify the .syscfg file 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 /* * simple_central.syscfg */
Caution
Workaround for Button Debouncing
There exists a known issue with FreeRTOS projects in the TI BLE5-Stack (see Release Notes for more information). This issue affects only the usage of the example application’s two button menu. To workaround this, manually add a delay inside board_key.c::Board_keyCallback, as opposed to starting a FreeRTOS timer. Below is an example of the modification:
//define this outside the function
+#define CPU_convertMsToDelayCycles(milliseconds) \
+ (((uint32_t)(milliseconds)) * (48000 / 3))
//Place the below changes at the end of Board_keyCallback
keysPressed |= KEY_RIGHT;
}
#endif
+#ifdef FREERTOS
+/* 100 ms delay */
+CPUdelay(CPU_convertMsToDelayCycles(100));
+// Notify the application
+(*appKeyChangeHandler)(keysPressed);
+#else
Util_startClock(&keyChangeClock);
+#endif