亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

FreeRTOS任務創建的方法

發布時間:2022-04-11 14:14:25 來源:億速云 閱讀:117 作者:iii 欄目:開發技術

本篇內容介紹了“FreeRTOS任務創建的方法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

我們這里先回顧一下這個函數的聲明:
        BaseType_t xTaskCreate(                            TaskFunction_tp vTaskCode,                            const char * constpcName,                            unsigned short usStackDepth,                            void *pvParameters,                            UBaseType_t uxPriority,                            TaskHandle_t *pvCreatedTask                          );

      這個API函數的作用是創建新的任務并將它加入到任務就緒列表,函數參數含義為:

pvTaskCode:函數指針,指向任務函數的入口。任務永遠不會返回(位于死循環內)。該參數類型TaskFunction_t定義在文件projdefs.h中,定義為:typedef void(*TaskFunction_t)( void * ),即參數為空指針類型并返回空類型。pcName:任務描述。主要用于調試。字符串的最大長度(包括字符串結束字符)由宏configMAX_TASK_NAME_LEN指定,該宏位于FreeRTOSConfig.h文件中。usStackDepth:指定任務堆棧大小,能夠支持的堆棧變量數量(堆棧深度),而不是字節數。比如,在16位寬度的堆棧下,usStackDepth定義為100,則實際使用200字節堆棧存儲空間。堆棧的寬度乘以深度必須不超過size_t類型所能表示的最大值。比如,size_t為16位,則可以表示堆棧的最大值是65535字節。這是因為堆棧在申請時是以字節為單位的,申請的字節數就是堆棧寬度乘以深度,如果這個乘積超出size_t所表示的范圍,就會溢出,分配的堆棧空間也不是我們想要的。pvParameters:指針,當任務創建時,作為一個參數傳遞給任務。uxPriority:任務的優先級。具有MPU支持的系統,可以通過置位優先級參數的portPRIVILEGE_BIT位,隨意的在特權(系統)模式下創建任務。比如,創建一個優先級為2的特權任務,參數uxPriority可以設置為 ( 2 | portPRIVILEGE_BIT )。pvCreatedTask:用于回傳一個句柄(ID),創建任務后可以使用這個句柄引用任務。

      雖然xTaskCreate()看上去很像函數,但其實是一個宏,真正被調用的函數是xTaskGenericCreate(),xTaskCreate()宏定義如下所示:

#define xTaskCreate( pvTaskCode, pcName, usStackDepth,pvParameters, uxPriority, pxCreatedTask )    \      xTaskGenericCreate( ( pvTaskCode ),( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask), ( NULL ), ( NULL ), ( NULL ) )

      可以看到,xTaskCreate比xTaskGenericCreate少了三個參數,在宏定義中,這三個參數被設置為NULL。這三個參數用于使用靜態變量的方法分配堆棧、任務TCB空間以及設置MPU相關的參數。一般情況下,這三個參數是不使用的,所以任務創建宏xTaskCreate定義的時候,將這三個參數對用戶隱藏了。接下來的章節中,為了方便,我們還是稱xTaskCreate()為函數,雖然它是一個宏定義。

      上面我們提到了任務TCB(任務控制塊),這是一個需要重點介紹的關鍵點。它用于存儲任務的狀態信息,包括任務運行時的環境。每個任務都有自己的任務TCB。任務TCB是一個相對比較大的數據結構,這也是情理之中的,因為與任務相關的代碼占到整個FreeRTOS代碼量的一半左右,這些代碼大都與任務TCB相關,我們先來介紹一下任務TCB數據結構的定義:

typedef struct tskTaskControlBlock{    volatile StackType_t    *pxTopOfStack; /*當前堆棧的棧頂,必須位于結構體的第一項*/     #if ( portUSING_MPU_WRAPPERS == 1 )        xMPU_SETTINGS   xMPUSettings;      /*MPU設置,必須位于結構體的第二項*/    #endif     ListItem_t          xStateListItem; /*任務的狀態列表項,以引用的方式表示任務的狀態*/    ListItem_t          xEventListItem;    /*事件列表項,用于將任務以引用的方式掛接到事件列表*/    UBaseType_t         uxPriority;        /*保存任務優先級,0表示最低優先級*/    StackType_t         *pxStack;           /*指向堆棧的起始位置*/    char               pcTaskName[ configMAX_TASK_NAME_LEN ];/*任務名字*/     #if ( portSTACK_GROWTH > 0 )        StackType_t     *pxEndOfStack;     /*指向堆棧的尾部*/    #endif     #if ( portCRITICAL_NESTING_IN_TCB == 1 )        UBaseType_t     uxCriticalNesting; /*保存臨界區嵌套深度*/    #endif     #if ( configUSE_TRACE_FACILITY == 1 )        UBaseType_t     uxTCBNumber;       /*保存一個數值,每個任務都有唯一的值*/        UBaseType_t     uxTaskNumber;      /*存儲一個特定數值*/    #endif     #if ( configUSE_MUTEXES == 1 )        UBaseType_t     uxBasePriority;    /*保存任務的基礎優先級*/        UBaseType_t     uxMutexesHeld;    #endif     #if ( configUSE_APPLICATION_TASK_TAG == 1 )        TaskHookFunction_t pxTaskTag;    #endif     #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )        void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS ];    #endif     #if( configGENERATE_RUN_TIME_STATS == 1 )        uint32_t        ulRunTimeCounter;  /*記錄任務在運行狀態下執行的總時間*/    #endif     #if ( configUSE_NEWLIB_REENTRANT == 1 )        /* 為任務分配一個Newlibreent結構體變量。Newlib是一個C庫函數,并非FreeRTOS維護,FreeRTOS也不對使用結果負責。如果用戶使用Newlib,必須熟知Newlib的細節*/        struct _reent xNewLib_reent;    #endif     #if( configUSE_TASK_NOTIFICATIONS == 1 )        volatile uint32_t ulNotifiedValue; /*與任務通知相關*/        volatile uint8_t ucNotifyState;    #endif     #if( configSUPPORT_STATIC_ALLOCATION == 1 )        uint8_t ucStaticAllocationFlags; /* 如果堆棧由靜態數組分配,則設置為pdTRUE,如果堆棧是動態分配的,則設置為pdFALSE*/    #endif     #if( INCLUDE_xTaskAbortDelay == 1 )        uint8_t ucDelayAborted;    #endif } tskTCB; typedef tskTCB TCB_t;

      下面我們詳細的介紹這個數據結構的主要成員:

      指針pxTopOfStack必須位于結構體的第一項,指向當前堆棧的棧頂,對于向下增長的堆棧,pxTopOfStack總是指向最后一個入棧的項目。

      如果使用MPU,xMPUSettings必須位于結構體的第二項,用于MPU設置。

      接下來是狀態列表項xStateListItem和事件列表項xEventListItem,我們在上一章介紹列表和列表項的文章中提到過:列表被FreeRTOS調度器使用,用于跟蹤任務,處于就緒、掛起、延時的任務,都會被掛接到各自的列表中。調度器就是通過把任務TCB中的狀態列表項xStateListItem和事件列表項xEventListItem掛接到不同的列表中來實現上述過程的。在task.c中,定義了一些靜態列表變量,其中有就緒、阻塞、掛起列表,例如當某個任務處于就緒態時,調度器就將這個任務TCB的xStateListItem列表項掛接到就緒列表。事件列表項也與之類似,當隊列滿的情況下,任務因入隊操作而阻塞時,就會將事件列表項掛接到隊列的等待入隊列表上。

      uxPriority用于保存任務的優先級,0為最低優先級。任務創建時,指定的任務優先級就被保存到該變量中。

      指針pxStack指向堆棧的起始位置,任務創建時會分配指定數目的任務堆棧,申請堆棧內存函數返回的指針就被賦給該變量。很多剛接觸FreeRTOS的人會分不清指針pxTopOfStack和pxStack的區別,這里簡單說一下:pxTopOfStack指向當前堆棧棧頂,隨著進棧出棧,pxTopOfStack指向的位置是會變化的;pxStack指向當前堆棧的起始位置,一經分配后,堆棧起始位置就固定了,不會被改變了。那么為什么需要pxStack變量呢,這是因為隨著任務的運行,堆棧可能會溢出,在堆棧向下增長的系統中,這個變量可用于檢查堆棧是否溢出;如果在堆棧向上增長的系統中,要想確定堆棧是否溢出,還需要另外一個變量pxEndOfStack來輔助診斷是否堆棧溢出,后面會講到這個變量。

      字符數組pcTaskName用于保存任務的描述或名字,在任務創建時,由參數指定。名字的長度由宏configMAX_TASK_NAME_LEN(位于FreeRTOSConfig.h中)指定,包含字符串結束標志。

      如果堆棧向上生長(portSTACK_GROWTH > 0),指針pxEndOfStack指向堆棧尾部,用于檢驗堆棧是否溢出。

      變量uxCriticalNesting用于保存臨界區嵌套深度,初始值為0。

      接下來兩個變量用于可視化追蹤,僅當宏configUSE_TRACE_FACILITY(位于FreeRTOSConfig.h中)為1時有效。變量uxTCBNumber存儲一個數值,在創建任務時由內核自動分配數值(通常每創建一個任務,值增加1),每個任務的uxTCBNumber值都不同,主要用于調試。變量uxTaskNumber用于存儲一個特定值,與變量uxTCBNumber不同,uxTaskNumber的數值不是由內核分配的,而是通過API函數vTaskSetTaskNumber()來設置的,數值由函數參數指定。

      如果使用互斥量(configUSE_MUTEXES == 1),任務優先級被臨時提高時,變量uxBasePriority用來保存任務原來的優先級。

      變量ucStaticAllocationFlags也需要說明一下,我們前面說過任務創建API函數xTaskCreate()只能使用動態內存分配的方式創建任務堆棧和任務TCB,如果要使用靜態變量實現任務堆棧和任務TCB就需要使用函數xTaskGenericCreate()來實現。如果任務堆棧或任務TCB由靜態數組和靜態變量實現,則將該變量設置為pdTRUE(任務堆棧空間由靜態數組變量實現時為0x01,任務TCB由靜態變量實現時為0x02,任務堆棧和任務TCB都由靜態變量實現時為0x03),如果堆棧是動態分配的,則將該變量設置為pdFALSE。

      到這里任務TCB的數據結構就講完了,下面我們用一個例子來講述任務創建的過程,為方便起見,假設被創建的任務叫“任務A”,任務函數為vTask_A():

    TaskHandle_t xHandle;    xTaskCreate(vTask_A,”Task A”,120,NULL,1,&xHandle);

      這里創建了一個任務,任務優先級為1,由于硬件平臺是32為架構,所以指定了120*4=480字節的任務堆棧,向任務函數vTask_A()傳遞的參數為空(NULL),任務句柄由變量xHandle保存。當這個語句執行后,任務A被創建并加入就緒任務列表,我們這章的主要目的,就是看看這個語句在執行過程中,發生了什么事情。

1.創建任務堆棧和任務TCB

      調用函數prvAllocateTCBAndStack()創建任務堆棧和任務TCB。有兩種方式創建任務堆棧和任務TCB,一種是使用動態內存分配方法,這樣當任務刪除時,任務堆棧和任務控制塊空間會被釋放,可用于其它任務;另一種是使用靜態變量來實現,在創建任務前定義好全局或者靜態堆棧數組和任務控制塊變量,在調用創建任務API函數時,將這兩個變量以參數的形式傳遞給任務創建函數xTaskGenericCreate()。如果使用默認的xTaskCreate()創建任務函數,則使用動態內存分配,因為與靜態內存分配有關的參數不可見(在本文一開始我們說過xTaskCreate()其實是一個帶參數的宏定義,真正被執行的函數是xTaskGenericCreate(),參考宏xTaskCreate()的定義可以知道,xTaskCreate()對外隱藏了使用靜態內存分配的參數,在調用xTaskGenericCreate()時,這些參數被設置為NULL)。

      任務堆棧成功分配后,經過對齊的堆棧起始地址被保存到任務TCB的pxStack字段。如果使能堆棧溢出檢查或者使用可視化追蹤功能,則使用固定值tskSTACK_FILL_BYTE(0xa5)填充堆棧。

      函數prvAllocateTCBAndStack()的源碼去除斷言和不常用的條件編譯后如下所示:

static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer, TCB_t * const pxTaskBuffer ){TCB_t *pxNewTCB;StackType_t *pxStack;     /* 分配堆棧空間*/    pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer );    if( pxStack != NULL )    {        /* 分配TCB空間 */        pxNewTCB = ( TCB_t * ) pvPortMallocAligned( sizeof( TCB_t ), pxTaskBuffer );         if( pxNewTCB != NULL )        {            /* 將堆棧起始位置存入TCB*/            pxNewTCB->pxStack = pxStack;        }        else        {            /* 如果TCB分配失敗,釋放之前申請的堆棧空間 */            if( puxStackBuffer == NULL )            {                vPortFree( pxStack );            }        }    }    else    {        pxNewTCB = NULL;    }     if( pxNewTCB != NULL )    {        /* 如果需要,使用固定值填充堆棧 */        #if( ( configCHECK_FOR_STACK_OVERFLOW> 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark== 1 ) )        {            /* 僅用于調試 */            ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) );        }        #endif    }     return pxNewTCB;}

2.初始化任務TCB必要的字段

      調用函數prvInitialiseTCBVariables()初始化任務TCB必要的字段。在調用創建任務API函數xTaskCreate()時,參數pcName(任務描述)、uxPriority(任務優先級)都會被寫入任務TCB相應的字段,TCB字段中的xStateListItem和xEventListItem列表項也會被初始化,初始化后的列表項如圖2-1所示。在圖2-1中,列表項xEventListItem的成員列表項值xItemValue被初始為4,這是因為我在應用中設置的最大優先級數目(configMAX_PRIORITIES)為5,而xEventListItem. xItemValue等于configMAX_PRIORITIES減去任務A的優先級(為1),即5-1=4。這一點很重要,在這里xItemValue不是直接保存任務優先級,而是保存優先級的補數,這意味著xItemValue的值越大,對應的任務優先級越小。FreeRTOS內核使用vListInsert函數(詳細見高級篇第一章)將事件列表項插入到一個列表,這個函數根據xItemValue的值的大小順序來進行插入操作。使用宏listGET_OWNER_OF_HEAD_ENTRY獲得列表中的第一個列表項的xItemValue值總是最小,也就是優先級最高的任務!

FreeRTOS任務創建的方法

圖2-1:初始化狀態和事件列表項

      此外,TCB其它的一些字段也被初始化,比如臨界區嵌套次數、運行時間計數器、任務通知值、任務通知狀態等,函數prvInitialiseTCBVariables()的源碼如下所示:

static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority,   \                              const MemoryRegion_t * const xRegions, const uint16_t usStackDepth ){UBaseType_t x;     /* 將任務描述存入TCB */    for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )    {        pxTCB->pcTaskName[ x ] = pcName[ x ];        if( pcName[ x ] == 0x00 )        {            break;        }    }    /* 確保字符串有結束 */    pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';     /* 調整優先級,宏configMAX_PRIORITIES的值在FreeRTOSConfig.h中設置 */    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )    {        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;    }     pxTCB->uxPriority = uxPriority;    #if ( configUSE_MUTEXES == 1 )              /*使用互斥量*/    {          pxTCB->uxBasePriority = uxPriority;        pxTCB->uxMutexesHeld = 0;    }    #endif /* configUSE_MUTEXES */       /*初始化列表項*/    vListInitialiseItem( &( pxTCB->xStateListItem ) );    vListInitialiseItem( &( pxTCB->xEventListItem ) );     /* 設置列表項xStateListItem的成員pvOwner指向當前任務控制塊 */    listSET_LIST_ITEM_OWNER( &( pxTCB->xStateListItem ), pxTCB );     /* 設置列表項xEventListItem的成員xItemValue*/    listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );    /* 設置列表項xEventListItem的成員pvOwner指向當前任務控制塊 */    listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );     #if ( portCRITICAL_NESTING_IN_TCB ==1 )    /*使能臨界區嵌套功能*/    {          pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U;    }    #endif /* portCRITICAL_NESTING_IN_TCB */     #if ( configUSE_APPLICATION_TASK_TAG == 1 ) /*使能任務標簽功能*/    {          pxTCB->pxTaskTag = NULL;    }    #endif /* configUSE_APPLICATION_TASK_TAG */     #if ( configGENERATE_RUN_TIME_STATS == 1 )  /*使能事件統計功能*/    {        pxTCB->ulRunTimeCounter = 0UL;    }    #endif /* configGENERATE_RUN_TIME_STATS */     #if ( portUSING_MPU_WRAPPERS == 1 )         /*使用MPU功能*/    {        vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth );    }    #else /* portUSING_MPU_WRAPPERS */    {        ( void ) xRegions;        ( void ) usStackDepth;    }    #endif /* portUSING_MPU_WRAPPERS */     #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )/*使能線程本地存儲指針*/    {        for( x = 0; x < ( UBaseType_t )configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )        {            pxTCB->pvThreadLocalStoragePointers[ x ] = NULL;        }    }    #endif     #if ( configUSE_TASK_NOTIFICATIONS == 1 )   /*使能任務通知功能*/    {        pxTCB->ulNotifiedValue = 0;        pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;    }    #endif     #if ( configUSE_NEWLIB_REENTRANT == 1 )     /*使用Newlib*/    {        _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) );    }    #endif     #if( INCLUDE_xTaskAbortDelay == 1 )    {        pxTCB->ucDelayAborted = pdFALSE;    }    #endif}

