Merge branch 'feature/freertos_backported_functions' into 'master'

freertos/backport and test v9.0.0 functions

See merge request !1515
This commit is contained in:
Angus Gratton 2017-11-24 08:49:59 +08:00
commit 02304ad83e
12 changed files with 932 additions and 133 deletions

View File

@ -209,7 +209,7 @@ config SUPPORT_STATIC_ALLOCATION
memory being allocated dynamically:
- Tasks
- Software Timers
- Software Timers (Daemon task is still dynamic. See documentation)
- Queues
- Event Groups
- Binary Semaphores

View File

@ -119,7 +119,11 @@ typedef struct xEventGroupDefinition
UBaseType_t uxEventGroupNumber;
#endif
portMUX_TYPE eventGroupMux;
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
#endif
portMUX_TYPE eventGroupMux; //Mutex required due to SMP
} EventGroup_t;
@ -137,26 +141,83 @@ static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, co
/*-----------------------------------------------------------*/
EventGroupHandle_t xEventGroupCreate( void )
{
EventGroup_t *pxEventBits;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
pxEventBits = pvPortMalloc( sizeof( EventGroup_t ) );
if( pxEventBits != NULL )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )
{
pxEventBits->uxEventBits = 0;
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
traceEVENT_GROUP_CREATE( pxEventBits );
}
else
{
traceEVENT_GROUP_CREATE_FAILED();
EventGroup_t *pxEventBits;
/* A StaticEventGroup_t object must be provided. */
configASSERT( pxEventGroupBuffer );
/* The user has provided a statically allocated event group - use it. */
pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 EventGroup_t and StaticEventGroup_t are guaranteed to have the same size and alignment requirement - checked by configASSERT(). */
if( pxEventBits != NULL )
{
pxEventBits->uxEventBits = 0;
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
{
/* Both static and dynamic allocation can be used, so note that
this event group was created statically in case the event group
is later deleted. */
pxEventBits->ucStaticallyAllocated = pdTRUE;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
vPortCPUInitializeMutex(&pxEventBits->eventGroupMux);
traceEVENT_GROUP_CREATE( pxEventBits );
}
else
{
traceEVENT_GROUP_CREATE_FAILED();
}
return ( EventGroupHandle_t ) pxEventBits;
}
vPortCPUInitializeMutex(&pxEventBits->eventGroupMux);
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
return ( EventGroupHandle_t ) pxEventBits;
}
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreate( void )
{
EventGroup_t *pxEventBits;
/* Allocate the event group. */
pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
if( pxEventBits != NULL )
{
pxEventBits->uxEventBits = 0;
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Both static and dynamic allocation can be used, so note this
event group was allocated statically in case the event group is
later deleted. */
pxEventBits->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
vPortCPUInitializeMutex(&pxEventBits->eventGroupMux);
traceEVENT_GROUP_CREATE( pxEventBits );
}
else
{
traceEVENT_GROUP_CREATE_FAILED();
}
return ( EventGroupHandle_t ) pxEventBits;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
/*-----------------------------------------------------------*/
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait )
@ -600,8 +661,29 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup )
( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
}
taskEXIT_CRITICAL( &pxEventBits->eventGroupMux );
vPortFree( pxEventBits );
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
{
/* The event group can only have been allocated dynamically - free
it again. */
taskEXIT_CRITICAL( &pxEventBits->eventGroupMux );
vPortFree( pxEventBits );
}
#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
{
/* The event group could have been allocated statically or
dynamically, so check before attempting to free the memory. */
if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
{
taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); //Exit mux of event group before deleting it
vPortFree( pxEventBits );
}
else
{
taskEXIT_CRITICAL( &pxEventBits->eventGroupMux );
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
( void ) xTaskResumeAll();
}

View File

