天天看點

【freertos】003-任務基礎知識

目錄

  • 前言
  • 3.1 任務概念
  • 3.2 任務狀态
  • 3.3 任務優先級
  • 3.4 空閑任務和空閑任務鈎子
    • 3.4.1 空閑任務
    • 3.4.2 空閑任務鈎子
    • 3.4.3 建立空閑鈎子
  • 3.5 建立任務
    • 3.5.1 任務參數相關概念
    • 3.5.2 建立靜态記憶體任務
      • 3.5.2.1 配置靜态記憶體
      • 3.5.2.2 實作空閑任務堆棧函數
      • 3.5.2.3 實作定時器任務堆棧函數
      • 3.5.2.4 配置記憶體對齊
      • 3.5.2.5 配置設定靜态記憶體
      • 3.5.2.6 建立任務原型
      • 3.5.2.7 建立任務
    • 3.5.3 建立動态記憶體任務
      • 3.5.3.1 配置動态記憶體
      • 3.5.3.2 任務句柄
      • 3.5.3.3 建立任務原型
      • 3.5.3.4 建立任務
  • 3.6 删除任務
    • 3.6.1 配置删除任務
    • 3.6.2 删除任務原型
  • 3.7 實戰

前言

資源:

  • https://www.freertos.org/taskandcr.html
  • 李柱明部落格

3.1 任務概念

程序:程序是程式執行的過程,是程式在執行過程中配置設定和管理資源的基本機關。擁有獨立的虛拟位址空間。

線程:線程是CPU排程和分派的基本機關。與其它同一程序的線程共享目前程序資源。

協程:比線程更加輕量級的存在,不是由作業系統核心管理,而是由程式控制的。其實就是在同一線程内時分地執行不同的子程式。(注意:不是函數調用)

還有管程、纖程。

并發:多個任務看起來是同時進行, 這是一種假并行。

并行:并行是指令同一時刻一起運作。

對于目前主流的RTOS的任務,大部分都屬于并發的線程。

因為MCU上的資源每個任務都是共享的,可以認為是單程序多線程模型。

3.2 任務狀态

freertos有四種狀态,每種狀态都有對應的狀态連結清單管理。

運作态:占用CPU使用權時的狀态。

就緒态:能夠運作(沒有被阻塞和挂起),但是目前沒有運作的任務的狀态。

阻塞态:由于等待信号量、消息隊列、事件标志組、調用延遲函數等而處于的狀态被稱之為阻塞态。

挂起态:調用函數vTaskSuspend()對指定任務進行挂起,挂起後這個任務将不被執行。

  • 調用函數xTaskResume()可退出挂起狀态。
  • 不可以指定逾時周期事件(不可以通過設定逾時事件而退出挂起狀态)

任務狀态轉換圖:

【freertos】003-任務基礎知識

3.3 任務優先級

每個任務被配置設定一個從0到(

configMAX_PRIORITIES

- 1)的優先級。

configMAX_PRIORITIES

是在

FreeRTOSConfig.h

檔案中被定義。

優先級數值越高,優先級越高。

idle任務的優先級為0。

多個任務可以共享一個任務優先級。

如果在FreeRTOSConfig.h檔案中配置宏定義

configUSE_TIME_SLICING

為1,或者沒有配置此宏定義,時間片排程都是使能的。

使能時間片後,處于就緒态的多個相同優先級任務将會以時間片切換的方式共享處理器。

如果硬體架構支援CLZ指令,可以使用該特性,使能配置如下:

  1. FreeRTOSConfig.h

    configUSE_PORT_OPTIMISED_TASK_SELECTION

    設定為1;
  2. 最大優先級數目

    configMAX_PRIORITIES

    不能大于CPU位數。

3.4 空閑任務和空閑任務鈎子

3.4.1 空閑任務

空閑任務是啟動RTOS排程器時由核心自動建立的任務,其優先級為0,確定系統中至少有一個任務在運作。

空閑任務可用來釋放RTOS配置設定給被删除任務的記憶體。

3.4.2 空閑任務鈎子

空閑任務鈎子是一個函數,每一個空閑任務周期被調用一次。

