Explorar el Código

Scheduler control functions (#20)

* Scheduler control functions
Co-authored-by: Man, Jianting (Meco) <920369182@qq.com>
tangzz98 hace 3 años
padre
commit
08ecb10961

+ 3 - 0
FreeRTOS/include/FreeRTOS.h

@@ -1136,6 +1136,9 @@ typedef struct xSTATIC_TCB
         uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
         uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
     #endif
+    #if ( INCLUDE_xTaskAbortDelay == 1 )
+        uint8_t ucDelayAborted;
+    #endif
 } StaticTask_t;
 
 typedef struct

+ 1 - 0
FreeRTOS/include/FreeRTOSConfig.h

@@ -141,5 +141,6 @@ standard names. */
 #define INCLUDE_eTaskGetState                   1
 #define configMAX_PRIORITIES                    ( 32 )
 #define configUSE_TIMERS                        1
+#define INCLUDE_xTaskGetSchedulerState          1
 
 #endif /* FREERTOS_CONFIG_H */

+ 1 - 7
FreeRTOS/include/portable.h

@@ -105,13 +105,7 @@ void vPortInitialiseBlocks( void );
 size_t xPortGetFreeHeapSize( void );
 size_t xPortGetMinimumEverFreeHeapSize( void );
 
-#if ( configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1 )
-    void * pvPortMallocStack( size_t xSize );
-    void vPortFreeStack( void * pv );
-#else
-    #define pvPortMallocStack    pvPortMalloc
-    #define vPortFreeStack       vPortFree
-#endif
+void vPortEndScheduler( void );
 
 /* *INDENT-OFF* */
 #ifdef __cplusplus

+ 226 - 9
FreeRTOS/include/task.h

@@ -82,6 +82,17 @@ typedef struct tskTaskControlBlock * TaskHandle_t;
  */
 typedef BaseType_t (* TaskHookFunction_t)( void * );
 
+/* Task states returned by eTaskGetState. */
+typedef enum
+{
+    eRunning = 0, /* A task is querying the state of itself, so must be running. */
+    eReady,       /* The task being queried is in a ready or pending ready list. */
+    eBlocked,     /* The task being queried is in the Blocked state. */
+    eSuspended,   /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
+    eDeleted,     /* The task being queried has been deleted, but its TCB has not yet been freed. */
+    eInvalid      /* Used as an 'invalid state' value. */
+} eTaskState;
+
 /* Actions that can be performed when vTaskNotify() is called. */
 typedef enum
 {
@@ -92,16 +103,14 @@ typedef enum
     eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */
 } eNotifyAction;
 
-/* Task states returned by eTaskGetState. */
-typedef enum
+/*
+ * Used internally only.
+ */
+typedef struct xTIME_OUT
 {
-    eRunning = 0, /* A task is querying the state of itself, so must be running. */
-    eReady,       /* The task being queried is in a ready or pending ready list. */
-    eBlocked,     /* The task being queried is in the Blocked state. */
-    eSuspended,   /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
-    eDeleted,     /* The task being queried has been deleted, but its TCB has not yet been freed. */
-    eInvalid      /* Used as an 'invalid state' value. */
-} eTaskState;
+    BaseType_t xOverflowCount;
+    TickType_t xTimeOnEntering;
+} TimeOut_t;
 
 /**
  * task. h
@@ -163,6 +172,13 @@ typedef enum
  */
 #define taskENABLE_INTERRUPTS()            portENABLE_INTERRUPTS()
 
+/* Definitions returned by xTaskGetSchedulerState().  taskSCHEDULER_SUSPENDED is
+ * 0 to generate more optimal code when configASSERT() is defined as the constant
+ * is used in assert() statements. */
+#define taskSCHEDULER_SUSPENDED      ( ( BaseType_t ) 0 )
+#define taskSCHEDULER_NOT_STARTED    ( ( BaseType_t ) 1 )
+#define taskSCHEDULER_RUNNING        ( ( BaseType_t ) 2 )
+
 /*-----------------------------------------------------------
 * TASK CREATION API
 *----------------------------------------------------------*/
@@ -860,6 +876,95 @@ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
 * SCHEDULER CONTROL
 *----------------------------------------------------------*/
 
