【嵌入式——FreeRTOS】任務
- 任務創建和刪除
- 動態方式創建任務
- 靜態方式創建任務
- 刪除任務
- 任務切換
- 調度器
- 任務切換流程
- 任務掛起
- 任務恢復
- 相關API函數
任務創建和刪除
動態方式創建任務
任務的任務控制塊以及任務的棧空間所需的內存,均由freeRTOS從freeRTOS管理的堆中分配。此函數創建任務會立刻進入就緒態,由任務調度器調度運行。
任務的優先級,值越大,優先級越高。
函數
xTaskCreate();
//返回值為pdPASS,任務創建成功。
//返回值為errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,任務創建失敗BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //指向任務函數的指針const char * const pcName, //任務名字 最大長度configMAX_TASK_NAME_LEN(20)const configSTACK_DEPTH_TYPE usStackDepth, //任務堆棧大小 字為單位void * const pvParameters, //傳遞給任務函數的參數UBaseType_t uxPriority, //任務優先級 范圍 0 ~ configMAX_PRIORITIES(32)-1 TaskHandle_t * const pxCreatedTask ) //任務句柄,任務的任務控制塊
示例
static void udpserver_sendto_client (void* argument){}static TaskHandle_t udpserver_tid;#define UDPSERVER_THREAD_NAME "task name"
#define UDPSERVER_THREAD_STKSZ (configMINIMAL_STACK_SIZE * 4)
#define UDPSERVER_THREAD_PRIO (tskIDLE_PRIORITY + 3)BaseType_t ret = xTaskCreate(udpserver_sendto_client, UDPSERVER_THREAD_NAME, UDPSERVER_THREAD_STKSZ,
NULL, UDPSERVER_THREAD_PRIO, &udpserver_tid);
實現動態創建任務流程
- 將FreeRTOSConfig.h文件中的configSUPPORT_DYNAMIC_ALLOCATION宏配置為1;
- 定義函數入口參數;
- 編寫任務函數。
動態創建任務內部實現
- 申請堆棧內存和任務控制塊內存;
- TCB結構體成員賦值;(把前面申請的堆棧地址,賦值給控制塊的堆棧成員)
- 初始化控制塊中的成員
- 添加新任務到就緒列表。
靜態方式創建任務
任務的任務控制塊以及任務的棧空間所需的內存,需用戶分配提供。
函數
xTaskCreateStatic();
//返回值為句柄或者其他值,任務創建成功。
//返回值為NULL,任務創建失敗。TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, //指向任務函數的指針const char * const pcName, //任務名字 最大長度configMAX_TASK_NAME_LEN(20)const uint32_t ulStackDepth, //任務堆棧大小 字為單位void * const pvParameters, //傳遞給任務函數的參數UBaseType_t uxPriority, //任務優先級 范圍 0 ~ configMAX_PRIORITIES(32)-1 StackType_t * const puxStackBuffer, //任務堆棧,一般為數組由用戶分配StaticTask_t * const pxTaskBuffer ) //任務控制塊指針,由用戶分配PRIVILEGED_FUNCTION;
示例
#define STACK_SIZE 200//空閑任務配置
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];//軟件定時器任務配置
StaticTask_t time_task_tcb;
StackType_t time_task_stack[configTIMER_TASK_STACK_DEPTH];StaticTask_t xTaskBuffer;
StackType_t xStack[ STACK_SIZE ];//空閑任務內存分配
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer = &idle_task_tcb;*ppxIdleTaskStackBuffer = idle_task_stack;*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;}//軟件定時器內存分配
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,StackType_t ** ppxTimerTaskStackBuffer,uint32_t * pulTimerTaskStackSize )
{*ppxTimerTaskTCBBuffer = &time_task_tcb;*ppxTimerTaskStackBuffer = time_task_stack;pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;}void vTaskCode( void * pvParameters ){}TaskHandle_t xHandle = xTaskCreateStatic(vTaskCode, // Function that implements the task."NAME", // Text name for the task.STACK_SIZE, // Stack size in words, not bytes.( void * ) 1, // Parameter passed into the task.tskIDLE_PRIORITY,// Priority at which the task is created.xStack, // Array to use as the task's stack.&xTaskBuffer ); // Variable to hold the task's data structure.
靜態創建任務使用流程
-
將FreeRTOSConfig.h文件中的configSUPPORT_STATIC_ALLOCATION宏配置為1;
-
定義空閑任務和定時器任務的任務堆棧即TCB;
-
實現兩個接口函數
- vApplicationGetIdleTaskMemory()
- vApplicationGetTimerTaskMemory();可選的
-
定義函數入口參數;
-
編寫任務函數;
靜態創建任務內部實現
- TCB結構體成員賦值;
- 添加新任務到就緒列表;
刪除任務
用于刪除已經被創建的任務。被刪除的任務將從就緒態任務列表,阻塞態任務列表,掛起態任務列表和事件列表中移除。
函數
vTaskDelete();void vTaskDelete( TaskHandle_t xTaskToDelete )
//xTaskToDelete 待刪除的任務句柄
注意
- 當傳入的參數為NULL,則代表刪除任務自身(當前正在運行的任務)。
- 空閑任務會負責釋放被刪除任務中由系統分配的內存,但是由用戶在任務刪除前申請的內存,則需要由用戶在任務被刪除前提前釋放,否則將導致內存泄漏。
刪除任務流程
- 將INCLUDE_vTaskDelete宏配置為1;
- 入口參數輸入需要刪除的任務句柄(NULL代表自身);
刪除任務內部實現過程
-
獲取所要刪除任務的控制塊;
-
將被刪除的任務移除所在列表;
-
判斷所需刪除的任務
- 刪除任務自身,需先添加到等待刪除列表,內存釋放將在空閑任務進行。
- 刪除其他任務,釋放內存,任務數量–
-
更新下個任務的阻塞時間;
任務切換
調度器
實現任務間的切換。本質就是CPU寄存器的切換
//啟動任務,開啟調度
vTaskStartScheduler();
當由任務A切換到任務B時,主要分為兩步
第一步:需暫停任務A的執行,并將此時任務A的寄存器保存到任務堆棧,這個過程叫做保存現場。
第二步:將任務B的各個寄存器值(被存于任務堆棧中)恢復到CPU寄存器中,這個過程叫做恢復現場。對任務A保存現場,對任務B恢復現場,這個過程被稱為上下文切換。
任務切換流程
- 觸發PendSV中斷
- 當前的psp是正在運行的任務的棧指針,讀取當前psp進程指針,存入r0
- 壓棧(保存現場)
- 獲取當前最高優先級任務的任務控制塊
- 出棧(恢復現場)
- 更新切換后的任務的棧指針給psp
- bx r14指向新任務函數
PendSV中斷如何觸發
- 滴答定時器中斷調用。
- 執行FreeRTOS提供的相關API函數,portYIELD();
任務掛起
掛起任務
函數
此函數用于掛起任務,使用時將FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend配置為1。
無論優先級如何,被掛起的任務都將不再被執行,直到任務被恢復。
當傳入參數為NULL,則代表掛起任務自身(當前正在運行的任務)。
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;
//xTaskToSuspend 待掛起任務的句柄
任務恢復
恢復被掛起的任務
函數
此函數用于恢復任務,使用時將FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend配置為1。
任務無論被掛起多少次,只需在任務中調用vTaskResume()恢復一次,就可以繼續運行,且被恢復的任務會進入就緒狀態。
在中斷中恢復被掛起的任務。帶有“FromISR”后綴是在終端函數中專用的API函數
void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
//xTaskToResume 待恢復任務的任務句柄
此函數用于恢復任務,使用時將FreeRTOSConfig.h文件中宏INCLUDE_vTaskSuspend配置為1,宏INCLUDE_xTaskResumeFromISR配置為1。
被恢復的任務的優先級大于當前執行的任務的優先級,就會返回pdTRUE需要手動執行任務切換(portYIELD_FROM_ISR()函數)。
中斷服務程序中要調用freeRTOS的API函數則中斷優先級不能高于freeRTOS所管理的最高優先級(5~15)。
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
//xTaskToResume 待恢復任務的任務句柄
//返回值 pdTRUE 任務恢復后需要進行任務切換 pdFALSE任務恢復后不需要進行任務切換
相關API函數
函數 | 描述 |
---|---|
uxTaskPriorityGet() | 獲取任務優先級 |
vTaskPrioritySet() | 設置任務優先級 |
uxTaskGetNumberOfTasks() | 獲取系統中任務的數量 |
uxTaskGetSystemState() | 獲取所有任務狀態信息 |
vTaskGetInfo() | 獲取指定單個任務信息 |
xTaskGetCurrentTaskHandle() | 獲取當前任務的任何句柄 |
xTaskGetHandle() | 根據任務名獲取該任務的任何句柄 |
uxTaskGetStackHighWaterMark() | 獲取任務的任務棧歷史剩余最小值 |
eTaskGetState() | 獲取任務狀態 |
vTaskList() | 以表格形式獲取所有任務的信息 |
vTaskGetRunTimeStats() | 獲取任務的運行時間 |
更多API請查看官網