空閑任務鈎子應該滿足一下條件:

  1. 不可以調用可能引起空閑任務阻塞的API函數;
  2. 不應該陷入死循環,需要留出部分時間用于系統處理系統資源回收。

3.4.3 建立空閑鈎子

FreeRTOSConfig.h

頭檔案中設定

configUSE_IDLE_HOOK

為1;

定義一個函數,名字和參數原型如下所示:

void vApplicationIdleHook( void ); // FreeRTOS 規定了函數的名字和參數
           

一般設定CPU進入低功耗模式都是使用空閑任務鈎子函數實作的。

3.5 建立任務

任務的建立有兩種:建立靜态記憶體任務和建立動态記憶體任務。

3.5.1 任務參數相關概念

任務入口函數:即是任務函數,是該任務需要跑的函數。

任務名稱:即是任務名,主要用于調試。

任務堆棧大小:即是任務棧大小,機關是word。

任務入口函數參數:傳遞給任務入口函數的參數。在任務函數裡,通過形參獲得。

任務控制塊:主要用于核心管理任務,記錄任務資訊。

任務句柄:用于區分不同的任務,用于找到該任務的任務控制塊。

3.5.2 建立靜态記憶體任務

xTaskCreateRestrictedStatic()

,該函數不講解,因為需要MPU,想研究的同學可以參考:freertos官網API

3.5.2.1 配置靜态記憶體

建立靜态記憶體任務需要先實作以下内容:

  1. 需要在

    FreeRTOSConfig.h

    打開

    configSUPPORT_STATIC_ALLOCATION

    宏,開啟靜态記憶體。
  2. 開啟靜态記憶體的同時需要實作兩個函數:(使用靜态記憶體配置設定任務堆棧和任務控制塊記憶體)
    1. vApplicationGetIdleTaskMemory()

      :空閑任務堆棧函數。
    2. vApplicationGetTimerTaskMemory()

      :定時器任務堆棧函數。
  3. 注意靜态記憶體對齊。

3.5.2.2 實作空閑任務堆棧函數

實作該函數是為了給核心提供空閑任務關于空閑任務控制塊和空閑任務堆棧的相關資訊。

/* 空閑任務控制塊 */
static StaticTask_t Idle_Task_TCB;
/* 空閑任務任務堆棧 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];

/** @brief vApplicationGetIdleTaskMemory
  * @details 擷取空閑任務的任務堆棧和任務控制塊記憶體
  * @param 
  * @retval 
  * @author lizhuming
  */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
                                   StackType_t **ppxIdleTaskStackBuffer,
                                   uint32_t *pulIdleTaskStackSize)
{
    *ppxIdleTaskTCBBuffer = &Idle_Task_TCB; /* 任務控制塊記憶體 */
    *ppxIdleTaskStackBuffer = Idle_Task_Stack; /* 任務堆棧記憶體 */
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* 任務堆棧大小 */
}
           

3.5.2.3 實作定時器任務堆棧函數

實作該函數是為了給核心建立定時器任務時提供定時器任務控制塊和定時器任務堆棧的相關資訊。

/* 定時器任務控制塊 */
static StaticTask_t Timer_Task_TCB;
/* 定時器任務堆棧 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];

/** @brief vApplicationGetTimerTaskMemory
  * @details 擷取定時器任務的任務堆棧和任務控制塊記憶體
  * @param 
  * @retval 
  * @author lizhuming
  */
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
                                    StackType_t **ppxTimerTaskStackBuffer,
                                    uint32_t *pulTimerTaskStackSize)
{
    *ppxTimerTaskTCBBuffer = &Timer_Task_TCB;/* 任務控制塊記憶體 */
    *ppxTimerTaskStackBuffer = Timer_Task_Stack;/* 任務堆棧記憶體 */
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;/* 任務堆棧大小 */
}
           

3.5.2.4 配置記憶體對齊

記憶體對齊的配置在

portmacro.h

裡面的

portBYTE_ALIGNMENT

宏,按自己需求配置即可。

在任務堆棧初始化時會把棧頂指針糾正為記憶體對齊。參考下列代碼:

pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
           

糾正後可以通過以下代碼檢查是否正确的代碼如下:

configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
           

3.5.2.5 配置設定靜态記憶體

靜态記憶體配置設定是有編譯器決定的。