@ -974,18 +974,73 @@ typedef struct xSTATIC_QUEUE
uint8_t ucDummy9;
#endif
struct {
volatile uint32_t ucDummy10;
uint32_t ucDummy11;
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
void *pvDummy8;
UBaseType_t uxDummy12;
#endif
} sDummy1;
portMUX_TYPE muxDummy; //Mutex required due to SMP
} StaticQueue_t;
typedef StaticQueue_t StaticSemaphore_t;
/*
* In line with software engineering best practice, especially when supplying a
* library that is likely to change in future versions, FreeRTOS implements a
* strict data hiding policy. This means the event group structure used
* internally by FreeRTOS is not accessible to application code. However, if
* the application writer wants to statically allocate the memory required to
* create an event group then the size of the event group object needs to be
* know. The StaticEventGroup_t structure below is provided for this purpose.
* Its sizes and alignment requirements are guaranteed to match those of the
* genuine structure, no matter which architecture is being used, and no matter
* how the values in FreeRTOSConfig.h are set. Its contents are somewhat
* obfuscated in the hope users will recognise that it would be unwise to make
* direct use of the structure members.
*/
typedef struct xSTATIC_EVENT_GROUP
{
TickType_t xDummy1;
StaticList_t xDummy2;
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxDummy3;
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucDummy4;
#endif
portMUX_TYPE muxDummy; //Mutex required due to SMP
} StaticEventGroup_t;
/*
* In line with software engineering best practice, especially when supplying a
* library that is likely to change in future versions, FreeRTOS implements a
* strict data hiding policy. This means the software timer structure used
* internally by FreeRTOS is not accessible to application code. However, if
* the application writer wants to statically allocate the memory required to
* create a software timer then the size of the queue object needs to be know.
* The StaticTimer_t structure below is provided for this purpose. Its sizes
* and alignment requirements are guaranteed to match those of the genuine
* structure, no matter which architecture is being used, and no matter how the
* values in FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in
* the hope users will recognise that it would be unwise to make direct use of
* the structure members.
*/
typedef struct xSTATIC_TIMER
{
void *pvDummy1;
StaticListItem_t xDummy2;
TickType_t xDummy3;
UBaseType_t uxDummy4;
void *pvDummy5[ 2 ];
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxDummy6;
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucDummy7;
#endif
} StaticTimer_t;
#ifdef __cplusplus
}
#endif

View File

