[FreeRTOS學習] 任務控制

創建任務

  1. 任務函數
void ATaskFunction( void *pvParameters );

FreeRTOS 任務不允許以任何方式從實現函數中返回——它們絕不能有一條”return”語句,也不能執行到函數末尾。如果一個任務不再需要,可以顯式地將其刪除

void ATaskFunction( void *pvParameters )
{
/* 可以像普通函數一樣定義變量。用這個函數創建的每個任務實例都有一個屬于自己的iVarialbleExample變
量。但如果iVariableExample被定義為static,這一點則不成立 – 這種情況下只存在一個變量,所有的任務實
例將會共享這個變量。 */
int iVariableExample = 0;
/* 任務通常實現在一個死循環中。 */
for( ;; )
{
/* 完成任務功能的代碼將放在這里。 */
}
/* 如果任務的具體實現會跳出上面的死循環,則此任務必須在函數運行完之前刪除。傳入NULL參數表示刪除
的是當前任務 */
vTaskDelete( NULL );
}

一個任務函數可以創建若干個函數,每一個函數獨立運行,擁有屬于自己的堆棧空間。

  1. 創建任務函數,刪除任務函數
voidvTaskDelete( TaskHandle_t xTask );
 xTaskCreate();
portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
                              const signed portCHAR * const pcName,
                              unsigned portSHORT usStackDepth,
                              void *pvParameters,
                              unsigned portBASE_TYPE uxPriority,
                              xTaskHandle *pxCreatedTask 
                             );

具體參數含義 見API文檔
Demo

void vTask1( void *pvParameters )
{
      const char *pcTaskName = "Task 1 is running\r\n";
      volatile unsigned long ul;
      /* 和大多數任務一樣,該任務處于一個死循環中。 */
      for( ;; )
      {
            /* Print out the name of this task. */
            vPrintString( pcTaskName );
            /* 延遲,以產生一個周期 */
            for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
            {
                  /* 這個空循環是最原始的延遲實現方式。在循環中不做任何事情。后面的示例程序將采用
                delay/sleep函數代替這個原始空循環。 */
            }
      }
  }
程序清單4 例1中的第一個任務實現代碼
void vTask2( void *pvParameters )
{
      const char *pcTaskName = "Task 2 is running\r\n";
      volatile unsigned long ul;
      /* 和大多數任務一樣,該任務處于一個死循環中。 */
      for( ;; )
      {
            /* Print out the name of this task. */
            vPrintString( pcTaskName );
            /* 延遲,以產生一個周期 */
            for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
            {
                  /* 這個空循環是最原始的延遲實現方式。在循環中不做任何事情。后面的示例程序將采用
                  delay/sleep函數代替這個原始空循環。 */
            }
      }
}
int main( void )
{
      /* 創建第一個任務。 需要說明的是一個實用的應用程序中應當檢測函      數xTaskCreate()的返回值,以確保任務創建成功。 */
      xTaskCreate( vTask1, /* 指向任務函數的指針 */
                    "Task 1", /* 任務的文本名字,只會在調試中用到 */
                    1000, /* 棧深度 – 大多數小型微控制器會使用的值會比此值小得多 */
                    NULL, /* 沒有任務參數 */
                    1, /* 此任務運行在優先級1上. */
                    NULL ); /* 不會用到任務句柄 */
      /* Create the other task in exactly the same way and at the same priority. */
      xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
      /* 啟動調度器,任務開始執行 */
      vTaskStartScheduler();
      /* 如果一切正常, main()函數不應該會執行到這里。但如果執行到這里,很可能是內存堆空間不足導致空閑
任務無法創建。第五章有講述更多關于內存管理方面的信息 */
      for( ;; );
}
  1. 優先級
  • vTaskPrioritySet() 修改優先級
  • 任意數量的任務可以共享同一個優先級——以保證最大設計彈性
  • 有效的優先級號范圍從 0 到(configMAX_PRIORITES – 1)
  • 被選中的優先級上具有不止一個任務,調度器會讓這些任務輪流執行,每個任務都執行一個”時間片”
  • 時間片的長度通過心跳中斷的頻率進行設定,configTICK_RATE_HZ 進行配置(如,設為100(HZ),則時間片長度為 10ms)
  • 常量 portTICK_RATE_MS 用于將以心跳為單位的時間值轉化
    為以毫秒為單位的時間值
  • 相關API
    • 相對延時
   void vTaskDelay( portTickTypexTicksToDelay )
常量portTICK_RATE_MS 用來輔助計算真實時間,此值是系統節拍時鐘中斷的周期,單位是毫秒。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必須設置成1,此函數才能有效。

比如vTaskDelay(100),那么從調用vTaskDelay()后,任務進入阻塞狀態,經過100個系統時鐘節拍周期,任務解除阻塞

調用vTaskDelay()到任務解除阻塞的時間不總是固定的并且該任務下一次調用vTaskDelay()函數的時間也不總是固定的(兩次執行同一任務的時間間隔本身就不固定,中斷或高優先級任務搶占也可能會改變每一次執行時間)。

  • 絕對延時

void vTaskDelayUntil( TickType_t *pxPreviousWakeTime,
const TickType_txTimeIncrement );

    * INCLUDE_vTaskDelayUntil 必須設置成1,此函數才有效
    * 這個函數不同于vTaskDelay()函數的一個重要之處在于:vTaskDelay()指定的延時時間是從調用vTaskDelay()之后(執行完該函數)開始算起的,但是vTaskDelayUntil()指定的延時時間是一個絕對時間。

  + 獲取任務優先級
       ```
      UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );
      ```
      * INCLUDE_vTaskPriorityGet必須設置成1
      * xTask:任務句柄。NULL表示獲取當前任務的優先級

  + 設置任務優先級
      ```
 void vTaskPrioritySet( TaskHandle_txTask,
                           UBaseType_tuxNewPriority );
* INCLUDE_vTaskPrioritySet 必須設置成1
  • 任務掛起

void vTaskSuspend( TaskHandle_txTaskToSuspend );

    * 被掛起的任務絕不會得到處理器時間
    * INCLUDE_vTaskSuspend必須設置成1
  + 恢復掛起任務
    ```
 void vTaskResume( TaskHandle_txTaskToResume );
* INCLUDE_vTaskSuspend必須置1
* 通過調用一次或多次vTaskSuspend()掛起的任務,可以調用一次vTaskResume ()函數來再次恢復運行
  • 恢復掛起的任務(在中斷服務函數中使用)

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume );

    * 用于恢復一個掛起的任務,用在ISR中
    *  INCLUDE_xTaskResumeFromISR 必須設置成1
    * xTaskResumeFromISR()不可用于任務和中斷間的同步,如果中斷恰巧在任務被掛起之前到達,這就會導致一次中斷丟失(任務還沒有掛起,調用xTaskResumeFromISR()函數是沒有意義的,只能等下一次中斷)。這種情況下,可以使用信號量作為同步機制。
    ```
xTaskHandlexHandle;               //注意這是一個全局變量  
 void vAFunction( void )  
 {  
     // 創建任務并保存任務句柄  
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
   
     // ... 剩余代碼.  
 }  
 void vTaskCode( void *pvParameters )  
 {  
     for( ;; )  
     {  
         // ... 在這里執行一些其它功能  
         // 掛起自己  
         vTaskSuspend( NULL );  
         //直到ISR恢復它之前,任務會一直掛起  
     }  
 }  
 void vAnExampleISR( void )  
 {  
     portBASE_TYPExYieldRequired;  
     // 恢復被掛起的任務  
     xYieldRequired = xTaskResumeFromISR(xHandle );  
     if( xYieldRequired == pdTRUE )  
     {  
         // 我們應該進行一次上下文切換  
         // 注:  如何做取決于你具體使用,可查看說明文檔和例程  
         portYIELD_FROM_ISR();  
     }  
 }  
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容