在freertos中,建立任務需要配置設定的記憶體主要是任務控制塊和任務堆棧。

/* 任務控制快 */
static StaticTask_t lzmStaticTestTaskTCB = {0};
/* 任務堆棧 */
static StackType_t lzmStaticTestTaskStack[256] = {0};
           

3.5.2.6 建立任務原型

建立任務函數原型:

TaskHandle_t xTaskCreateStatic( // 傳回任務句柄
                                TaskFunction_t pxTaskCode, // 任務入口函數
                                const char * const pcName, // 任務名稱
                                const uint32_t ulStackDepth, // 任務堆棧大小
                                void * const pvParameters, // 傳遞給任務入口函數的參數
                                UBaseType_t uxPriority, // 任務優先級
                                StackType_t * const puxStackBuffer, // 任務堆棧
                                StaticTask_t * const pxTaskBuffer ) // 任務控制塊
           

3.5.2.7 建立任務

/* 建立靜态記憶體任務 */
lzmStaticTestTaskHandle = xTaskCreateStatic((TaskFunction_t) lzmStaticTestTask, // 任務入口函數
                                            (const char*) "lzm static test task", // 任務函數名
                                            (uint32_t   )256, // 任務堆棧大小
                                            (void*      )NULL, // 傳遞給任務入口函數的參數
                                            (UBaseType_t)5, // 任務優先及
                                            (StackType_t*  )lzmStaticTestTaskStack, // 任務堆棧位址
                                            (StaticTask_t* )&lzmStaticTestTaskTCB); // 任務控制塊位址
           

3.5.3 建立動态記憶體任務

3.5.3.1 配置動态記憶體

動态記憶體配置是在

FreeRTOSConfig.h

配置的,這些記憶體主要供給FreeRTOS動态記憶體配置設定函數使用。

#define configTOTAL_HEAP_SIZE	( ( size_t ) ( 32 * 1024 ) ) // 系統總堆大小
           

而freertos的動态記憶體管理是有檔案

heap_x.c

實作的,具體實作算法,後面講到記憶體時會分析。

uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; // 系統總堆
           

3.5.3.2 任務句柄

static TaskHandle_t lzmTestTaskHandle = NULL;
           

3.5.3.3 建立任務原型

建立任務函數原型:

BaseType_t xTaskCreate( // 傳回任務句柄
                        TaskFunction_t pxTaskCode, // 任務入口函數
                        const char * const pcName, // 任務名稱
                        const configSTACK_DEPTH_TYPE usStackDepth, // 任務堆棧大小
                        void * const pvParameters, // 傳遞給任務入口函數的參數
                        UBaseType_t uxPriority, // 任務優先級
                        TaskHandle_t * const pxCreatedTask ) // 任務控制塊指針  
           

3.5.3.4 建立任務

/* 建立動态記憶體任務 */
xReturn = xTaskCreate((TaskFunction_t) lzmTestTask, // 任務入口函數
                      (const char*) "lzm test task", // 任務函數名
                      (uint16_t   )256, // 任務堆棧大小
                      (void*      )NULL, // 傳遞給任務入口函數的參數
                      (UBaseType_t)5, // 任務優先及
                      (TaskHandle_t* )&lzmTestTaskHandle); // 任務句柄
           

3.6 删除任務

3.6.1 配置删除任務

在檔案

FreeRTOSConfig.h

中,必須定義宏

INCLUDE_vTaskDelete

為 1,删除任務的API才會失效。

調用API删除任務後,将會從就緒、阻塞、暫停和事件清單中移除該任務。

如果是動态記憶體建立任務,删除任務後,其占用的空間資源有空閑任務釋放,是以删除任務後盡量保證空閑任務擷取一定的CPU時間。

如果是靜态記憶體建立任務,删除任務後,需要自己處理釋放任務占用的空間資源。

3.6.2 删除任務原型

void vTaskDelete( TaskHandle_t xTaskToDelete ); // 參數為任務句柄
           

注意:傳入的參數為任務句柄,當出入的參數為NULL時,表示删除調用者目前的任務。

3.7 實戰

源碼:拉取 freertos_on_linux_task_01 檔案夾

結果:

【freertos】003-任務基礎知識