1. 任務(wù)和協(xié)程(Co-routines)
應(yīng)用程序可以使用任務(wù)也可以使用協(xié)程,或者兩者混合使用,但是任務(wù)和協(xié)程使用不同的API函數(shù),因此在任務(wù)和協(xié)程之間不能使用同一個(gè)隊(duì)列或信號(hào)量傳遞數(shù)據(jù)。
通常情況下,協(xié)程僅用在資源非常少的微處理器中,特別是RAM非常稀缺的情況下。目前協(xié)程很少被使用到,因此對(duì)于協(xié)程FreeRTOS作者既沒(méi)有把它刪除也沒(méi)有進(jìn)一步開發(fā)。
所以本系列文章以后不會(huì)對(duì)協(xié)程過(guò)多描述,包括其API函數(shù)。
1.1任務(wù)的特性
簡(jiǎn)而言之:使用RTOS的實(shí)時(shí)應(yīng)用程序可認(rèn)為是一系列獨(dú)立任務(wù)的集合。每個(gè)任務(wù)在自己的環(huán)境中運(yùn)行,不依賴于系統(tǒng)中的其它任務(wù)或者RTOS調(diào)度器。在任何時(shí)刻,只有一個(gè)任務(wù)得到運(yùn)行,RTOS調(diào)度器決定運(yùn)行哪個(gè)任務(wù)。調(diào)度器會(huì)不斷的啟動(dòng)、停止每一個(gè)任務(wù),宏觀看上去就像整個(gè)應(yīng)用程序都在執(zhí)行。作為任務(wù),不需要對(duì)調(diào)度器的活動(dòng)有所了解,在任務(wù)切入切出時(shí)保存上下文環(huán)境(寄存器值、堆棧內(nèi)容)是調(diào)度器主要的職責(zé)。為了實(shí)現(xiàn)這點(diǎn),每個(gè)任務(wù)都需要有自己的堆棧。當(dāng)任務(wù)切出時(shí),它的執(zhí)行環(huán)境會(huì)被保存在該任務(wù)的堆棧中,這樣當(dāng)再次運(yùn)行時(shí),就能從堆棧中正確的恢復(fù)上次的運(yùn)行環(huán)境。
1.2任務(wù)概要
- 簡(jiǎn)單
- 沒(méi)有使用限制
- 支持完全搶占
- 支持優(yōu)先級(jí)
- 每個(gè)任務(wù)都有自己的堆棧,消耗RAM較多
- 如果使用搶占,必須小心的考慮可重入問(wèn)題
2. 任務(wù)狀態(tài)
一個(gè)任務(wù)可為下面中的一個(gè):
- 運(yùn)行:如果一個(gè)任務(wù)正在執(zhí)行,那么說(shuō)這個(gè)任務(wù)處于運(yùn)行狀態(tài)。此時(shí)它占用處理器。
- 就緒:就緒的任務(wù)已經(jīng)具備執(zhí)行的能力(不同于阻塞和掛起),但是因?yàn)橛幸粋€(gè)同優(yōu)先級(jí)或者更高優(yōu)先級(jí)的任務(wù)處于運(yùn)行狀態(tài)而還沒(méi)有真正執(zhí)行。
- 阻塞:如果任務(wù)當(dāng)前正在等待某個(gè)時(shí)序或外部中斷,我們就說(shuō)這個(gè)任務(wù)處于阻塞狀態(tài)。比如一個(gè)任務(wù)調(diào)用vTaskDelay()后會(huì)阻塞到延時(shí)周期到為止。任務(wù)也可能阻塞在隊(duì)列或信號(hào)量事件上。進(jìn)入阻塞狀態(tài)的任務(wù)通常有一個(gè)“超時(shí)”周期,當(dāng)事件超時(shí)后解除阻塞。
- 掛起:處于掛起狀態(tài)的任務(wù)同樣對(duì)調(diào)度器無(wú)效。僅當(dāng)明確的分別調(diào)用vTaskSuspend() 和xTaskResume() API函數(shù)后,任務(wù)才會(huì)進(jìn)入或退出掛起狀態(tài)。不可以指定超時(shí)周期事件(不可以通過(guò)設(shè)定超時(shí)事件而退出掛起狀態(tài))
3.任務(wù)優(yōu)先級(jí)
每個(gè)任務(wù)都要被指定一個(gè)優(yōu)先級(jí),從0~configMAX_PRIORITIES,configMAX_PRIORITIES定義在FreeRTOSConfig.h中。
如果某架構(gòu)硬件支持CLZ(或類似)指令(計(jì)算前導(dǎo)零的數(shù)目,Cortex-M3是支持該指令的,從ARMv6T2才支持這個(gè)指令),并且打算在移植層使用這個(gè)特性來(lái)優(yōu)化任務(wù)調(diào)度機(jī)制,需要有一些步驟,首先將FreeRTOSConfig.h中configUSE_PORT_OPTIMISED_TASK_SELECTION設(shè)置為1,并且最大優(yōu)先級(jí)數(shù)目configMAX_PRIORITIES不能大于32。除此之外,configMAX_PRIORITIES可以設(shè)置為任意值,但是考慮到configMAX_PRIORITIES設(shè)置越大,RAM消耗也越大,一般設(shè)置為滿足使用的最小值。
低優(yōu)先級(jí)數(shù)值代表低優(yōu)先級(jí)。空閑任務(wù)(idle task)的優(yōu)先級(jí)為0(tskIDLE_PRIORITY)。
FreeRTOS調(diào)度器確保處于最高優(yōu)先級(jí)的就緒或運(yùn)行態(tài)任務(wù)獲取處理器,換句話說(shuō),處于運(yùn)行狀態(tài)的任務(wù),只有其中的最高優(yōu)先級(jí)任務(wù)才會(huì)運(yùn)行。
任何數(shù)量的任務(wù)可以共享同一個(gè)優(yōu)先級(jí)。如果宏configUSE_TIME_SLICING未定義或著宏configUSE_TIME_SLICING定義為1,處于就緒態(tài)的多個(gè)相同優(yōu)先級(jí)任務(wù)將會(huì)以時(shí)間片切換的方式共享處理器。
4.實(shí)現(xiàn)一個(gè)任務(wù)
一個(gè)任務(wù)具有以下結(jié)構(gòu):
- void vATaskFunction( void *pvParameters )
- {
- for( ;; )
- {
- /*-- 應(yīng)用程序代碼放在這里. --*/
- }
- /* 任務(wù)不可以從這個(gè)函數(shù)返回或退出。在較新的FreeRTOS移植包中,如果
- 試圖從一個(gè)任務(wù)中返回,將會(huì)調(diào)用configASSERT()(如果定義的話)。
- 如果一個(gè)任務(wù)確實(shí)要退出函數(shù),那么這個(gè)任務(wù)應(yīng)調(diào)用vTaskDelete(NULL)
- 函數(shù),以便處理一些清理工作。*/
- vTaskDelete( NULL );
- }
任務(wù)函數(shù)返回為void,參數(shù)只有一個(gè)void類型指針。所有的任務(wù)函數(shù)都應(yīng)該是這樣。void類型指針可以向任務(wù)傳遞任意類型信息。
任務(wù)函數(shù)決不應(yīng)該返回,因此通常任務(wù)函數(shù)都是一個(gè)死循環(huán)。
任務(wù)由xTaskCreate()函數(shù)創(chuàng)建,由vTaskDelete()函數(shù)刪除。
5.空閑任務(wù)和空閑任務(wù)鉤子(idle task和Idle Task hook)
5.1空閑任務(wù)
空閑任務(wù)是啟動(dòng)RTOS調(diào)度器時(shí)由內(nèi)核自動(dòng)創(chuàng)建的任務(wù),這樣可以確保至少有一個(gè)任務(wù)在運(yùn)行??臻e任務(wù)具有最低任務(wù)優(yōu)先級(jí),這樣如果有其它更高優(yōu)先級(jí)的任務(wù)進(jìn)入就緒態(tài)就可以立刻讓出CPU。
刪除任務(wù)后,空閑任務(wù)用來(lái)釋放RTOS分配給被刪除任務(wù)的內(nèi)存。因此,在應(yīng)用中使用vTaskDelete()函數(shù)后確??臻e任務(wù)能獲得處理器時(shí)間就很重要了。除此之外,空閑任務(wù)沒(méi)有其它有效功能,所以可以被合理的剝奪處理器時(shí)間,并且它的優(yōu)先級(jí)也是最低的。
應(yīng)用程序任務(wù)共享空閑任務(wù)優(yōu)先級(jí)(tskIDLE_PRIORITY)也是可能的。這種情況如何配置可以參考configIDLE_SHOULE_YIELD配置參數(shù)類獲取更多信息。
5.2空閑任務(wù)鉤子
空閑任務(wù)鉤子是一個(gè)函數(shù),每一個(gè)空閑任務(wù)周期被調(diào)用一次。如果你想將任務(wù)程序功能運(yùn)行在空閑優(yōu)先級(jí)上,可以有兩種選擇:
- 在一個(gè)空閑任務(wù)鉤子中實(shí)現(xiàn)這個(gè)功能:因?yàn)镕reeRTOS必須至少有一個(gè)任務(wù)處于就緒或運(yùn)行狀態(tài),因此鉤子函數(shù)不可以調(diào)用可能引起空閑任務(wù)阻塞的API函數(shù)(比如vTaskDelay()或者帶有超時(shí)事件的隊(duì)列或信號(hào)量函數(shù))。
- 創(chuàng)建一個(gè)具有空閑優(yōu)先級(jí)的任務(wù)去實(shí)現(xiàn)這個(gè)功能:這是個(gè)更靈活的解決方案,但是會(huì)帶來(lái)更多RAM開銷。
創(chuàng)建一個(gè)空閑鉤子步驟如下:
- 在FreeRTOSConfig.h頭文件中設(shè)置configUSE_IDLE_HOOK為1;
- 定義一個(gè)函數(shù),名字和參數(shù)原型如下所示:
void vApplicationIdleHook( void );
通常,使用這個(gè)空閑鉤子函數(shù)設(shè)置CPU進(jìn)入低功耗模式。