广东湘恒智能科技有限公司
主营产品: 西门子PLC代理商,plc变频器,伺服电机,人机界面,触摸屏,线缆,DP接头
SIEMENS天津市 西门子代理商——西门子华北一级总代理

5 FreeRTOS任务调度相关源码

5.1 任务控制块TCB_t

FreeRTOS对各个任务进行调度,首先需要一种方式来访问和控制各个任务,任务控制块就可以实现这种功能,它本质是一个结构体,记录了任务的堆栈指针、任务当前状态、任务优先级等。

















typedef struct tskTaskControlBlock{    volatile StackType_t    *pxTopOfStack;  /*栈顶 */
   ListItem_t          xStateListItem;     /*标记任务状态的列表(Ready, Blocked, Suspended ) */    ListItem_t          xEventListItem;     /*任务的事件列表 */    UBaseType_t         uxPriority;         /*任务优先级 */    StackType_t         *pxStack;           /*任务栈起始地址 */    char                pcTaskName[ configMAX_TASK_NAME_LEN ]; /*创建时给任务的描述性名称,便于调试 */    #if(...省略部分)    ...省略部分    #endif
} tskTCB;typedef tskTCB TCB_t;

5.2 阻塞延时vTaskDelay

当某个任务需要延时,调用vTaskDelay(),则该任务进入阻塞态,此时调度器会从就绪列表中找到优先级最高的就绪任务开始执行。

那vTaskDelay()里面具体执行了哪些内容你呢?





































#if ( INCLUDE_vTaskDelay == 1 )    void vTaskDelay( const TickType_t xTicksToDelay ){    BaseType_t xAlreadyYielded = pdFALSE;
       /*delay时间为0则强制进行任务切换 */        if( xTicksToDelay > ( TickType_t ) 0U )        {            configASSERT( uxSchedulerSuspended == 0 );            /*停止任务调度*/            vTaskSuspenvdAll();            {                traceTASK_DELAY();
               /* 在调度器被挂起时从事件列表中删除的任务,在恢复调度器之前,不会被放置在就绪列表中或从阻塞列表中删除                此任务不能出现在事件列表中,因为它是当前正在执行的任务。*/                prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );            }            xAlreadyYielded = xTaskResumeAll();        }        else        {            mtCOVERAGE_TEST_MARKER();        }
       /* 如果xTaskResumeAll没有进行任务切换,则强制进行任务切换 */        if( xAlreadyYielded == pdFALSE )        {            portYIELD_WITHIN_API();        }        else        {            mtCOVERAGE_TEST_MARKER();        }    }#endif /* INCLUDE_vTaskDelay */

vTaskDelay的延时参数是以tick为单位的,即vTaskDelay(1)延时1ms。

当延时参数不为0时,即正常调用延时函数时,先停止任务调度,将当前任务添加至延时列表中,再恢复任务调度。

当延时参数为0时,会强制进行任务切换(portYIELD_WITHIN_API)(疑问:如果当前任务的优先级是最高的,虽然强制切换,但由于该任务的优先级最高,所起其实没有切换到其它任务?如果真的是强制切换到另一个任务,那上面时候这个最高优先级的任务再抢会CPU的使用权呢?)。

5.2.1添加任务到延时列表






























































static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ){TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;
   #if( INCLUDE_xTaskAbortDelay == 1 )    //...省略部分    #endif
   /* 在将任务添加到阻塞列表前先将其从就绪列表中移除*/    if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )    {        /* 当前任务必定在就绪列表中, 所以无需检测, 并且port reset macro 可以被立即调用 */        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );    }    else    {        mtCOVERAGE_TEST_MARKER();    }
   #if ( INCLUDE_vTaskSuspend == 1 )    {        if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )        {            /* 为确保不是被定时器事件唤醒,添加任务到挂起列表而不是延时列表. 它将会无定期的阻塞*/            vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );        }        else        {            /* 如果事件没有发生,计算任务应该被唤醒的时间。这可能会溢出,但这无关紧要,内核会正确地管理它*/            xTimeToWake = xConstTickCount + xTicksToWait;
           /* 列表项将按唤醒顺序插入 */            listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
           if( xTimeToWake < xConstTickCount )            {                /* 唤醒时间已溢出,将该项放入溢出列表 */                vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );            }            else            {                /* 唤醒时间未溢出,所以当前的阻塞列表被使用. */                vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
               /* 如果进入阻塞状态的任务被放置在阻塞任务列表的顶部,那么xNextTaskUnblockTime也需要更新 */                if( xTimeToWake < xNextTaskUnblockTime )                {                    xNextTaskUnblockTime = xTimeToWake;                }                else                {                    mtCOVERAGE_TEST_MARKER();                }            }        }    }    #else /* INCLUDE_vTaskSuspend */    //...省略部分    #endif /* INCLUDE_vTaskSuspend */}


展开全文
相关产品
拨打电话 微信咨询 发送询价