@ -137,7 +137,17 @@ typedef TickType_t EventBits_t;
EventGroupHandle_t xEventGroupCreate( void );
</pre>
*
* Create a new event group. This function cannot be called from an interrupt.
* Create a new event group.
*
* Internally, within the FreeRTOS implementation, event groups use a [small]
* block of memory, in which the event group's structure is stored. If an event
* groups is created using xEventGropuCreate() then the required memory is
* automatically dynamically allocated inside the xEventGroupCreate() function.
* (see http://www.freertos.org/a00111.html). If an event group is created
* using xEventGropuCreateStatic() then the application writer must instead
* provide the memory that will get used by the event group.
* xEventGroupCreateStatic() therefore allows an event group to be created
* without using any dynamic memory allocation.
*
* Although event groups are not related to ticks, for internal implementation
* reasons the number of bits available for use in an event group is dependent
@ -173,7 +183,62 @@ typedef TickType_t EventBits_t;
* \defgroup xEventGroupCreate xEventGroupCreate
* \ingroup EventGroup
*/
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
#endif
/**
* event_groups.h
*<pre>
EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
</pre>
*
* Create a new event group.
*
* Internally, within the FreeRTOS implementation, event groups use a [small]
* block of memory, in which the event group's structure is stored. If an event
* groups is created using xEventGropuCreate() then the required memory is
* automatically dynamically allocated inside the xEventGroupCreate() function.
* (see http://www.freertos.org/a00111.html). If an event group is created
* using xEventGropuCreateStatic() then the application writer must instead
* provide the memory that will get used by the event group.
* xEventGroupCreateStatic() therefore allows an event group to be created
* without using any dynamic memory allocation.
*
* Although event groups are not related to ticks, for internal implementation
* reasons the number of bits available for use in an event group is dependent
* on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If
* configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit
* 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has
* 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store
* event bits within an event group.
*
* @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type
* StaticEventGroup_t, which will be then be used to hold the event group's data
* structures, removing the need for the memory to be allocated dynamically.
*
* @return If the event group was created then a handle to the event group is
* returned. If pxEventGroupBuffer was NULL then NULL is returned.
*
* Example usage:
<pre>
// StaticEventGroup_t is a publicly accessible structure that has the same
// size and alignment requirements as the real event group structure. It is
// provided as a mechanism for applications to know the size of the event
// group (which is dependent on the architecture and configuration file
// settings) without breaking the strict data hiding policy by exposing the
// real event group internals. This StaticEventGroup_t variable is passed
// into the xSemaphoreCreateEventGroupStatic() function and is used to store
// the event group's data structures
StaticEventGroup_t xEventGroupBuffer;
// Create the event group without dynamically allocating any memory.
xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
</pre>
*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
#endif
/**
* event_groups.h

View File

@ -1179,6 +1179,18 @@ typedef QueueHandle_t SemaphoreHandle_t;
*/
#define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) )
/**
* semphr.h
* <pre>UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );</pre>
*
* If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns
* its current count value. If the semaphore is a binary semaphore then
* uxSemaphoreGetCount() returns 1 if the semaphore is available, and 0 if the
* semaphore is not available.
*
*/
#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
#endif /* SEMAPHORE_H */

View File

@ -135,9 +135,17 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
* void * pvTimerID,
* TimerCallbackFunction_t pxCallbackFunction );
*
* Creates a new software timer instance. This allocates the storage required
* by the new timer, initialises the new timers internal state, and returns a
* handle by which the new timer can be referenced.
* Creates a new software timer instance, and returns a handle by which the
* created software timer can be referenced.
*
* Internally, within the FreeRTOS implementation, software timers use a block
* of memory, in which the timer data structure is stored. If a software timer
* is created using xTimerCreate() then the required memory is automatically
* dynamically allocated inside the xTimerCreate() function. (see
* http://www.freertos.org/a00111.html). If a software timer is created using
* xTimerCreateStatic() then the application writer must provide the memory that
* will get used by the software timer. xTimerCreateStatic() therefore allows a
* software timer to be created without using any dynamic memory allocation.
*
* Timers are created in the dormant state. The xTimerStart(), xTimerReset(),
* xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and
@ -250,14 +258,151 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
*
* // Starting the scheduler will start the timers running as they have already
* // been set into the active state.
* xTaskStartScheduler();
* vTaskStartScheduler();
*
* // Should not reach here.
* for( ;; );
* }
* @endverbatim
*/
TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#endif
/**
* TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
* TickType_t xTimerPeriodInTicks,
* UBaseType_t uxAutoReload,
* void * pvTimerID,
* TimerCallbackFunction_t pxCallbackFunction,
* StaticTimer_t *pxTimerBuffer );
*
* Creates a new software timer instance, and returns a handle by which the
* created software timer can be referenced.
*
* Internally, within the FreeRTOS implementation, software timers use a block
* of memory, in which the timer data structure is stored. If a software timer
* is created using xTimerCreate() then the required memory is automatically
* dynamically allocated inside the xTimerCreate() function. (see
* http://www.freertos.org/a00111.html). If a software timer is created using
* xTimerCreateStatic() then the application writer must provide the memory that
* will get used by the software timer. xTimerCreateStatic() therefore allows a
* software timer to be created without using any dynamic memory allocation.
*
* Timers are created in the dormant state. The xTimerStart(), xTimerReset(),
* xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and
* xTimerChangePeriodFromISR() API functions can all be used to transition a
* timer into the active state.
*
* @param pcTimerName A text name that is assigned to the timer. This is done
* purely to assist debugging. The kernel itself only ever references a timer
* by its handle, and never by its name.
*
* @param xTimerPeriodInTicks The timer period. The time is defined in tick
* periods so the constant portTICK_PERIOD_MS can be used to convert a time that
* has been specified in milliseconds. For example, if the timer must expire
* after 100 ticks, then xTimerPeriodInTicks should be set to 100.
* Alternatively, if the timer must expire after 500ms, then xPeriod can be set
* to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or
* equal to 1000.
*
* @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will
* expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter.
* If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and
* enter the dormant state after it expires.
*
* @param pvTimerID An identifier that is assigned to the timer being created.
* Typically this would be used in the timer callback function to identify which
* timer expired when the same callback function is assigned to more than one
* timer.
*
* @param pxCallbackFunction The function to call when the timer expires.
* Callback functions must have the prototype defined by TimerCallbackFunction_t,
* which is "void vCallbackFunction( TimerHandle_t xTimer );".
*
* @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which
* will be then be used to hold the software timer's data structures, removing
* the need for the memory to be allocated dynamically.
*
* @return If the timer is created then a handle to the created timer is
* returned. If pxTimerBuffer was NULL then NULL is returned.
*
* Example usage:
* @verbatim
*
* // The buffer used to hold the software timer's data structure.
* static StaticTimer_t xTimerBuffer;
*
* // A variable that will be incremented by the software timer's callback
* // function.
* UBaseType_t uxVariableToIncrement = 0;
*
* // A software timer callback function that increments a variable passed to
* // it when the software timer was created. After the 5th increment the
* // callback function stops the software timer.
* static void prvTimerCallback( TimerHandle_t xExpiredTimer )
* {
* UBaseType_t *puxVariableToIncrement;
* BaseType_t xReturned;
*
* // Obtain the address of the variable to increment from the timer ID.
* puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer );
*
* // Increment the variable to show the timer callback has executed.
* ( *puxVariableToIncrement )++;
*
* // If this callback has executed the required number of times, stop the
* // timer.
* if( *puxVariableToIncrement == 5 )
* {
* // This is called from a timer callback so must not block.
* xTimerStop( xExpiredTimer, staticDONT_BLOCK );
* }
* }
*
*
* void main( void )
* {
* // Create the software time. xTimerCreateStatic() has an extra parameter
* // than the normal xTimerCreate() API function. The parameter is a pointer
* // to the StaticTimer_t structure that will hold the software timer
* // structure. If the parameter is passed as NULL then the structure will be
* // allocated dynamically, just as if xTimerCreate() had been called.
* xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS.
* xTimerPeriod, // The period of the timer in ticks.
* pdTRUE, // This is an auto-reload timer.
* ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function
* prvTimerCallback, // The function to execute when the timer expires.
* &xTimerBuffer ); // The buffer that will hold the software timer structure.
*
* // The scheduler has not started yet so a block time is not used.
* xReturned = xTimerStart( xTimer, 0 );
*
* // ...
* // Create tasks here.
* // ...
*
* // Starting the scheduler will start the timers running as they have already
* // been set into the active state.
* vTaskStartScheduler();
*
* // Should not reach here.
* for( ;; );
* }
* @endverbatim
*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#endif /* configSUPPORT_STATIC_ALLOCATION */
/**
* void *pvTimerGetTimerID( TimerHandle_t xTimer );
@ -281,6 +426,27 @@ TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTi
*/
void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/**
* void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );
*
* Sets the ID assigned to the timer.
*
* IDs are assigned to timers using the pvTimerID parameter of the call to
* xTimerCreated() that was used to create the timer.
*
* If the same callback function is assigned to multiple timers then the timer
* ID can be used as time specific (timer local) storage.
*
* @param xTimer The timer being updated.
*
* @param pvNewID The ID to assign to the timer.
*
* Example usage:
*
* See the xTimerCreate() API function example usage scenario.
*/
void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION;
/**
* BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );
*
@ -329,6 +495,32 @@ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
*/
TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
/**
* TickType_t xTimerGetPeriod( TimerHandle_t xTimer );
*
* Returns the period of a timer.
*
* @param xTimer The handle of the timer being queried.
*
* @return The period of the timer in ticks.
*/
TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/**
* TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );
*
* Returns the time in ticks at which the timer will expire. If this is less
* than the current tick count then the expiry time has overflowed from the
* current time.
*
* @param xTimer The handle of the timer being queried.
*
* @return If the timer is running then the time in ticks at which the timer
* will next expire is returned. If the timer is not running then the return
* value is undefined.
*/
TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/**
* BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
*

View File

@ -165,7 +165,7 @@ typedef struct QueueDefinition
uint8_t ucQueueType;
#endif
portMUX_TYPE mux;
portMUX_TYPE mux; //Mutex required due to SMP
} xQUEUE;

View File

@ -0,0 +1,253 @@
/*
* Test features that are backported from version FreeRTOS 9.0.0.
*
* 1) Test backported timer functions
* - xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime()
* 2) Test backported queue/semaphore functions
* - xQueueCreateStatic()
* - xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic(), uxSemaphoreGetCount()
* - xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic()
* 3) Test static allocation of tasks
* - xTaskCreateStaticPinnedToCore()
* 4) Test static allocation of event group
* - xEventGroupCreateStatic()
* 5) Test Thread Local Storage Pointers and Deletion Callbacks
* - vTaskSetThreadLocalStoragePointerAndDelCallback()
* - pvTaskGetThreadLocalStoragePointer()
*
* Note: The *pcQueueGetName() function is also backported, but is not tested in
* the following test cases (see Queue Registry test cases instead)
* For more details please refer the the ESP-IDF FreeRTOS changes documentation
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "unity.h"
/* ---------------------Test 1: Backported Timer functions-----------------------
* Test xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime()
*
* This test creates a one-shot static timer, sets/checks the timer's id and period. Then ensures
* the timer cb is executed in a timely fashion.
*/
#define TMR_PERIOD_TICKS 10
#define TIMER_ID 0xFF
#define TICK_DELTA 5
static StaticTimer_t timer_buffer;
static TickType_t tmr_ideal_exp;
static void tmr_cb(TimerHandle_t xtimer)
{
//Check cb is called in timely fashion
TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTaskGetTickCount());
}
//No need for smp test as Timer Task always runs on core 0
TEST_CASE("Test FreeRTOS backported timer functions", "[freertos]")
{
//Create one shot static timer with period TMR_PERIOD_TICKS
TimerHandle_t tmr_handle = xTimerCreateStatic("static_tmr", TMR_PERIOD_TICKS, pdFALSE, NULL, tmr_cb, &timer_buffer);
TEST_ASSERT_EQUAL(TMR_PERIOD_TICKS, xTimerGetPeriod(tmr_handle)); //Test xTimerGetPeriod()
vTimerSetTimerID(tmr_handle, (void *)TIMER_ID);
TEST_ASSERT_EQUAL(TIMER_ID, (uint32_t)pvTimerGetTimerID(tmr_handle)); //Test vTimerSetTimerID()
TEST_ASSERT_EQUAL(pdTRUE, xTimerStart(tmr_handle, 1)); //Start Timer
tmr_ideal_exp = xTaskGetTickCount() + TMR_PERIOD_TICKS; //Calculate ideal expiration time
vTaskDelay(2); //Need to yield to allow daemon task to process start command, or else expiration time will be NULL
TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTimerGetExpiryTime(tmr_handle)); //Test xTimerGetExpiryTime()
vTaskDelay(2*TMR_PERIOD_TICKS); //Delay until one shot timer has triggered
TEST_ASSERT_EQUAL(pdPASS, xTimerDelete(tmr_handle, portMAX_DELAY)); //Clean up
}
/* ---------------Test backported queue/semaphore functions-------------------
* xQueueCreateStatic()
* xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic()
* xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic()
* uxSemaphoreGetCount() is also tested on the static counting semaphore
*
* This test creates various static queue/semphrs listed above and tests them by
* doing a simple send/give and rec/take.
*/
#define ITEM_SIZE 3
#define NO_OF_ITEMS 3
#define DELAY_TICKS 2
static StaticQueue_t queue_buffer; //Queues, Semaphores, and Mutex use the same queue structure
static uint8_t queue_storage_area[(ITEM_SIZE*NO_OF_ITEMS)]; //Queue storage provided in separate buffer to queue struct
TEST_CASE("Test FreeRTOS backported Queue and Semphr functions", "[freertos]")
{
//Test static queue
uint8_t queue_item_to_send[ITEM_SIZE];
uint8_t queue_item_received[ITEM_SIZE];
for(int i = 0; i < ITEM_SIZE; i++){
queue_item_to_send[i] = (0xF << i);
}
QueueHandle_t handle = xQueueCreateStatic(NO_OF_ITEMS, ITEM_SIZE,(uint8_t*) &queue_storage_area, &queue_buffer);
TEST_ASSERT_EQUAL(pdTRUE, xQueueSendToBack(handle, &queue_item_to_send, DELAY_TICKS));
vTaskDelay(1);
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(handle, queue_item_received, DELAY_TICKS));
vTaskDelay(1);
for(int i = 0; i < ITEM_SIZE; i++){
TEST_ASSERT_EQUAL(queue_item_to_send[i], queue_item_received[i]); //Check received contents are correct
}
vQueueDelete(handle); //Technically not needed as deleting static queue/semphr doesn't clear static memory
//Test static binary semaphore
handle = xSemaphoreCreateBinaryStatic(&queue_buffer); //Queue and Semphr handles are the same
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
vTaskDelay(1);
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
vTaskDelay(1);
vSemaphoreDelete(handle);
//Test static counting semaphore and uxSemaphoreGetCount()
handle = xSemaphoreCreateCountingStatic(NO_OF_ITEMS, 0, &queue_buffer);
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL(NO_OF_ITEMS, uxSemaphoreGetCount(handle)); //Test uxSemaphoreGetCount()
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL(0, uxSemaphoreGetCount(handle));
vSemaphoreDelete(handle);
//Test static mutex
handle = xSemaphoreCreateMutexStatic(&queue_buffer);
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should now hold mutex
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released
vSemaphoreDelete(handle);
//Test static mutex recursive
handle = xSemaphoreCreateRecursiveMutexStatic(&queue_buffer);
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTakeRecursive(handle, DELAY_TICKS));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should hold mutex
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGiveRecursive(handle));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released
vSemaphoreDelete(handle);
}
/* -----------------Test backported static task allocation -------------------
* Test xTaskCreateStaticPinnedToCore() but creating static task on each core
* and checking the task cb has run successfully.
*/
#define STACK_SIZE 2048 //Task stack size
static StackType_t task_stack[STACK_SIZE]; //Static buffer for task stack
static StaticTask_t task_buffer; //Static buffer for TCB
static bool has_run[portNUM_PROCESSORS];
static void task(void *arg)
{
has_run[xPortGetCoreID()] = true; //Signify static task cb has run
vTaskDelete(NULL);
}
TEST_CASE("Test FreeRTOS static task allocation", "[freertos]")
{
for(int core = 0; core < portNUM_PROCESSORS; core++){
has_run[core] = false; //Clear has_run flag
TaskHandle_t handle = xTaskCreateStaticPinnedToCore(task, "static task", STACK_SIZE, NULL,
UNITY_FREERTOS_PRIORITY + 1, (StackType_t *)&task_stack,
(StaticTask_t *)&task_buffer, core);
vTaskDelay(5); //Allow for static task to run, delete, and idle to clean up
TEST_ASSERT_NOT_EQUAL(NULL, handle); //Check static task was successfully allocated
TEST_ASSERT_TRUE(has_run[core]) //Check static task has run
}
}
/* ------------- Test backported static event group allocation -------------------
* Test xEventGroupCreateStatic() but creating static event group then waiting
* for an event.
*/
#define WAIT_BITS 0x01 //Wait for first bit
static StaticEventGroup_t event_group;
static EventGroupHandle_t eg_handle;
TEST_CASE("Test FreeRTOS backported eventgroup functions", "[freertos]")
{
eg_handle = xEventGroupCreateStatic((StaticEventGroup_t *)&event_group);
xEventGroupSetBits(eg_handle, WAIT_BITS);
TEST_ASSERT_EQUAL(WAIT_BITS, xEventGroupWaitBits(eg_handle, WAIT_BITS, pdTRUE, pdTRUE, portMAX_DELAY));
//Cleanup static event
vEventGroupDelete(eg_handle);
}
/* --------Test backported thread local storage pointer and deletion cb feature----------
* vTaskSetThreadLocalStoragePointerAndDelCallback()
* pvTaskGetThreadLocalStoragePointer(),
*
* This test creates a task and set's the task's TLSPs. The task is then deleted
* which should trigger the deletion cb.
*/
#define NO_OF_TLSP configNUM_THREAD_LOCAL_STORAGE_POINTERS
#define TLSP_SET_BASE 0x0F //0b1111 to be bit shifted by index
#define TLSP_DEL_BASE 0x05 //0b0101 to be bit shifted by index
//The variables pointed to by Thread Local Storage Pointer
static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0};
static void del_cb(int index, void *ptr)
{
*((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value
}
static void task_cb()
{
int core = xPortGetCoreID();
for(int i = 0; i < NO_OF_TLSP; i++){
task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element
}
for(int i = 0; i < NO_OF_TLSP; i++){
uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i);
TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value
}
vTaskDelete(NULL); //Delete Task to Trigger TSLP deletion callback
}
TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]")
{
//Create Task
for(int core = 0; core < portNUM_PROCESSORS; core++){
xTaskCreatePinnedToCore(task_cb, "task", 1024, NULL, UNITY_FREERTOS_PRIORITY+1, NULL, core);
}
vTaskDelay(10); //Delay long enough for tasks to run to completion
for(int core = 0; core < portNUM_PROCESSORS; core++){
for(int i = 0; i < NO_OF_TLSP; i++){
TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value
}
}
}

