]> git.sur5r.net Git - freertos/blobdiff - Source/timers.c
Add some tick interrupt overflow protection to the timers module. This is not tested...
[freertos] / Source / timers.c
index e28b78f3adebceb7ab805ea84995fc2ea75223bd..b153fd9e281df807b7572d373fa5dc146c5a7c5a 100644 (file)
@@ -1,4 +1,7 @@
-/* Need a method of switching to an overflow list. _RB_*/\r
+/* Need to consider the switching of timer lists, and the placement of tasks into\r
+the current and overflow timer lists very carefully.  For example, should the\r
+assessment as to which list a timer should be inserted into be relative the the\r
+tick count at the timer, or the tick count when the timer task unblocked, etc. */\r
 \r
 /*\r
     FreeRTOS V6.1.1 - Copyright (C) 2011 Real Time Engineers Ltd.\r
@@ -65,11 +68,8 @@ task.h is included from an application file. */
 \r
 #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
 \r
-/* IDs for commands that can be sent/received on the timer queue. */\r
-#define tmrSTART               0\r
-\r
 /* Misc definitions. */\r
-#define timerNO_DELAY  ( portTickType ) 0U\r
+#define tmrNO_DELAY            ( portTickType ) 0U\r
 \r
 /* The definition of the timers themselves. */\r
 typedef struct tmrTimerControl\r
@@ -82,20 +82,23 @@ typedef struct tmrTimerControl
        tmrTIMER_CALLBACK               pxCallbackFunction;     /*<< The function that will be called when the timer expires. */\r
 } xTIMER;\r
 \r
-/* The definition of messages that can be sent and received on the timer \r
+/* The definition of messages that can be sent and received on the timer\r
 queue. */\r
 typedef struct tmrTimerQueueMessage\r
 {\r
-       portBASE_TYPE                   xMessageID;\r
-       portTickType                    xMessageValue;\r
-       xTIMER *                                pxTimer;\r
+       portBASE_TYPE                   xMessageID;                     /*<< The command being sent to the timer service task. */\r
+       portTickType                    xMessageValue;          /*<< An optional value used by a subset of commands, for example, when chaning the period of a timer. */\r
+       xTIMER *                                pxTimer;                        /*<< The timer to which the command will be applied. */\r
 } xTIMER_MESSAGE;\r
 \r
 \r
 /* The list in which active timers are stored.  Timers are referenced in expire\r
 time order, with the nearest expiry time at the front of the list.  Only the\r
 timer service task is allowed to access xActiveTimerList. */\r
-PRIVILEGED_DATA static xList xActiveTimerList;\r
+PRIVILEGED_DATA static xList xActiveTimerList1;\r
+PRIVILEGED_DATA static xList xActiveTimerList2;\r
+PRIVILEGED_DATA static xList *pxCurrentTimerList;\r
+PRIVILEGED_DATA static xList *pxOverflowTimerList;\r
 \r
 /* A queue that is used to send commands to the timer service task. */\r
 PRIVILEGED_DATA static xQueueHandle xTimerQueue = NULL;\r
@@ -103,21 +106,29 @@ PRIVILEGED_DATA static xQueueHandle xTimerQueue = NULL;
 /*-----------------------------------------------------------*/\r
 \r
 /*\r
- * Called when a timer is about to be modified.  If the timer is already in the\r
- * list of active timers then it is removed prior to the modification.\r
+ * Initialise the infrustructure used by the timer service task if it has not\r
+ * been initialised already.\r
  */\r
-static void prvRemoveTimerFromActiveList( xTIMER *pxTimer ) PRIVILEGED_FUNCTION;\r
-\r
 static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION;\r
 \r
 /*\r
- * The timer service task (daemon).\r
+ * The timer service task (daemon).  Timer functionality is controlled by this\r
+ * task.  Other tasks communicate with the timer service task using the\r
+ * xTimerQueue queue.\r
  */\r
 static void prvTimerTask( void *pvParameters ) PRIVILEGED_FUNCTION;\r
 \r
+/*\r
+ * Called by the timer service task to interpret and process a command it\r
+ * received on the timer queue.\r
+ */\r
+static void    prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION;\r
 \r
-/* Handlers for commands received on the timer queue. */\r
-static void prvTimerStart( xTIMER *pxTimer );\r
+/*\r
+ * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, \r
+ * depending on if the expire time causes a timer counter overflow. \r
+ */\r
+static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime );\r
 \r
 /*-----------------------------------------------------------*/\r
 \r
@@ -125,13 +136,15 @@ portBASE_TYPE xTimerCreateTimerTask( void )
 {\r
 portBASE_TYPE xReturn = pdFAIL;\r
 \r
-       /* This function is called when the scheduler is started if \r
-       configUSE_TIMERS is set to 1. */\r
+       /* This function is called when the scheduler is started if\r
+       configUSE_TIMERS is set to 1.  Check that the infrustructure used by the\r
+       timer service task has been created/initialised.  If timers have already\r
+       been created then the initialisation will already have been performed. */\r
        prvCheckForValidListAndQueue();\r
 \r
        if( xTimerQueue != NULL )\r
        {\r
-               xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Timers", configMINIMAL_STACK_SIZE, NULL, configTIMER_TASK_PRIORITY, NULL );\r
+               xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Timer Service", configTIMER_TASK_STACK_DEPTH, NULL, configTIMER_TASK_PRIORITY, NULL );\r
        }\r
 \r
        return xReturn;\r
@@ -146,9 +159,11 @@ xTIMER *pxNewTimer;
        pxNewTimer = ( xTIMER * ) pvPortMalloc( sizeof( xTIMER ) );\r
        if( pxNewTimer != NULL )\r
        {\r
+               /* Ensure the infrustructure used by the timer service task has been\r
+               created/initialised. */\r
                prvCheckForValidListAndQueue();\r
 \r
-               /* Initialise the timer structure members. */\r
+               /* Initialise the timer structure members using the function parameters. */\r
                pxNewTimer->pcTimerName = pcTimerName;\r
                pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;\r
                pxNewTimer->uxAutoReload = uxAutoReload;\r
@@ -161,74 +176,70 @@ xTIMER *pxNewTimer;
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-portBASE_TYPE xTimerStart( xTimerHandle xTimer, portTickType xBlockTime )\r
+portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portTickType xBlockTime )\r
 {\r
 portBASE_TYPE xReturn = pdFAIL;\r
 xTIMER_MESSAGE xMessage;\r
 \r
+       /* Send a message to the timer service task to perform a particular action\r
+       on a particular timer definition. */\r
        if( xTimerQueue != NULL )\r
        {\r
-               xMessage.xMessageID = tmrSTART;\r
+               /* Send a command to the timer service task to start the xTimer timer. */\r
+               xMessage.xMessageID = xCommandID;\r
+               xMessage.xMessageValue = xOptionalValue;\r
                xMessage.pxTimer = ( xTIMER * ) xTimer;\r
 \r
-               xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );\r
+               if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )\r
+               {\r
+                       xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );\r
+               }\r
+               else\r
+               {\r
+                       xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );\r
+               }\r
        }\r
 \r
        return xReturn;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-void *pvTimerGetTimerID( xTimerHandle xTimer )\r
-{\r
-xTIMER *pxTimer = ( xTIMER * ) xTimer;\r
-\r
-       return pxTimer->pvTimerID;\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
-static void prvRemoveTimerFromActiveList( xTIMER *pxTimer )\r
-{\r
-       /* Is the timer already in the list of active timers? */\r
-       if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE )\r
-       {\r
-               /* The timer is in the list, remove it. */\r
-               vListRemove( &( pxTimer->xTimerListItem ) );\r
-       }\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
 static void prvTimerTask( void *pvParameters )\r
 {\r
-portTickType xNextWakeTime, xTimeNow;\r
+portTickType xNextExpireTime, xTimeNow;\r
 xTIMER *pxTimer;\r
-xTIMER_MESSAGE xMessage;\r
 \r
        /* Just to avoid compiler warnings. */\r
        ( void ) pvParameters;\r
 \r
        for( ;; )\r
        {\r
-               if( listLIST_IS_EMPTY( &xActiveTimerList ) == pdFALSE )\r
+               /* Timers are listed in expiry time order, with the head of the list\r
+               referencing the task that will expire first.  Obtain the time at which\r
+               the timer with the nearest expiry time will expire.  If there are no\r
+               active timers then just set the next expire time to the maximum possible\r
+               time to ensure this task does not run unnecessarily. */\r
+               if( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )\r
                {\r
-                       xNextWakeTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( &xActiveTimerList );\r
+                       xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );\r
                }\r
                else\r
                {\r
-                       xNextWakeTime = portMAX_DELAY;\r
+                       xNextExpireTime = portMAX_DELAY;\r
                }\r
 \r
-               if( xNextWakeTime <= xTaskGetTickCount() )\r
+               /* Has the timer expired? */\r
+               if( xNextExpireTime <= xTaskGetTickCount() )\r
                {\r
-                       /* Remove the timer from the list.  This functionality relies on\r
-                       the list of active timers not being accessed from outside of this\r
-                       task. */\r
-                       pxTimer = listGET_OWNER_OF_HEAD_ENTRY( &xActiveTimerList );\r
+                       /* Remove the timer from the list of active timers. */\r
+                       pxTimer = listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );\r
                        vListRemove( &( pxTimer->xTimerListItem ) );\r
 \r
+                       /* If the timer is an autoreload timer then calculate the next\r
+                       expiry time and re-insert the timer in the list of active timers. */\r
                        if( pxTimer->uxAutoReload == pdTRUE )\r
                        {\r
-                               listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), ( xNextWakeTime + pxTimer->xTimerPeriodInTicks ) );\r
-                               vListInsert( &xActiveTimerList, &( pxTimer->xTimerListItem ) );\r
+                               prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ) );                              \r
                        }\r
 \r
                        /* Call the timer callback. */\r
@@ -236,29 +247,106 @@ xTIMER_MESSAGE xMessage;
                }\r
                else\r
                {\r
-                       /* Calculate the block time. */\r
+                       /* Block this task until the next timer expires, or a command is\r
+                       received. */\r
                        taskENTER_CRITICAL();\r
                        {\r
                                xTimeNow = xTaskGetTickCount();\r
-                               if( xTimeNow < xNextWakeTime )\r
+                               if( xTimeNow < xNextExpireTime )\r
                                {\r
-                                       vQueueWaitForMessageRestricted( xTimerQueue, ( xNextWakeTime - xTimeNow ) );\r
+                                       /* This is a simple fast function - a yield will not be\r
+                                       performed until after this critical section exits. */\r
+                                       vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) );\r
                                }\r
                        }\r
                        taskEXIT_CRITICAL();\r
+\r
+                       /* Yield to wait for either a command to arrive, or the block time\r
+                       to expire.  If a command arrived between the critical section being\r
+                       exited and this yeild then the yield will just return to the same\r
+                       task. */\r
                        portYIELD_WITHIN_API();\r
 \r
-                       while( xQueueReceive( xTimerQueue, &xMessage, timerNO_DELAY ) != pdFAIL )\r
+                       /* Empty the command queue, if it contains any commands. */\r
+                       prvProcessReceivedCommands();\r
+               }\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime )\r
+{\r
+       listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );\r
+       listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );\r
+       \r
+       if( xNextExpiryTime < xTaskGetTickCount() )\r
+       {\r
+               vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );\r
+       }\r
+       else\r
+       {\r
+               vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void    prvProcessReceivedCommands( void )\r
+{\r
+xTIMER_MESSAGE xMessage;\r
+xTIMER *pxTimer;\r
+xList *pxTemp;\r
+\r
+       while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )\r
+       {\r
+               pxTimer = xMessage.pxTimer;\r
+\r
+               /* Is the timer already in the list of active timers?  When the command\r
+               is trmCOMMAND_PROCESS_TIMER_OVERFLOW, the timer will be NULL as the\r
+               command is to the task rather than to an individual timer. */\r
+               if( pxTimer != NULL )\r
+               {\r
+                       if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE )\r
                        {\r
-                               switch( xMessage.xMessageID )\r
-                               {\r
-                                       case tmrSTART   :       prvTimerStart( xMessage.pxTimer );\r
-                                                                               break;\r
-                                       default                 :       /* Don't expect to get here. */\r
-                                                                               break;\r
-                               }\r
+                               /* The timer is in the list, remove it. */\r
+                               vListRemove( &( pxTimer->xTimerListItem ) );\r
                        }\r
                }\r
+\r
+               switch( xMessage.xMessageID )\r
+               {\r
+                       case tmrCOMMAND_START : \r
+                               /* Start or restart a timer. */\r
+                               prvInsertTimerInActiveList( pxTimer,  xTaskGetTickCount() + pxTimer->xTimerPeriodInTicks );\r
+                               break;\r
+\r
+                       case tmrCOMMAND_STOP :  \r
+                               /* The timer has already been removed from the active list.\r
+                               There is nothing to do here. */\r
+                               break;\r
+\r
+                       case tmrCOMMAND_CHANGE_PERIOD :\r
+                               pxTimer->xTimerPeriodInTicks = xMessage.xMessageValue;\r
+                               prvInsertTimerInActiveList( pxTimer, ( xTaskGetTickCount() + pxTimer->xTimerPeriodInTicks ) );\r
+                               break;\r
+\r
+                       case tmrCOMMAND_DELETE :\r
+                               /* The timer has already been removed from the active list,\r
+                               just free up the memory. */\r
+                               vPortFree( pxTimer );\r
+                               break;\r
+                               \r
+                       case trmCOMMAND_PROCESS_TIMER_OVERFLOW :\r
+                               /* The tick count has overflowed.  The timer lists must be\r
+                               switched. */\r
+                               pxTemp = pxCurrentTimerList;\r
+                               pxCurrentTimerList = pxOverflowTimerList;\r
+                               pxOverflowTimerList = pxTemp;\r
+                               break;\r
+\r
+                       default :                       \r
+                               /* Don't expect to get here. */\r
+                               break;\r
+               }\r
        }\r
 }\r
 /*-----------------------------------------------------------*/\r
