FreeRTOS-03-其它任務相關函數

說明:
本文僅作為學習FreeRTOS的記錄文檔,作為初學者肯定很多理解不對甚至錯誤的地方,望網友指正。
FreeRTOS是一個RTOS(實時作業系統)系統,支援搶佔式、合作式和時間片調度。適用於微處理器或小型微處理器的實時應用。
本文檔使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1
參考文檔:《FreeRTOS_Reference_Manual_V10.0.0.pdf》《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》《STM32F4 FreeRTOS開發手冊_V1.1.pdf》
參考影片:正點原子FreeRTOS手把手教學-基於STM32_嗶哩嗶哩_bilibili

5 其它任務相關函數

介紹一些任務輔助函數,方便查詢任務的相關資訊。

5.1 設置任務優先順序

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority );

函數描述:設置任務優先順序。

函數參數:pxTask設置任務優先順序的任務句柄。如果任務設置自己的優先順序,這個參數可以填為NULL。

返回值:重新設置的任務優先順序值。0表示最低優先順序,configMAX_PRIORITIES – 1表示最高優先順序。

5.2 獲取任務優先順序

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );

函數描述:獲取任務優先順序。

函數參數:pxTask查詢任務優先順序的任務句柄。如果任務查詢自己的優先順序,這個參數可以填為NULL。

返回值:查詢任務的優先順序值。

測試程式碼:

configSTACK_DEPTH_TYPE Task_STACK_SIZE = 5;
UBaseType_t  Task_Priority = 12;

void task_code(void *para)
{
    static unsigned int cnt = 0;

    for (;;)
    {
        PRINT(" task cnt %u...", cnt);
        cnt++;
        vTaskDelay(1000);
    }
}

