創建任務
- 任務函數
void ATaskFunction( void *pvParameters );
FreeRTOS 任務不允許以任何方式從實現函數中返回——它們絕不能有一條”return”語句,也不能執行到函數末尾。如果一個任務不再需要,可以顯式地將其刪除
void ATaskFunction( void *pvParameters )
{
/* 可以像普通函數一樣定義變量。用這個函數創建的每個任務實例都有一個屬于自己的iVarialbleExample變
量。但如果iVariableExample被定義為static,這一點則不成立 – 這種情況下只存在一個變量,所有的任務實
例將會共享這個變量。 */
int iVariableExample = 0;
/* 任務通常實現在一個死循環中。 */
for( ;; )
{
/* 完成任務功能的代碼將放在這里。 */
}
/* 如果任務的具體實現會跳出上面的死循環,則此任務必須在函數運行完之前刪除。傳入NULL參數表示刪除
的是當前任務 */
vTaskDelete( NULL );
}
一個任務函數可以創建若干個函數,每一個函數獨立運行,擁有屬于自己的堆棧空間。
- 創建任務函數,刪除任務函數
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( ;; );
}
- 優先級
- 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();
}
}