3.初始化任務堆棧

      調用函數pxPortInitialiseStack()初始化任務堆棧,并將最新的棧頂指針賦值給任務TCB的pxTopOfStack字段。

      調用函數pxPortInitialiseStack()后,相當于執行了一次系統節拍時鐘中斷:將一些重要寄存器入棧。雖然任務還沒開始執行,也并沒有中斷發生,但看上去就像寄存器已經被入棧了,并且部分堆棧值被修改成了我們需要的已知值。對于不同的硬件架構,入棧的寄存器也不相同,所以我們看到這個函數是由移植層提供的。對于Cortex-M3架構,需要依次入棧xPSR、PC、LR、R12、R3~R0、R11~R4,假設堆棧是向下生長的,初始化后的堆棧如圖3-1所示。

      在圖3-1中我們看到寄存器xPSR被初始為0x01000000,其中bit24被置1,表示使用Thumb指令;寄存器PC被初始化為任務函數指針vTask_A,這樣當某次任務切換后,任務A獲得CPU控制權,任務函數vTask_A被出棧到PC寄存器,之后會執行任務A的代碼;LR寄存器初始化為函數指針prvTaskExitError,這是由移植層提供的一個出錯處理函數。當中斷發生時,LR被設置成中斷要返回的地址,但是每個任務都是一個死循環,正常情況下不應該退出任務函數,所以一旦從任務函數退出,說明那里出錯了,這個時候會調用寄存器LR指向的函數來處理這個錯誤,即prvTaskExitError;根據ATPCS(ARM-Thumb過程調用標準),我們知道子函數調用通過寄存器R0~R3傳遞參數,在文章的最開始講xTaskCreate()函數時,提到這個函數有一個空指針類型的參數pvParameters,當任務創建時,它作為一個參數傳遞給任務,所以這個參數被保存到R0中,用來向任務傳遞參數。

      任務TCB結構體成員pxTopOfStack表示當前堆棧的棧頂,它指向最后一個入棧的項目,所以在圖中它指向R4,TCB結構體另外一個成員pxStack表示堆棧的起始位置,所以在圖中它指向堆棧的最開始處。

