freertos内核走读2——task任务调度机制(三)

本文为jorhai原创,转载请注明,谢谢!

继续任务操作相关函数走读.

vTaskDelayUntil,
vTaskDelay的可以实现当前任务阻塞一定的tick时间,然后唤醒任务。任务从vTaskDelay被调用时阻塞,但是任务什么时候调用vTaskDelay,这是一个不可定义的,因为任务本身还要等到调度然后才能运行。而vTaskDelayUntil以绝对的唤醒时间,保证了任务按照一个周期不停的唤醒,执行代码,然后delay。
pxPreviousWakeTime保存了任务上次唤醒的时间tick点,而xTimeIncrement则表示在pxPreviousWakeTime上延迟多长时间唤醒任务。

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
    {
    TickType_t xTimeToWake;
    BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

        configASSERT( pxPreviousWakeTime );
        configASSERT( ( xTimeIncrement > 0U ) );
        configASSERT( uxSchedulerSuspended == 0 );

        vTaskSuspendAll();
        {/*暂停调度器*/
            /* Minor optimisation.  The tick count cannot change in this
            block. */
            const TickType_t xConstTickCount = xTickCount;
            /* xTimeToWake 下次唤醒时间为:任务上次唤醒时间加上延时时间;
不管vTaskDelayUntil是在什么时间被调用,下次的唤醒时间永远是一个绝对值。
这样就保证了唤醒时间始终是一个固定周期。*/
            /* Generate the tick time at which the task wants to wake. */
            xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;

            if( xConstTickCount < *pxPreviousWakeTime )
            {/*当前的tick点,小于上次唤醒时的tick点,表示xTickCount 发生了一次翻转,
这是判断任务的下次唤醒时间同样小于上次唤醒时间(表示确实发生了翻转),
同时下次唤醒时间大于当前tick,设置xShouldDelay 为TRUE*/
                /* The tick count has overflowed since this function was
                lasted called.  In this case the only time we should ever
                actually delay is if the wake time has also overflowed,
                and the wake time is greater than the tick time.  When this
                is the case it is as if neither time had overflowed. */
                if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
                {
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /* The tick time has not overflowed.  In this case we will
                delay if either the wake time has overflowed, and/or the
                tick time is less than the wake time. */
                /*tick没有发生翻转,两种情形下任务应该延时:
1.  下次唤醒时间小于上次唤醒——表示下次唤醒时间是在tick发生翻转之后;
2.  下次唤醒时间大于当前tick/
                if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
                {
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }

            /* Update the wake time ready for the next call. */
            /*将下次唤醒时间更新为上次唤醒时间,然后开始任务阻塞*/
            *pxPreviousWakeTime = xTimeToWake;

            if( xShouldDelay != pdFALSE )
            {
                traceTASK_DELAY_UNTIL();

                /* Remove the task from the ready list before adding it to the
                blocked list as the same list item is used for both lists. */
                /*能调用vTaskDelayUntil 的函数本身就是运行状态的函数,
因此将函数从ready list中删除,并使用portRESET_READY_PRIORITY 
清楚该优先级在uxTopReadyPriority 中的标记*/
                if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
                {
                    /* The current task must be in a ready list, so there is
                    no need to check, and the port reset macro can be called
                    directly. */
                    portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
                /*将任务添加大delay list中,等待tick前进到xTimeToWake ,唤醒任务*/
                prvAddCurrentTaskToDelayedList( xTimeToWake );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    /*恢复调度器,调度器有可能完成了一次切换*/
        xAlreadyYielded = xTaskResumeAll();

        /* Force a reschedule if xTaskResumeAll has not already done so, we may
        have put ourselves to sleep. */
        if( xAlreadyYielded == pdFALSE )
        {/*如果xTaskResumeAll 没有完成切换,则强制完成任务切换*/
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

vTaskDelayUntil就是vTaskDelay的改进版,如果你想让任务周期的执行某一操作,那么请使用vTaskDelayUntil而不是vTaskDelay。
Freertos给出的demo:

void vTaskFunction( void * pvParameters )
 {
 TickType_t xLastWakeTime;
 const TickType_t xFrequency = 10;

     // Initialise the xLastWakeTime variable with the current time.
     xLastWakeTime = xTaskGetTickCount ();
     for( ;; )
     {
         // Wait for the next cycle.
         vTaskDelayUntil( &xLastWakeTime, xFrequency );

         // Perform action here.
     }
 }

获取任务优先级:
如果入参xTask为NULL则返回当前运行任务优先级。

UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
    {
    TCB_t *pxTCB;
    UBaseType_t uxReturn;

        taskENTER_CRITICAL();
        {
            /* If null is passed in here then it is the priority of the that
            called uxTaskPriorityGet() that is being queried. */
            pxTCB = prvGetTCBFromHandle( xTask );
            uxReturn = pxTCB->uxPriority;
        }
        taskEXIT_CRITICAL();

        return uxReturn;
    }

还有一种情况是从中断回调里获取任务优先级

UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask )
    {
    TCB_t *pxTCB;
    UBaseType_t uxReturn, uxSavedInterruptState;

        /* RTOS ports that support interrupt nesting have the concept of a
        maximum system call (or maximum API call) interrupt priority.
        Interrupts that are above the maximum system call priority are keep
        permanently enabled, even when the RTOS kernel is in a critical section,
        but cannot make any calls to FreeRTOS API functions.  If configASSERT()
        is defined in FreeRTOSConfig.h then
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
        failure if a FreeRTOS API function is called from an interrupt that has
        been assigned a priority above the configured maximum system call
        priority.  Only FreeRTOS functions that end in FromISR can be called
        from interrupts that have been assigned a priority at or (logically)
        below the maximum system call interrupt priority.  FreeRTOS maintains a
        separate interrupt safe API to ensure interrupt entry is as fast and as
        simple as possible.  More information (albeit Cortex-M specific) is
        provided on the following link:
        http://www.freertos.org/RTOS-Cortex-M3-M4.html */
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

        uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR();
        {
            /* If null is passed in here then it is the priority of the calling
            task that is being queried. */
            pxTCB = prvGetTCBFromHandle( xTask );
            uxReturn = pxTCB->uxPriority;
        }
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptState );

        return uxReturn;
    }

返回任务状态
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 read 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. */
} eTaskState;
eTaskState eTaskGetState( TaskHandle_t xTask )
    {
    eTaskState eReturn;
    List_t *pxStateList;
    const TCB_t * const pxTCB = ( TCB_t * ) xTask;

        configASSERT( pxTCB );

        if( pxTCB == pxCurrentTCB )
        {/*任务上下文和当前运行任务上下文一致,返回运行*/
            /* The task calling this function is querying its own state. */
            eReturn = eRunning;
        }
        else
        {
            taskENTER_CRITICAL();
            {/*获取任务xGenericListItem 所在的list*/
                pxStateList = ( List_t * ) listLIST_ITEM_CONTAINER( &( pxTCB->xGenericListItem ) );
            }
            taskEXIT_CRITICAL();

            if( ( pxStateList == pxDelayedTaskList ) || ( pxStateList == pxOverflowDelayedTaskList ) )
            {/*如果任务所在链表为delay list,返回阻塞状态*/
                /* The task being queried is referenced from one of the Blocked
                lists. */
                eReturn = eBlocked;
            }

            #if ( INCLUDE_vTaskSuspend == 1 )
                else if( pxStateList == &xSuspendedTaskList )
                {/*如果任务所在suspend list,分两种情况:1.任务没有等待某时间,
返回暂停状态;2. 任务在等待某个时间,返回阻塞状态*/
                    /* The task being queried is referenced from the suspended
                    list.  Is it genuinely suspended or is it block
                    indefinitely? */
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL )
                    {
                        eReturn = eSuspended;
                    }
                    else
                    {
                        eReturn = eBlocked;
                    }
                }
            #endif

            #if ( INCLUDE_vTaskDelete == 1 )
                else if( pxStateList == &xTasksWaitingTermination )
                {/*如果任务在xTasksWaitingTermination ,表示任务在删除状态,
等待IDLE任务释放其资源*/
                    /* The task being queried is referenced from the deleted
                    tasks list. */
                    eReturn = eDeleted;
                }
            #endif

            else /*lint !e525 Negative indentation is intended to make use of pre-processor clearer. */
            {/*其他情况的,就只有处于某个ready list的情况了,返回任务状态为就绪*/
                /* If the task is not in any other state, it must be in the
                Ready (including pending ready) state. */
                eReturn = eReady;
            }
        }

        return eReturn;
    } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */

任务优先级的设置
Freertos支持动态的设置任务的优先级。这在某些情形很有用处。
先看这个函数的实现。

void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
    {
    TCB_t *pxTCB;
    UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
    BaseType_t xYieldRequired = pdFALSE;
        /*新设置的优先级必须小于系统规定最大优先级*/
        configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) );

        /* Ensure the new priority is valid. */
        if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
        {/*如果设置的优先级过大,则将优先级设置为最大优先级*/
            uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        taskENTER_CRITICAL();
        {/*进入中断临界区*/
            /* If null is passed in here then it is the priority of the calling
            task that is being changed. */
            /*获取任务上下文,如果xTask 为NULL返回当前运行任务上下文*/
            pxTCB = prvGetTCBFromHandle( xTask );

            traceTASK_PRIORITY_SET( pxTCB, uxNewPriority );

            #if ( configUSE_MUTEXES == 1 )
            {/*如果使能了MUTEX,那么任务有可能发生了优先级提升,在mutex时再详细介绍该现象
那么从uxBasePriority取出原始的优先级*/
                uxCurrentBasePriority = pxTCB->uxBasePriority;
            }
            #else
            {/*没有使能MUTEX则直接从uxPriority 获取原始优先级*/
                uxCurrentBasePriority = pxTCB->uxPriority;
            }
            #endif

            if( uxCurrentBasePriority != uxNewPriority )
            {/*原始优先级和设置优先级不同*/
                /* The priority change may have readied a task of higher
                priority than the calling task. */
                if( uxNewPriority > uxCurrentBasePriority )
                {/*如果新设置的优先级比原始优先级大*/
                    if( pxTCB != pxCurrentTCB )
                    {/*设置优先级的任务不是当前运行任务,如果设置的优先级比当前运行
任务的优先级高,表示需要切换,xYieldRequired 为TRUE*/
                        /* The priority of a task other than the currently
                        running task is being raised.  Is the priority being
                        raised above that of the running task? */
                        if( uxNewPriority >= pxCurrentTCB->uxPriority )
                        {
                            xYieldRequired = pdTRUE;
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {/*设置的任务为当前任务,但是当前运行任务一定是就绪队列中
优先级最高的那个,因此也就不需要切换了*/
                        /* The priority of the running task is being raised,
                        but the running task must already be the highest
                        priority task able to run so no yield is required. */
                    }
                }
                else if( pxTCB == pxCurrentTCB )
                {/*如果新设置的优先级比原始优先级低,同时是当前的任务降低了优先级。
这时当然要进行一次切换了*/
                    /* Setting the priority of the running task down means
                    there may now be another task of higher priority that
                    is ready to execute. */
                    xYieldRequired = pdTRUE;
                }
                else
                {/*其他情况下,表示当前运行任务降低了其他任务的优先级,
当前任务运行的优先级当然比其他任务级别高了,因此也不需要进行切换了*/
                    /* Setting the priority of any other task down does not
                    require a yield as the running task must be above the
                    new priority of the task being modified. */
                }

                /* Remember the ready list the task might be referenced from
                before its uxPriority member is changed so the
                taskRESET_READY_PRIORITY() macro can function correctly. */
                uxPriorityUsedOnEntry = pxTCB->uxPriority;

                #if ( configUSE_MUTEXES == 1 )
                {/*如果使能了configUSE_MUTEXES ,判断mutex是否已经进行了优先级提升,
如果pxTCB->uxBasePriority == pxTCB->uxPriority 表示优先级没有提升,则设置pxTCB->uxPriority ;
如果优先级提升了则只设置pxTCB->uxBasePriority ,等待任务释放mutex后,再恢复到新设置的优先级*/
                    /* Only change the priority being used if the task is not
                    currently using an inherited priority. */
                    if( pxTCB->uxBasePriority == pxTCB->uxPriority )
                    {
                        pxTCB->uxPriority = uxNewPriority;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

                    /* The base priority gets set whatever. */
                    pxTCB->uxBasePriority = uxNewPriority;
                }
                #else
                {
                    pxTCB->uxPriority = uxNewPriority;
                }
                #endif

                /* Only reset the event list item value if the value is not
                being used for anything else. */
/*任务的xEventListItem 是用于存放在特定的queue的时间链表里,用来表示任务阻塞
等待queue的资源(如果是发阻塞,则等待队列非满,如果是接收阻塞就等待队列非空)。
Queue的资源可能被很多任务等待,肯定是优先级高的任务优先获得queue资源,
因此queue的事件等待队列,按照优先级越高越在链表前面
(按照configMAX_PRIORITIES - ( TickType_t ) uxNewPriority进行排序)*/
                if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
                {/*因为修改优先级的任务在某个事件队列里,因此重置其在事件队列里的位置*/
                    listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                /* If the task is in the blocked or suspended list we need do
                nothing more than change it's priority variable. However, if
                the task is in a ready list it needs to be removed and placed
                in the list appropriate to its new priority. */
                if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xGenericListItem ) ) != pdFALSE )
                {/*如果任务原来就处于就绪状态,那么将任务从原来的就绪队列中删除,
然后添加到新的就绪队列中*/
                    /* The task is currently in its ready list - remove before adding
                    it to it's new ready list.  As we are in a critical section we
                    can do this even if the scheduler is suspended. */
                    if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
                    {
                        /* It is known that the task is in its ready list so
                        there is no need to check again and the port level
                        reset macro can be called directly. */
                        portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                    prvAddTaskToReadyList( pxTCB );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
                /*如果前面处理判定需要进行任务切换,调用taskYIELD_IF_USING_PREEMPTION()完成任务切换*/
                if( xYieldRequired == pdTRUE )
                {
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                /* Remove compiler warning about unused variables when the port
                optimised task selection is not being used. */
                /*无意义,只是为了消解编译时的告警*/
                ( void ) uxPriorityUsedOnEntry;
            }
        }
        taskEXIT_CRITICAL();
    }

走读完后,发现vTaskPrioritySet,可以动态的设置某个任务的优先级,并且完成任务切换(如果需要)。vTaskPrioritySet在有些情形可能很有用,比如短暂提升某个任务优先级,以保证处理某些操作时快速完成,而其他情况下任务则运行在较低的优先级。又比如,freertos在防止中断优先级翻转时,在mutex操作中采用了优先级提升的策略,这是我们其实可以通过vTaskPrioritySet来实现优先级天花板策略,或者在操作信号量二进制锁时采用优先级提升的策略。

任务的暂停
前面介绍了freertos 任务存在的几种状态,其中vTaskSuspend可是实现对某个任务进行暂停操作,暂停的任务在不会被调度器调度到,又不会占用CPU。
请思考vTaskSuspend需要实现的操作:首次要从其他的任务队列中删除该任务节点,然后添加到suspend list中,如果暂停的是当前任务需要进行切换,如果暂停的是阻塞任务需要更新阻塞tick。具体还要走读vTaskSuspend函数。

void vTaskSuspend( TaskHandle_t xTaskToSuspend )
    {
    TCB_t *pxTCB;

        taskENTER_CRITICAL();
        {/*进入临界区*/
            /* If null is passed in here then it is the running task that is
            being suspended. */
            /*获取任务句柄,如果入参xTaskToSuspend 为NULL,则返回当前运行任务*/
            pxTCB = prvGetTCBFromHandle( xTaskToSuspend );

            traceTASK_SUSPEND( pxTCB );

            /* Remove task from the ready/delayed list and place in the
            suspended list. */
            if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
            {/*将任务从其他任务队列里删除,然后更新系统的uxTopReadyPriority 
清楚该任务的优先级就绪标记*/
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* Is the task waiting on an event also? */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {/*将任务从事件等待队列中删除*/
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            /*将任务添加到xSuspendedTaskList 队列中*/
            vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) );
        }
        taskEXIT_CRITICAL();/*退出临界区*/

        if( pxTCB == pxCurrentTCB )
        {/*如果暂停的任务是当前任务,进行切换*/
            if( xSchedulerRunning != pdFALSE )
            {
                /* The current task has just been suspended. */
                /*如果暂停的是当前任务,并且调度器正在运行,则完成一次任务切换*/
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {/*暂停任务时,调度器处于停止状态,此时判断是否所有任务都已经暂停
如果所有任务都暂停,当前运行任务为NULL;否则调用vTaskSwitchContext 
完成一次任务的切换*/
                /* The scheduler is not running, but the task that was pointed
                to by pxCurrentTCB has just been suspended and pxCurrentTCB
                must be adjusted to point to a different task. */
                if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
                {
                    /* No other tasks are ready, so set pxCurrentTCB back to
                    NULL so when the next task is created pxCurrentTCB will
                    be set to point to it no matter what its relative priority
                    is. */
                    pxCurrentTCB = NULL;
                }
                else
                {
                    vTaskSwitchContext();
                }
            }
        }
        else
        {/*暂停的任务不是当前运行任务,这是更新系统最近的阻塞任务tick点*/
            if( xSchedulerRunning != pdFALSE )
            {
                /* A task other than the currently running task was suspended,
                reset the next expected unblock time in case it referred to the
                task that is now in the Suspended state. */
                taskENTER_CRITICAL();
                {
                    prvResetNextTaskUnblockTime();
                }
                taskEXIT_CRITICAL();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }

任务恢复
又暂停自然有恢复。直接看vTaskResume的代码实现。

void vTaskResume( TaskHandle_t xTaskToResume )
    {
    TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;

        /* It does not make sense to resume the calling task. */
        configASSERT( xTaskToResume );

        /* The parameter cannot be NULL as it is impossible to resume the
        currently executing task. */
        if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) )
        {/*resume的任务不为空并且不是当前任务,实时上任务不可能是当前运行任务,
当前运行的任务怎么可能处于暂停状态呢,因此该函数与其他函数对入参判断不同,不处理为NULL的入参
这在其他一些函数里表示的是当前运行任务*/
            taskENTER_CRITICAL();
            {
                if( prvTaskIsTaskSuspended( pxTCB ) == pdTRUE )
                {/*判断任务是否是否已经暂停,其实就是查看任务是否在xSuspendedTaskList */
                    traceTASK_RESUME( pxTCB );

                    /* As we are in a critical section we can access the ready
                    lists even if the scheduler is suspended. */
                    /*不过任务之前是什么状态,之前将任务添加从xSuspendedTaskList 中删除
然后添加到ready list中*/
                    ( void ) uxListRemove(  &( pxTCB->xGenericListItem ) );
                    prvAddTaskToReadyList( pxTCB );

                    /* We may have just resumed a higher priority task. */
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {/*resume的任务优先级高于或等于当前任务,则进行一次任务切换*/
                        /* This yield may not cause the task just resumed to run,
                        but will leave the lists in the correct state for the
                        next yield. */
                        taskYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            taskEXIT_CRITICAL();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

从中断回调里恢复任务
从isr中恢复任务,要关中断,在任务切换的时候,如果调度器停止则添加到xPendingReadyList中,否则直接添加到ready list。等待调度器恢复后将任务真正的放到ready list中。

BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
    {
    BaseType_t xYieldRequired = pdFALSE;
    TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;
    UBaseType_t uxSavedInterruptStatus;

        configASSERT( xTaskToResume );

        /* RTOS ports that support interrupt nesting have the concept of a
        maximum system call (or maximum API call) interrupt priority.
        Interrupts that are above the maximum system call priority are keep
        permanently enabled, even when the RTOS kernel is in a critical section,
        but cannot make any calls to FreeRTOS API functions.  If configASSERT()
        is defined in FreeRTOSConfig.h then
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
        failure if a FreeRTOS API function is called from an interrupt that has
        been assigned a priority above the configured maximum system call
        priority.  Only FreeRTOS functions that end in FromISR can be called
        from interrupts that have been assigned a priority at or (logically)
        below the maximum system call interrupt priority.  FreeRTOS maintains a
        separate interrupt safe API to ensure interrupt entry is as fast and as
        simple as possible.  More information (albeit Cortex-M specific) is
        provided on the following link:
        http://www.freertos.org/RTOS-Cortex-M3-M4.html */
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

        uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
        {/*禁止系统中断,保证操作完整性*/
            if( prvTaskIsTaskSuspended( pxTCB ) == pdTRUE )
            {
                traceTASK_RESUME_FROM_ISR( pxTCB );

                /* Check the ready lists can be accessed. */
                if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
                {
                    /* Ready lists can be accessed so move the task from the
                    suspended list to the ready list directly. */
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {/*表示需要完成一次切换*/
                        xYieldRequired = pdTRUE;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

                    ( void ) uxListRemove(  &( pxTCB->xGenericListItem ) );
                    /*添加到ready list中*/
                    prvAddTaskToReadyList( pxTCB );
                }
                else
                {
                    /* The delayed or ready lists cannot be accessed so the task
                    is held in the pending ready list until the scheduler is
                    unsuspended. */
                    vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        /*恢复CPU中断*/
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
        /*返回是否需要切换,这个值返回到中断中,中断可以根据该值选择是否在中断中或
者中断结束后完成任务切换。注意上面测操作xYieldRequired 在调度器运行时使能,这样才
能保证切换。如果调度器停止,则添加到xPendingReadyList 中,等待调度器再次启动时
添加到ready list中,重新调度任务*/
        return xYieldRequired;
    }

其他一些简单的函数

TickType_t xTaskGetTickCount( void ):获取当前系统的tick点。

TickType_t xTaskGetTickCountFromISR :从终端回调中获取当前系统的tick 点。

UBaseType_t uxTaskGetNumberOfTasks( void ):获取当前系统的任务总数。

char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ):获取当前任务名称。

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ):
获取栈的最高使用量,这个函数比较有意思:从栈的“顶端”位置,向栈底查找,如果内存位置为a5(栈初始化时初始化的值),如果为a5表示栈没有“漫到”该位置,直到不是a5的内存后返回。

TaskHandle_t xTaskGetIdleTaskHandle( void ):获取空闲任务的句柄。

UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION;
void vTaskList( char * pcWriteBuffer );
void vTaskGetRunTimeStats( char *pcWriteBuffer );
这三个函数都是获取系统运行状态的函数。计划在后面的freertos调试中详细的介绍。

Task.h中还有一些函数。包括task notify相关函数和供其他内核对象调用。task notify将在task 机制的后面章节单独介绍。
其他的一些函数大多实在其他内核对象里调用的,将在相关的章节中详细介绍,这里就不在说明。

后面一个章节介绍task notify机制。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值