void task_func(void)
{
    TaskHandle_t xhandle;
    UBaseType_t  uxCreatedPriorty, uxOurPriorty;
    
    if (xTaskCreate(task_code, "demo task", 
        Task_STACK_SIZE, NULL, Task_Priority,
        &xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    } else
    {
        uxCreatedPriorty = uxTaskPriorityGet(xhandle);
        uxOurPriorty = uxTaskPriorityGet(NULL);
        PRINT("created task priority: %d", uxCreatedPriorty);
        PRINT("our task priority: %d", uxOurPriorty);

        vTaskPrioritySet(xhandle, 3);
        uxCreatedPriorty = uxTaskPriorityGet(xhandle);
        uxOurPriorty = uxTaskPriorityGet(NULL);
        PRINT("after changed, created task priority: %d", uxCreatedPriorty);
        PRINT("after changed, our task priority: %d", uxOurPriorty);
    }
}

默認創建任務優先順序為12,然後更改任務優先順序為3。

編譯,運行,結果如下:

$ ./build/freertos-simulator 
created task priority: 12
our task priority: 12
after changed, created task priority: 3
after changed, our task priority: 3
 task cnt 0...
 task cnt 1...
 ... ...

5.3 獲取系統中所有任務的狀態

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
UBaseType_t uxTaskGetSystemState( 
    TaskStatus_t * const pxTaskStatusArray,
	const UBaseType_t uxArraySize,
	unsigned long * const pulTotalRunTime );

函數描述:獲取系統中所有任務的任務狀態。TaskStatus_t是一個保存任務狀態資訊的結構體,結構體中包括任務句柄、任務名稱、堆棧、優先順序等資訊。要使用這個函數需要打開configUSE_TRACE_FACILITY宏。

函數參數:pxTaskStatusArray:指向TaskStatus_t數據結構數組的首地址,每個任務至少包含一個TaskStatus_t結構體。任務的結構體數目可以使用uxTaskGetNumberOfTasks函數獲得。

uxArraySize:保存任務狀態數組的數組的大小。

pulTotalRunTime:如果configGENERATE_RUN_TIME_STATS配置為1,這個參數保存系統總的運行時間。

返回值:統計到的任務狀態的數目,也就是pxTaskStatusArray數組成員個數,如果uxArraySize參數太小,返回值可能為0。

TaskStatus_t結構體定義如下:

typedef struct xTASK_STATUS
{
    TaskHandle_t xHandle;                       //任務句柄
    const char * pcTaskName;                    //任務名字
    UBaseType_t xTaskNumber;                    //任務編號
    eTaskState eCurrentState;                   //任務當前狀態
    UBaseType_t uxCurrentPriority;            	//任務當前優先順序  
    UBaseType_t uxBasePriority;                 //任務基礎優先順序
    uint32_t ulRunTimeCounter;                  //任務運行總時間
    StackType_t * pxStackBase;                  //堆棧基地址
    configSTACK_DEPTH_TYPE usStackHighWaterMark;//從任務創建以來任務堆棧剩餘的最小大小,這個值太小說明堆棧有溢出的風險。
} TaskStatus_t;

5.4 獲取系統中單個任務的狀態

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void vTaskGetTaskInfo( TaskHandle_t xTask,
	TaskStatus_t *pxTaskStatus,
	BaseType_t xGetFreeStackSpace,
	eTaskState eState );

函數描述:獲取單個任務的任務狀態。要使用這個函數需要打開configUSE_TRACE_FACILITY宏。

函數參數:xTask:任務句柄;pxTaskStatus:存放獲取的任務狀態資訊;

xGetFreeStackSpace:TaskStatus_t結構中有個成員usStackHighWaterMark存放了任務創建以來任務堆棧剩餘的最小大小,但是計算這個值需要一些時間,所以可以通過設置xGetFreeStackSpace值為pdFALSE來跳過這個步驟,當設置為pdTRUE才會檢查堆棧剩餘的最小大小。

eState:TaskStatus_t結構中有個成員eCurrentState存放任務的當前運行狀態,但是獲取任務狀態需要花費不少時間,可通過參數eState直接將任務狀態賦值給eCurrentState。也可以將eStates設置為eInvalid,那麼任務狀態資訊有函數vTaskGetInfo()函數獲取。

測試程式碼:

configSTACK_DEPTH_TYPE Task_STACK_SIZE = 20;
UBaseType_t  Task_Priority = 12;

void task_code(void *para)
{
    static unsigned int cnt = 0;

    for (;;)
    {
        PRINT(" task cnt %u...", cnt);
        cnt++;
        vTaskDelay(1000);
    }
}

void task_func(void)
{
    TaskHandle_t xhandle;
    TaskStatus_t xTaskDetails;
    char *state_str[] = {"running", "ready", "blocked", "suspended", "deleted", "invalid"};
    
    if (xTaskCreate(task_code, "demo task", 
        Task_STACK_SIZE, NULL, Task_Priority,
        &xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    } else
    {
        vTaskPrioritySet(xhandle, 3);
        vTaskGetTaskInfo(xhandle, &xTaskDetails, pdTRUE, eInvalid);
        PRINT("Task name           : %s", xTaskDetails.pcTaskName);
        PRINT("Task number         : %d", xTaskDetails.xTaskNumber);
        PRINT("Task CurrentState   : %s", state_str[xTaskDetails.eCurrentState]);
        PRINT("Task CurrentPriority: %d", xTaskDetails.uxCurrentPriority);
        PRINT("Task BasePriority   : %d", xTaskDetails.uxBasePriority);
        PRINT("Task RunTimeCounter : %d", xTaskDetails.ulRunTimeCounter);
        PRINT("Task StackBase      : %p", xTaskDetails.pxStackBase);
        PRINT("Task StackHighWaterMark: %u", xTaskDetails.usStackHighWaterMark);
    }
}

編譯,運行,結果如下:

$ ./build/freertos-simulator 
Task name           : demo task
Task number         : 1
Task CurrentState   : running
Task CurrentPriority: 3
Task BasePriority   : 3
Task RunTimeCounter : 0
Task StackBase      : 0x2060010
Task StackHighWaterMark: 15
 task cnt 0...
 task cnt 1...

5.5 獲取調度器運行狀態

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
BaseType_t xTaskGetSchedulerState( void );

函數描述:獲取調度器當前的運行狀態。使用這個函數需要將宏INCLUDE_xTaskGetSchedulerState置為1。

函數參數:

返回值:INCLUDE_xTaskGetSchedulerState:調度器未啟動。調度器啟動使用vTaskStartSchedule()函數完成,所以xTaskGetSchedulerState()函數在vTaskStartSchedule()函數之前調用會返回這個值。

taskSCHEDULER_RUNNING:調度器正在運行。

taskSCHEDULER_SUSPENDED:調度器被掛起,因為調用了vTaskSuspendAll()函數。

5.6 獲取任務運行狀態

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
eTaskState eTaskGetState( TaskHandle_t pxTask );

函數描述:獲取任務的運行狀態。使用此函數需要將INCLUDE_eTaskGetState宏置為1。

函數參數:xTask:要獲取的任務句柄

返回值:任務的運行狀態,eTaskState是一個枚舉變數。

/* 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 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. */
    eInvalid          /* Used as an 'invalid state' value. */
} eTaskState;

5.7 設置任務的tag(標籤)值

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxTagValue );

函數描述:設置任務的標籤值,標籤值的具體函數和用法由用戶決定。FreeRTOS內核不會使用這個標籤值。如果要使用這個函數必須將configUSE_APPLICATION_TASK_TAG宏置為1。

函數參數:xTask:任務句柄,如果設為NULL表示設置自身任務的標籤值。

pxTagValue:要設置的標籤值,這是一個TaskHookFunction_t類型的函數指針,也可以設置為其它值。

返回值:

5.8 獲取任務的tag(標籤)值

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask );

函數描述:獲取任務的tag(標籤)值,任務控制塊中有個成員變數pxTaskTag來保存任務的標籤值。標籤的功能由用戶決定。內核一般不會使用這個標籤值。使用這個函數需要將configUSE_APPLICATION_TASK_TAG 宏置為1。

函數參數:xTask:任務句柄

返回值:任務的標籤值。

5.9 獲取當前任務句柄

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
TaskHandle_t xTaskGetCurrentTaskHandle( void );

函數描述:獲取當前任務(運行態)的任務的句柄。其實獲取到的就是任務控制塊。使用這個函數需要將INCLUDE_xTaskGetCurrentTaskHandle宏置為1。

函數參數:

返回值:當前任務的任務句柄。

5.10 獲取某個任務句柄

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );

函數描述:根據任務名回去任務句柄。使用xTaskCreate()或者xTaskCreateStatic()函數創建任務時,有一個pcName參數,這個參數就是存放的任務名。xTaskGetHandle就是通過這個任務名來查詢任務句柄的。使用這個函數必須將INCLUDE_xTaskGetHandle宏置為1。

函數參數:任務名,C語言字元串。

返回值:沒有找到pcNameToQuery對應的任務返回NULL;找到了返回對應的任務句柄。

5.11 獲取空閑任務的句柄

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
TaskHandle_t xTaskGetIdleTaskHandle( void );

函數描述:獲取空閑任務的任務句柄。使用這個函數需要將INCLUDE_xTaskGetIdleTaskHandle宏置為1。

函數參數:

返回值:空閑函數的任務句柄。

5.12 檢查任務堆棧剩餘大小

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

函數描述:每個任務都有自己的堆棧。當任務創建的時候指定了堆棧的大小。這個函數用於檢查任務從創建到現在的歷史剩餘最小值。值越小說明堆棧溢出的可能性越大。FreeRTOS把這個歷史最小值叫做」高水位線「。使用此函數需要將INCLUDE_uxTaskGetStackHighWaterMark宏設置為1。

函數參數:xTask:要查詢的任務句柄。參數為NULL表示查詢任務自身。

返回值:任務堆棧的」高水位線「值,也就是歷史剩餘最小值。

5.13 獲取任務名

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
char * pcTaskGetName( TaskHandle_t xTaskToQuery );

函數描述:獲取任務的任務名。

函數參數:xTask:要查詢的任務句柄,此參數為NULL表示查詢自身任務。

返回值:返回任務所對應的任務名。

5.14 查詢任務調度器計數器值

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
TickType_t xTaskGetTickCount( void );

函數描述:查詢任務調度器從啟動到現在時間計數器xTickCount的值。

函數參數:

返回值:時間計數器xTickCount的值

xTickCount:是系統的時鐘節拍值,並不是真實的時間值。每個滴答定時器中斷xTickCount就會加1,1秒滴答定時器中斷多少次取決於宏configTICK_RATE_HZ。理論上xTickCount存在溢出的問題,但是這個溢出對內核沒有影響,如果用戶有使用的話就要考慮溢出。什麼時候溢出取決於宏configUSE_16_BIT_TICKS,此宏為1的時候xTickCount為16位的變數,此宏為0的時候xTickCount為32位的變數。

5.15 獲取系統任務數

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
UBaseType_t uxTaskGetNumberOfTasks( void );

函數描述:獲取系統當前任務數

函數參數:

返回值:當前系統中的任務數量。包括掛起態、阻塞態、就緒態、空閑任務、運行態任務。