FreeRTOS任務創建的方法

圖3-1:初始化任務堆棧

4.進入臨界區

      調用taskENTER_CRITICAL()進入臨界區,這是一個宏定義,最終進入臨界區的代碼由移植層提供。

5.當前任務數量增加1

      在tasks.c中 ,定義了一些靜態私有變量,用來跟蹤任務的數量或者狀態等等,其中變量uxCurrentNumberOfTasks表示當前任務的總數量,每創建一個任務,這個變量都會增加1。

6.為第一次運行做必要的初始化

      如果這是第一個任務(uxCurrentNumberOfTasks等于1),則調用函數prvInitialiseTaskLists()初始化任務列表。FreeRTOS使用列表來跟蹤任務,在tasks.c中,定義了靜態類型的列表變量:

PRIVILEGED_DATAstatic List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*按照優先級排序的就緒態任務*/PRIVILEGED_DATAstatic List_t xDelayedTaskList1;                        /*延時的任務 */PRIVILEGED_DATAstatic List_t xDelayedTaskList2;                        /*延時的任務 */PRIVILEGED_DATAstatic List_t xPendingReadyList;                        /*任務已就緒,但調度器被掛起 */ #if (INCLUDE_vTaskDelete == 1 )    PRIVILEGED_DATA static List_t xTasksWaitingTermination;             /*任務已經被刪除,但內存尚未釋放*/#endif #if (INCLUDE_vTaskSuspend == 1 )    PRIVILEGED_DATA static List_t xSuspendedTaskList;                   /*當前掛起的任務*/#endif

      現在這些列表都要進行初始化,會調用API函數vListInitialise()初始化列表,這個函數在《FreeRTOS高級篇1---FreeRTOS列表和列表項》中講過,每個列表的初始化方式都是相同的,以就緒態列表pxReadyTasksLists[0]為例,初始化后如圖6-1所示:

FreeRTOS任務創建的方法

圖6-1:初始化后的列表

      函數prvInitialiseTaskLists()的源代碼如下所示:

static void prvInitialiseTaskLists( void ){UBaseType_tuxPriority;     for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )    {        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );    }     vListInitialise( &xDelayedTaskList1 );    vListInitialise( &xDelayedTaskList2 );    vListInitialise( &xPendingReadyList );     #if ( INCLUDE_vTaskDelete == 1 )    {        vListInitialise( &xTasksWaitingTermination );    }    #endif /* INCLUDE_vTaskDelete */     #if ( INCLUDE_vTaskSuspend == 1 )    {        vListInitialise( &xSuspendedTaskList );    }    #endif /* INCLUDE_vTaskSuspend */     /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskListusing list2. */    pxDelayedTaskList = &xDelayedTaskList1;    pxOverflowDelayedTaskList = &xDelayedTaskList2;}

7.更新當前正在運行的任務TCB指針

      tasks.c中定義了一個任務TCB指針型變量:

      PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB= NULL;

      這是一個全局變量,在tasks.c中只定義了這一個全局變量。這個變量用來指向當前正在運行的任務TCB,我們需要多了解一下這個變量。FreeRTOS的核心是確保處于優先級最高的就緒任務獲得CPU運行權。在下一章講述任務切換時會知道,任務切換就是找到優先級最高的就緒任務,而找出的這個最高優先級任務的TCB,就被賦給變量pxCurrentTCB。

      如果調度器還沒有準備好(程序剛開始運行時,可能會先創建幾個任務,之后才會啟動調度器),并且新創建的任務優先級大于變量pxCurrentTCB指向的任務優先級,則設置pxCurrentTCB指向當前新創建的任務TCB(確保pxCurrentTCB指向優先級最高的就緒任務)。