View File

@ -1,58 +0,0 @@
#include <esp_types.h>
#include <stdio.h>
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "unity.h"
#include "soc/uart_reg.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
static void tskdelcb(int no, void *arg)
{
ets_printf("Delete callback: %d = %p!\n", no, arg);
}
static void tska(void *pvParameters)
{
vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xAAAAAAAA, tskdelcb);
while (1) {
vTaskDelay(10000000 / portTICK_PERIOD_MS);
}
}
static void tskb(void *pvParameters)
{
vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xBBBBBBBB, tskdelcb);
vTaskDelay(2000 / portTICK_PERIOD_MS);
TaskHandle_t a = (TaskHandle_t)pvParameters;
printf("Killing task A\n");
vTaskDelete(a);
while (1) {
vTaskDelay(10000000 / portTICK_PERIOD_MS);
}
}
// TODO: split this thing into separate orthogonal tests
TEST_CASE("Freertos TLS delete cb", "[freertos]")
{
TaskHandle_t a, b;
xTaskCreatePinnedToCore(tska , "tska" , 2048, NULL, 3, &a, 0);
xTaskCreatePinnedToCore(tskb , "tska" , 2048, a, 3, &b, 0);
// Let stuff run for 20s
vTaskDelay(5000 / portTICK_PERIOD_MS);
printf("Killing task B\n");
//Shut down b
vTaskDelete(b);
}

View File

@ -113,6 +113,10 @@ typedef struct tmrTimerControl
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */
#endif
} xTIMER;
/* The old xTIMER name is maintained above then typedefed to the new Timer_t
@ -239,6 +243,16 @@ static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIV
*/
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION;
/*
* Called after a Timer_t structure has been allocated either statically or
* dynamically to fill in the structure's members.
*/
static void prvInitialiseNewTimer( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
/*-----------------------------------------------------------*/
BaseType_t xTimerCreateTimerTask( void )
@ -257,7 +271,10 @@ BaseType_t xReturn = pdFAIL;
if( xTimerQueue != NULL )
{
#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 )
/* Although static allocation has been backported from FreeRTOS v9.0.0,
the timer task is still allocated dynamically. The actual timers
however can be allocated statically.*/
#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 )
{
/* Create the timer task, storing its handle in xTimerTaskHandle so
it can be returned by the xTimerGetTimerDaemonTaskHandle() function. */
@ -280,44 +297,108 @@ BaseType_t xReturn = pdFAIL;
}
/*-----------------------------------------------------------*/
TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
Timer_t *pxNewTimer;
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
/* Allocate the timer structure. */
if( xTimerPeriodInTicks == ( TickType_t ) 0U )
{
pxNewTimer = NULL;
}
else
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
Timer_t *pxNewTimer;
pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) );
if( pxNewTimer != NULL )
{
/* Ensure the infrastructure used by the timer service task has been
created/initialised. */
prvCheckForValidListAndQueue();
prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );
/* Initialise the timer structure members using the function parameters. */
pxNewTimer->pcTimerName = pcTimerName;
pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
pxNewTimer->uxAutoReload = uxAutoReload;
pxNewTimer->pvTimerID = pvTimerID;
pxNewTimer->pxCallbackFunction = pxCallbackFunction;
vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Timers can be created statically or dynamically, so note this
timer was created dynamically in case the timer is later
deleted. */
pxNewTimer->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
}
traceTIMER_CREATE( pxNewTimer );
}
else
{
traceTIMER_CREATE_FAILED();
}
return pxNewTimer;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
Timer_t *pxNewTimer;
#if( configASSERT_DEFINED == 1 )
{
/* Sanity check that the size of the structure used to declare a
variable of type StaticTimer_t equals the size of the real timer
structures. */
volatile size_t xSize = sizeof( StaticTimer_t );
configASSERT( xSize == sizeof( Timer_t ) );
}
#endif /* configASSERT_DEFINED */
/* A pointer to a StaticTimer_t structure MUST be provided, use it. */
configASSERT( pxTimerBuffer );
pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
if( pxNewTimer != NULL )
{
prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
{
/* Timers can be created statically or dynamically so note this
timer was created statically in case it is later deleted. */
pxNewTimer->ucStaticallyAllocated = pdTRUE;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}
return pxNewTimer;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
static void prvInitialiseNewTimer( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
Timer_t *pxNewTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
/* 0 is not a valid value for xTimerPeriodInTicks. */
configASSERT( ( xTimerPeriodInTicks > 0 ) );
return ( TimerHandle_t ) pxNewTimer;
if( pxNewTimer != NULL )
{
/* Ensure the infrastructure used by the timer service task has been
created/initialised. */
prvCheckForValidListAndQueue();
/* Initialise the timer structure members using the function
parameters. */
pxNewTimer->pcTimerName = pcTimerName;
pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
pxNewTimer->uxAutoReload = uxAutoReload;
pxNewTimer->pvTimerID = pvTimerID;
pxNewTimer->pxCallbackFunction = pxCallbackFunction;
vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );
traceTIMER_CREATE( pxNewTimer );
}
}
/*-----------------------------------------------------------*/
@ -375,6 +456,26 @@ DaemonTaskMessage_t xMessage;
#endif
/*-----------------------------------------------------------*/
TickType_t xTimerGetPeriod( TimerHandle_t xTimer )
{
Timer_t *pxTimer = ( Timer_t * ) xTimer;
configASSERT( xTimer );
return pxTimer->xTimerPeriodInTicks;
}
/*-----------------------------------------------------------*/
TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer )
{
Timer_t * pxTimer = ( Timer_t * ) xTimer;
TickType_t xReturn;
configASSERT( xTimer );
xReturn = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) );
return xReturn;
}
/*-----------------------------------------------------------*/
const char * pcTimerGetTimerName( TimerHandle_t xTimer )
{
Timer_t *pxTimer = ( Timer_t * ) xTimer;
@ -703,8 +804,29 @@ TickType_t xTimeNow;
case tmrCOMMAND_DELETE :
/* The timer has already been removed from the active list,
just free up the memory. */
vPortFree( pxTimer );
just free up the memory if the memory was dynamically
allocated. */
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
{
/* The timer can only have been allocated dynamically -
free it again. */
vPortFree( pxTimer );
}
#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
{
/* The timer could have been allocated statically or
dynamically, so check before attempting to free the
memory. */
if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
{
vPortFree( pxTimer );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
break;
default :
@ -847,6 +969,20 @@ Timer_t * const pxTimer = ( Timer_t * ) xTimer;
}
/*-----------------------------------------------------------*/
void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID )
{
Timer_t * const pxTimer = ( Timer_t * ) xTimer;
configASSERT( xTimer );
//taskENTER_CRITICAL(); //Atomic instruction, critical not necessary
//{
pxTimer->pvTimerID = pvNewID;
//}
//taskEXIT_CRITICAL();
}
/*-----------------------------------------------------------*/
#if( INCLUDE_xTimerPendFunctionCall == 1 )
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken )

View File

@ -12,11 +12,13 @@ run tasks interchangeably between them.
The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports
symmetric multiprocessing (SMP). ESP-IDF FreeRTOS is based on the Xtensa port
of FreeRTOS v8.2.0, however features such as static task creation and Thread
Local Storage Pointers have been backported from later versions of FreeRTOS.
This guide outlines the major differences between vanilla FreeRTOS and
ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be found
via http://www.freertos.org/a00106.html
of FreeRTOS v8.2.0. This guide outlines the major differences between vanilla
FreeRTOS and ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be
found via http://www.freertos.org/a00106.html
:ref:`backported-features`: Although ESP-IDF FreeRTOS is based on the Xtensa
port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
to ESP-IDF.
:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or
``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The
@ -24,30 +26,30 @@ last parameter of the two functions is ``xCoreID``. This parameter specifies
which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**,
``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on
both.
:ref:`round-robin-scheduling`: The ESP-IDF FreeRTOS scheduler will skip tasks when
implementing Round-Robin scheduling between multiple tasks in the Ready state
that are of the same priority. To avoid this behavior, ensure that those tasks either
enter a blocked state, or are distributed across a wider range of priorities.
:ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only
affect the scheduler on the the calling core. In other words, calling
``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
vice versa. Use critical sections or semaphores instead for simultaneous
access protection.
:ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU**
are not synchronized. Do not expect to use ``vTaskDelay`` or
``vTaskDelayUntil`` as an accurate method of synchronizing task execution
between the two cores. Use a counting semaphore instead as their context
switches are not tied to tick interrupts due to preemption.
:ref:`critical-sections`: In ESP-IDF FreeRTOS, critical sections are implemented using
mutexes. Entering critical sections involve taking a mutex, then disabling the
scheduler and interrupts of the calling core. However the other core is left
unaffected. If the other core attemps to take same mutex, it will spin until
the calling core has released the mutex by exiting the critical section.
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has
backported the Thread Local Storage Pointers feature. However they have the
extra feature of deletion callbacks. Deletion callbacks are used to
@ -64,6 +66,61 @@ configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode,
or configuring the number of Thread Local Storage Pointers each task will have.
.. _backported-features:
Backported Features
-------------------
The following features have been backported from FreeRTOS v9.0.0 to ESP-IDF.
Static Alocation
^^^^^^^^^^^^^^^^^
This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The
:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` option must be enabled in `menuconfig`
in order for static allocation functions to be available. Once enabled, the
following functions can be called...
- ``xTaskCreateStatic()`` See :ref:`backporting-notes` below
- ``xQueueCreateStatic()``
- ``xSemaphoreCreateBinaryStatic()``
- ``xSemaphoreCreateCountingStatic()``
- ``xSemaphoreCreateMutexStatic()``
- ``xSemaphoreCreateRecursiveMutexStatic()``
- ``xTimerCreateStatic()`` See :ref:`backporting-notes` below
- ``xEventGroupCreateStatic()``
Other Features
^^^^^^^^^^^^^^
- ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below
- ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below
- ``vTimerSetTimerID()``
- ``xTimerGetPeriod()``
- ``xTimerGetExpiryTime()``
- ``pcQueueGetName()``
- ``uxSemaphoreGetCount()``
.. _backporting-notes:
Backporting Notes
^^^^^^^^^^^^^^^^^
**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar
fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore
``xTaskCreateStaticPinnedToCore()`` can also be called.
**2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to
be statically allocated, the daemon task is always dynamically allocated in
ESP-IDF. Therefore ``vApplicationGetTimerTaskMemory`` **does not** need to be
defined when using statically allocated timers in ESP-IDF FreeRTOS.
**3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF
FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore
the function ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` can also be
called.
.. _tasks-and-task-creation:
Tasks and Task Creation
@ -75,7 +132,7 @@ appending ``PinnedToCore`` to the names of the task creation functions in
vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()``
and ``xTaskCreateStatic()`` have led to the addition of
``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in
ESP-IDF FreeRTOS.
ESP-IDF FreeRTOS (see :ref:`backported-features`).
For more details see :component_file:`freertos/task.c`
@ -234,6 +291,7 @@ protecting shared resources in ESP-IDF FreeRTOS.
In general, it's better to use other RTOS primitives like mutex semaphores to protect
against data shared between tasks, rather than ``vTaskSuspendAll()``.
.. _tick-interrupt-synchronicity:
Tick Interrupt Synchronicity
@ -266,6 +324,7 @@ Therefore, task delays should **NOT** be used as a method of synchronization
between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore
to unblock multiple tasks at the same time.
.. _critical-sections:
Critical Sections & Disabling Interrupts
@ -315,6 +374,7 @@ called as they are all defined to call the same function. As long as the same
mutex is provided upon entering and exiting, the type of call should not
matter.
.. _deletion-callbacks:
Thread Local Storage Pointers & Deletion Callbacks
@ -351,6 +411,7 @@ Other indexes can be used for any purpose, provided
For more details see :component_file:`freertos/include/freertos/task.h`
.. _esp-idf-freertos-configuration:
Configuring ESP-IDF FreeRTOS

View File

@ -23,3 +23,4 @@ CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7
CONFIG_STACK_CHECK_STRONG=y
CONFIG_STACK_CHECK=y
CONFIG_SUPPORT_STATIC_ALLOCATION=y