FreeRTOS系列第9篇---FreeRTOS任務(wù)概述基礎(chǔ)篇
ID:技術(shù)讓夢想更偉大
作者:李肖遙
1. 任務(wù)和協(xié)程(Co-routines)
應(yīng)用程序可以使用任務(wù)也可以使用協(xié)程,或者兩者混合使用,但是任務(wù)和協(xié)程使用不同的API函數(shù),因此在任務(wù)和協(xié)程之間不能使用同一個隊(duì)列或信號量傳遞數(shù)據(jù)。
通常情況下,協(xié)程僅用在資源非常少的微處理器中,特別是RAM非常稀缺的情況下。
目前協(xié)程很少被使用到,因此對于協(xié)程FreeRTOS作者既沒有把它刪除也沒有進(jìn)一步開發(fā)。
所以本系列文章以后不會對協(xié)程過多描述,包括其API函數(shù)。
1.1任務(wù)的特性
「簡而言之:」
使用RTOS的實(shí)時應(yīng)用程序可認(rèn)為是一系列獨(dú)立任務(wù)的集合。每個任務(wù)在自己的環(huán)境中運(yùn)行,不依賴于系統(tǒng)中的其它任務(wù)或者RTOS調(diào)度器。
在任何時刻,只有一個任務(wù)得到運(yùn)行,RTOS調(diào)度器決定運(yùn)行哪個任務(wù)。調(diào)度器會不斷的啟動、停止每一個任務(wù),宏觀看上去就像整個應(yīng)用程序都在執(zhí)行。
作為任務(wù),不需要對調(diào)度器的活動有所了解,在任務(wù)切入切出時保存上下文環(huán)境(寄存器值、堆棧內(nèi)容)是調(diào)度器主要的職責(zé)。
為了實(shí)現(xiàn)這點(diǎn),每個任務(wù)都需要有自己的堆棧。當(dāng)任務(wù)切出時,它的執(zhí)行環(huán)境會被保存在該任務(wù)的堆棧中,這樣當(dāng)再次運(yùn)行時,就能從堆棧中正確的恢復(fù)上次的運(yùn)行環(huán)境。
1.2任務(wù)概要
簡單 沒有使用限制 支持完全搶占 支持優(yōu)先級 每個任務(wù)都有自己的堆棧,消耗RAM較多 如果使用搶占,必須小心的考慮可重入問題
2. 任務(wù)狀態(tài)
「一個任務(wù)可為下面中的一個:」
「運(yùn)行」:如果一個任務(wù)正在執(zhí)行,那么說這個任務(wù)處于運(yùn)行狀態(tài)。此時它占用處理器。 「就緒」:就緒的任務(wù)已經(jīng)具備執(zhí)行的能力(不同于阻塞和掛起),但是因?yàn)橛幸粋€同優(yōu)先級或者更高優(yōu)先級的任務(wù)處于運(yùn)行狀態(tài)而還沒有真正執(zhí)行。 「阻塞」:如果任務(wù)當(dāng)前正在等待某個時序或外部中斷,我們就說這個任務(wù)處于阻塞狀態(tài)。比如一個任務(wù)調(diào)用 vTaskDelay()后會阻塞到延時周期到為止。任務(wù)也可能阻塞在隊(duì)列或信號量的事件上。進(jìn)入阻塞狀態(tài)的任務(wù)通常有一個“超時”周期,當(dāng)事件超時后解除阻塞。「掛起」:處于掛起狀態(tài)的任務(wù)同樣對調(diào)度器無效。僅當(dāng)明確的分別調(diào)用 vTaskSuspend()和xTaskResume()API函數(shù)后,任務(wù)才會進(jìn)入或退出掛起狀態(tài)。不可以指定超時周期事件(不可以通過設(shè)定超時事件而退出掛起狀態(tài))
3.任務(wù)優(yōu)先級
每個任務(wù)都要被指定一個優(yōu)先級,從0~configMAX_PRIORITIES,configMAX_PRIORITIES定義在FreeRTOSConfig.h中。
如果某架構(gòu)硬件支持CLZ(或類似)指令(計(jì)算前導(dǎo)零的數(shù)目,Cortex-M3是支持該指令的,從ARMv6T2才支持這個指令),并且打算在移植層使用這個特性來優(yōu)化任務(wù)調(diào)度機(jī)制,需要有一些步驟。
首先將FreeRTOSConfig.h中configUSE_PORT_OPTIMISED_TASK_SELECTION設(shè)置為1,并且最大優(yōu)先級數(shù)目configMAX_PRIORITIES不能大于32。
除此之外,configMAX_PRIORITIES可以設(shè)置為任意值,但是考慮到configMAX_PRIORITIES設(shè)置越大,RAM消耗也越大,一般設(shè)置為滿足使用的最小值。
低優(yōu)先級數(shù)值代表低優(yōu)先級。空閑任務(wù)(idle task)的優(yōu)先級為0(tskIDLE_PRIORITY)。
FreeRTOS調(diào)度器確保處于最高優(yōu)先級的就緒或運(yùn)行態(tài)任務(wù)獲取處理器,換句話說,處于運(yùn)行狀態(tài)的任務(wù),只有其中的最高優(yōu)先級任務(wù)才會運(yùn)行。
任何數(shù)量的任務(wù)可以共享同一個優(yōu)先級。如果宏configUSE_TIME_SLICING未定義或者宏configUSE_TIME_SLICING定義為1,處于就緒態(tài)的多個相同優(yōu)先級任務(wù)將會以時間片切換的方式共享處理器。
4.實(shí)現(xiàn)一個任務(wù)
「一個任務(wù)具有以下結(jié)構(gòu):」
void?vATaskFunction(?void?*pvParameters?)
{
????for(?;;?)
????{
????????/*--?應(yīng)用程序代碼放在這里.?--*/
????}
?
????/*?任務(wù)不可以從這個函數(shù)返回或退出。在較新的FreeRTOS移植包中,如果
????試圖從一個任務(wù)中返回,將會調(diào)用configASSERT()(如果定義的話)。
????如果一個任務(wù)確實(shí)要退出函數(shù),那么這個任務(wù)應(yīng)調(diào)用vTaskDelete(NULL)
????函數(shù),以便處理一些清理工作。*/
????vTaskDelete(?NULL?);
}
任務(wù)函數(shù)返回為void,參數(shù)只有一個void類型指針。所有的任務(wù)函數(shù)都應(yīng)該是這樣。void類型指針可以向任務(wù)傳遞任意類型信息。
任務(wù)函數(shù)決不應(yīng)該返回,因此通常任務(wù)函數(shù)都是一個死循環(huán)。
任務(wù)由xTaskCreate()函數(shù)創(chuàng)建,由vTaskDelete()函數(shù)刪除。
5.空閑任務(wù)和空閑任務(wù)鉤子(idle task和Idle Task hook)
5.1空閑任務(wù)
空閑任務(wù)是啟動RTOS調(diào)度器時由內(nèi)核自動創(chuàng)建的任務(wù),這樣可以確保至少有一個任務(wù)在運(yùn)行。
空閑任務(wù)具有最低任務(wù)優(yōu)先級,這樣如果有其它更高優(yōu)先級的任務(wù)進(jìn)入就緒態(tài)就可以立刻讓出CPU。
刪除任務(wù)后,空閑任務(wù)用來釋放RTOS分配給被刪除任務(wù)的內(nèi)存。因此,在應(yīng)用中使用vTaskDelete()函數(shù)后確保空閑任務(wù)能獲得處理器時間就很重要了。
除此之外,空閑任務(wù)沒有其它有效功能,所以可以被合理的剝奪處理器時間,并且它的優(yōu)先級也是最低的。
應(yīng)用程序任務(wù)共享空閑任務(wù)優(yōu)先級(tskIDLE_PRIORITY)也是可能的。這種情況如何配置可以參考configIDLE_SHOULE_YIELD配置參數(shù)類獲取更多信息。
5.2空閑任務(wù)鉤子
空閑任務(wù)鉤子是一個函數(shù),每一個空閑任務(wù)周期被調(diào)用一次。如果你想將任務(wù)程序功能運(yùn)行在空閑優(yōu)先級上,可以有兩種選擇:
在一個空閑任務(wù)鉤子中實(shí)現(xiàn)這個功能:因?yàn)镕reeRTOS必須至少有一個任務(wù)處于就緒或運(yùn)行狀態(tài),因此鉤子函數(shù)不可以調(diào)用可能引起空閑任務(wù)阻塞的API函數(shù)(比如vTaskDelay()或者帶有超時事件的隊(duì)列或信號量函數(shù))
創(chuàng)建一個具有空閑優(yōu)先級的任務(wù)去實(shí)現(xiàn)這個功能:這是個更靈活的解決方案,但是會帶來更多RAM開銷。
「創(chuàng)建一個空閑鉤子步驟如下」:
在 FreeRTOSConfig.h頭文件中設(shè)置configUSE_IDLE_HOOK為1;定義一個函數(shù),名字和參數(shù)原型如下所示:
void?vApplicationIdleHook(?void?);
通常,使用這個空閑鉤子函數(shù)設(shè)置CPU進(jìn)入低功耗模式。
6.任務(wù)創(chuàng)建
任務(wù)創(chuàng)建和刪除API函數(shù)位于文件task.c中,需要包含task.h頭文件。
6.1 函數(shù)描述
BaseType_t?xTaskCreate(
????????TaskFunction_t?pvTaskCode,
????????const?char?*?const?pcName,
????????unsigned?short?usStackDepth,
????????void?*pvParameters,
????????UBaseType_t?uxPriority,
????????TaskHandle_t?*?pvCreatedTask
??????);
創(chuàng)建新的任務(wù)并加入任務(wù)就緒列表。
如果使用FreeRTOS-MPU(在官方下載包中,為Cortex-M3內(nèi)核寫了兩個移植方案,一個是普通的FreeRTOS移植層,還有一個是FreeRTOS-MPU移植層。后者包含完整的內(nèi)存保護(hù)),那么推薦使用函數(shù)xTaskCreateRestricted()來代替xTaskCreate()。
在使用FreeRTOS-MPU的情況下,使用xTaskCreate()函數(shù)可以創(chuàng)建運(yùn)行在特權(quán)模式或用戶模式(見下面對函數(shù)參數(shù)uxPriority的描述)的任務(wù)。
當(dāng)運(yùn)行在特權(quán)模式下,任務(wù)可以訪問整個內(nèi)存映射;當(dāng)處于用戶模式下,任務(wù)僅能訪問自己的堆棧。
無論在何種模式下,MPU都不會自動捕獲堆棧溢出,因此標(biāo)準(zhǔn)的FreeRTOS堆棧溢出檢測機(jī)制仍然會被用到。xTaskCreateRestricted()函數(shù)具有更大的靈活性。
6.2參數(shù)描述
「pvTaskCode」:指針,指向任務(wù)函數(shù)的入口。任務(wù)永遠(yuǎn)不會返回(位于死循環(huán)內(nèi))。該參數(shù)類型 TaskFunction_t定義在文件projdefs.h中,定義為:typedefvoid (*TaskFunction_t)( void * )。「pcName」:任務(wù)描述。主要用于調(diào)試。字符串的最大長度由宏 configMAX_TASK_NAME_LEN指定,該宏位于FreeRTOSConfig.h文件中。「usStackDepth」:指定任務(wù)堆棧大小,能夠支持的堆棧變量數(shù)量,而不是字節(jié)數(shù)。比如,在16位寬度的堆棧下, usStackDepth定義為100,則實(shí)際使用200字節(jié)堆棧存儲空間。堆棧的寬度乘以深度必須不超過size_t類型所能表示的最大值。比如,size_t為16位,則可以表示的最大值是65535。「pvParameters」:指針,當(dāng)任務(wù)創(chuàng)建時,作為一個參數(shù)傳遞給任務(wù)。 uxPriority:任務(wù)的優(yōu)先級。具有MPU支持的系統(tǒng),可以通過置位優(yōu)先級參數(shù)的 portPRIVILEGE_BIT位,隨意的在特權(quán)(系統(tǒng))模式下創(chuàng)建任務(wù)。比如,創(chuàng)建一個優(yōu)先級為2的特權(quán)任務(wù),參數(shù)uxPriority可以設(shè)置為( 2 | portPRIVILEGE_BIT )。「pvCreatedTask」:用于回傳一個句柄(ID),創(chuàng)建任務(wù)后可以使用這個句柄引用任務(wù)。
6.3返回值
如果任務(wù)成功創(chuàng)建并加入就緒列表函數(shù)返回pdPASS,否則函數(shù)返回錯誤碼,具體參見projdefs.h。
6.4用法舉例
/*?創(chuàng)建任務(wù).?*/
void?vTaskCode(?void?*?pvParameters?)
{
????for(?;;?)
????{
???????/*?任務(wù)代碼放在這里?*/
????}
}
?
/*?創(chuàng)建任務(wù)函數(shù)?*/
void?vOtherFunction(?void?)
{
????static?unsigned?char?ucParameterToPass;
????xTaskHandlexHandle;
?
?????/*?創(chuàng)建任務(wù),存儲句柄。注:傳遞的參數(shù)ucParameterToPass必須和任務(wù)具有相同的生存周期,
????????因此這里定義為靜態(tài)變量。如果它只是一個自動變量,可能不會有太長的生存周期,因?yàn)?br>????????????????中斷和高優(yōu)先級任務(wù)可能會用到它。?*/
?????xTaskCreate(?vTaskCode,?"NAME",?STACK_SIZE,&ucParameterToPass,?tskIDLE_PRIORITY,?&xHandle?);
?
?????/*?使用句柄刪除任務(wù).?*/
????if(?xHandle?!=NULL?)
????{
????????vTaskDelete(?xHandle?);
????}
}
7.任務(wù)刪除
7.1 任務(wù)描述
voidvTaskDelete(?TaskHandle_t?xTask?);
從RTOS內(nèi)核管理器中刪除一個任務(wù)。任務(wù)刪除后將會從就緒、阻塞、暫停和事件列表中移除。在文件FreeRTOSConfig.h中,必須定義宏INCLUDE_vTaskDelete 為1,本函數(shù)才有效。
「注意:」
被刪除的任務(wù),其在任務(wù)創(chuàng)建時由內(nèi)核分配的存儲空間,會由空閑任務(wù)釋放。
如果有應(yīng)用程序調(diào)用xTaskDelete(),必須保證空閑任務(wù)獲取一定的微控制器處理時間。
任務(wù)代碼自己分配的內(nèi)存是不會自動釋放的,因此刪除任務(wù)前,應(yīng)該將這些內(nèi)存釋放。
7.2參數(shù)描述
「xTask」:被刪除任務(wù)的句柄。為 NULL表示刪除當(dāng)前任務(wù)。
推薦閱讀:嵌入式編程專輯 Linux 學(xué)習(xí)專輯 C/C++編程專輯 關(guān)注微信公眾號『技術(shù)讓夢想更偉大』,后臺回復(fù)“m”查看更多內(nèi)容,回復(fù)“加群”加入技術(shù)交流群。 長按前往圖中包含的公眾號關(guān)注
