操作系統(tǒng)允許多個(gè)任務(wù)同時(shí)運(yùn)行,其任務(wù)調(diào)度器的責(zé)任就是:決定某一時(shí)刻究竟運(yùn)行那個(gè)任務(wù)。
有的OS調(diào)度方式:基于時(shí)間的任務(wù)調(diào)度,如Unix。但是,RTOS的任務(wù)調(diào)度器被設(shè)計(jì)為可預(yù)測(cè)的,這是實(shí)時(shí)系統(tǒng)所必須的,實(shí)時(shí)就要求os必須對(duì)某一個(gè)事件做出實(shí)時(shí)的響應(yīng)。所以實(shí)時(shí)系統(tǒng)的任務(wù)調(diào)度方法是非常重要的,如給任務(wù)分配優(yōu)先級(jí)。
實(shí)時(shí):硬實(shí)時(shí)和軟實(shí)時(shí)。
硬實(shí)時(shí):規(guī)定時(shí)間內(nèi)必須完成,不允許超時(shí)。
軟實(shí)時(shí):沒有那么嚴(yán)格。任務(wù)
把功能劃分為多個(gè)任務(wù),每個(gè)任務(wù)負(fù)責(zé)實(shí)現(xiàn)其中一部分,每個(gè)任務(wù)都是一個(gè)簡(jiǎn)單的程序,通常是死循環(huán)。RTOS核心內(nèi)容
實(shí)時(shí)內(nèi)核:可剝奪型內(nèi)核,內(nèi)核負(fù)責(zé)管理任務(wù),決定運(yùn)行哪個(gè)任務(wù),如某個(gè)任務(wù)告訴任務(wù)調(diào)度器表明自己準(zhǔn)備好,可以被調(diào)度執(zhí)行。
為什么選擇freeRTOS:
- 開源、免費(fèi)
- 很多半導(dǎo)體廠商的sdk使用freeRTOS
- 文件少,用C和匯編來寫,學(xué)習(xí)簡(jiǎn)單,可讀性極強(qiáng)。
一、FreeRTOS 系統(tǒng)配置和風(fēng)格
FreeRTOS的系統(tǒng)配置文件為:FreeRTOSConfig.h,可以完成FreeRTOS的裁剪和配置。
1.1 變量風(fēng)格
- 非stdint類型的變量使用前綴x,比如基本的Type_t和TickType_t類型,這些類型在移植層定義,定義成符合處理器架構(gòu)的最高效類型;
- size_t類型的變量使用前綴x
- 枚舉類型變量使用前綴e
- 指針類型變量在類型基礎(chǔ)上附加前綴p
1.2 函數(shù)
- 在文件作用域范圍的函數(shù)前綴為prv
- API函數(shù)的前綴為它們的返回類型,當(dāng)返回為空時(shí),前綴為v
- API函數(shù)名字起始部分為該函數(shù)所在的文件名。比如vTaskDelete函數(shù)定義在tasks.c,并且該函數(shù)返回空。
1.3 宏
- INCLUDE_ 開始的宏表示使能FreeRTOS中相應(yīng)的API函數(shù),F(xiàn)reeRTOS中的裁剪和配置就是通過這種用條件編譯的方法來實(shí)現(xiàn)的。條件編譯的好處就是節(jié)省空間,不需要的功能就不用編譯,減少占用ROM和RAM大小。
- 宏的名字起始部分為該宏定義所在的文件名的一部分。比如configUSE_PREEMPTION定義在FreeRTOSConfig.h文件中。
1.4 misc
- 注釋:注釋單行不超過80列,特殊情況除外。不使用C++風(fēng)格的雙斜線(//)注釋,使用 /*.. */
- 源碼包中,RTOS核心代碼位于三個(gè)源文件中,分別是tasks.c、queue.c和list.c
1.5 移植FreeRTOS
- 將tasks.c、queue.c和list.c這三個(gè)內(nèi)核代碼加入工程,將port.c和heap_1.c這兩個(gè)與處理器相關(guān)代碼加入工程。
- FreeRTOS內(nèi)核是高度可定制的,使用配置文件FreeRTOSConfig.h進(jìn)行定制。每個(gè)FreeRTOS應(yīng)用都必須包含這個(gè)頭文件,用戶根據(jù)實(shí)際應(yīng)用來裁剪FreeRTOS內(nèi)核。這個(gè)配置文件是針對(duì)用戶程序的,而非內(nèi)核,因此配置文件一般放在應(yīng)用程序目錄下,不要放在RTOS內(nèi)核源碼目錄下。配置文件具體的參數(shù)可以參考:FreeRTOS系列第6篇---FreeRTOS內(nèi)核配置說明。 如: configUSE_PREEMPTION: 為1時(shí)RTOS使用搶占式調(diào)度器,為0時(shí)RTOS使用協(xié)作式調(diào)度器(時(shí)間片)。
二、 FreeRTOS任務(wù)基礎(chǔ)知識(shí)
FreeRTOS的核心就是任務(wù)管理,必須掌握任務(wù)的創(chuàng)建、刪除、掛起和恢復(fù)等操作。
2.1 多任務(wù)系統(tǒng)
未使用系統(tǒng)時(shí),都是在main中使用while(1)做一個(gè)大循環(huán)來完成所有的處理,即應(yīng)用程序是一個(gè)無(wú)限的循環(huán),循環(huán)中調(diào)用相應(yīng)的函數(shù)完成所需的處理。中斷服務(wù)函數(shù)作為前臺(tái)程序,大循環(huán)while(1)作為后臺(tái)程序。

缺點(diǎn):實(shí)時(shí)性差,所有任務(wù)都是排隊(duì)等著輪流執(zhí)行,緊急的任務(wù)不能及時(shí)執(zhí)行,并且任務(wù)管理不方便。
多任務(wù)系統(tǒng):把一個(gè)大問題(應(yīng)用)“分而治之”,把大問題劃分成很多個(gè)小問題(小任務(wù)),逐步把小問題解決掉。這些小任務(wù)是并發(fā)處理的。那么哪個(gè)任務(wù)先執(zhí)行呢,可以通過任務(wù)調(diào)度器來完成。FreeRTOS是一個(gè)搶占式的實(shí)時(shí)多任務(wù)系統(tǒng)。
從圖中可以看出,中斷返回后會(huì)進(jìn)行一次任務(wù)調(diào)度。永遠(yuǎn)運(yùn)行的是就緒態(tài)優(yōu)先級(jí)最高的任務(wù)。

2.2 FreeRTOS任務(wù)與協(xié)程
FreeRTOS可以使用任務(wù)或協(xié)程(Co-Routine),但是任務(wù)和協(xié)程使用不同的API函數(shù),不能互傳數(shù)據(jù)。
FreeRTOS的一個(gè)實(shí)時(shí)應(yīng)用可以作為一個(gè)獨(dú)立的任務(wù),每個(gè)任務(wù)都有自己的運(yùn)行環(huán)境,不依賴于系統(tǒng)中其他的任務(wù)。FreeRTOS會(huì)重復(fù)的開啟、關(guān)閉每個(gè)任務(wù),職責(zé)是確保當(dāng)一個(gè)任務(wù)開始執(zhí)行時(shí),上下文環(huán)境(寄存器值、堆棧內(nèi)容)和任務(wù)上一次退出時(shí)相同。因此,每個(gè)任務(wù)都有堆棧。
- 任務(wù)特性
簡(jiǎn)單,沒有使用限制,支持搶占,支持優(yōu)先級(jí)
每個(gè)任務(wù)都擁有堆棧導(dǎo)致RAM使用量增大。
如果使用搶占的話,必須仔細(xì)考慮重入的問題。
協(xié)程:為了資源很少的MCU而做的。如今使用較少了。
2.2.1 任務(wù)狀態(tài)
運(yùn)行態(tài)、就緒態(tài)(可以運(yùn)行了)、阻塞態(tài)(等待某個(gè)外部事件)、掛起態(tài)
運(yùn)行態(tài):當(dāng)前運(yùn)行的
就緒態(tài):當(dāng)前已經(jīng)準(zhǔn)備好,告訴調(diào)度器可以被調(diào)度運(yùn)行了
阻塞態(tài):某個(gè)任務(wù)等待某個(gè)事件,必須等待事件發(fā)生才能被調(diào)度
掛起態(tài):暫時(shí)不運(yùn)行。

2.2.2 任務(wù)優(yōu)先級(jí)
每個(gè)任務(wù)都可以分配從0~(configMAX_PRIORITIES-1)的優(yōu)先級(jí),數(shù)字越大,優(yōu)先級(jí)越高,數(shù)字越低表示任務(wù)的優(yōu)先級(jí)越低,0的優(yōu)先級(jí)最低。
空閑任務(wù)是啟動(dòng)RTOS調(diào)度器時(shí)由內(nèi)核自動(dòng)創(chuàng)建的任務(wù),這樣可以確保至少有一個(gè)任務(wù)在運(yùn)行??臻e任務(wù)具有最低任務(wù)優(yōu)先級(jí)。
空閑任務(wù)鉤子是一個(gè)函數(shù),每一個(gè)空閑任務(wù)周期被調(diào)用一次。 通常,使用這個(gè)空閑鉤子函數(shù)設(shè)置CPU進(jìn)入低功耗模式。
FreeRTOS調(diào)度器確保:處于就緒態(tài)或運(yùn)行態(tài)的高優(yōu)先級(jí)任務(wù)獲取處理器使用權(quán),即就緒態(tài)的最高優(yōu)先級(jí)的任務(wù)才會(huì)運(yùn)行。
2.2.3 任務(wù)實(shí)現(xiàn)
任務(wù)實(shí)現(xiàn):即任務(wù)的具體工作內(nèi)容。
FreeRTOS中,可以使用xTaskCreate()或xTaskCreateStatic()來創(chuàng)建任務(wù),參數(shù)是pxTaskCode,就是這個(gè)任務(wù)的任務(wù)函數(shù)(就是完成本任務(wù)工作的函數(shù))
模板:
void vATaskFunction(void *pvParameters)
{
for(;;){
Specific code to implement the task; //任務(wù)應(yīng)用程序
vTaskDelay();//用來引起任務(wù)調(diào)度的函數(shù),這最好要有。
}
/* 不能從任務(wù)函數(shù)中返回或者退出,如果使用則調(diào)用configASSERT(),如果一定要從任務(wù)函數(shù)中退出的話,一定要調(diào)用函數(shù) vTaskDelete(NULL);來刪除此任務(wù)。*/
vTaskDelete(NULL);
}
example:
//創(chuàng)建開始任務(wù)
xTaskCreate((TaskFunction_t )start_task, //任務(wù)函數(shù)
(const char* )"start_task", //任務(wù)名稱
(uint16_t )START_STK_SIZE, //任務(wù)堆棧大小
(void* )NULL, //傳遞給任務(wù)函數(shù)的參數(shù)
(UBaseType_t )START_TASK_PRIO, //任務(wù)優(yōu)先級(jí)
(TaskHandle_t* )&StartTask_Handler); //任務(wù)句柄
vTaskStartScheduler(); //開啟任務(wù)調(diào)度
//開始任務(wù)任務(wù)函數(shù)
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)
//創(chuàng)建LED0任務(wù)
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//創(chuàng)建LED1任務(wù)
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(StartTask_Handler); //刪除開始任務(wù)
taskEXIT_CRITICAL(); //退出臨界區(qū)
}
//LED0任務(wù)函數(shù)
void led0_task(void *pvParameters)
{
while(1)
{
LED0=~LED0;
vTaskDelay(500);
}
}
//LED1任務(wù)函數(shù)
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
- 任務(wù)函數(shù)的返回值一定是void類型。
- 任務(wù)函數(shù)一般不允許跳出循環(huán),如果想要退出,就是一定要調(diào)用vTaskDelete(NULL)刪除此任務(wù)。
2.2.4 任務(wù)控制塊
描述任務(wù)屬性的數(shù)據(jù)結(jié)構(gòu)叫做任務(wù)控制塊。
FreeRTOS的每個(gè)任務(wù)都有一些屬性需要存儲(chǔ),可以集合到一個(gè)結(jié)構(gòu)體來表示。TCB_t。在使用創(chuàng)建任務(wù)時(shí)自動(dòng)為任務(wù)分配一個(gè)任務(wù)控制塊。
如:
棧頂、列表、任務(wù)優(yōu)先級(jí)、任務(wù)名字。
PS:很大,如果有條件編譯,先不看,沒有條件編譯的是核心,也是重點(diǎn)理解的。
任務(wù)控制塊很大,很占內(nèi)存,一般在創(chuàng)建任務(wù)(vTaskCreate())的時(shí)候,如果使用的是動(dòng)態(tài)內(nèi)存申請(qǐng),那么函數(shù)為任務(wù)控制塊自動(dòng)調(diào)用創(chuàng)建,靜態(tài)的話需要自己手動(dòng)填寫。
2.2.5 任務(wù)堆棧
用來保存任務(wù)現(xiàn)場(chǎng)(CPU寄存器的值)。
FreeRTOS能夠恢復(fù)上一個(gè)任務(wù)的運(yùn)行是因?yàn)橛?,任?wù)堆棧。任務(wù)調(diào)度器切換任務(wù)時(shí),將當(dāng)前任務(wù)的現(xiàn)場(chǎng)(CPU寄存器值)保存在此任務(wù)堆棧中,等下次任務(wù)運(yùn)行時(shí),用堆棧中保存的值來恢復(fù)線程,然后從上次中斷的地方開始運(yùn)行。
創(chuàng)建任務(wù)時(shí),需要給任務(wù)指定堆棧。xTaskCreate()動(dòng)態(tài)創(chuàng)建時(shí)任務(wù)堆棧自動(dòng)創(chuàng)建,xTaskCreateStatic()靜態(tài)創(chuàng)建任務(wù)時(shí),需要程序員自己定義任務(wù)堆棧,然后將首地址作為函數(shù)的參數(shù)(puxStackBuffer)傳遞給函數(shù)。
PS: 要根據(jù)任務(wù)動(dòng)態(tài)的修改任務(wù)堆棧的大小。如任務(wù)堆棧分配小了,很容易卡死。