if( xSchedulerRunning == pdFALSE ){    if( pxCurrentTCB->uxPriority <= uxPriority )    {        pxCurrentTCB = pxNewTCB;    }    else    {        mtCOVERAGE_TEST_MARKER();    }}

8.將新創建的任務加入就緒列表數組

      調用prvAddTaskToReadyList(pxNewTCB)將創建的任務TCB加入到就緒列表數組中,任務的優先級確定了加入到就緒列表數組的哪個下標。比如我們新創建的任務優先級為1,則這個任務被加入到列表pxReadyTasksLists[1]中。

       prvAddTaskToReadyList()其實是一個宏,由一系列語句組成,去除其中的跟蹤宏外,這個宏定義如下所示:

#defineprvAddTaskToReadyList( pxTCB )                        \    taskRECORD_READY_PRIORITY( ( pxTCB)->uxPriority );       \    vListInsertEnd( &( pxReadyTasksLists[ (pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );

      宏taskRECORD_READY_PRIORITY()用來更新變量uxTopReadyPriority,這個變量在tasks.c中定義為靜態變量,記錄處于就緒態的最高任務優先級。這個變量參與了FreeRTOS的最核心代碼:確保處于優先級最高的就緒任務獲得CPU運行權。它在這里參與如何最快的找到優先級最高的就緒任務。為了最快,不同的架構會各顯神通,一些架構還有特殊指令可用,所以這個宏由移植層提供。我們會在下一章介紹任務切換時,以Cortex-M3架構為例,詳細介紹如何最快的找到優先級最高的就緒任務。

      函數vListInsertEnd()將列表項插入到列表末端,在《FreeRTOS高級篇1---FreeRTOS列表和列表項》中已經提到過,這里會結合著例子再看一下這個函數。從前面我們直到,在調用函數vListInsertEnd()之前,就緒列表pxReadyTasksLists[1]和任務TCB的狀態列表項xStateListItem都已經初始化好了,見圖6-1和圖2-1,為了方便查看,我們將這兩幅圖合成一副,見圖8-1。