5.16 獲取任務列表

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void vTaskList( char *pcWriteBuffer );

函數描述:獲取任務的詳細資訊。函數會創建一個表格來描述每個任務的詳細資訊。使用這個函數必須將configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS宏置為1。

函數參數:pcWriteBuffer保存任務狀態資訊的存儲,這個存儲要足夠大。

返回值:無。

任務的詳細資訊如下:

image-20210801205218591

Name:任務名

State:任務狀態。X:任務正在執行;B:阻塞態;R:就緒態;S:掛起態;D:任務已經被刪除。

Priority:任務優先順序

Stack:任務堆棧」高水位線「,也就是堆棧歷史最小剩餘大小。

Num:任務編號,這個編號是唯一的。當多個任務使用同一個任務名時,可以使用這個編號進行區分。

5.17 統計任務的運行時間資訊

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void vTaskGetRunTimeStats( char *pcWriteBuffer );

函數描述:獲取任務的運行時間統計資訊。使用這個函數必須將configGENERATE_RUN_TIME_STATS和configUSE_STATS_FORMATTING_FUNCTIONS宏置為1。

如果宏configGENERATE_RUN_TIME_STATS設置為1,還需要定義下列的宏:

portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():此宏用來初始化一個外設來提供時間統計功能所需要的時基,一般是定時器/計數器。這個時基的解析度一定要比FreeRTOS的系統時鐘高,一般設置為比系統時鐘高10~20倍。

portGET_RUN_TIME_COUNTER_VALUE()或者portALT_GET_RUN_TIME_COUNTER_VALUE(Time):這兩個宏實現其中一個即可。用於獲取當前的時基的時間值。

函數參數:pcWriteBuffer保存任務運行時間資訊的存儲,這個存儲要足夠大。

返回值:

任務的運行時間統計資訊如下:

image-20210801210902871

任務的統計資訊提供了每個任務獲取到的CPU使用權總的時間。表裡面提供了每個任務的運行時間和其所佔總時間的百分比。

5.18 設置執行緒本地存儲指針的值

函數原型:

#include 「FreeRTOS.h
#include 「task.h」
void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,
                                       BaseType_t xIndex, void *pvValue );

函數描述:此函數用於設置執行緒本地存儲指針的值,每個任務都有自己的指針數組來作為執行緒本地存儲,使用這些執行緒本地存儲可以用來在任務控制塊中存儲一些應用資訊,這些資訊只屬於任務自己。執行緒本地存儲指針數組的大小由configNUM_THREAD_LOCAL_STORAGE_POINTERS宏決定。如果要使用這個函數,這個宏就能設置為0。

函數參數:xTaskToSet:任務句柄,如果設為NULL表示自身任務。xIndex:要設置的執行緒本地存儲指針數組的索引。pvValue:要存儲的值。

返回值:

5.19 獲取執行緒本地存儲指針的值

函數原型:

#include 「FreeRTOS.h」
#include 「task.h」
void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery,
                                         BaseType_t xIndex );

函數描述:此函數用於獲取執行緒本地存儲指針的值,如果要使用這個函數configNUM_THREAD_LOCAL_STORAGE_POINTERS宏不能設置為0。

函數參數:xTaskToQuery:任務句柄,如果設為NULL表示自身任務。xIndex:要設置的執行緒本地存儲指針數組的索引。

返回值:獲取到的執行緒本地存儲指針的值。

5.19 任務狀態查詢API函數實驗

目的:學習使用任務狀態查詢相關API函數,包括uxTaskGetSystemState()、vTaskGetInfo()、eTaskGetState()、vTaskList()。

設計:創建query_task,用於任務狀態和資訊查詢任務,此任務中使用任務狀態和資訊相關的API函數。創建print_task:間隔1s不停列印計數資訊,提示系統正在運行。

測試程式碼:

configSTACK_DEPTH_TYPE Print_Task_STACK_SIZE = 5;
UBaseType_t  Print_Task_Priority = 1;
TaskHandle_t Print_xhandle;

configSTACK_DEPTH_TYPE Query_Task_STACK_SIZE = 20;
UBaseType_t  Query_Task_Priority = 2;
TaskHandle_t Query_xhandle;

char InfoBuffer[1000];
void print_task_code(void *para)
{
    static unsigned int cnt = 0;

    for (;;)
    {
        PRINT(" print task cnt %u...", cnt);
        cnt++;
        vTaskDelay(1000);
    }
}