+/**
+ * task. h
+ * @code{c}
+ * void vTaskStartScheduler( void );
+ * @endcode
+ *
+ * Starts the real time kernel tick processing.  After calling the kernel
+ * has control over which tasks are executed and when.
+ *
+ * See the demo application file main.c for an example of creating
+ * tasks and starting the kernel.
+ *
+ * Example usage:
+ * @code{c}
+ * void vAFunction( void )
+ * {
+ *   // Create at least one task before starting the kernel.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+ *
+ *   // Start the real time kernel with preemption.
+ *   vTaskStartScheduler ();
+ *
+ *   // Will not get here unless a task calls vTaskEndScheduler ()
+ * }
+ * @endcode
+ *
+ * \defgroup vTaskStartScheduler vTaskStartScheduler
+ * \ingroup SchedulerControl
+ */
+void vTaskStartScheduler( void );
+
+/**
+ * task. h
+ * @code{c}
+ * void vTaskEndScheduler( void );
+ * @endcode
+ *
+ * NOTE:  At the time of writing only the x86 real mode port, which runs on a PC
+ * in place of DOS, implements this function.
+ *
+ * Stops the real time kernel tick.  All created tasks will be automatically
+ * deleted and multitasking (either preemptive or cooperative) will
+ * stop.  Execution then resumes from the point where vTaskStartScheduler ()
+ * was called, as if vTaskStartScheduler () had just returned.
+ *
+ * See the demo application file main. c in the demo/PC directory for an
+ * example that uses vTaskEndScheduler ().
+ *
+ * vTaskEndScheduler () requires an exit function to be defined within the
+ * portable layer (see vPortEndScheduler () in port. c for the PC port).  This
+ * performs hardware specific operations such as stopping the kernel tick.
+ *
+ * vTaskEndScheduler () will cause all of the resources allocated by the
+ * kernel to be freed - but will not free resources allocated by application
+ * tasks.
+ *
+ * Example usage:
+ * @code{c}
+ * void vTaskCode( void * pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *       // Task code goes here.
+ *
+ *       // At some point we want to end the real time kernel processing
+ *       // so call ...
+ *       vTaskEndScheduler ();
+ *   }
+ * }
+ *
+ * void vAFunction( void )
+ * {
+ *   // Create at least one task before starting the kernel.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+ *
+ *   // Start the real time kernel with preemption.
+ *   vTaskStartScheduler ();
+ *
+ *   // Will only get here when the vTaskCode () task has called
+ *   // vTaskEndScheduler ().  When we get here we are back to single task
+ *   // execution.
+ * }
+ * @endcode
+ *
+ * \defgroup vTaskEndScheduler vTaskEndScheduler
+ * \ingroup SchedulerControl
+ */
+void vTaskEndScheduler( void );
+
 /**
  * task. h
  * @code{c}
@@ -1997,11 +2102,123 @@ uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask,
 #define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \
     ulTaskGenericNotifyValueClear( ( xTask ), ( uxIndexToClear ), ( ulBitsToClear ) )
 
+/**
+ * task.h
+ * @code{c}
+ * void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut );
+ * @endcode
+ *
+ * Capture the current time for future use with xTaskCheckForTimeOut().
+ *
+ * @param pxTimeOut Pointer to a timeout object into which the current time
+ * is to be captured.  The captured time includes the tick count and the number
+ * of times the tick count has overflowed since the system first booted.
+ * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState
+ * \ingroup TaskCtrl
+ */
+void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut );
+
+/**
+ * task.h
+ * @code{c}
+ * BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
+ * @endcode
+ *
+ * Determines if pxTicksToWait ticks has passed since a time was captured
+ * using a call to vTaskSetTimeOutState().  The captured time includes the tick
+ * count and the number of times the tick count has overflowed.
+ *
+ * @param pxTimeOut The time status as captured previously using
+ * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated
+ * to reflect the current time status.
+ * @param pxTicksToWait The number of ticks to check for timeout i.e. if
+ * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by
+ * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred.
+ * If the timeout has not occurred, pxTicksToWait is updated to reflect the
+ * number of remaining ticks.
+ *
+ * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is
+ * returned and pxTicksToWait is updated to reflect the number of remaining
+ * ticks.
+ *
+ * @see https://www.FreeRTOS.org/xTaskCheckForTimeOut.html
+ *
+ * Example Usage:
+ * @code{c}
+ *  // Driver library function used to receive uxWantedBytes from an Rx buffer
+ *  // that is filled by a UART interrupt. If there are not enough bytes in the
+ *  // Rx buffer then the task enters the Blocked state until it is notified that
+ *  // more data has been placed into the buffer. If there is still not enough
+ *  // data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
+ *  // is used to re-calculate the Block time to ensure the total amount of time
+ *  // spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
+ *  // continues until either the buffer contains at least uxWantedBytes bytes,
+ *  // or the total amount of time spent in the Blocked state reaches
+ *  // MAX_TIME_TO_WAIT - at which point the task reads however many bytes are
+ *  // available up to a maximum of uxWantedBytes.
+ *
+ *  size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
+ *  {
+ *  size_t uxReceived = 0;
+ *  TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
+ *  TimeOut_t xTimeOut;
+ *
+ *      // Initialize xTimeOut.  This records the time at which this function
+ *      // was entered.
+ *      vTaskSetTimeOutState( &xTimeOut );
+ *
+ *      // Loop until the buffer contains the wanted number of bytes, or a
+ *      // timeout occurs.
+ *      while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
+ *      {
+ *          // The buffer didn't contain enough data so this task is going to
+ *          // enter the Blocked state. Adjusting xTicksToWait to account for
+ *          // any time that has been spent in the Blocked state within this
+ *          // function so far to ensure the total amount of time spent in the
+ *          // Blocked state does not exceed MAX_TIME_TO_WAIT.
+ *          if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
+ *          {
+ *              //Timed out before the wanted number of bytes were available,
+ *              // exit the loop.
+ *              break;
+ *          }
+ *
+ *          // Wait for a maximum of xTicksToWait ticks to be notified that the
+ *          // receive interrupt has placed more data into the buffer.
+ *          ulTaskNotifyTake( pdTRUE, xTicksToWait );
+ *      }
+ *
+ *      // Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
+ *      // The actual number of bytes read (which might be less than
+ *      // uxWantedBytes) is returned.
+ *      uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
+ *                                                  pucBuffer,
+ *                                                  uxWantedBytes );
+ *
+ *      return uxReceived;
+ *  }
+ * @endcode
+ * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut
+ * \ingroup TaskCtrl
+ */
+BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut,
+                                 TickType_t * const pxTicksToWait );
+
+/*-----------------------------------------------------------
+* SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES
+*----------------------------------------------------------*/
+
 /*
  * Return the handle of the calling task.
  */
 TaskHandle_t xTaskGetCurrentTaskHandle( void );
 
+/*
+ * Returns the scheduler state as taskSCHEDULER_RUNNING,
+ * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED.
+ */
+BaseType_t xTaskGetSchedulerState( void );
+
 /* *INDENT-OFF* */
 #ifdef __cplusplus
     }

+ 5 - 0
FreeRTOS/portable/rt-thread/port.c

@@ -12,6 +12,11 @@ void vPortExitCritical( void )
     rt_hw_interrupt_enable(level);
 }
 
+void vPortEndScheduler( void )
+{
+    /* Not implemented in ports where there is nothing to return to. */
+}
+
 BaseType_t rt_err_to_freertos(rt_err_t rt_err)
 {
     switch(-rt_err)

+ 128 - 0
FreeRTOS/task.c

@@ -66,9 +66,15 @@ typedef struct tskTaskControlBlock
         volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
         volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
     #endif
+    #if ( INCLUDE_xTaskAbortDelay == 1 )
+        uint8_t ucDelayAborted;
+    #endif
 } tskTCB;
 typedef tskTCB TCB_t;
 
+/* Other file private variables. --------------------------------*/
+static volatile BaseType_t xSchedulerRunning = pdFALSE;
+
 /*-----------------------------------------------------------*/
 
 /*
@@ -189,6 +195,10 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
     rt_memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) );
 #endif
 
+#if ( INCLUDE_xTaskAbortDelay == 1 )
+    pxNewTCB->ucDelayAborted = pdFALSE;
+#endif
+
     if ( pxCreatedTask != NULL )
     {
         *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
@@ -463,6 +473,19 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
 #endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */
 /*-----------------------------------------------------------*/
 
