/* | |
FreeRTOS V8.0.1 - Copyright (C) 2014 Real Time Engineers Ltd. | |
All rights reserved | |
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. | |
*************************************************************************** | |
* * | |
* FreeRTOS provides completely free yet professionally developed, * | |
* robust, strictly quality controlled, supported, and cross * | |
* platform software that has become a de facto standard. * | |
* * | |
* Help yourself get started quickly and support the FreeRTOS * | |
* project by purchasing a FreeRTOS tutorial book, reference * | |
* manual, or both from: http://www.FreeRTOS.org/Documentation * | |
* * | |
* Thank you! * | |
* * | |
*************************************************************************** | |
This file is part of the FreeRTOS distribution. | |
FreeRTOS is free software; you can redistribute it and/or modify it under | |
the terms of the GNU General Public License (version 2) as published by the | |
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. | |
>>! NOTE: The modification to the GPL is included to allow you to !<< | |
>>! distribute a combined work that includes FreeRTOS without being !<< | |
>>! obliged to provide the source code for proprietary components !<< | |
>>! outside of the FreeRTOS kernel. !<< | |
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY | |
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
FOR A PARTICULAR PURPOSE. Full license text is available from the following | |
link: http://www.freertos.org/a00114.html | |
1 tab == 4 spaces! | |
*************************************************************************** | |
* * | |
* Having a problem? Start by reading the FAQ "My application does * | |
* not run, what could be wrong?" * | |
* * | |
* http://www.FreeRTOS.org/FAQHelp.html * | |
* * | |
*************************************************************************** | |
http://www.FreeRTOS.org - Documentation, books, training, latest versions, | |
license and Real Time Engineers Ltd. contact details. | |
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, | |
including FreeRTOS+Trace - an indispensable productivity tool, a DOS | |
compatible FAT file system, and our tiny thread aware UDP/IP stack. | |
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High | |
Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS | |
licenses offer ticketed support, indemnification and middleware. | |
http://www.SafeRTOS.com - High Integrity Systems also provide a safety | |
engineered and independently SIL3 certified version for use in safety and | |
mission critical applications that require provable dependability. | |
1 tab == 4 spaces! | |
*/ | |
/* Standard includes. */ | |
#include <stdlib.h> | |
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining | |
all the API functions to use the MPU wrappers. That should only be done when | |
task.h is included from an application file. */ | |
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "timers.h" | |
#include "static-allocator.h" | |
#if ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 0 ) | |
#error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. | |
#endif | |
/* Lint e961 and e750 are suppressed as a MISRA exception justified because the | |
MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the | |
header files above, but not in this file, in order to generate the correct | |
privileged Vs unprivileged linkage and placement. */ | |
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ | |
/* This entire source file will be skipped if the application is not configured | |
to include software timer functionality. This #if is closed at the very bottom | |
of this file. If you want to include software timer functionality then ensure | |
configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ | |
#if ( configUSE_TIMERS == 1 ) | |
/* Misc definitions. */ | |
#define tmrNO_DELAY ( TickType_t ) 0U | |
/* The definition of the timers themselves. */ | |
typedef struct tmrTimerControl | |
{ | |
const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ | |
ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ | |
TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ | |
UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */ | |
void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ | |
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ | |
#if( configUSE_TRACE_FACILITY == 1 ) | |
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ | |
#endif | |
} xTIMER; | |
/* The old xTIMER name is maintained above then typedefed to the new Timer_t | |
name below to enable the use of older kernel aware debuggers. */ | |
typedef xTIMER Timer_t; | |
static struct { | |
static_pool_header_t pool_header; | |
xTIMER pool[configMAX_NUM_TIMERS]; | |
} sTimersPool __attribute__((section(".noinit"))); | |
/* The definition of messages that can be sent and received on the timer queue. | |
Two types of message can be queued - messages that manipulate a software timer, | |
and messages that request the execution of a non-timer related callback. The | |
two message types are defined in two separate structures, xTimerParametersType | |
and xCallbackParametersType respectively. */ | |
typedef struct tmrTimerParameters | |
{ | |
TickType_t xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ | |
Timer_t * pxTimer; /*<< The timer to which the command will be applied. */ | |
} TimerParameter_t; | |
typedef struct tmrCallbackParameters | |
{ | |
PendedFunction_t pxCallbackFunction; /* << The callback function to execute. */ | |
void *pvParameter1; /* << The value that will be used as the callback functions first parameter. */ | |
uint32_t ulParameter2; /* << The value that will be used as the callback functions second parameter. */ | |
} CallbackParameters_t; | |
/* The structure that contains the two message types, along with an identifier | |
that is used to determine which message type is valid. */ | |
typedef struct tmrTimerQueueMessage | |
{ | |
BaseType_t xMessageID; /*<< The command being sent to the timer service task. */ | |
union | |
{ | |
TimerParameter_t xTimerParameters; | |
/* Don't include xCallbackParameters if it is not going to be used as | |
it makes the structure (and therefore the timer queue) larger. */ | |
#if ( INCLUDE_xTimerPendFunctionCall == 1 ) | |
CallbackParameters_t xCallbackParameters; | |
#endif /* INCLUDE_xTimerPendFunctionCall */ | |
} u; | |
} DaemonTaskMessage_t; | |
/*lint -e956 A manual analysis and inspection has been used to determine which | |
static variables must be declared volatile. */ | |
/* The list in which active timers are stored. Timers are referenced in expire | |
time order, with the nearest expiry time at the front of the list. Only the | |
timer service task is allowed to access these lists. */ | |
PRIVILEGED_DATA static List_t xActiveTimerList1; | |
PRIVILEGED_DATA static List_t xActiveTimerList2; | |
PRIVILEGED_DATA static List_t *pxCurrentTimerList; | |
PRIVILEGED_DATA static List_t *pxOverflowTimerList; | |
/* A queue that is used to send commands to the timer service task. */ | |
PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; | |
#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) | |
PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; | |
#endif | |
/*lint +e956 */ | |
/*-----------------------------------------------------------*/ | |
void xInitTimers(void); | |
/* | |
* Initialise the infrastructure used by the timer service task if it has not | |
* been initialised already. | |
*/ | |
static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; | |
/* | |
* The timer service task (daemon). Timer functionality is controlled by this | |
* task. Other tasks communicate with the timer service task using the | |
* xTimerQueue queue. | |
*/ | |
static void prvTimerTask( void *pvParameters ) PRIVILEGED_FUNCTION; | |
/* | |
* Called by the timer service task to interpret and process a command it | |
* received on the timer queue. | |
*/ | |
static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; | |
/* | |
* Insert the timer into either xActiveTimerList1, or xActiveTimerList2, | |
* depending on if the expire time causes a timer counter overflow. | |
*/ | |
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; | |
/* | |
* An active timer has reached its expire time. Reload the timer if it is an | |
* auto reload timer, then call its callback. | |
*/ | |
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; | |
/* | |
* The tick count has overflowed. Switch the timer lists after ensuring the | |
* current timer list does not still reference some timers. | |
*/ | |
static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; | |
/* | |
* Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE | |
* if a tick count overflow occurred since prvSampleTimeNow() was last called. | |
*/ | |
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; | |
/* | |
* If the timer list contains any active timers then return the expire time of | |
* the timer that will expire first and set *pxListWasEmpty to false. If the | |
* timer list does not contain any timers then return 0 and set *pxListWasEmpty | |
* to pdTRUE. | |
*/ | |
static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; | |
/* | |
* If a timer has expired, process it. Otherwise, block the timer service task | |
* until either a timer does expire or a command is received. | |
*/ | |
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; | |
/*-----------------------------------------------------------*/ | |
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) | |
void xInitTimers(void) | |
{ | |
poolInit( (static_pool_t*)&sTimersPool, | |
sizeof(sTimersPool.pool[0]), | |
ARRAY_SIZE(sTimersPool.pool)); | |
} | |
static portSTACK_TYPE sTimerStack[configTIMER_TASK_STACK_DEPTH]; | |
BaseType_t xTimerCreateTimerTask( void ) | |
{ | |
BaseType_t xReturn = pdFAIL; | |
/* This function is called when the scheduler is started if | |
configUSE_TIMERS is set to 1. Check that the infrastructure used by the | |
timer service task has been created/initialised. If timers have already | |
been created then the initialisation will already have been performed. */ | |
prvCheckForValidListAndQueue(); | |
if( xTimerQueue != NULL ) | |
{ | |
#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) | |
{ | |
/* Create the timer task, storing its handle in xTimerTaskHandle so | |
it can be returned by the xTimerGetTimerDaemonTaskHandle() function. */ | |
xReturn = xTaskGenericCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, &xTimerTaskHandle, (portSTACK_TYPE*)&sTimerStack, NULL ); | |
} | |
#else | |
{ | |
/* Create the timer task without storing its handle. */ | |
xReturn = xTaskGenericCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, NULL, (portSTACK_TYPE*)&sTimerStack, NULL ); | |
} | |
#endif | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
configASSERT( xReturn ); | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
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; | |
/* Allocate the timer structure. */ | |
if( xTimerPeriodInTicks == ( TickType_t ) 0U ) | |
{ | |
pxNewTimer = NULL; | |
} | |
else | |
{ | |
pxNewTimer = ( xTIMER * ) poolAllocateBuffer((static_pool_t*)&sTimersPool); | |
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 ); | |
} | |
else | |
{ | |
traceTIMER_CREATE_FAILED(); | |
} | |
} | |
/* 0 is not a valid value for xTimerPeriodInTicks. */ | |
configASSERT( ( xTimerPeriodInTicks > 0 ) ); | |
return ( TimerHandle_t ) pxNewTimer; | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) | |
{ | |
BaseType_t xReturn = pdFAIL; | |
DaemonTaskMessage_t xMessage; | |
/* Send a message to the timer service task to perform a particular action | |
on a particular timer definition. */ | |
if( xTimerQueue != NULL ) | |
{ | |
/* Send a command to the timer service task to start the xTimer timer. */ | |
xMessage.xMessageID = xCommandID; | |
xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; | |
xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; | |
if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) | |
{ | |
if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) | |
{ | |
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); | |
} | |
else | |
{ | |
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); | |
} | |
} | |
else | |
{ | |
xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); | |
} | |
traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) | |
TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) | |
{ | |
/* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been | |
started, then xTimerTaskHandle will be NULL. */ | |
configASSERT( ( xTimerTaskHandle != NULL ) ); | |
return xTimerTaskHandle; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
const char * pcTimerGetTimerName( TimerHandle_t xTimer ) | |
{ | |
Timer_t *pxTimer = ( Timer_t * ) xTimer; | |
return pxTimer->pcTimerName; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) | |
{ | |
BaseType_t xResult; | |
Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); | |
/* Remove the timer from the list of active timers. A check has already | |
been performed to ensure the list is not empty. */ | |
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); | |
traceTIMER_EXPIRED( pxTimer ); | |
/* If the timer is an auto reload timer then calculate the next | |
expiry time and re-insert the timer in the list of active timers. */ | |
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) | |
{ | |
/* The timer is inserted into a list using a time relative to anything | |
other than the current time. It will therefore be inserted into the | |
correct list relative to the time this task thinks it is now. */ | |
if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) == pdTRUE ) | |
{ | |
/* The timer expired before it was added to the active timer | |
list. Reload it now. */ | |
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); | |
configASSERT( xResult ); | |
( void ) xResult; | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
/* Call the timer callback. */ | |
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvTimerTask( void *pvParameters ) | |
{ | |
TickType_t xNextExpireTime; | |
BaseType_t xListWasEmpty; | |
/* Just to avoid compiler warnings. */ | |
( void ) pvParameters; | |
for( ;; ) | |
{ | |
/* Query the timers list to see if it contains any timers, and if so, | |
obtain the time at which the next timer will expire. */ | |
xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); | |
/* If a timer has expired, process it. Otherwise, block this task | |
until either a timer does expire, or a command is received. */ | |
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); | |
/* Empty the command queue. */ | |
prvProcessReceivedCommands(); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) | |
{ | |
TickType_t xTimeNow; | |
BaseType_t xTimerListsWereSwitched; | |
vTaskSuspendAll(); | |
{ | |
/* Obtain the time now to make an assessment as to whether the timer | |
has expired or not. If obtaining the time causes the lists to switch | |
then don't process this timer as any timers that remained in the list | |
when the lists were switched will have been processed within the | |
prvSampleTimeNow() function. */ | |
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); | |
if( xTimerListsWereSwitched == pdFALSE ) | |
{ | |
/* The tick count has not overflowed, has the timer expired? */ | |
if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) | |
{ | |
( void ) xTaskResumeAll(); | |
prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); | |
} | |
else | |
{ | |
/* The tick count has not overflowed, and the next expire | |
time has not been reached yet. This task should therefore | |
block to wait for the next expire time or a command to be | |
received - whichever comes first. The following line cannot | |
be reached unless xNextExpireTime > xTimeNow, except in the | |
case when the current timer list is empty. */ | |
vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) ); | |
if( xTaskResumeAll() == pdFALSE ) | |
{ | |
/* Yield to wait for either a command to arrive, or the block time | |
to expire. If a command arrived between the critical section being | |
exited and this yield then the yield will not cause the task | |
to block. */ | |
portYIELD_WITHIN_API(); | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
} | |
else | |
{ | |
( void ) xTaskResumeAll(); | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) | |
{ | |
TickType_t xNextExpireTime; | |
/* Timers are listed in expiry time order, with the head of the list | |
referencing the task that will expire first. Obtain the time at which | |
the timer with the nearest expiry time will expire. If there are no | |
active timers then just set the next expire time to 0. That will cause | |
this task to unblock when the tick count overflows, at which point the | |
timer lists will be switched and the next expiry time can be | |
re-assessed. */ | |
*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); | |
if( *pxListWasEmpty == pdFALSE ) | |
{ | |
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); | |
} | |
else | |
{ | |
/* Ensure the task unblocks when the tick count rolls over. */ | |
xNextExpireTime = ( TickType_t ) 0U; | |
} | |
return xNextExpireTime; | |
} | |
/*-----------------------------------------------------------*/ | |
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) | |
{ | |
TickType_t xTimeNow; | |
PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ | |
xTimeNow = xTaskGetTickCount(); | |
if( xTimeNow < xLastTime ) | |
{ | |
prvSwitchTimerLists(); | |
*pxTimerListsWereSwitched = pdTRUE; | |
} | |
else | |
{ | |
*pxTimerListsWereSwitched = pdFALSE; | |
} | |
xLastTime = xTimeNow; | |
return xTimeNow; | |
} | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) | |
{ | |
BaseType_t xProcessTimerNow = pdFALSE; | |
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); | |
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); | |
if( xNextExpiryTime <= xTimeNow ) | |
{ | |
/* Has the expiry time elapsed between the command to start/reset a | |
timer was issued, and the time the command was processed? */ | |
if( ( xTimeNow - xCommandTime ) >= pxTimer->xTimerPeriodInTicks ) | |
{ | |
/* The time between a command being issued and the command being | |
processed actually exceeds the timers period. */ | |
xProcessTimerNow = pdTRUE; | |
} | |
else | |
{ | |
vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); | |
} | |
} | |
else | |
{ | |
if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) | |
{ | |
/* If, since the command was issued, the tick count has overflowed | |
but the expiry time has not, then the timer must have already passed | |
its expiry time and should be processed immediately. */ | |
xProcessTimerNow = pdTRUE; | |
} | |
else | |
{ | |
vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); | |
} | |
} | |
return xProcessTimerNow; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvProcessReceivedCommands( void ) | |
{ | |
DaemonTaskMessage_t xMessage; | |
Timer_t *pxTimer; | |
BaseType_t xTimerListsWereSwitched, xResult; | |
TickType_t xTimeNow; | |
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ | |
{ | |
#if ( INCLUDE_xTimerPendFunctionCall == 1 ) | |
{ | |
/* Negative commands are pended function calls rather than timer | |
commands. */ | |
if( xMessage.xMessageID < 0 ) | |
{ | |
const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); | |
/* The timer uses the xCallbackParameters member to request a | |
callback be executed. Check the callback is not NULL. */ | |
configASSERT( pxCallback ); | |
/* Call the function. */ | |
pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
#endif /* INCLUDE_xTimerPendFunctionCall */ | |
/* Commands that are positive are timer commands rather than pended | |
function calls. */ | |
if( xMessage.xMessageID >= ( BaseType_t ) 0 ) | |
{ | |
/* The messages uses the xTimerParameters member to work on a | |
software timer. */ | |
pxTimer = xMessage.u.xTimerParameters.pxTimer; | |
if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) | |
{ | |
/* The timer is in a list, remove it. */ | |
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); | |
/* In this case the xTimerListsWereSwitched parameter is not used, but | |
it must be present in the function call. prvSampleTimeNow() must be | |
called after the message is received from xTimerQueue so there is no | |
possibility of a higher priority task adding a message to the message | |
queue with a time that is ahead of the timer daemon task (because it | |
pre-empted the timer daemon task after the xTimeNow value was set). */ | |
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); | |
switch( xMessage.xMessageID ) | |
{ | |
case tmrCOMMAND_START : | |
case tmrCOMMAND_START_FROM_ISR : | |
case tmrCOMMAND_RESET : | |
case tmrCOMMAND_RESET_FROM_ISR : | |
case tmrCOMMAND_START_DONT_TRACE : | |
/* Start or restart a timer. */ | |
if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) == pdTRUE ) | |
{ | |
/* The timer expired before it was added to the active | |
timer list. Process it now. */ | |
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); | |
traceTIMER_EXPIRED( pxTimer ); | |
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) | |
{ | |
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); | |
configASSERT( xResult ); | |
( void ) xResult; | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
break; | |
case tmrCOMMAND_STOP : | |
case tmrCOMMAND_STOP_FROM_ISR : | |
/* The timer has already been removed from the active list. | |
There is nothing to do here. */ | |
break; | |
case tmrCOMMAND_CHANGE_PERIOD : | |
case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : | |
pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; | |
configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); | |
/* The new period does not really have a reference, and can be | |
longer or shorter than the old one. The command time is | |
therefore set to the current time, and as the period cannot be | |
zero the next expiry time can only be in the future, meaning | |
(unlike for the xTimerStart() case above) there is no fail case | |
that needs to be handled here. */ | |
( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); | |
break; | |
case tmrCOMMAND_DELETE : | |
/* The timer has already been removed from the active list, | |
just free up the memory. */ | |
poolFreeBuffer((static_pool_t*)&sTimersPool, pxTimer); | |
break; | |
default : | |
/* Don't expect to get here. */ | |
break; | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvSwitchTimerLists( void ) | |
{ | |
TickType_t xNextExpireTime, xReloadTime; | |
List_t *pxTemp; | |
Timer_t *pxTimer; | |
BaseType_t xResult; | |
/* The tick count has overflowed. The timer lists must be switched. | |
If there are any timers still referenced from the current timer list | |
then they must have expired and should be processed before the lists | |
are switched. */ | |
while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) | |
{ | |
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); | |
/* Remove the timer from the list. */ | |
pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); | |
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); | |
traceTIMER_EXPIRED( pxTimer ); | |
/* Execute its callback, then send a command to restart the timer if | |
it is an auto-reload timer. It cannot be restarted here as the lists | |
have not yet been switched. */ | |
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); | |
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) | |
{ | |
/* Calculate the reload value, and if the reload value results in | |
the timer going into the same timer list then it has already expired | |
and the timer should be re-inserted into the current list so it is | |
processed again within this loop. Otherwise a command should be sent | |
to restart the timer to ensure it is only inserted into a list after | |
the lists have been swapped. */ | |
xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); | |
if( xReloadTime > xNextExpireTime ) | |
{ | |
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); | |
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); | |
vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); | |
} | |
else | |
{ | |
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); | |
configASSERT( xResult ); | |
( void ) xResult; | |
} | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
pxTemp = pxCurrentTimerList; | |
pxCurrentTimerList = pxOverflowTimerList; | |
pxOverflowTimerList = pxTemp; | |
} | |
/*-----------------------------------------------------------*/ | |
static struct { | |
DaemonTaskMessage_t messages[configTIMER_QUEUE_LENGTH]; | |
uint8_t extra; | |
} timersQueue; | |
static void prvCheckForValidListAndQueue( void ) | |
{ | |
/* Check that the list from which active timers are referenced, and the | |
queue used to communicate with the timer service, have been | |
initialised. */ | |
taskENTER_CRITICAL(); | |
{ | |
if( xTimerQueue == NULL ) | |
{ | |
vListInitialise( &xActiveTimerList1 ); | |
vListInitialise( &xActiveTimerList2 ); | |
pxCurrentTimerList = &xActiveTimerList1; | |
pxOverflowTimerList = &xActiveTimerList2; | |
xTimerQueue = xQueueCreate( (void*) &timersQueue, ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) ); | |
configASSERT( xTimerQueue ); | |
#if ( configQUEUE_REGISTRY_SIZE > 0 ) | |
{ | |
if( xTimerQueue != NULL ) | |
{ | |
vQueueAddToRegistry( xTimerQueue, "TmrQ" ); | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
#endif /* configQUEUE_REGISTRY_SIZE */ | |
} | |
else | |
{ | |
mtCOVERAGE_TEST_MARKER(); | |
} | |
} | |
taskEXIT_CRITICAL(); | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) | |
{ | |
BaseType_t xTimerIsInActiveList; | |
Timer_t *pxTimer = ( Timer_t * ) xTimer; | |
/* Is the timer in the list of active timers? */ | |
taskENTER_CRITICAL(); | |
{ | |
/* Checking to see if it is in the NULL list in effect checks to see if | |
it is referenced from either the current or the overflow timer lists in | |
one go, but the logic has to be reversed, hence the '!'. */ | |
xTimerIsInActiveList = ( BaseType_t ) !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) ); | |
} | |
taskEXIT_CRITICAL(); | |
return xTimerIsInActiveList; | |
} /*lint !e818 Can't be pointer to const due to the typedef. */ | |
/*-----------------------------------------------------------*/ | |
void *pvTimerGetTimerID( const TimerHandle_t xTimer ) | |
{ | |
Timer_t * const pxTimer = ( Timer_t * ) xTimer; | |
return pxTimer->pvTimerID; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( INCLUDE_xTimerPendFunctionCall == 1 ) | |
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) | |
{ | |
DaemonTaskMessage_t xMessage; | |
BaseType_t xReturn; | |
/* Complete the message with the function parameters and post it to the | |
daemon task. */ | |
xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR; | |
xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; | |
xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; | |
xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; | |
xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); | |
tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); | |
return xReturn; | |
} | |
#endif /* INCLUDE_xTimerPendFunctionCall */ | |
/*-----------------------------------------------------------*/ | |
#if( INCLUDE_xTimerPendFunctionCall == 1 ) | |
BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) | |
{ | |
DaemonTaskMessage_t xMessage; | |
BaseType_t xReturn; | |
/* Complete the message with the function parameters and post it to the | |
daemon task. */ | |
xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK; | |
xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; | |
xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; | |
xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; | |
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); | |
tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); | |
return xReturn; | |
} | |
#endif /* INCLUDE_xTimerPendFunctionCall */ | |
/*-----------------------------------------------------------*/ | |
/* This entire source file will be skipped if the application is not configured | |
to include software timer functionality. If you want to include software timer | |
functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ | |
#endif /* configUSE_TIMERS == 1 */ | |