void query_task_code(void *para)
{
    unsigned int totalRunTime;
    UBaseType_t  arraySize, x;
    TaskStatus_t *statusArray;

    PRINT("----------- uxTaskGetSystemState() ---------------");
    arraySize = uxTaskGetNumberOfTasks();
    statusArray = pvPortMalloc(arraySize * sizeof(TaskStatus_t));
    if (statusArray != NULL)
    {
        arraySize = uxTaskGetSystemState(statusArray, arraySize, &totalRunTime);
        PRINT("TaskName      TaskPriority    TaskNumber");
        for (x = 0; x < arraySize; x++)
        {
            PRINT("%-16s%-16d%-2d",
                statusArray[x].pcTaskName,
                statusArray[x].uxCurrentPriority,
                statusArray[x].xTaskNumber);
        }
    }
    vPortFree(statusArray);
    PRINT("----------- uxTaskGetSystemState() end -----------\n");
    
    PRINT("----------- vTaskGetInfo() ----------------");
    TaskHandle_t taskHandle;
    TaskStatus_t taskStatus;
    taskHandle = xTaskGetHandle("print task");
    vTaskGetInfo(taskHandle, &taskStatus, pdTRUE, eInvalid);
    PRINT("              task name: %s", taskStatus.pcTaskName);
    PRINT("            task number: %d", taskStatus.xTaskNumber);
    PRINT("             task state: %d", taskStatus.eCurrentState);
    PRINT("  task current priority: %d", taskStatus.uxCurrentPriority);
    PRINT("     task base priority: %d", taskStatus.uxBasePriority);
    PRINT("task stack base address: 0x%x", taskStatus.pxStackBase);
    PRINT("   task high water mark: %d", taskStatus.usStackHighWaterMark);
    PRINT("  task run time counter: %d",taskStatus.ulRunTimeCounter);
    PRINT("----------- vTaskGetInfo() end ------------\n");

    PRINT("----------- eTaskGetState() ----------------");
    eTaskState taskState;
    char *state_str[] = {"running", "ready", "blocked", "suspended", "deleted", "invalid"};
    taskHandle = xTaskGetHandle("query task");
    taskState = eTaskGetState(taskHandle);
    PRINT("task state:%s", state_str[taskState]);
    PRINT("----------- eTaskGetState() end ------------\n");

    PRINT("----------- vTaskList() ----------------");
    PRINT("Name         State  Priority   Stack   Num");
    PRINT("******************************************");
    vTaskList(InfoBuffer);
    PRINT("%s", InfoBuffer);
    PRINT("----------- vTaskList() end ------------\n");
    
    for (;;)
    {
        static unsigned int cnt = 0;
        PRINT(" query task cnt %u...", cnt);
        cnt++;
        vTaskDelay(1000);
    }
}

void creat_task(void)
{
    if (xTaskCreate(print_task_code, "print task", 
        Print_Task_STACK_SIZE, NULL, Print_Task_Priority,
        &Print_xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    }
    
    if (xTaskCreate(query_task_code, "query task", 
        Query_Task_STACK_SIZE, NULL, Query_Task_Priority,
        &Query_xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    }
    vTaskStartScheduler();
}

編譯、運行,結果如下:

$ ./build/freertos-simulator 
----------- uxTaskGetSystemState() ---------------
TaskName      TaskPriority    TaskNumber
query task      2               2 
print task      1               1 
IDLE            0               3 
Tmr Svc         30              4 
----------- uxTaskGetSystemState() end -----------

----------- vTaskGetInfo() ----------------
              task name: print task
            task number: 1
             task state: 1
  task current priority: 1
     task base priority: 1
task stack base address: 0x868010
   task high water mark: 0
  task run time counter: 0
----------- vTaskGetInfo() end ------------

----------- eTaskGetState() ----------------
task state:running
----------- eTaskGetState() end ------------

----------- vTaskList() ----------------
Name         State  Priority   Stack   Num
******************************************
query task      X       2       15      2
print task      R       1       0       1
IDLE            R       0       65      3
Tmr Svc         B       30      135     4

----------- vTaskList() end ------------

 query task cnt 0...
 print task cnt 0...
 query task cnt 1...
 print task cnt 1...

可以得到,「print task」任務處於就緒態,任務優先順序為1,棧空間已經用完了。任務編號為1。「query task」處於運行態,任務優先順序為2,棧空間剩餘15,任務編號為2。

Tags: