FreeRTOS的設計小巧且簡易,整個核心代碼只有3到4個C文件,爲了讓代碼容易閱讀、移植和維護,大部分的代碼都是以C語言編寫,只有一些函數(多數是架構特定排班副程序)採用彙編語言編寫。
FreeRTOS提供許多方法以實現多線程(threads)、多作業(task)、互斥鎖(mutex)、信號量(semaphore)和軟件計時器(software timer),有個爲低耗電應用程序提供的無嘀嗒(tick-less)模式,線程的優先權管理也有支持。
FreeRTOS所有的工作都是在任務中完成的,所以我們來看看任務是怎麼創建的;
創建任務API函數
創建任務使用xTaskCreate函數
portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
const signed portCHAR * const pcName,
unsigned portSHORT usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pxCreatedTask );
參數名 | 描述 |
---|---|
pvTaskCode | 任務只是永不退出的C 函數,實現常通常是一個死循環。參數pvTaskCode 只一個指向任務的實現函數的指針(效果上僅僅是函數名)。 |
pcName | 具有描述性的任務名。這個參數不會被FreeRTOS 使用。其只是單純地用於輔助調試。識別一個具有可讀性的名字總是比通過句柄來識別容易得多。應用程序可以通過定義常量config_MAX_TASK_NAME_LEN 來定義任務名的最大長度——包括’\0’結束符。如果傳入的字符串長度超過了這個最大值,字符串將會自動被截斷。 |
usStackDepth | 當任務創建時,內核會分爲每個任務分配屬於任務自己的唯一狀態。usStackDepth 值用於告訴內核爲它分配多大的棧空間。這個值指定的是棧空間可以保存多少個字(word),而不是多少個字節(byte)。比如說,如果是32 位寬的棧空間,傳入的usStackDepth值爲100,則將會分配400 字節的棧空間(100 * 4bytes)。棧深度乘以棧寬度的結果千萬不能超過一個size_t 類型變量所能表達的最大值。應用程序通過定義常量configMINIMAL_STACK_SIZE 來決定空閒任務任用的棧空間大小。 |
pvParameters | 任務函數接受一個指向void 的指針(void*)。pvParameters 的值即是傳遞到任務中的值。這篇文檔中的一些範例程序將會示範這個參數可以如何使用。 |
uxPriority | 指定任務執行的優先級。優先級的取值範圍可以從最低優先級0 到最高優先級(configMAX_PRIORITIES – 1)。configMAX_PRIORITIES 是一個由用戶定義的常量。優先級號並沒有上限(除了受限於採用的數據類型和系統的有效內存空間),但最好使用實際需要的最小數值以避免內存浪費。如果uxPriority 的值超過了(configMAX_PRIORITIES – 1),將會導致實際賦給任務的優先級被自動封頂到最大合法值。 |
pxCreatedTask | pxCreatedTask 用於傳出任務的句柄。這個句柄將在API 調用中對該創建出來的任務進行引用,比如改變任務優先級,或者刪除任務。如果應用程序中不會用到這個任務的句柄,則pxCreatedTask 可以被設爲NULL。 |
返回值
1. pdTRUE
表明任務創建成功。
2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY
由於內存堆空間不足,FreeRTOS 無法分配足夠的空間來保存任務
結構數據和任務棧,因此無法創建任務。
例子
1.創建任務1
void Task1( void *pvParameters )
{
const char *pcTaskName = "Task1 output ........";
volatile unsigned long ul;
/* 和該任務處於一個死循環中。 */
while(1)
{
/* 輸出變量名,此處可以用自己方式驗證 */
vPrintString( pcTaskName );
/* 延遲,以產生一個週期 */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* 這個空循環是最原始的延遲實現方式。 */
}
}
}
1.創建任務2
void Task2( void *pvParameters )
{
const char *pcTaskName = "Task2 output ........";
volatile unsigned long ul;
/* 和該任務處於一個死循環中。 */
while(1)
{
/* 輸出變量名,此處可以用自己方式驗證 */
vPrintString( pcTaskName );
/* 延遲,以產生一個週期 */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* 這個空循環是最原始的延遲實現方式。 */
}
}
}
3、main()函數只是簡單地創建這兩個任務,然後啓動調度器
int main( void )
{
/* 創建第一個任務。需要說明的是一個實用的應用程序中應當檢測函數xTaskCreate()的返回值,以確保任
務創建成功。 */
xTaskCreate( Task1, /* 指向任務函數的指針 */
"Task 1", /* 任務的文本名字,只會在調試中用到 */
1000, /* 棧深度 – 大多數小型微控制器會使用的值會比此值小得多 */
NULL, /* 沒有任務參數 */
1, /* 此任務運行在優先級1上. */
NULL ); /* 不會用到任務句柄 */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( Task2, "Task 2", 1000, NULL, 1, NULL );
/* 啓動調度器,任務開始執行 */
vTaskStartScheduler();
/* 如果一切正常,main()函數不應該會執行到這裏。但如果執行到這裏,很可能是內存堆空間不足導致空閒
任務無法創建。第五章有講述更多關於內存管理方面的信息 */
while(1);
}
這樣兩個簡單的任務就創建完成了