FreeRTOS任務創建的方法

圖8-1:初始化后的列表和列表項

         調用vListInsertEnd(a,b)會將列表項b,插入到列表a的后面,函數執行完畢后,列表和列表項的關系如圖8-2所示。

FreeRTOS任務創建的方法

圖8-2:插入一個列表項后的列表

      在此基礎上,假設又創建了任務B,任務A和任務B優先級相同,都為1。和任務A一樣,任務B也有它自己的任務TCB,其中的狀態列表項字段xStateListItem也要插入到列表pxReadyTasksLists[1]中,新的列表和列表項如圖8-3所示。

FreeRTOS任務創建的方法

圖8-3:相同優先級就緒列表掛接兩個列表項

9.退出臨界區

      調用taskEXIT_CRITICAL()退出臨界區,這是一個宏定義,最終退出臨界區的代碼由移植層提供。

10.執行上下文切換

    如果上面的步驟都正確執行,并且調度器也開始工作,則判斷當前任務的優先級是否大于新創建的任務優先級。如果新創建的任務優先級更高,則調用taskYIELD_IF_USING_PREEMPTION()強制進行一次上下文切換,切換后,新創建的任務將獲得CPU控制權,精簡后的代碼如下所示。

 if( xReturn == pdPASS )    {        if( xSchedulerRunning != pdFALSE )        {            /* 如果新創建的任務優先級大于當前任務優先級,則新創建的任務應該被立即執行。*/            if(pxCurrentTCB->uxPriority < uxPriority )            {                taskYIELD_IF_USING_PREEMPTION();            }        }    }

“FreeRTOS任務創建的方法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

兰坪| 孝感市| 凤台县| 隆林| 于田县| 南召县| 华坪县| 东至县| 成武县| 苏尼特右旗| 霍州市| 镇康县| 和顺县| 宜宾县| 昌平区| 宜阳县| 旺苍县| 辽宁省| 莒南县| 仙桃市| 仲巴县| 喀什市| 察隅县| 沧源| 田阳县| 平罗县| 漾濞| 镇原县| 夏邑县| 三门县| 宣武区| 和平区| 岑溪市| 张家界市| 汝南县| 图们市| 盖州市| 德州市| 隆回县| 邳州市| 博客|