@@ -266,13 +354,16 @@ xTIMER_MESSAGE xMessage;
 static void prvCheckForValidListAndQueue( void )\r
 {\r
        /* Check that the list from which active timers are referenced, and the\r
-       queue used to communicate with the timer service, have been \r
+       queue used to communicate with the timer service, have been\r
        initialised. */\r
        taskENTER_CRITICAL();\r
        {\r
                if( xTimerQueue == NULL )\r
                {\r
-                       vListInitialise( &xActiveTimerList );\r
+                       vListInitialise( &xActiveTimerList1 );\r
+                       vListInitialise( &xActiveTimerList2 );\r
+                       pxCurrentTimerList = &xActiveTimerList1;\r
+                       pxOverflowTimerList = &xActiveTimerList2;\r
                        xTimerQueue = xQueueCreate( configTIMER_QUEUE_LENGTH, sizeof( xTIMER_MESSAGE ) );\r
                }\r
        }\r
@@ -280,46 +371,30 @@ static void prvCheckForValidListAndQueue( void )
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-static void prvTimerStart( xTIMER *pxTimer )\r
+portBASE_TYPE xTimerIsTimerActive( xTimerHandle xTimer )\r
 {\r
-portTickType xTimeToWake;\r
+portBASE_TYPE xTimerIsInActiveList;\r
+xTIMER *pxTimer = ( xTIMER * ) xTimer;\r
 \r
-       if( pxTimer != NULL )\r
+       /* Is the timer in the list of active timers? */\r
+       taskENTER_CRITICAL();\r
        {\r
-               /* Is the timer already in the list of active timers? */\r
-               prvRemoveTimerFromActiveList( pxTimer );\r
-\r
-               xTimeToWake = xTaskGetTickCount() + pxTimer->xTimerPeriodInTicks;\r
-               listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xTimeToWake );\r
-               listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );\r
-               vListInsert( &xActiveTimerList, &( pxTimer->xTimerListItem ) );\r
+               /* Checking to see if it is in the NULL list in effect checks to see if\r
+               it is referenced from either the current or the overflow timer lists in\r
+               one go, but the logic has to be reversed, hence the '!'. */\r
+               xTimerIsInActiveList = !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) );\r
        }\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-portBASE_TYPE xTimerIsTimerActive( xTimerHandle xTimer )\r
-{\r
-       return pdFALSE;\r
-}\r
+       taskEXIT_CRITICAL();\r
 \r
-void vTimerStop( xTimerHandle xTimer )\r
-{\r
+       return xTimerIsInActiveList;\r
 }\r
+/*-----------------------------------------------------------*/\r
 \r
-\r
-void vTimerChangePeriod( xTimerHandle xTimer )\r
+void *pvTimerGetTimerID( xTimerHandle xTimer )\r
 {\r
-}\r
+xTIMER *pxTimer = ( xTIMER * ) xTimer;\r
 \r
-void vTimerDelete( xTimerHandle xTimer )\r
-{\r
+       return pxTimer->pvTimerID;\r
 }\r
 /*-----------------------------------------------------------*/\r
+\r