簡(jiǎn)介
??首先,,我們需要明確一個(gè)問(wèn)題,,FreeRTOSConfig.h是一個(gè)用戶級(jí)別的文件,不屬于內(nèi)核文件,。每個(gè)用戶可以有不同的FreeRTOSConfig.h,。 ??FreeRTOS作為一個(gè)可高度配置的實(shí)時(shí)內(nèi)核,,其絕大多數(shù)配置選項(xiàng)都體現(xiàn)在FreeRTOS.h(注意是FreeRTOS.h不是FreeRTOSConfig.h)中。為什么這么說(shuō),?打開(kāi)FreeRTOS.h看看就知道了,,這個(gè)文件唯一要干的活就是負(fù)責(zé)根據(jù)宏值來(lái)對(duì)FreeRTOS進(jìn)行配置的。那么,,如果用戶想要根據(jù)自己的需要來(lái)配置FreeRTOS,,那該怎么辦呢?難道需要直接改內(nèi)核頭文件FreeRTOS.h,?當(dāng)然不是,。FreeRTOS的設(shè)計(jì)者采用了一個(gè)很靈活的方式:FreeRTOS.h通過(guò)檢查一個(gè)名為FreeRTOSConfig.h的用戶級(jí)別的配置文件來(lái)實(shí)現(xiàn)對(duì)FreeRTOS的配置。這樣,,既實(shí)現(xiàn)了靈活配置,,又保證了所有用戶就只有FreeRTOSConfig.h不同,而不需要修改內(nèi)核源碼,。 ??那么用戶具體如何配置呢,?簡(jiǎn)單,用戶只需要根據(jù)需要,,在FreeRTOSConfig.h中以宏值的形式給出定義即可,。那么,宏值如何來(lái)的呢,?為什么用戶定義了宏值就可以呢,?首先,宏值全部來(lái)自于FreeRTOS.h這個(gè)系統(tǒng)級(jí)頭文件,,用戶需要什么功能,,需要在該文件中查找對(duì)應(yīng)的宏值;其次,,FreeRTOS.h中會(huì)檢查特定功能的宏值有沒(méi)有定義(默認(rèn)認(rèn)為用戶使用FreeRTOSConfig.h這個(gè)文件),,如果用戶定義了指定宏值,則FreeRTOS就根據(jù)用戶的定義來(lái)實(shí)現(xiàn),。這樣就實(shí)現(xiàn)了用戶配置,。
裁剪、配置詳解
注意1:以下絕大多數(shù)是 FreeRTOS官方文檔的翻譯,,原版地址:http://www./a00110.html 注意2:下面的說(shuō)明的引用部分為譯者所添加,,非FreeRTOS原有文檔內(nèi)容。 注意3:部分專業(yè)詞匯參考網(wǎng)絡(luò),,不足之處隨時(shí)指正,。 首先,看看官網(wǎng)給出的FreeRTOSConfig.h 的模板:
#ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H
/* 再此包含自己的頭文件,例如使用STM32系列芯片時(shí),,可以再次包含STM32庫(kù)的頭文件 */ /* 以下為一些常用的配置項(xiàng)目 */ #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configUSE_TICKLESS_IDLE 0 #define configCPU_CLOCK_HZ 60000000 /* 這個(gè) 必須 更改為自己芯片的實(shí)際頻率 */ #define configTICK_RATE_HZ 250 /* FreeRTOS的中斷頻率,,根據(jù)需要修改 */ #define configMAX_PRIORITIES 5 /* 任務(wù)的最大優(yōu)先級(jí),根據(jù)需要修改*/ #define configMINIMAL_STACK_SIZE 128 #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_MUTEXES 0 #define configUSE_RECURSIVE_MUTEXES 0 #define configUSE_COUNTING_SEMAPHORES 0 #define configUSE_ALTERNATIVE_API 0 /* Deprecated! */ #define configQUEUE_REGISTRY_SIZE 10 #define configUSE_QUEUE_SETS 0 #define configUSE_TIME_SLICING 0 #define configUSE_NEWLIB_REENTRANT 0 #define configENABLE_BACKWARD_COMPATIBILITY 0 #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
/* Memory allocation related definitions. */ #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configTOTAL_HEAP_SIZE 10240 /* 為FreeRTOS分配的內(nèi)存,,具體和選擇的heap_x.c相關(guān) */ #define configAPPLICATION_ALLOCATED_HEAP 1
/* Hook function related definitions. */ #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_DAEMON_TASK_STARTUP_HOOK 0
/* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine related definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES 1
/* Software timer related definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY 3 #define configTIMER_QUEUE_LENGTH 10 #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Interrupt nesting behaviour configuration. */ #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
/* Define to trap errors during development. */ #define configASSERT( ( x ) ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
/* FreeRTOS MPU specific definitions. */ #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
/* Optional functions - most linkers will remove unused functions anyway. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_xResumeFromISR 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 #define INCLUDE_uxTaskGetStackHighWaterMark 0 #define INCLUDE_xTaskGetIdleTaskHandle 0 #define INCLUDE_eTaskGetState 0 #define INCLUDE_xEventGroupSetBitFromISR 1 #define INCLUDE_xTimerPendFunctionCall 0 #define INCLUDE_xTaskAbortDelay 0 #define INCLUDE_xTaskGetHandle 0 #define INCLUDE_xTaskResumeFromISR 1
/* A header file that defines trace macro can be included here. */
#endif /* FREERTOS_CONFIG_H */
??模板中的配置項(xiàng)還是挺多的,,下面一一詳細(xì)進(jìn)行說(shuō)明: 1. configUSE_PREEMPTION ??為 1 時(shí)RTOS使用搶占式調(diào)度器,即當(dāng)進(jìn)程位于內(nèi)核空間時(shí),,有一個(gè)更高優(yōu)先級(jí)的任務(wù)出現(xiàn)時(shí),,如果當(dāng)前內(nèi)核允許搶占,則可以將當(dāng)前任務(wù)掛起,,執(zhí)行優(yōu)先級(jí)更高的進(jìn)程,;為 0 時(shí)RTOS使用協(xié)作式調(diào)度器(時(shí)間片)高優(yōu)先級(jí)的進(jìn)程不能中止正在內(nèi)核中運(yùn)行的低優(yōu)先級(jí)的進(jìn)程而搶占CPU運(yùn)行。,。
2. configUSE_PORT_OPTIMISED_TASK_SELECTION ??某些運(yùn)行FreeRTOS的硬件有兩種方法選擇下一個(gè)要執(zhí)行的任務(wù):通用方法和特定于硬件的方法,。 通用方法:
- configUSE_PORT_OPTIMISED_TASK_SELECTION設(shè)置為 0 或者 沒(méi)有實(shí)現(xiàn)端口特定方法時(shí),將顯式或者隱式使用通用方法,。
- 可以用于所有FreeRTOS支持的硬件,。
- 完全用C實(shí)現(xiàn),效率略低于特殊方法,。
- 不強(qiáng)制要求限制最大可用優(yōu)先級(jí)數(shù)量,。
特定硬件方法:
- 并非所有硬件都支持。
- 必須將configUSE_PORT_OPTIMISED_TASK_SELECTION設(shè)置為 1 ,。
- 依賴于一個(gè)或多個(gè)架構(gòu)特定匯編指令(通常為計(jì)數(shù)前導(dǎo)零(CLZ)或等效指令),,因此只能與其專門(mén)編寫(xiě)的體系結(jié)構(gòu)一起使用。
- 比通用方法更有效率,。
- 一般強(qiáng)制限定最大可用優(yōu)先級(jí)數(shù)目為32,。
Tips:官方使用了 FreeRTOS port 這個(gè)詞,源碼的port.c即與特定硬件相關(guān)的實(shí)現(xiàn),,因此上面翻譯為特定硬件
3. configUSE_TICKLESS_IDLE ??設(shè)置configUSE_TICKLESS_IDLE 為 1,,使能低功耗tickless模式;為 0 保持系統(tǒng)節(jié)拍(SysTick)中斷一直運(yùn)行,。
??通常情況下,,F(xiàn)reeRTOS回調(diào)空閑任務(wù)鉤子函數(shù)(需要設(shè)計(jì)者自己實(shí)現(xiàn)),在空閑任務(wù)鉤子函數(shù)中設(shè)置微處理器進(jìn)入低功耗模式來(lái)達(dá)到省電的目的,。因?yàn)橄到y(tǒng)要響應(yīng)系統(tǒng)節(jié)拍中斷事件,,因此使用這種方法會(huì)周期性的退出,、再進(jìn)入低功耗狀態(tài),。如果系統(tǒng)節(jié)拍中斷頻率過(guò)快,則大部分電能和CPU時(shí)間會(huì)消耗在進(jìn)入和退出低功耗狀態(tài)上。 ??FreeRTOS的tickless空閑模式會(huì)在空閑周期時(shí)停止周期性系統(tǒng)節(jié)拍中斷,。停止周期性系統(tǒng)節(jié)拍中斷可以使微控制器長(zhǎng)時(shí)間處于低功耗模式,。移植層需要配置外部喚醒中斷,當(dāng)喚醒事件到來(lái)時(shí),,將微控制器從低功耗模式喚醒,。微控制器喚醒后,會(huì)重新使能系統(tǒng)節(jié)拍中斷,。由于微控制器在進(jìn)入低功耗后,,系統(tǒng)節(jié)拍計(jì)數(shù)器是停止的,但我們又需要知道這段時(shí)間能折算成多少次系統(tǒng)節(jié)拍中斷周期,,這就需要有一個(gè)不受低功耗影響的外部時(shí)鐘源,,即微處理器處于低功耗模式時(shí)它也在計(jì)時(shí)的,這樣在重啟系統(tǒng)節(jié)拍中斷時(shí)就可以根據(jù)這個(gè)外部計(jì)時(shí)器計(jì)算出一個(gè)調(diào)整值并寫(xiě)入RTOS 系統(tǒng)節(jié)拍計(jì)數(shù)器變量中,。 ??關(guān)于低功耗模式,可參見(jiàn)此文https://blog.csdn.net/zcshoucsdn/article/details/77879746
4. configUSE_IDLE_HOOK ??設(shè)置為1使用空閑鉤子(類(lèi)似于回調(diào)函數(shù)),,0忽略空閑鉤子,。
??當(dāng)RTOS調(diào)度器開(kāi)始工作后,為了保證至少有一個(gè)任務(wù)在運(yùn)行,,空閑任務(wù)被自動(dòng)創(chuàng)建,,占用最低優(yōu)先級(jí)(0優(yōu)先級(jí))。對(duì)于已經(jīng)刪除的RTOS任務(wù),,空閑任務(wù)可以釋放分配給它們的堆棧內(nèi)存,。因此,在應(yīng)用中應(yīng)該注意,,使用vTaskDelete()函數(shù)時(shí)要確??臻e任務(wù)獲得一定的處理器時(shí)間。除此之外,,空閑任務(wù)沒(méi)有其它特殊功能,,因此可以任意的剝奪空閑任務(wù)的處理器時(shí)間。 ??空閑任務(wù)鉤子是一個(gè)函數(shù),,這個(gè)函數(shù)由用戶來(lái)實(shí)現(xiàn),,F(xiàn)reeRTOS規(guī)定了函數(shù)的名字和參數(shù),這個(gè)函數(shù)在每個(gè)空閑任務(wù)周期都會(huì)被調(diào)用,。要?jiǎng)?chuàng)建一個(gè)空閑鉤子: ?1. 設(shè)置FreeRTOSConfig.h 文件中的configUSE_IDLE_HOOK 為 1,; ?2. 定義一個(gè)函數(shù),函數(shù)名和參數(shù)如下所示: ??void vApplicationIdleHook(void ); ?該鉤子函數(shù)不可以調(diào)用會(huì)引起空閑任務(wù)阻塞的API函數(shù)(例如:vTaskDelay(),、帶有阻塞時(shí)間函數(shù)),,在鉤子函數(shù)內(nèi)部可以使用協(xié)程,。 ?使用空閑鉤子函數(shù)設(shè)置CPU進(jìn)入省電模式是很常見(jiàn)的。
5. configUSE_MALLOC_FAILED_HOOK ??每當(dāng)一個(gè)任務(wù),、隊(duì)列,、信號(hào)量被創(chuàng)建時(shí),內(nèi)核使用一個(gè)名為pvPortMalloc()的函數(shù)來(lái)從堆中分配內(nèi)存,。官方的下載包中包含5個(gè)簡(jiǎn)單內(nèi)存分配策略,,分別保存在源文件heap_1.c、heap_2.c,、heap_3.c,、heap_4.c、heap_5.c 中,。 ?? 僅當(dāng)使用以上這五個(gè)簡(jiǎn)單策略之一時(shí),,這個(gè)宏才有意義。 ??如果定義并正確配置malloc()失敗鉤子函數(shù),,則這個(gè)函數(shù)會(huì)在pvPortMalloc()函數(shù)返回NULL時(shí)被調(diào)用,。只有FreeRTOS在響應(yīng)內(nèi)存分配請(qǐng)求時(shí)發(fā)現(xiàn)堆內(nèi)存不足才會(huì)返回NULL。 ??如果宏configUSE_MALLOC_FAILED_HOOK 設(shè)置為1,,那么必須定義一個(gè)malloc()失敗鉤子函數(shù),,如果宏configUSE_MALLOC_FAILED_HOOK 設(shè)置為0,malloc()失敗鉤子函數(shù)不會(huì)被調(diào)用,,即便已經(jīng)定義了這個(gè)函數(shù),。malloc()失敗鉤子函數(shù)的函數(shù)名和原型必須如下所示:
void vApplicationMallocFailedHook( void);
6. configUSE_DAEMON_TASK_STARTUP_HOOK ??如果configUSE_TIMERS 和configUSE_DAEMON_TASK_STARTUP_HOOK 都設(shè)置為1,那么應(yīng)用程序必須定義一個(gè)鉤子函數(shù),,其名稱和原型如下所示: ?void void vApplicationDaemonTaskStartupHook( void ); ??當(dāng)RTOS守護(hù)程序任務(wù)(也稱為定時(shí)器服務(wù)任務(wù))第一次執(zhí)行時(shí),,鉤子函數(shù)將被精確調(diào)用一次。 需要RTOS運(yùn)行的任何應(yīng)用程序初始化代碼都可以放在hook函數(shù)中,。
7. configUSE_TICK_HOOK ??設(shè)置為1使用時(shí)間片鉤子(Tick Hook),,0忽略時(shí)間片鉤子。
??時(shí)間片中斷可以周期性的調(diào)用一個(gè)被稱為鉤子函數(shù)(回調(diào)函數(shù))的應(yīng)用程序,。時(shí)間片鉤子函數(shù)可以很方便的實(shí)現(xiàn)一個(gè)定時(shí)器功能,。 ??只有在FreeRTOSConfig.h中的configUSE_TICK_HOOK設(shè)置成1時(shí)才可以使用時(shí)間片鉤子。一旦此值設(shè)置成1,,就要定義鉤子函數(shù),,函數(shù)名和參數(shù)如下所示:
void vApplicationTickHook( void ); ??vApplicationTickHook() 函數(shù)在中斷服務(wù)程序中執(zhí)行,因此這個(gè)函數(shù)必須非常短小,,不能大量使用堆棧,,不能調(diào)用以”FromISR” 或 “FROM_ISR”結(jié)尾的API函數(shù)。
8. configCPU_CLOCK_HZ ??寫(xiě)入實(shí)際的CPU內(nèi)核時(shí)鐘頻率,,也就是CPU指令執(zhí)行頻率,,通常稱為,。配置此值是為了正確的配置系統(tǒng)節(jié)拍中斷周期。 例如,,在STM32中時(shí),常用#define configCPU_CLOCK_HZ ( SystemCoreClock )
9. configTICK_RATE_HZ ??RTOS 系統(tǒng)自己的節(jié)拍中斷的頻率,。即一秒中斷的次數(shù),,每次中斷RTOS都會(huì)進(jìn)行任務(wù)調(diào)度。
??系統(tǒng)節(jié)拍中斷用來(lái)測(cè)量時(shí)間,,因此,,越高的測(cè)量頻率意味著可測(cè)到越高的分辨率時(shí)間。但是,,高的系統(tǒng)節(jié)拍中斷頻率也意味著RTOS內(nèi)核占用更多的CPU時(shí)間,,因此會(huì)降低效率。RTOS演示例程都是使用系統(tǒng)節(jié)拍中斷頻率為1000HZ,,這是為了測(cè)試RTOS內(nèi)核,,比實(shí)際使用的要高。(實(shí)際使用時(shí)不用這么高的系統(tǒng)節(jié)拍中斷頻率) ??多個(gè)任務(wù)可以共享一個(gè)優(yōu)先級(jí),,RTOS調(diào)度器為相同優(yōu)先級(jí)的任務(wù)分享CPU時(shí)間,,在每一個(gè)RTOS 系統(tǒng)節(jié)拍中斷到來(lái)時(shí)進(jìn)行任務(wù)切換。高的系統(tǒng)節(jié)拍中斷頻率會(huì)降低分配給每一個(gè)任務(wù)的“時(shí)間片”持續(xù)時(shí)間,。
10. configMAX_PRIORITIES ??配置應(yīng)用程序有效的優(yōu)先級(jí)數(shù)目,。任何數(shù)量的任務(wù)都可以共享一個(gè)優(yōu)先級(jí),使用協(xié)程可以單獨(dú)的給與它們優(yōu)先權(quán),。見(jiàn)configMAX_CO_ROUTINE_PRIORITIES,。 ??在RTOS內(nèi)核中,每個(gè)有效優(yōu)先級(jí)都會(huì)消耗一定量的RAM,,因此這個(gè)值不要超過(guò)你的應(yīng)用實(shí)際需要的優(yōu)先級(jí)數(shù)目,。
??每一個(gè)任務(wù)都會(huì)被分配一個(gè)優(yōu)先級(jí),優(yōu)先級(jí)值從0~ (configMAX_PRIORITIES - 1)之間,。低優(yōu)先級(jí)數(shù)表示低優(yōu)先級(jí)任務(wù),。空閑任務(wù)的優(yōu)先級(jí)為0(tskIDLE_PRIORITY),因此它是最低優(yōu)先級(jí)任務(wù),。 ??FreeRTOS調(diào)度器將確保處于就緒狀態(tài)(Ready)或運(yùn)行狀態(tài)(Running)的高優(yōu)先級(jí)任務(wù)比同樣處于就緒狀態(tài)的低優(yōu)先級(jí)任務(wù)優(yōu)先獲取處理器時(shí)間,。換句話說(shuō),處于運(yùn)行狀態(tài)的任務(wù)永遠(yuǎn)是高優(yōu)先級(jí)任務(wù),。 ??處于就緒狀態(tài)的相同優(yōu)先級(jí)任務(wù)使用時(shí)間片調(diào)度機(jī)制共享處理器時(shí)間,。
11. configMINIMAL_STACK_SIZE ??定義空閑任務(wù)使用的堆棧大小。通常此值不應(yīng)小于對(duì)應(yīng)處理器演示例程文件FreeRTOSConfig.h中定義的數(shù)值,。 ??就像xTaskCreate() 和xTaskCreateStatic() 函數(shù)的堆棧大小參數(shù)一樣,,堆棧大小不是以字節(jié)為單位而是以字為單位的,,比如在32位架構(gòu)下,棧大小為100表示棧內(nèi)存占用400字節(jié)的空間,。
12. configMAX_TASK_NAME_LEN ??調(diào)用任務(wù)函數(shù)時(shí),,需要設(shè)置描述任務(wù)信息的字符串,這個(gè)宏用來(lái)定義該字符串的最大長(zhǎng)度,。這里定義的長(zhǎng)度包括字符串結(jié)束符'\0' ,。
13.configUSE_TRACE_FACILITY ??設(shè)置成1表示啟動(dòng)可視化跟蹤調(diào)試,會(huì)激活一些附加的結(jié)構(gòu)體成員和函數(shù),。 ??該配置通常在調(diào)試時(shí)才會(huì)使用,,在真正發(fā)布程序時(shí)必須將其關(guān)閉,因?yàn)槠鋵?duì)于FreeRTOS的性能是有影響的,。如下圖是使用Percepio Tracealyzer分析的FreeRTOS的運(yùn)行情況圖:
14.configUSE_STATS_FORMATTING_FUNCTIONS ??設(shè)置宏configUSE_TRACE_FACILITY 和configUSE_STATS_FORMATTING_FUNCTIONS 為1會(huì)編譯vTaskList()和vTaskGetRunTimeStats()函數(shù),。如果將這兩個(gè)宏任意一個(gè)設(shè)置為0,上述兩個(gè)函數(shù)將被忽略,。通常也是在調(diào)試時(shí)才使用,,用來(lái)觀察各任務(wù)。
15.configUSE_16_BIT_TICKS ??時(shí)間是以“ticks”來(lái)衡量的 - 這是自RTOS內(nèi)核啟動(dòng)以來(lái)tick事件中斷已執(zhí)行的次數(shù),。 tick計(jì)數(shù)保存在TickType_t類(lèi)型 的變量中,。 ??將configUSE_16_BIT_TICKS 定義為 1 會(huì)使TickType_t為無(wú)符號(hào)的16位類(lèi)型定義(typedef’ed)。 將configUSE_16_BIT_TICKS 定義為 0 會(huì)使TickType_t為無(wú)符號(hào)32位類(lèi)型定義(typedef’ed),。 ??使用16位類(lèi)型將大大提高8位和16位架構(gòu)的性能,,但將最大可指定時(shí)間限制為65535個(gè)“ticks”。 因此,,假設(shè)節(jié)拍頻率為250Hz,,當(dāng)使用16位計(jì)數(shù)器時(shí),任務(wù)可以延遲或阻塞的最大時(shí)間為262秒,,而使用32位計(jì)數(shù)器時(shí)為17179869秒,。
16.configIDLE_SHOULD_YIELD
- 使用搶占式內(nèi)核調(diào)度
- 用戶任務(wù)使用空閑優(yōu)先級(jí)。
?? 通過(guò)時(shí)間片共享同一個(gè)優(yōu)先級(jí)的多個(gè)任務(wù),,如果共享的優(yōu)先級(jí)大于空閑優(yōu)先級(jí),,并假設(shè)沒(méi)有更高優(yōu)先級(jí)任務(wù),這些任務(wù)應(yīng)該獲得相同的處理器時(shí)間,。 ?? 但如果共享空閑優(yōu)先級(jí)時(shí),,情況會(huì)稍微有些不同。當(dāng)configIDLE_SHOULD_YIELD 為1時(shí),,其它共享空閑優(yōu)先級(jí)的用戶任務(wù)就緒時(shí),,空閑任務(wù)立刻讓出CPU,,用戶任務(wù)運(yùn)行,,這樣確保了能最快響應(yīng)用戶任務(wù)。處于這種模式下也會(huì)有不良效果(取決于你的程序需要),,描述如下: ??圖中描述了四個(gè)處于空閑優(yōu)先級(jí)的任務(wù),任務(wù)A,、B和C是用戶任務(wù),,任務(wù)I是空閑任務(wù)。上下文切換周期性的發(fā)生在T0,、T1…T6時(shí)刻,。當(dāng)用戶任務(wù)運(yùn)行時(shí),空閑任務(wù)立刻讓出CPU,,但是,空閑任務(wù)已經(jīng)消耗了當(dāng)前時(shí)間片中的一定時(shí)間,。這樣的結(jié)果就是空閑任務(wù)I和用戶任務(wù)A共享一個(gè)時(shí)間片,。用戶任務(wù)B和用戶任務(wù)C因此獲得了比用戶任務(wù)A更多的處理器時(shí)間。 ??可以通過(guò)下面方法避免:
- 如果合適的話,,將處于空閑優(yōu)先級(jí)的各單獨(dú)的任務(wù)放置到空閑鉤子函數(shù)中,;
- 創(chuàng)建的用戶任務(wù)優(yōu)先級(jí)大于空閑優(yōu)先級(jí);
- 設(shè)置IDLE_SHOULD_YIELD為0,;
??設(shè)置configIDLE_SHOULD_YIELD 為0將阻止空閑任務(wù)為用戶任務(wù)讓出CPU,,直到空閑任務(wù)的時(shí)間片結(jié)束。這確保所有處在空閑優(yōu)先級(jí)的任務(wù)分配到相同多的處理器時(shí)間,,但是,,這是以分配給空閑任務(wù)更高比例的處理器時(shí)間為代價(jià)的。
17.configUSE_TASK_NOTIFICATIONS ??設(shè)置宏configUSE_TASK_NOTIFICATIONS 為1(或不定義宏configUSE_TASK_NOTIFICATIONS )將會(huì)開(kāi)啟任務(wù)通知功能,,有關(guān)的API函數(shù)也會(huì)被編譯,。設(shè)置宏configUSE_TASK_NOTIFICATIONS 為0則關(guān)閉任務(wù)通知功能,相關(guān)API函數(shù)也不會(huì)被編譯,。默認(rèn)這個(gè)功能是開(kāi)啟的,。開(kāi)啟后,每個(gè)任務(wù)多增加8字節(jié)RAM,。
??每個(gè)RTOS任務(wù)具有一個(gè)32位的通知值,,RTOS任務(wù)通知相當(dāng)于直接向任務(wù)發(fā)送一個(gè)事件,接收到通知的任務(wù)可以解除任務(wù)的阻塞狀態(tài)(因等待任務(wù)通知而進(jìn)入阻塞狀態(tài)),。相對(duì)于以前必須分別創(chuàng)建隊(duì)列,、二進(jìn)制信號(hào)量、計(jì)數(shù)信號(hào)量或事件組的情況,,使用任務(wù)通知顯然更靈活,。更好的是,相比于使用信號(hào)量解除任務(wù)阻塞,,使用任務(wù)通知可以快45%(使用GCC編譯器,,-o2優(yōu)化級(jí)別),。
18.configUSE_MUTEXES ??設(shè)置為 1 表示使用互斥量,設(shè)置成0表示忽略互斥量,。讀者應(yīng)該了解在FreeRTOS中互斥量和二進(jìn)制信號(hào)量的區(qū)別,。
關(guān)于互斥量和二進(jìn)制信號(hào)量簡(jiǎn)單說(shuō):
- 互斥型信號(hào)量必須是同一個(gè)任務(wù)申請(qǐng),同一個(gè)任務(wù)釋放,,其他任務(wù)釋放無(wú)效,。
- 二進(jìn)制信號(hào)量,一個(gè)任務(wù)申請(qǐng)成功后,,可以由另一個(gè)任務(wù)釋放,。
- 互斥型信號(hào)量是二進(jìn)制信號(hào)量的子集。
19.configUSE_RECURSIVE_MUTEXES ??設(shè)置成1表示使用遞歸互斥量,,設(shè)置成0表示不使用,。
20.configUSE_COUNTING_SEMAPHORES ??設(shè)置成1表示使用計(jì)數(shù)信號(hào)量,設(shè)置成0表示不使用,。
21.configUSE_ALTERNATIVE_API ??設(shè)置成1表示使用“替代”隊(duì)列函數(shù)(’alternative’ queue functions),,設(shè)置成0不使用?!疤娲盇PI在queue.h頭文件中有詳細(xì)描述,。
注:“替代”隊(duì)列函數(shù)已經(jīng)被棄用,在新的設(shè)計(jì)中不要使用它,!
22. configCHECK_FOR_STACK_OVERFLOW ?以下介紹均為官網(wǎng)的stack overflow detection章節(jié)內(nèi)容 ??每個(gè)任務(wù)維護(hù)自己的??臻g,任務(wù)創(chuàng)建時(shí)會(huì)自動(dòng)分配任務(wù)需要的棧內(nèi)存,,分配內(nèi)存大小由創(chuàng)建任務(wù)函數(shù)(xTaskCreate())的一個(gè)參數(shù)指定,。堆棧溢出是設(shè)備運(yùn)行不穩(wěn)定的最常見(jiàn)原因,因此FreeeRTOS提供了兩個(gè)可選機(jī)制用來(lái)輔助檢測(cè)和改正堆棧溢出,。配置宏configCHECK_FOR_STACK_OVERFLOW 為不同的常量來(lái)使用不同堆棧溢出檢測(cè)機(jī)制,。 ??注意,這個(gè)選項(xiàng)僅適用于內(nèi)存映射未分段的微處理器架構(gòu),。并且,,在RTOS檢測(cè)到堆棧溢出發(fā)生之前,一些處理器可能先產(chǎn)生故障(fault)或異常(exception)來(lái)反映堆棧使用的惡化,。如果宏configCHECK_FOR_STACK_OVERFLOW 沒(méi)有設(shè)置成0,,用戶必須提供一個(gè)棧溢出鉤子函數(shù),這個(gè)鉤子函數(shù)的函數(shù)名和參數(shù)必須如下所示:
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
??參數(shù)xTask和pcTaskName為堆棧溢出任務(wù)的句柄和名字,。請(qǐng)注意,,如果溢出非常嚴(yán)重,這兩個(gè)參數(shù)信息也可能是錯(cuò)誤的!在這種情況下,,可以直接檢查pxCurrentTCb變量,。 ??推薦僅在開(kāi)發(fā)或測(cè)試階段使用棧溢出檢查,因?yàn)槎褩R绯鰴z測(cè)會(huì)增大上下文切換開(kāi)銷(xiāo),。 方法一: ??任務(wù)切換出去后,,該任務(wù)的上下文環(huán)境被保存到自己的堆棧空間,,這時(shí)很可能堆棧的使用量達(dá)到了最大(最深)值,。在這個(gè)時(shí)候,RTOS內(nèi)核會(huì)檢測(cè)堆棧指針是否還指向有效的堆??臻g,。如果堆棧指針指向了有效堆棧空間之外的地方,,堆棧溢出鉤子函數(shù)會(huì)被調(diào)用,。 ??這個(gè)方法速度很快,但是不能檢測(cè)到所有堆棧溢出情況(比如,,堆棧溢出沒(méi)有發(fā)生在上下文切換時(shí)),。設(shè)置configCHECK_FOR_STACK_OVERFLOW 為 1 會(huì)使用這種方法,。 方法二: ??當(dāng)堆棧首次創(chuàng)建時(shí),,在它的堆棧區(qū)中填充一些已知值(標(biāo)記)。當(dāng)任務(wù)切換時(shí),,RTOS內(nèi)核會(huì)檢測(cè)堆棧最后的16個(gè)字節(jié),,確保標(biāo)記數(shù)據(jù)沒(méi)有被覆蓋。如果這16個(gè)字節(jié)有任何一個(gè)被改變,,則調(diào)用堆棧溢出鉤子函數(shù),。 ??這個(gè)方法比第一種方法要慢,但也相當(dāng)快了,。它能有效捕捉堆棧溢出事件(即使堆棧溢出沒(méi)有發(fā)生在上下文切換時(shí)),,但是理論上它也不能百分百的捕捉到所有堆棧溢出(比如堆棧溢出的值和標(biāo)記值相同,當(dāng)然,,這種情況發(fā)生的概率極?。?nbsp; ??使用這個(gè)方法需要設(shè)置configCHECK_FOR_STACK_OVERFLOW 為 2 .
23.configQUEUE_REGISTRY_SIZE ??通過(guò)此定義來(lái)設(shè)置可以注冊(cè)的信號(hào)量和消息隊(duì)列個(gè)數(shù),。隊(duì)列記錄有兩個(gè)目的,,都涉及到RTOS內(nèi)核的調(diào)試:
- 注冊(cè)隊(duì)列的時(shí)候,可以給隊(duì)列起一個(gè)名字,,當(dāng)使用調(diào)試組件的時(shí)候,,通過(guò)名字可以很容易的區(qū)分不同隊(duì)列。
- 包含調(diào)試器需要的每一個(gè)記錄隊(duì)列和信號(hào)量定位信息,;
除了進(jìn)行內(nèi)核調(diào)試外,,隊(duì)列記錄沒(méi)有其它任何目的,。
??configQUEUE_REGISTRY_SIZE 定義可以記錄的隊(duì)列和信號(hào)量的最大數(shù)目。如果你想使用RTOS內(nèi)核調(diào)試器查看隊(duì)列和信號(hào)量信息,,則必須先將這些隊(duì)列和信號(hào)量進(jìn)行注冊(cè),,只有注冊(cè)后的隊(duì)列和信號(hào)量才可以使用RTOS內(nèi)核調(diào)試器查看。查看API參考手冊(cè)中的vQueueAddToRegistry() 和vQueueUnregisterQueue() 函數(shù)獲取更多信息,。
24.configUSE_QUEUE_SETS ??設(shè)置成1使能隊(duì)列集功能(可以阻塞,、掛起到多個(gè)隊(duì)列和信號(hào)量),設(shè)置成0取消隊(duì)列集功能,。
25.configUSE_TIME_SLICING ??默認(rèn)情況下,,該宏會(huì)被定義為1。1 表示:FreeRTOS使用基于時(shí)間片的優(yōu)先級(jí)搶占式調(diào)度器,。這意味著FreeRTOS調(diào)度器總是運(yùn)行處于最高優(yōu)先級(jí)的就緒任務(wù),,在每個(gè)FreeRTOS 系統(tǒng)節(jié)拍中斷時(shí)在相同優(yōu)先級(jí)的多個(gè)任務(wù)間進(jìn)行任務(wù)切換。 如果configUSE_TIME_SLICING 設(shè)置為0,,那么RTOS調(diào)度程序仍將運(yùn)行處于就緒狀態(tài)的最高優(yōu)先級(jí)任務(wù),,但是不會(huì)因?yàn)榘l(fā)生滴答中斷而在相同優(yōu)先級(jí)的任務(wù)之間切換。
26.configUSE_NEWLIB_REENTRANT ??設(shè)置為1,,每一個(gè)創(chuàng)建的任務(wù)會(huì)分配一個(gè)newlib(一個(gè)嵌入式C庫(kù))reent結(jié)構(gòu),。
??注意Newlib支持已被普遍需求包括,但FreeRTOS維護(hù)者本身不使用,。 FreeRTOS不對(duì)產(chǎn)生的newlib操作負(fù)責(zé),。 用戶必須熟悉newlib,并且必須提供必要存根的系統(tǒng)范圍實(shí)現(xiàn),。 請(qǐng)注意(在編寫(xiě)本文時(shí)),,當(dāng)前的newlib設(shè)計(jì)實(shí)現(xiàn)了必須提供鎖的系統(tǒng)范圍的malloc()。
27.configENABLE_BACKWARD_COMPATIBILITY ??頭文件FreeRTOS.h 包含一系列#define宏定義 ,,用來(lái)映射版本V8.0.0和V8.0.0之前版本的數(shù)據(jù)類(lèi)型名字,。這些宏可以確保RTOS內(nèi)核升級(jí)到V8.0.0或以上版本時(shí),之前的應(yīng)用代碼不用做任何修改,。在FreeRTOSConfig.h 文件中設(shè)置宏configENABLE_BACKWARD_COMPATIBILITY 為0會(huì)去掉這些宏定義,,并且需要用戶確認(rèn)升級(jí)之前的應(yīng)用沒(méi)有用到這些名字。 ??就是為了兼容之前的版本用的宏,。例如:在之前的版本中,,F(xiàn)reeRTOS的各種類(lèi)型均為xAAA(如xSemaphoreHandle),在最新版中,,均使用AAA_t(如SemaphoreHandle_t),。因此,在新項(xiàng)目中,最好使用FreeRTOS的最新的各種類(lèi)型定義,。具體如下:
#if configENABLE_BACKWARD_COMPATIBILITY == 1 #define eTaskStateGet eTaskGetState #define portTickType TickType_t #define xTaskHandle TaskHandle_t #define xQueueHandle QueueHandle_t #define xSemaphoreHandle SemaphoreHandle_t #define xQueueSetHandle QueueSetHandle_t #define xQueueSetMemberHandle QueueSetMemberHandle_t #define xTimeOutType TimeOut_t #define xMemoryRegion MemoryRegion_t #define xTaskParameters TaskParameters_t #define xTaskStatusType TaskStatus_t #define xTimerHandle TimerHandle_t #define xCoRoutineHandle CoRoutineHandle_t #define pdTASK_HOOK_CODE TaskHookFunction_t #define portTICK_RATE_MS portTICK_PERIOD_MS #define pcTaskGetTaskName pcTaskGetName #define pcTimerGetTimerName pcTimerGetName #define pcQueueGetQueueName pcQueueGetName #define vTaskGetTaskInfo vTaskGetInfo /* Backward compatibility within the scheduler code only - these definitions are not really required but are included for completeness. */ #define tmrTIMER_CALLBACK TimerCallbackFunction_t #define pdTASK_CODE TaskFunction_t #define xListItem ListItem_t #endif /* configENABLE_BACKWARD_COMPATIBILITY */
28. configNUM_THREAD_LOCAL_STORAGE_POINTERS ??設(shè)置每個(gè)任務(wù)的線程本地存儲(chǔ)指針數(shù)組大小,。關(guān)于這部分的說(shuō)明,詳細(xì)參考Thread Local Storage Pointers
29. configSUPPORT_STATIC_ALLOCATION ??設(shè)置為1,,那么可以使用應(yīng)用程序編寫(xiě)器提供的RAM創(chuàng)建RTOS對(duì)象,。設(shè)置為0,則只能使用從FreeRTOS堆分配的RAM創(chuàng)建RTOS對(duì)象,。默認(rèn)(未定義時(shí))為0 ??如果設(shè)置為1,,則應(yīng)用程序編寫(xiě)器還必須提供兩個(gè)回調(diào)函數(shù):vApplicationGetIdleTaskMemory()以提供內(nèi)存供RTOS空閑任務(wù)使用;(如果configUSE_TIMERS設(shè)置為1)vApplicationGetTimerTaskMemory() 以提供內(nèi)存供RTOS守護(hù)進(jìn)程/定時(shí)器服務(wù)任務(wù)使用,。 下面是官方給出的一個(gè)例子:
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an implementation of vApplicationGetIdleTaskMemory() to provide the memory that is used by the Idle task. */ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) /* If the buffers to be provided to the Idle task are declared inside this function then they must be declared static - otherwise they will be allocated on the stack and so not exists after this function exits. */ static StaticTask_t xIdleTaskTCB; static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task's stack. */ *ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. Note that, as the array is necessarily of type StackType_t, configMINIMAL_STACK_SIZE is specified in words, not bytes. */ *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /*-----------------------------------------------------------*/
/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the application must provide an implementation of vApplicationGetTimerTaskMemory() to provide the memory that is used by the Timer service task. */ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) /* If the buffers to be provided to the Timer task are declared inside this function then they must be declared static - otherwise they will be allocated on the stack and so not exists after this function exits. */ static StaticTask_t xTimerTaskTCB; static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
/* Pass out a pointer to the StaticTask_t structure in which the Timer task's state will be stored. */ *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task's stack. */ *ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. Note that, as the array is necessarily of type StackType_t, configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
更多詳細(xì)信息,,參考Static Vs Dynamic Memory Allocation
30. configSUPPORT_DYNAMIC_ALLOCATION ??設(shè)置為1,則可以使用從FreeRTOS堆中自動(dòng)分配的RAM創(chuàng)建RTOS對(duì)象,。設(shè)置為0,,則只能使用應(yīng)用程序編寫(xiě)器提供的RAM創(chuàng)建RTOS對(duì)象。默認(rèn)(未定義)為1
詳細(xì)信息,,參見(jiàn)Static Vs Dynamic Memory Allocation
31.configTOTAL_HEAP_SIZE ??FreeRTOS堆中可用的RAM總量,。該值僅在configSUPPORT_DYNAMIC_ALLOCATION 設(shè)置為 1 且應(yīng)用程序使用FreeRTOS源代碼下載中提供的示例內(nèi)存分配方案之一時(shí)該定義才有效。 有關(guān)詳細(xì)信息,,請(qǐng)參閱內(nèi)存配置部分,。
32. configAPPLICATION_ALLOCATED_HEAP ??默認(rèn)情況下,F(xiàn)reeRTOS堆由FreeRTOS聲明,,并由鏈接器放置在內(nèi)存中,。 將configAPPLICATION_ALLOCATED_HEAP 設(shè)置為1允許應(yīng)用程序編寫(xiě)器聲明堆,,這允許應(yīng)用程序?qū)⒍逊胖迷趦?nèi)存中的任何位置,。 ??如果使用heap_1.c,heap_2.c或heap_4.c ,,并且configAPPLICATION_ALLOCATED_HEAP 設(shè)置為1,,那么應(yīng)用程序編寫(xiě)器必須提供一個(gè)具有完全名稱和維度的uint8_t數(shù)組,如下所示: ?uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; ??該數(shù)組將用作FreeRTOS堆,。 如何將數(shù)組放置在特定的內(nèi)存位置取決于使用的編譯器 - 請(qǐng)參閱您的編譯器文檔,。
33.configGENERATE_RUN_TIME_STATS 以下說(shuō)明源自官方文檔Run Time Statistics ??設(shè)置為1使能運(yùn)行時(shí)間統(tǒng)計(jì)功能。一旦設(shè)置為1,,則下面兩個(gè)宏必須被定義:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() :用戶程序需要提供一個(gè)基準(zhǔn)時(shí)鐘函數(shù),,函數(shù)完成初始化基準(zhǔn)時(shí)鐘功能,這個(gè)函數(shù)要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上,。這是因?yàn)檫\(yùn)行時(shí)間統(tǒng)計(jì)需要一個(gè)比系統(tǒng)節(jié)拍中斷頻率還要高分辨率的基準(zhǔn)定時(shí)器,,否則,統(tǒng)計(jì)可能不精確?;鶞?zhǔn)定時(shí)器中斷頻率要比統(tǒng)節(jié)拍中斷快10~100倍,。基準(zhǔn)定時(shí)器中斷頻率越快,,統(tǒng)計(jì)越精準(zhǔn),,但能統(tǒng)計(jì)的運(yùn)行時(shí)間也越短(比如,基準(zhǔn)定時(shí)器10ms中斷一次,,8位無(wú)符號(hào)整形變量可以計(jì)到2.55秒,,但如果是1秒中斷一次,8位無(wú)符號(hào)整形變量可以統(tǒng)計(jì)到255秒),。portGET_RUN_TIME_COUNTER_VALUE() :用戶程序需要提供一個(gè)返回基準(zhǔn)時(shí)鐘當(dāng)前“時(shí)間”的函數(shù),,這個(gè)函數(shù)要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。
??舉一個(gè)例子,,假如我們配置了一個(gè)定時(shí)器,,每500us中斷一次。在定時(shí)器中斷服務(wù)例程中簡(jiǎn)單的使長(zhǎng)整形變量ulHighFrequencyTimerTicks自增,。那么上面提到兩個(gè)宏定義如下(可以在FreeRTOSConfig.h中添加):
extern volatile unsigned longulHighFrequencyTimerTicks; #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL ) #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks
34. configUSE_CO_ROUTINES ??設(shè)置成1表示使用協(xié)程,,0表示不使用協(xié)程。如果使用協(xié)程,,必須在工程中包含croutine.c文件,。
??注:協(xié)程(Co-routines)主要用于資源發(fā)非常受限的嵌入式系統(tǒng)(RAM非常少),通常不會(huì)用于32位微處理器,。 ??在當(dāng)前嵌入式硬件環(huán)境下,,不建議使用協(xié)程,F(xiàn)reeRTOS的開(kāi)發(fā)者早已經(jīng)停止開(kāi)發(fā)協(xié)程,。
35. configMAX_CO_ROUTINE_PRIORITIES ??應(yīng)用程序協(xié)程(Co-routines)的有效優(yōu)先級(jí)數(shù)目,,任何數(shù)目的協(xié)程都可以共享一個(gè)優(yōu)先級(jí)。使用協(xié)程可以單獨(dú)的分配給任務(wù)優(yōu)先級(jí),。見(jiàn)configMAX_PRIORITIES ,。
36. configUSE_TIMERS ??設(shè)置成1使用軟件定時(shí)器,為0不使用軟件定時(shí)器功能,。詳細(xì)描述見(jiàn)Software Timers,。
37. configTIMER_TASK_PRIORITY ??設(shè)置軟件定時(shí)器服務(wù)/守護(hù)進(jìn)程的優(yōu)先級(jí)。詳細(xì)描述見(jiàn)Software Timers,。
38. 【configTIMER_QUEUE_LENGTH】 ??設(shè)置軟件定時(shí)器命令隊(duì)列的長(zhǎng)度,。詳詳細(xì)描述見(jiàn)Software Timers。
39.configTIMER_TASK_STACK_DEPTH ??設(shè)置軟件定時(shí)器服務(wù)/守護(hù)進(jìn)程任務(wù)的堆棧深度,。詳詳細(xì)描述見(jiàn)Software Timers,。
40. configKERNEL_INTERRUPT_PRIORITY,、configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY ??Cortex-M3、PIC24,、dsPIC,、PIC32、SuperH和RX600硬件設(shè)備需要設(shè)置宏configKERNEL_INTERRUPT_PRIORITY ,;PIC32,、RX600和Cortex-M硬件設(shè)備需要設(shè)置宏configMAX_SYSCALL_INTERRUPT_PRIORITY 。 ??configMAX_SYSCALL_INTERRUPT_PRIORITY 和configMAX_API_CALL_INTERRUPT_PRIORITY ,,這兩個(gè)宏是等價(jià)的,,后者是前者的新名字,用于更新的移植層代碼,。 ??應(yīng)該將configKERNEL_INTERRUPT_PRIORITY 設(shè)置為最低優(yōu)先級(jí),。 ??注意下面的描述中,在中斷服務(wù)例程中僅可以調(diào)用以“FromISR”結(jié)尾的API函數(shù),。 僅需要設(shè)置configKERNEL_INTERRUPT_PRIORITY 的硬件設(shè)備 ??configKERNEL_INTERRUPT_PRIORITY 用來(lái)設(shè)置FreeRTOS內(nèi)核自己的中斷優(yōu)先級(jí),。調(diào)用FreeRTOS API的中斷必須運(yùn)行在這個(gè)優(yōu)先級(jí);不調(diào)用FreeRTOS API的中斷,,可以運(yùn)行在更高的優(yōu)先級(jí),,所以這些中斷不會(huì)被因FreeRTOS內(nèi)核活動(dòng)而延時(shí)。
configKERNEL_INTERRUPT_PRIORITY 和configMAX_SYSCALL_INTERRUPT_PRIORITY 都需要設(shè)置的硬件設(shè)備 ??configKERNEL_INTERRUPT_PRIORITY 用來(lái)設(shè)置FreeRTOS內(nèi)核自己的中斷優(yōu)先級(jí)(因?yàn)镕reeRTOS內(nèi)核中斷不允許搶占用戶使用的中斷,,因此這個(gè)宏一般定義為硬件最低優(yōu)先級(jí)),。configMAX_SYSCALL_INTERRUPT_PRIORITY 用來(lái)設(shè)置可以在中斷服務(wù)程序中安全調(diào)用FreeRTOS API函數(shù)的最高中斷優(yōu)先級(jí)。優(yōu)先級(jí)小于等于這個(gè)宏所代表的優(yōu)先級(jí)時(shí),,程序可以在中斷服務(wù)程序中安全的調(diào)用FreeRTOS API函數(shù),;如果優(yōu)先級(jí)大于這個(gè)宏所代表的優(yōu)先級(jí),表示FreeRTOS無(wú)法禁止這個(gè)中斷,,在這個(gè)中斷服務(wù)程序中絕不可以調(diào)用任何FreeRTOS API函數(shù)(即使是FromISR結(jié)尾的函數(shù)),。 ??通過(guò)設(shè)置configMAX_SYSCALL_INTERRUPT_PRIORITY的優(yōu)先級(jí)級(jí)別高于configKERNEL_INTERRUPT_PRIORITY可以實(shí)現(xiàn)完整的中斷嵌套模式。這意味著FreeRTOS內(nèi)核不能完全禁止中斷,,即使在臨界區(qū),。此外,,這對(duì)于分段內(nèi)核架構(gòu)的微處理器是有利的,。需要注意的是,當(dāng)一個(gè)新中斷發(fā)生后,,某些微處理器架構(gòu)會(huì)(在硬件上)禁止中斷,,這意味著從硬件響應(yīng)中斷到FreeRTOS重新使能中斷之間的這段短時(shí)間內(nèi),中斷是不可避免的被禁止的,。 ??不調(diào)用FreeRTOS API的中斷可以運(yùn)行在比configMAX_SYSCALL_INTERRUPT_PRIORITY 高的優(yōu)先級(jí),,這些級(jí)別的中斷不會(huì)被FreeRTOS禁止,,因此不會(huì)因?yàn)閳?zhí)行RTOS內(nèi)核而被延時(shí)。 ??例如:假如一個(gè)微控制器有8個(gè)中斷優(yōu)先級(jí)別:0表示最低優(yōu)先級(jí),,7表示最高優(yōu)先級(jí)(Cortex-M3和Cortex-M4內(nèi)核優(yōu)先數(shù)和優(yōu)先級(jí)別正好與之相反,,后續(xù)文章會(huì)專門(mén)介紹它們)。當(dāng)兩個(gè)配置選項(xiàng)分別為4和0時(shí),,下圖描述了每一個(gè)優(yōu)先級(jí)別可以和不可做的事件:
configMAX_SYSCALL_INTERRUPT_PRIORITY=4
configKERNEL_INTERRUPT_PRIORITY=0 ??這些配置參數(shù)允許非常靈活的中斷處理: ??在系統(tǒng)中可以像其它任務(wù)一樣為中斷處理任務(wù)分配優(yōu)先級(jí),。這些任務(wù)通過(guò)一個(gè)相應(yīng)中斷喚醒。中斷服務(wù)例程(ISR)內(nèi)容應(yīng)盡可能的精簡(jiǎn)—僅用于更新數(shù)據(jù)然后喚醒高優(yōu)先級(jí)任務(wù),。ISR退出后,,直接運(yùn)行被喚醒的任務(wù),因此中斷處理(根據(jù)中斷獲取的數(shù)據(jù)來(lái)進(jìn)行的相應(yīng)處理)在時(shí)間上是連續(xù)的,,就像ISR在完成這些工作,。這樣做的好處是當(dāng)中斷處理任務(wù)執(zhí)行時(shí),所有中斷都可以處在使能狀態(tài),。 ??中斷,、中斷服務(wù)例程(ISR)和中斷處理任務(wù)是三碼事:當(dāng)中斷來(lái)臨時(shí)會(huì)進(jìn)入中斷服務(wù)例程,中斷服務(wù)例程做必要的數(shù)據(jù)收集(更新),,之后喚醒高優(yōu)先級(jí)任務(wù),。這個(gè)高優(yōu)先級(jí)任務(wù)在中斷服務(wù)例程結(jié)束后立即執(zhí)行,它可能是其它任務(wù)也可能是中斷處理任務(wù),,如果是中斷處理任務(wù),,那么就可以根據(jù)中斷服務(wù)例程中收集的數(shù)據(jù)做相應(yīng)處理。
- 根據(jù)系統(tǒng)中的任何其他任務(wù),,中斷處理“任務(wù)”可以被寫(xiě)入并優(yōu)先化,。 這些是由中斷喚醒的任務(wù)。 中斷服務(wù)程序(ISR)本身應(yīng)該寫(xiě)得盡可能短 - 它只是抓取數(shù)據(jù),,然后喚醒高優(yōu)先級(jí)的處理程序任務(wù),。 然后,ISR直接返回到喚醒處理程序任務(wù)中,,因此中斷處理在時(shí)間上是連續(xù)的,,就像它們都在ISR本身完成一樣。 這樣做的好處是所有中斷在處理程序任務(wù)執(zhí)行時(shí)保持啟用狀態(tài),。
configMAX_SYSCALL_INTERRUPT_PRIORITY 接口有著更深一層的意義:在優(yōu)先級(jí)介于RTOS內(nèi)核中斷優(yōu)先級(jí)(等于configKERNEL_INTERRUPT_PRIORITY )和configMAX_SYSCALL_INTERRUPT_PRIORITY 之間的中斷允許全嵌套中斷模式并允許調(diào)用API函數(shù),。大于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中斷優(yōu)先級(jí)絕不會(huì)因?yàn)閳?zhí)行RTOS內(nèi)核而延時(shí)。- 運(yùn)行在大于
configMAX_SYSCALL_INTERRUPT_PRIORITY 的優(yōu)先級(jí)中斷是不會(huì)被RTOS內(nèi)核所屏蔽的,,因此也不受RTOS內(nèi)核功能影響,。這主要用于非常高的實(shí)時(shí)需求中。比如執(zhí)行電機(jī)轉(zhuǎn)向,。但是,,這類(lèi)中斷的中斷服務(wù)例程中絕不可以調(diào)用FreeRTOS的API函數(shù),。 ??為了使用這個(gè)方案,應(yīng)用程序要必須符合以下規(guī)則:調(diào)用FreeRTOS API函數(shù)的任何中斷,,都必須和RTOS內(nèi)核處于同一優(yōu)先級(jí)(由宏configKERNEL_INTERRUPT_PRIORITY 設(shè)置),,或者小于等于宏configMAX_SYSCALL_INTERRUPT_PRIORITY 定義的優(yōu)先級(jí)。
??ARM Cortex-M3和ARM Cortex-M4用戶的特別注意事項(xiàng):請(qǐng)閱讀專用于中斷ARM Cortex-M器件優(yōu)先級(jí)設(shè)置的頁(yè)面,。至少,,請(qǐng)記住,ARM Cortex-M3內(nèi)核使用數(shù)字優(yōu)先級(jí)較低的數(shù)字來(lái)表示高優(yōu)先級(jí)的中斷,,這看起來(lái)是違反直覺(jué)的,,很容易忘記!如果你想分配一個(gè)低優(yōu)先級(jí)的中斷,,不要給它賦予0(或者其他低數(shù)字值)的優(yōu)先級(jí),,因?yàn)檫@會(huì)導(dǎo)致系統(tǒng)中實(shí)際上具有最高優(yōu)先級(jí)的中斷,因此如果這樣會(huì)導(dǎo)致系統(tǒng)崩潰優(yōu)先級(jí)高configMAX_SYSCALL_INTERRUPT_PRIORITY ,。
??實(shí)際上,,ARM Cortex-M3內(nèi)核的最低優(yōu)先級(jí)為255 - 不過(guò),不同的ARM Cortex-M3供應(yīng)商實(shí)現(xiàn)了不同數(shù)量的優(yōu)先級(jí)位,,并提供了期望以不同方式指定優(yōu)先級(jí)的庫(kù)函數(shù),。例如,在STM32上,,您可以在ST驅(qū)動(dòng)程序庫(kù)調(diào)用中指定的最低優(yōu)先級(jí)實(shí)際上是15 - 您可以指定的最高優(yōu)先級(jí)為0,。
41. configASSERT ??configASSERT() 宏的語(yǔ)義與標(biāo)準(zhǔn)C assert()宏的語(yǔ)義相同。 如果傳遞給configASSERT() 的參數(shù)為零,,則觸發(fā)斷言,。 ??在整個(gè)FreeRTOS源文件中調(diào)用configASSERT() ,以檢查應(yīng)用程序如何使用FreeRTOS,。 強(qiáng)烈建議使用configASSERT() 定義來(lái)開(kāi)發(fā)FreeRTOS應(yīng)用程序,。 ??舉一個(gè)例子,我們想把非法參數(shù)所在的文件名和代碼行數(shù)打印出來(lái),,可以先定義一個(gè)函數(shù)vAssertCalled,,該函數(shù)有兩個(gè)參數(shù),分別接收觸發(fā)configASSERT宏的文件名和該宏所在行,,然后通過(guò)顯示屏或者串口輸出,。代碼如下:
#define configASSERT( ( x ) ) if( ( x ) == 0 )vAssertCalled( __FILE__, __LINE__ )
??這里__FILE__和__LINE__ 是大多數(shù)編譯器預(yù)定義的宏,分別表示代碼所在的文件名(字符串格式)和行數(shù)(整形),。 ??如果在調(diào)試器的控制下運(yùn)行FreeRTOS,,那么可以將configASSERT() 定義為僅禁用中斷并置于循環(huán)中,如下所示,。 這將有效的停止在assert測(cè)試失敗的代碼 - 暫停調(diào)試器然后會(huì)立即帶你到違規(guī)行,,所以你可以看到為什么它失敗。
/* Define configASSERT() to disable interrupts and sit in a loop. */ #define configASSERT( ( x ) ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
??這個(gè)例子雖然看起來(lái)很簡(jiǎn)單,,但由于要把整形__LINE__ 轉(zhuǎn)換成字符串再顯示,,在效率和實(shí)現(xiàn)上,都不能讓人滿意,。我們可以使用C標(biāo)準(zhǔn)庫(kù)assert的實(shí)現(xiàn)方法,,這樣函數(shù)vAssertCalled只需要接收一個(gè)字符串形式的參數(shù)(推薦仔細(xì)研讀下面的代碼并理解其中的技巧):
#define configASSERT(x) ((x)?(void) 0 :xAssertCalld(__FILE__ ":" STR(__LINE__) " " #x"\n"))
??這里稍微講解一下,由于內(nèi)置宏LINE是整數(shù)型的而不是字符串型,,把它轉(zhuǎn)化成字符串需要一個(gè)額外的處理層,。宏STR和和宏VAL正是用來(lái)輔助完成這個(gè)轉(zhuǎn)化。宏STR用來(lái)把整形行號(hào)替換掉LINE,,宏VAL用來(lái)把這個(gè)整形行號(hào)字符串化,。忽略宏STR和VAL中的任何一個(gè),只能得到字符串”LINE”,,這不是我們想要的,。 ??這里使用三目運(yùn)算符’?:’來(lái)代替參數(shù)判斷if語(yǔ)句,這樣可以接受任何參數(shù)或表達(dá)式,,代碼也更緊湊,,更重要的是代碼優(yōu)化度更高,因?yàn)槿绻麉?shù)恒為真,,在編譯階段就可以去掉不必要的輸出語(yǔ)句,。
42. configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS ??該值僅被FreeRTOS MPU使用。如果設(shè)置為1,,那么應(yīng)用程序編寫(xiě)器必須提供一個(gè)名為“application_defined_privileged_functions.h”的頭文件,,在該頭文件中可以實(shí)現(xiàn)應(yīng)用程序writer需要在特權(quán)模式下執(zhí)行的功能。* 注意,,盡管有.h擴(kuò)展名,,頭文件應(yīng)該包含C函數(shù)的實(shí)現(xiàn),而不僅僅是函數(shù)的原型(類(lèi)似于ARM提供的Cortex-m的內(nèi)核源文件),。*
43. INCLUDE Parameters ??以“INCLUDE”起始的宏允許用戶不編譯那些應(yīng)用程序不需要的實(shí)時(shí)內(nèi)核組件(函數(shù)),,這可以確保在你的嵌入式系統(tǒng)中RTOS占用最少的ROM和RAM。 每個(gè)宏以這樣的形式出現(xiàn): ??INCLUDE_FunctionName ??這里的FunctionName表示一個(gè)你可以控制是否編譯的API函數(shù),。如果你想使用該函數(shù),,就將這個(gè)宏設(shè)置成1,如果不想使用,,就將這個(gè)宏設(shè)置成0,。比如,對(duì)于API函數(shù)vTaskDelete():
#define INCLUDE_vTaskDelete 1
??表示希望使用vTaskDelete(),,允許編譯器編譯該函數(shù)
#define INCLUDE_vTaskDelete 0
??表示禁止編譯器編譯該函數(shù),。
參考
|