+void vTaskStartScheduler( void )
+{
+    xSchedulerRunning = pdTRUE;
+}
+/*-----------------------------------------------------------*/
+
+void vTaskEndScheduler( void )
+{
+    xSchedulerRunning = pdFALSE;
+    vPortEndScheduler();
+}
+/*----------------------------------------------------------*/
+
 void vTaskSuspendAll( void )
 {
     rt_enter_critical();
@@ -542,6 +565,7 @@ char * pcTaskGetName( TaskHandle_t xTaskToQuery )
 
     BaseType_t xTaskAbortDelay( TaskHandle_t xTask )
     {
+        TCB_t * pxTCB = xTask;
         BaseType_t xReturn;
         rt_thread_t thread = ( rt_thread_t ) xTask;
         rt_bool_t need_schedule = RT_FALSE;
@@ -555,6 +579,7 @@ char * pcTaskGetName( TaskHandle_t xTaskToQuery )
         {
             rt_thread_resume( thread );
             thread->error = -RT_ETIMEOUT;
+            pxTCB->ucDelayAborted = pdTRUE;
             if ( thread->current_priority < rt_thread_self()->current_priority ){
                 need_schedule = RT_TRUE;
             }
@@ -645,6 +670,81 @@ char * pcTaskGetName( TaskHandle_t xTaskToQuery )
 #endif /* configUSE_APPLICATION_TASK_TAG */
 /*-----------------------------------------------------------*/
 
+void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
+{
+    rt_base_t level;
+
+    configASSERT( pxTimeOut );
+    level = rt_hw_interrupt_disable();
+    pxTimeOut->xOverflowCount = 0;
+    pxTimeOut->xTimeOnEntering = ( TickType_t ) rt_tick_get();
+    rt_hw_interrupt_enable( level );
+}
+/*-----------------------------------------------------------*/
+
+void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut )
+{
+    /* For internal use only as it does not use a critical section. */
+    pxTimeOut->xOverflowCount = 0;
+    pxTimeOut->xTimeOnEntering = ( TickType_t ) rt_tick_get();;
+}
+/*-----------------------------------------------------------*/
+
+BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut,
+                                 TickType_t * const pxTicksToWait )
+{
+    TCB_t * pxCurrentTCB = ( TCB_t * ) rt_thread_self();
+    BaseType_t xReturn;
+    rt_base_t level;
+
+    configASSERT( pxTimeOut );
+    configASSERT( pxTicksToWait );
+
+    level = rt_hw_interrupt_disable();
+    /* Minor optimisation.  The tick count cannot change in this block. */
+    const TickType_t xConstTickCount = ( TickType_t ) rt_tick_get();
+    const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering;
+
+#if ( INCLUDE_xTaskAbortDelay == 1 )
+    if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE )
+    {
+        /* The delay was aborted, which is not the same as a time out,
+         * but has the same result. */
+        pxCurrentTCB->ucDelayAborted = pdFALSE;
+        xReturn = pdTRUE;
+    }
+    else
+#endif
+
+#if ( INCLUDE_vTaskSuspend == 1 )
+    if( *pxTicksToWait == portMAX_DELAY )
+    {
+        /* If INCLUDE_vTaskSuspend is set to 1 and the block time
+         * specified is the maximum block time then the task should block
+         * indefinitely, and therefore never time out. */
+        xReturn = pdFALSE;
+    }
+    else
+#endif
+
+    if( xElapsedTime < *pxTicksToWait )
+    {
+        /* Not a genuine timeout. Adjust parameters for time remaining. */
+        *pxTicksToWait -= xElapsedTime;
+        vTaskInternalSetTimeOutState( pxTimeOut );
+        xReturn = pdFALSE;
+    }
+    else
+    {
+        *pxTicksToWait = ( TickType_t ) 0;
+        xReturn = pdTRUE;
+    }
+    rt_hw_interrupt_enable( level );
+
+    return xReturn;
+}
+/*-----------------------------------------------------------*/
+
 #if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) )
 
     TaskHandle_t xTaskGetCurrentTaskHandle( void )
@@ -662,6 +762,34 @@ char * pcTaskGetName( TaskHandle_t xTaskToQuery )
 #endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
 /*-----------------------------------------------------------*/
 
+#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) )
+
+    BaseType_t xTaskGetSchedulerState( void )
+    {
+        BaseType_t xReturn;
+
+        if( xSchedulerRunning == pdFALSE )
+        {
+            xReturn = taskSCHEDULER_NOT_STARTED;
+        }
+        else
+        {
+            if( rt_critical_level() == 0 )
+            {
+                xReturn = taskSCHEDULER_RUNNING;
+            }
+            else
+            {
+                xReturn = taskSCHEDULER_SUSPENDED;
+            }
+        }
+
+        return xReturn;
+    }
+
+#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) ) */
+/*-----------------------------------------------------------*/
+
 #if ( configUSE_TASK_NOTIFICATIONS == 1 )
 
     uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWait,