mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-19 21:11:57 -04:00
Continue work on the new timer implementation. Nearly complete.
This commit is contained in:
parent
07a2021676
commit
b4ff4820cb
|
@ -181,6 +181,10 @@ typedef portBASE_TYPE (*pdTASK_HOOK_CODE)( void * );
|
||||||
#define INCLUDE_xTaskResumeFromISR 1
|
#define INCLUDE_xTaskResumeFromISR 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef configASSERT
|
||||||
|
#define configASSERT( x )
|
||||||
|
#endif
|
||||||
|
|
||||||
/* The timers module relies on xTaskGetSchedulerState(). */
|
/* The timers module relies on xTaskGetSchedulerState(). */
|
||||||
#if configUSE_TIMERS == 1
|
#if configUSE_TIMERS == 1
|
||||||
|
|
||||||
|
|
|
@ -67,11 +67,10 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* IDs for commands that can be sent/received on the timer queue. */
|
/* IDs for commands that can be sent/received on the timer queue. */
|
||||||
#define trmCOMMAND_PROCESS_TIMER_OVERFLOW 0 /* For use by the kernel only! */
|
#define tmrCOMMAND_START 0
|
||||||
#define tmrCOMMAND_START 1
|
#define tmrCOMMAND_STOP 1
|
||||||
#define tmrCOMMAND_STOP 2
|
#define tmrCOMMAND_CHANGE_PERIOD 2
|
||||||
#define tmrCOMMAND_CHANGE_PERIOD 3
|
#define tmrCOMMAND_DELETE 3
|
||||||
#define tmrCOMMAND_DELETE 4
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
* MACROS AND DEFINITIONS
|
* MACROS AND DEFINITIONS
|
||||||
|
|
|
@ -1469,20 +1469,27 @@ signed portBASE_TYPE xReturn;
|
||||||
|
|
||||||
void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait )
|
void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait )
|
||||||
{
|
{
|
||||||
/* This function should not be called by application code hence the
|
/* This function should not be called by application code hence the
|
||||||
'Restricted' in its name. It is not part of the public API. It is designed
|
'Restricted' in its name. It is not part of the public API. It is
|
||||||
for use by kernel code, and has special calling requirements - it should be
|
designed for use by kernel code, and has special calling requirements.
|
||||||
called from a critical section, and then a yield performed after it is
|
It can result in vListInsert() being called on a list that can only
|
||||||
called. Also, the call tree makes use of vListInsert() which should normally
|
possibly ever have one item in it, so the list will be fast, but even
|
||||||
not be called from a critical section - so an assumption is made that the list
|
so it should be called with the scheduler locked and not from a critical
|
||||||
being inserted into is empty and therefore the insertion will be fast. */
|
section. */
|
||||||
|
|
||||||
/* Only do anything if there are no message in the queue. */
|
/* Only do anything if there are no messages in the queue. This function
|
||||||
|
will not actually cause the task to block, just place it on a blocked
|
||||||
|
list. It will not block until the scheduler is unlocked - at which
|
||||||
|
time a yield will be performed. If an item is added to the queue while
|
||||||
|
the queue is locked, and the calling task blocks on the queue, then the
|
||||||
|
calling task will be immediately unblocked when the queue is unlocked. */
|
||||||
|
prvLockQueue( pxQueue );
|
||||||
if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0U )
|
if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0U )
|
||||||
{
|
{
|
||||||
/* There is nothing in the queue, block for the specified period. */
|
/* There is nothing in the queue, block for the specified period. */
|
||||||
vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
|
vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
|
||||||
}
|
}
|
||||||
|
prvUnlockQueue( pxQueue );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -754,7 +754,7 @@ tskTCB *pxTCB = ( tskTCB * ) pxTask;
|
||||||
|
|
||||||
/* This function is not intended to be a public API function and definitely
|
/* This function is not intended to be a public API function and definitely
|
||||||
is not for generic use as it assumes pxTask is not the running task and not
|
is not for generic use as it assumes pxTask is not the running task and not
|
||||||
suspended, does not remove the task from any event lists it might be
|
suspended, does not remove the task from any event lists it might be
|
||||||
blocked on, and does not take care of mutual exclusion. */
|
blocked on, and does not take care of mutual exclusion. */
|
||||||
vListRemove( &( pxTCB->xGenericListItem ) );
|
vListRemove( &( pxTCB->xGenericListItem ) );
|
||||||
prvAddTaskToReadyQueue( pxTCB );
|
prvAddTaskToReadyQueue( pxTCB );
|
||||||
|
@ -1444,19 +1444,13 @@ void vTaskIncrementTick( void )
|
||||||
/* Tick count has overflowed so we need to swap the delay lists.
|
/* Tick count has overflowed so we need to swap the delay lists.
|
||||||
If there are any items in pxDelayedTaskList here then there is
|
If there are any items in pxDelayedTaskList here then there is
|
||||||
an error! */
|
an error! */
|
||||||
|
configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );
|
||||||
|
|
||||||
pxTemp = pxDelayedTaskList;
|
pxTemp = pxDelayedTaskList;
|
||||||
pxDelayedTaskList = pxOverflowDelayedTaskList;
|
pxDelayedTaskList = pxOverflowDelayedTaskList;
|
||||||
pxOverflowDelayedTaskList = pxTemp;
|
pxOverflowDelayedTaskList = pxTemp;
|
||||||
xNumOfOverflows++;
|
xNumOfOverflows++;
|
||||||
|
|
||||||
#if configUSE_TIMERS == 1
|
|
||||||
{
|
|
||||||
/* The timer service task needs to know to switch its lists
|
|
||||||
too. */
|
|
||||||
xTimerGenericCommand( NULL, trmCOMMAND_PROCESS_TIMER_OVERFLOW, 0, 0 );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
|
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
|
||||||
{
|
{
|
||||||
/* The delayed list is empty. Set xNextTaskUnblockTime to the
|
/* The delayed list is empty. Set xNextTaskUnblockTime to the
|
||||||
|
@ -1756,9 +1750,9 @@ portTickType xTimeToWake;
|
||||||
{
|
{
|
||||||
portTickType xTimeToWake;
|
portTickType xTimeToWake;
|
||||||
|
|
||||||
/* This function should not be called by application code hence the
|
/* This function should not be called by application code hence the
|
||||||
'Restricted' in its name. It is not part of the public API. It is
|
'Restricted' in its name. It is not part of the public API. It is
|
||||||
designed for use by kernel code, and has special calling requirements -
|
designed for use by kernel code, and has special calling requirements -
|
||||||
it should be called from a critical section. */
|
it should be called from a critical section. */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1769,7 +1763,7 @@ portTickType xTimeToWake;
|
||||||
vListInsertEnd( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) );
|
vListInsertEnd( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) );
|
||||||
|
|
||||||
/* We must remove this task from the ready list before adding it to the
|
/* We must remove this task from the ready list before adding it to the
|
||||||
blocked list as the same list item is used for both lists. This
|
blocked list as the same list item is used for both lists. This
|
||||||
function is called form a critical section. */
|
function is called form a critical section. */
|
||||||
vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
|
vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
|
||||||
|
|
||||||
|
|
308
Source/timers.c
308
Source/timers.c
|
@ -117,25 +117,45 @@ static void prvTimerTask( void *pvParameters ) PRIVILEGED_FUNCTION;
|
||||||
* Called by the timer service task to interpret and process a command it
|
* Called by the timer service task to interpret and process a command it
|
||||||
* received on the timer queue.
|
* received on the timer queue.
|
||||||
*/
|
*/
|
||||||
static void prvProcessReceivedCommands( portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
|
static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert the timer into either xActiveTimerList1, or xActiveTimerList2,
|
* Insert the timer into either xActiveTimerList1, or xActiveTimerList2,
|
||||||
* depending on if the expire time causes a timer counter overflow.
|
* depending on if the expire time causes a timer counter overflow.
|
||||||
*/
|
*/
|
||||||
static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
|
static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An active timer has reached its expire time. Reload the timer if it is an
|
* An active timer has reached its expire time. Reload the timer if it is an
|
||||||
* auto reload timer, then call its callback.
|
* auto reload timer, then call its callback.
|
||||||
*/
|
*/
|
||||||
static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
|
static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The tick count has overflowed. Switch the timer lists after ensuring the
|
* The tick count has overflowed. Switch the timer lists after ensuring the
|
||||||
* current timer list does not still reference some timers.
|
* current timer list does not still reference some timers.
|
||||||
*/
|
*/
|
||||||
static void prvSwitchTimerLists( portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
|
static void prvSwitchTimerLists( portTickType xTimeNow, portTickType xLastTime ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE
|
||||||
|
* if a tick count overflow occurred since prvSampleTimeNow() was last called.
|
||||||
|
*/
|
||||||
|
static portTickType prvSampleTimeNow( portBASE_TYPE *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 portTickType prvLookForExpiredTimer( portBASE_TYPE *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( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -151,7 +171,7 @@ portBASE_TYPE xReturn = pdFAIL;
|
||||||
|
|
||||||
if( xTimerQueue != NULL )
|
if( xTimerQueue != NULL )
|
||||||
{
|
{
|
||||||
xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Timer Service", configTIMER_TASK_STACK_DEPTH, NULL, configTIMER_TASK_PRIORITY, NULL );
|
xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Tmr Svc", configTIMER_TASK_STACK_DEPTH, NULL, configTIMER_TASK_PRIORITY, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
|
@ -183,7 +203,7 @@ xTIMER *pxNewTimer;
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portTickType xBlockTime )
|
portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portBASE_TYPE *pxHigherPriorityTaskWoken, portTickType xBlockTime )
|
||||||
{
|
{
|
||||||
portBASE_TYPE xReturn = pdFAIL;
|
portBASE_TYPE xReturn = pdFAIL;
|
||||||
xTIMER_MESSAGE xMessage;
|
xTIMER_MESSAGE xMessage;
|
||||||
|
@ -197,13 +217,20 @@ xTIMER_MESSAGE xMessage;
|
||||||
xMessage.xMessageValue = xOptionalValue;
|
xMessage.xMessageValue = xOptionalValue;
|
||||||
xMessage.pxTimer = ( xTIMER * ) xTimer;
|
xMessage.pxTimer = ( xTIMER * ) xTimer;
|
||||||
|
|
||||||
if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
|
if( pxHigherPriorityTaskWoken == NULL )
|
||||||
{
|
{
|
||||||
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );
|
if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
|
||||||
|
{
|
||||||
|
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
|
xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,128 +238,155 @@ xTIMER_MESSAGE xMessage;
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xAssumedTimeNow )
|
static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow )
|
||||||
{
|
{
|
||||||
xTIMER *pxTimer;
|
xTIMER *pxTimer;
|
||||||
|
|
||||||
if( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
|
/* Remove the timer from the list of active timers. A check has already
|
||||||
|
been performed to ensure the list is not empty. */
|
||||||
|
pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
|
||||||
|
vListRemove( &( pxTimer->xTimerListItem ) );
|
||||||
|
|
||||||
|
/* 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 == pdTRUE )
|
||||||
{
|
{
|
||||||
/* Remove the timer from the list of active timers. */
|
/* This is the only time a timer is inserted into a list using
|
||||||
pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
|
a time relative to anything other than the current time. It
|
||||||
vListRemove( &( pxTimer->xTimerListItem ) );
|
will therefore be inserted into the correct list relative to
|
||||||
|
the time this task thinks it is now, even if a command to
|
||||||
/* If the timer is an auto reload timer then calculate the next
|
switch lists due to a tick count overflow is already waiting in
|
||||||
expiry time and re-insert the timer in the list of active timers. */
|
the timer queue. */
|
||||||
if( pxTimer->uxAutoReload == pdTRUE )
|
prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow );
|
||||||
{
|
|
||||||
/* This is the only time a 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, even if a command to
|
|
||||||
switch lists due to a tick count overflow is already waiting in
|
|
||||||
the timer queue. */
|
|
||||||
prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xAssumedTimeNow );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call the timer callback. */
|
|
||||||
pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Call the timer callback. */
|
||||||
|
pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer );
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
static void prvTimerTask( void *pvParameters )
|
static void prvTimerTask( void *pvParameters )
|
||||||
{
|
{
|
||||||
portTickType xNextExpireTime, xTimeNow, xFrozenTimeNow;
|
portTickType xNextExpireTime;
|
||||||
|
portBASE_TYPE xListWasEmpty;
|
||||||
|
|
||||||
/* Just to avoid compiler warnings. */
|
/* Just to avoid compiler warnings. */
|
||||||
( void ) pvParameters;
|
( void ) pvParameters;
|
||||||
|
|
||||||
for( ;; )
|
for( ;; )
|
||||||
{
|
{
|
||||||
/* Take a snapshot of the time to use while assessing expiry and auto
|
/* Query the timers list to see if it contains any timers, and if so,
|
||||||
reload times. */
|
obtain the time at which the next timer will expire. */
|
||||||
xFrozenTimeNow = xTaskGetTickCount();
|
xNextExpireTime = prvLookForExpiredTimer( &xListWasEmpty );
|
||||||
|
|
||||||
/* Timers are listed in expiry time order, with the head of the list
|
/* If a timer has expired, process it. Otherwise, block this task
|
||||||
referencing the task that will expire first. Obtain the time at which
|
until either a timer does expire, or a command is received. */
|
||||||
the timer with the nearest expiry time will expire. If there are no
|
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );
|
||||||
active timers then just set the next expire time to the maximum possible
|
|
||||||
time to ensure this task does not run unnecessarily. */
|
/* Empty the command queue. */
|
||||||
if( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
|
prvProcessReceivedCommands();
|
||||||
{
|
|
||||||
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xNextExpireTime = portMAX_DELAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Has the timer expired? This expiry time is relative to the snapshot
|
|
||||||
of the time taken to be used in this loop iteration - so it doesn't
|
|
||||||
matter at this point if a tick count overflows here. */
|
|
||||||
if( xNextExpireTime <= xFrozenTimeNow )
|
|
||||||
{
|
|
||||||
prvProcessExpiredTimer( xNextExpireTime, xFrozenTimeNow );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Block this task until the next timer expires, or a command is
|
|
||||||
received. */
|
|
||||||
vTaskSuspendAll();
|
|
||||||
{
|
|
||||||
/* Has the tick overflowed since a time snapshot was taken? */
|
|
||||||
xTimeNow = xTaskGetTickCount();
|
|
||||||
if( xTimeNow >= xFrozenTimeNow )
|
|
||||||
{
|
|
||||||
/* Has the expire not still not been met? The tick count
|
|
||||||
may be greater now than when the time snapshot was taken. */
|
|
||||||
if( xNextExpireTime <= xTimeNow )
|
|
||||||
{
|
|
||||||
prvProcessExpiredTimer( xNextExpireTime, xFrozenTimeNow );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The tick count has not overflowed since the time
|
|
||||||
snapshot, and the next expire time has not been reached
|
|
||||||
since the last snapshot was taken. This task should
|
|
||||||
therefore block to wait for the next expire time. */
|
|
||||||
vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The tick count has overflowed since the time snapshot
|
|
||||||
was taken, therefore, the task should not block but continue
|
|
||||||
with another loop. The command queue should contain a
|
|
||||||
command to switch lists. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 just return to the same
|
|
||||||
task. */
|
|
||||||
portYIELD_WITHIN_API();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Take a snapshot of the time now for use in this iteration of the
|
|
||||||
task loop. */
|
|
||||||
xFrozenTimeNow = xTaskGetTickCount();
|
|
||||||
|
|
||||||
/* Empty the command queue, if it contains any commands. */
|
|
||||||
prvProcessReceivedCommands( xFrozenTimeNow );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xAssumedTimeNow )
|
static void prvProcessTimerOrBlockTask( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty )
|
||||||
|
{
|
||||||
|
portTickType xTimeNow;
|
||||||
|
portBASE_TYPE 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
|
||||||
|
prvSampelTimeNow() function. */
|
||||||
|
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
|
||||||
|
if( xTimerListsWereSwitched == pdFALSE )
|
||||||
|
{
|
||||||
|
/* The tick count has not overflowed, has the timer expired? */
|
||||||
|
if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static portTickType prvLookForExpiredTimer( portBASE_TYPE *pxListWasEmpty )
|
||||||
|
{
|
||||||
|
portTickType 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 = ( portTickType ) 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xNextExpireTime;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static portTickType prvSampleTimeNow( portBASE_TYPE *pxTimerListsWereSwitched )
|
||||||
|
{
|
||||||
|
portTickType xTimeNow;
|
||||||
|
static portTickType xLastTime = ( portTickType ) 0U;
|
||||||
|
|
||||||
|
xTimeNow = xTaskGetTickCount();
|
||||||
|
|
||||||
|
if( xTimeNow < xLastTime )
|
||||||
|
{
|
||||||
|
prvSwitchTimerLists( xTimeNow, xLastTime );
|
||||||
|
*pxTimerListsWereSwitched = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pxTimerListsWereSwitched = pdFALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
xLastTime = xTimeNow;
|
||||||
|
|
||||||
|
return xTimeNow;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow )
|
||||||
{
|
{
|
||||||
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );
|
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );
|
||||||
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
|
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
|
||||||
|
|
||||||
if( xNextExpiryTime < xAssumedTimeNow )
|
if( xNextExpiryTime < xTimeNow )
|
||||||
{
|
{
|
||||||
vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );
|
vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );
|
||||||
}
|
}
|
||||||
|
@ -343,11 +397,16 @@ static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpir
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
static void prvProcessReceivedCommands( portTickType xAssumedTimeNow )
|
static void prvProcessReceivedCommands( void )
|
||||||
{
|
{
|
||||||
xTIMER_MESSAGE xMessage;
|
xTIMER_MESSAGE xMessage;
|
||||||
xTIMER *pxTimer;
|
xTIMER *pxTimer;
|
||||||
portBASE_TYPE xSwitchListsOnExit = pdFALSE;
|
portBASE_TYPE xTimerListsWereSwitched;
|
||||||
|
portTickType xTimeNow;
|
||||||
|
|
||||||
|
/* In this case the xTimerListsWereSwitched parameter is not used, but it
|
||||||
|
must be present in the function call. */
|
||||||
|
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
|
||||||
|
|
||||||
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )
|
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )
|
||||||
{
|
{
|
||||||
|
@ -369,7 +428,7 @@ portBASE_TYPE xSwitchListsOnExit = pdFALSE;
|
||||||
{
|
{
|
||||||
case tmrCOMMAND_START :
|
case tmrCOMMAND_START :
|
||||||
/* Start or restart a timer. */
|
/* Start or restart a timer. */
|
||||||
prvInsertTimerInActiveList( pxTimer, xAssumedTimeNow + pxTimer->xTimerPeriodInTicks, xAssumedTimeNow );
|
prvInsertTimerInActiveList( pxTimer, xTimeNow + pxTimer->xTimerPeriodInTicks, xTimeNow );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tmrCOMMAND_STOP :
|
case tmrCOMMAND_STOP :
|
||||||
|
@ -379,7 +438,7 @@ portBASE_TYPE xSwitchListsOnExit = pdFALSE;
|
||||||
|
|
||||||
case tmrCOMMAND_CHANGE_PERIOD :
|
case tmrCOMMAND_CHANGE_PERIOD :
|
||||||
pxTimer->xTimerPeriodInTicks = xMessage.xMessageValue;
|
pxTimer->xTimerPeriodInTicks = xMessage.xMessageValue;
|
||||||
prvInsertTimerInActiveList( pxTimer, ( xAssumedTimeNow + pxTimer->xTimerPeriodInTicks ), xAssumedTimeNow );
|
prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tmrCOMMAND_DELETE :
|
case tmrCOMMAND_DELETE :
|
||||||
|
@ -387,39 +446,32 @@ portBASE_TYPE xSwitchListsOnExit = pdFALSE;
|
||||||
just free up the memory. */
|
just free up the memory. */
|
||||||
vPortFree( pxTimer );
|
vPortFree( pxTimer );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case trmCOMMAND_PROCESS_TIMER_OVERFLOW :
|
|
||||||
/* Hold this pending until all the other messages have been
|
|
||||||
processed. */
|
|
||||||
xSwitchListsOnExit = pdTRUE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
default :
|
||||||
/* Don't expect to get here. */
|
/* Don't expect to get here. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( xSwitchListsOnExit == pdTRUE )
|
|
||||||
{
|
|
||||||
prvSwitchTimerLists( xAssumedTimeNow );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
static void prvSwitchTimerLists( portTickType xAssumedTimeNow )
|
static void prvSwitchTimerLists( portTickType xTimeNow, portTickType xLastTime )
|
||||||
{
|
{
|
||||||
portTickType xNextExpireTime;
|
portTickType xNextExpireTime;
|
||||||
xList *pxTemp;
|
xList *pxTemp;
|
||||||
|
|
||||||
/* The tick count has overflowed. The timer lists must be switched.
|
/* Remove compiler warnings if configASSERT() is not defined. */
|
||||||
If there are any timers still referenced from the current timer list
|
( void ) xLastTime;
|
||||||
then they must have expired and should be processed before the lists
|
|
||||||
|
/* 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. */
|
are switched. */
|
||||||
while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
|
while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
|
||||||
{
|
{
|
||||||
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
|
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
|
||||||
prvProcessExpiredTimer( xNextExpireTime, xAssumedTimeNow );
|
configASSERT( ( xNextExpireTime >= xLastTime ) );
|
||||||
|
prvProcessExpiredTimer( xNextExpireTime, xTimeNow );
|
||||||
}
|
}
|
||||||
|
|
||||||
pxTemp = pxCurrentTimerList;
|
pxTemp = pxCurrentTimerList;
|
||||||
|
|
Loading…
Reference in a new issue