3 - FreeRTOS內核配置說明

  • FreeRTOS內核是高度可定製的,使用配置文件FreeRTOSConfig.h進行定製。每個FreeRTOS應用都必須包含這個頭文件,用戶根據實際應用來裁剪定製FreeRTOS內核。這個配置文件是針對用戶程序的,而非內核,因此配置文件一般放在應用程序目錄下,不要放在RTOS內核源碼目錄下。
  • 在下載的FreeRTOS文件包中,每個演示例程都有一個FreeRTOSConfig.h文件。有些例程的配置文件是比較舊的版本,可能不會包含所有有效選項。如果沒有在配置文件中指定某個選項,那麼RTOS內核會使用默認值。典型的FreeRTOSConfig.h配置文件定義如下所示,隨後會說明裏面的每一個參數。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
 
/*Here is a good place to include header files that are required across
yourapplication. */
#include "something.h"
 
#define configUSE_PREEMPTION                    1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE                 0
#define configCPU_CLOCK_HZ                      60000000
#define configTICK_RATE_HZ                      250
#define configMAX_PRIORITIES                    5
#define configMINIMAL_STACK_SIZE                128
#define configTOTAL_HEAP_SIZE                   10240
#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
 
/*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
 
/*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_xTimerGetTimerDaemonTaskHandle  0
#define INCLUDE_pcTaskGetTaskName               0
#define INCLUDE_eTaskGetState                   0
#define INCLUDE_xEventGroupSetBitFromISR        1
#define INCLUDE_xTimerPendFunctionCall          0
 
/* Aheader file that defines trace macro can be included here. */
 
#end if/* FREERTOS_CONFIG_H*/
 
  1. configUSE_PREEMPTION
    爲1時RTOS使用搶佔式調度器,爲0時RTOS使用協作式調度器(時間片)。

     注:在多任務管理機制上,操作系統可以分爲搶佔式和協作式兩種。協作式操作系統是任務主動釋放CPU後,切換到下一個任務。任務切換的時機完全取決於正在運行的任務。
    
  2. configUSE_PORT_OPTIMISED_TASK_SELECTION

  • 某些運行FreeRTOS的硬件有兩種方法選擇下一個要執行的任務:通用方法和特定於硬件的方法(以下簡稱“特殊方法”)。
  • 通用方法:
    1. configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲0或者硬件不支持這種特殊方法。
    2. 可以用於所有FreeRTOS支持的硬件。
    3. 完全用C實現,效率略低於特殊方法。
    4. 不強制要求限制最大可用優先級數目
  • 特殊方法:
    1. 並非所有硬件都支持。
    2. 必須將configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲1。
    3. 依賴一個或多個特定架構的彙編指令(一般是類似計算前導零[CLZ]指令)。
    4. 比通用方法更高效。
    5. 一般強制限定最大可用優先級數目爲32。
  1. configUSE_TICKLESS_IDLE
  • 設置configUSE_TICKLESS_IDLE爲1使能低功耗tickless模式,爲0保持系統節拍(tick)中斷一直運行。
  • 通常情況下,FreeRTOS回調空閒任務鉤子函數(需要設計者自己實現),在空閒任務鉤子函數中設置微處理器進入低功耗模式來達到省電的目的。因爲系統要響應系統節拍中斷事件,因此使用這種方法會週期性的退出、再進入低功耗狀態。如果系統節拍中斷頻率過快,則大部分電能和CPU時間會消耗在進入和退出低功耗狀態上。
  • FreeRTOS的tickless空閒模式會在空閒週期時停止週期性系統節拍中斷。停止週期性系統節拍中斷可以使微控制器長時間處於低功耗模式。移植層需要配置外部喚醒中斷,當喚醒事件到來時,將微控制器從低功耗模式喚醒。微控制器喚醒後,會重新使能系統節拍中斷。由於微控制器在進入低功耗後,系統節拍計數器是停止的,但我們又需要知道這段時間能折算成多少次系統節拍中斷週期,這就需要有一個不受低功耗影響的外部時鐘源,即微處理器處於低功耗模式時它也在計時的,這樣在重啓系統節拍中斷時就可以根據這個外部計時器計算出一個調整值並寫入RTOS 系統節拍計數器變量中。
  1. configUSE_IDLE_HOOK
  • 設置爲1使用空閒鉤子(Idle Hook類似於回調函數),0忽略空閒鉤子。
  • 當RTOS調度器開始工作後,爲了保證至少有一個任務在運行,空閒任務被自動創建,佔用最低優先級(0優先級)。對於已經刪除的RTOS任務,空閒任務可以釋放分配給它們的堆棧內存。因此,在應用中應該注意,使用vTaskDelete()函數時要確保空閒任務獲得一定的處理器時間。除此之外,空閒任務沒有其它特殊功能,因此可以任意的剝奪空閒任務的處理器時間。
  • 應用程序也可能和空閒任務共享同個優先級。
  • 空閒任務鉤子是一個函數,這個函數由用戶來實現,RTOS規定了函數的名字和參數,這個函數在每個空閒任務週期都會被調用。
  • 要創建一個空閒鉤子:
    1. 設置FreeRTOSConfig.h 文件中的configUSE_IDLE_HOOK 爲1;
    2. 定義一個函數,函數名和參數如下所示:
void vApplicationIdleHook(void );
  • 這個鉤子函數不可以調用會引起空閒任務阻塞的API函數(例如:vTaskDelay()、帶有阻塞時間的隊列和信號量函數),在鉤子函數內部使用協程是被允許的。
  • 使用空閒鉤子函數設置CPU進入省電模式是很常見的。
  1. configUSE_MALLOC_FAILED_HOOK
  • 每當一個任務、隊列、信號量被創建時,內核使用一個名爲pvPortMalloc()的函數來從堆中分配內存。官方的下載包中包含5個簡單內存分配策略,分別保存在源文件heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c中。 僅當使用這五個簡單策略之一時,宏configUSE_MALLOC_FAILED_HOOK纔有意義。
  • 如果定義並正確配置malloc()失敗鉤子函數,則這個函數會在pvPortMalloc()函數返回NULL時被調用。只有FreeRTOS在響應內存分配請求時發現堆內存不足纔會返回NULL。
  • 如果宏configUSE_MALLOC_FAILED_HOOK設置爲1,那麼必須定義一個malloc()失敗鉤子函數,如果宏configUSE_MALLOC_FAILED_HOOK設置爲0,malloc()失敗鉤子函數不會被調用,即便已經定義了這個函數。malloc()失敗鉤子函數的函數名和原型必須如下所示:
void vApplicationMallocFailedHook( void);
  1. configUSE_TICK_HOOK
  • 設置爲1使用時間片鉤子(Tick Hook),0忽略時間片鉤子。

     注:時間片鉤子函數(Tick Hook Function)
    
  • 時間片中斷可以週期性的調用一個被稱爲鉤子函數(回調函數)的應用程序。時間片鉤子函數可以很方便的實現一個定時器功能。

  • 只有在FreeRTOSConfig.h中的configUSE_TICK_HOOK設置成1時纔可以使用時間片鉤子。一旦此值設置成1,就要定義鉤子函數,函數名和參數如下所示:

void vApplicationTickHook( void );
 
  • vApplicationTickHook()函數在中斷服務程序中執行,因此這個函數必須非常短小,不能大量使用堆棧,不能調用任何不是以”FromISR" 或 "FROM_ISR”結尾的API函數。
  • 在FreeRTOSVx.x.x\FreeRTOS\Demo\Common\Minimal文件夾下的crhook.c文件中有使用時間片鉤子函數的例程。
  1. configCPU_CLOCK_HZ
    寫入實際的CPU內核時鐘頻率,也就是CPU指令執行頻率,通常稱爲Fcclk。配置此值是爲了正確的配置系統節拍中斷週期。

  2. configTICK_RATE_HZ

  • RTOS 系統節拍中斷的頻率。即一秒中斷的次數,每次中斷RTOS都會進行任務調度。
  • 系統節拍中斷用來測量時間,因此,越高的測量頻率意味着可測到越高的分辨率時間。但是,高的系統節拍中斷頻率也意味着RTOS內核佔用更多的CPU時間,因此會降低效率。RTOS演示例程都是使用系統節拍中斷頻率爲1000HZ,這是爲了測試RTOS內核,比實際使用的要高。(實際使用時不用這麼高的系統節拍中斷頻率)
  • 多個任務可以共享一個優先級,RTOS調度器爲相同優先級的任務分享CPU時間,在每一個RTOS 系統節拍中斷到來時進行任務切換。高的系統節拍中斷頻率會降低分配給每一個任務的“時間片”持續時間。
  1. configMAX_PRIORITIES
  • 配置應用程序有效的優先級數目。任何數量的任務都可以共享一個優先級,使用協程可以單獨的給與它們優先權。見configMAX_CO_ROUTINE_PRIORITIES。
  • 在RTOS內核中,每個有效優先級都會消耗一定量的RAM,因此這個值不要超過你的應用實際需要的優先級數目。
  • 注:任務優先級
    • 每一個任務都會被分配一個優先級,優先級值從0~ (configMAX_PRIORITIES - 1)之間。低優先級數表示低優先級任務。空閒任務的優先級爲0(tskIDLE_PRIORITY),因此它是最低優先級任務。
    • FreeRTOS調度器將確保處於就緒狀態(Ready)或運行狀態(Running)的高優先級任務比同樣處於就緒狀態的低優先級任務優先獲取處理器時間。換句話說,處於運行狀態的任務永遠是高優先級任務。
    • 處於就緒狀態的相同優先級任務使用時間片調度機制共享處理器時間。
  1. configMINIMAL_STACK_SIZE
  • 定義空閒任務使用的堆棧大小。通常此值不應小於對應處理器演示例程文件FreeRTOSConfig.h中定義的數值。
  • 就像xTaskCreate()函數的堆棧大小參數一樣,堆棧大小不是以字節爲單位而是以字爲單位的,比如在32位架構下,棧大小爲100表示棧內存佔用400字節的空間。
  1. configTOTAL_HEAP_SIZE
    RTOS內核總計可用的有效的RAM大小。僅在你使用官方下載包中附帶的內存分配策略時,纔有可能用到此值。每當創建任務、隊列、互斥量、軟件定時器或信號量時,RTOS內核會爲此分配RAM,這裏的RAM都屬於configTOTAL_HEAP_SIZE指定的內存區。後續的內存配置會詳細講到官方給出的內存分配策略。

  2. configMAX_TASK_NAME_LEN
    調用任務函數時,需要設置描述任務信息的字符串,這個宏用來定義該字符串的最大長度。這裏定義的長度包括字符串結束符’\0’。

  3. configUSE_TRACE_FACILITY
    設置成1表示啓動可視化跟蹤調試,會激活一些附加的結構體成員和函數。

  4. configUSE_STATS_FORMATTING_FUNCTIONS (V7.5.0新增)
    設置宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS爲1會編譯vTaskList()和vTaskGetRunTimeStats()函數。如果將這兩個宏任意一個設置爲0,上述兩個函數不會被編譯。

  5. configUSE_16_BIT_TICKS

  • 定義系統節拍計數器的變量類型,即定義portTickType是表示16位變量還是32位變量。
  • 定義configUSE_16_BIT_TICKS爲1意味着portTickType代表16位無符號整形,定義configUSE_16_BIT_TICKS爲0意味着portTickType代表32位無符號整形。
  • 使用16位類型可以大大提高8位和16位架構微處理器的性能,但這也限制了最大時鐘計數爲65535個’Tick’。因此,如果Tick頻率爲250HZ(4MS中斷一次),對於任務最大延時或阻塞時間,16位計數器是262秒,而32位是17179869秒。
  1. configIDLE_SHOULD_YIELD
  • 這個參數控制任務在空閒優先級中的行爲。僅在滿足下列條件後,纔會起作用。
    1. 使用搶佔式內核調度
    2. 用戶任務使用空閒優先級。
  • 通過時間片共享同一個優先級的多個任務,如果共享的優先級大於空閒優先級,並假設沒有更高優先級任務,這些任務應該獲得相同的處理器時間。
  • 但如果共享空閒優先級時,情況會稍微有些不同。當configIDLE_SHOULD_YIELD爲1時,其它共享空閒優先級的用戶任務就緒時,空閒任務立刻讓出CPU,用戶任務運行,這樣確保了能最快響應用戶任務。處於這種模式下也會有不良效果(取決於你的程序需要),描述如下:
    在這裏插入圖片描述
  • 圖中描述了四個處於空閒優先級的任務,任務A、B和C是用戶任務,任務I是空閒任務。上下文切換週期性的發生在T0、T1…T6時刻。當用戶任務運行時,空閒任務立刻讓出CPU,但是,空閒任務已經消耗了當前時間片中的一定時間。這樣的結果就是空閒任務I和用戶任務A共享一個時間片。用戶任務B和用戶任務C因此獲得了比用戶任務A更多的處理器時間。
  • 可以通過下面方法避免:
    1. 如果合適的話,將處於空閒優先級的各單獨的任務放置到空閒鉤子函數中;
    2. 創建的用戶任務優先級大於空閒優先級;
    3. 設置IDLE_SHOULD_YIELD爲0;
  • 設置configIDLE_SHOULD_YIELD爲0將阻止空閒任務爲用戶任務讓出CPU,直到空閒任務的時間片結束。這確保所有處在空閒優先級的任務分配到相同多的處理器時間,但是,這是以分配給空閒任務更高比例的處理器時間爲代價的。
  1. configUSE_TASK_NOTIFICATIONS(V8.2.0新增)
  • 設置宏configUSE_TASK_NOTIFICATIONS爲1(或不定義宏configUSE_TASK_NOTIFICATIONS)將會開啓任務通知功能,有關的API函數也會被編譯。設置宏configUSE_TASK_NOTIFICATIONS爲0則關閉任務通知功能,相關API函數也不會被編譯。默認這個功能是開啓的。開啓後,每個任務多增加8字節RAM。
  • 這是個很有用的特性,一大亮點。
  • 每個RTOS任務具有一個32位的通知值,RTOS任務通知相當於直接向任務發送一個事件,接收到通知的任務可以解除任務的阻塞狀態(因等待任務通知而進入阻塞狀態)。相對於以前必須分別創建隊列、二進制信號量、計數信號量或事件組的情況,使用任務通知顯然更靈活。更好的是,相比於使用信號量解除任務阻塞,使用任務通知可以快45%(使用GCC編譯器,-o2優化級別)。
  1. configUSE_MUTEXES
  • 設置爲1表示使用互斥量,設置成0表示忽略互斥量。讀者應該瞭解在FreeRTOS中互斥量和二進制信號量的區別。
  • 關於互斥量和二進制信號量簡單說:
    1. 互斥型信號量必須是同一個任務申請,同一個任務釋放,其他任務釋放無效。
    2. 二進制信號量,一個任務申請成功後,可以由另一個任務釋放。
    3. 互斥型信號量是二進制信號量的子集
  1. configUSE_RECURSIVE_MUTEXES
    設置成1表示使用遞歸互斥量,設置成0表示不使用。

  2. configUSE_COUNTING_SEMAPHORES
    設置成1表示使用計數信號量,設置成0表示不使用。

  3. configUSE_ALTERNATIVE_API

  • 設置成1表示使用“替代”隊列函數(‘alternative’ queue functions),設置成0不使用。替代API在queue.h頭文件中有詳細描述。
  • 注:“替代”隊列函數已經被棄用,在新的設計中不要使用它!
  1. configCHECK_FOR_STACK_OVERFLOW
  • 每個任務維護自己的棧空間,任務創建時會自動分配任務需要的佔內存,分配內存大小由創建任務函數(xTaskCreate())的一個參數指定。堆棧溢出是設備運行不穩定的最常見原因,因此FreeeRTOS提供了兩個可選機制用來輔助檢測和改正堆棧溢出。配置宏configCHECK_FOR_STACK_OVERFLOW爲不同的常量來使用不同堆棧溢出檢測機制。
  • 注意,這個選項僅適用於內存映射未分段的微處理器架構。並且,在RTOS檢測到堆棧溢出發生之前,一些處理器可能先產生故障(fault)或異常(exception)來反映堆棧使用的惡化。如果宏configCHECK_FOR_STACK_OVERFLOW沒有設置成0,用戶必須提供一個棧溢出鉤子函數,這個鉤子函數的函數名和參數必須如下所示:
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
 
  • 參數xTask和pcTaskName爲堆棧溢出任務的句柄和名字。請注意,如果溢出非常嚴重,這兩個參數信息也可能是錯誤的!在這種情況下,可以直接檢查pxCurrentTCb變量。
  • 推薦僅在開發或測試階段使用棧溢出檢查,因爲堆棧溢出檢測會增大上下文切換開銷。
  • 任務切換出去後,該任務的上下文環境被保存到自己的堆棧空間,這時很可能堆棧的使用量達到了最大(最深)值。在這個時候,RTOS內核會檢測堆棧指針是否還指向有效的堆棧空間。如果堆棧指針指向了有效堆棧空間之外的地方,堆棧溢出鉤子函數會被調用。
  • 這個方法速度很快,但是不能檢測到所有堆棧溢出情況(比如,堆棧溢出沒有發生在上下文切換時)。設置configCHECK_FOR_STACK_OVERFLOW爲1會使用這種方法。
  • 當堆棧首次創建時,在它的堆棧區中填充一些已知值(標記)。當任務切換時,RTOS內核會檢測堆棧最後的16個字節,確保標記數據沒有被覆蓋。如果這16個字節有任何一個被改變,則調用堆棧溢出鉤子函數。
  • 這個方法比第一種方法要慢,但也相當快了。它能有效捕捉堆棧溢出事件(即使堆棧溢出沒有發生在上下文切換時),但是理論上它也不能百分百的捕捉到所有堆棧溢出(比如堆棧溢出的值和標記值相同,當然,這種情況發生的概率極小)。
  • 使用這個方法需要設置configCHECK_FOR_STACK_OVERFLOW爲2.
  1. configQUEUE_REGISTRY_SIZE
  • 隊列記錄有兩個目的,都涉及到RTOS內核的調試:
    1. 它允許在調試GUI中使用一個隊列的文本名稱來簡單識別隊列;
    2. 包含調試器需要的每一個記錄隊列和信號量定位信息;
  • 除了進行內核調試外,隊列記錄沒有其它任何目的。
  • configQUEUE_REGISTRY_SIZE定義可以記錄的隊列和信號量的最大數目。如果你想使用RTOS內核調試器查看隊列和信號量信息,則必須先將這些隊列和信號量進行註冊,只有註冊後的隊列和信號量纔可以使用RTOS內核調試器查看。查看API參考手冊中的vQueueAddToRegistry() 和vQueueUnregisterQueue()函數獲取更多信息。
  1. configUSE_QUEUE_SETS
    設置成1使能隊列集功能(可以阻塞、掛起到多個隊列和信號量),設置成0取消隊列集功能。

  2. configUSE_TIME_SLICING(V7.5.0新增)
    默認情況下(宏configUSE_TIME_SLICING未定義或者宏configUSE_TIME_SLICING設置爲1),FreeRTOS使用基於時間片的優先級搶佔式調度器。這意味着RTOS調度器總是運行處於最高優先級的就緒任務,在每個RTOS 系統節拍中斷時在相同優先級的多個任務間進行任務切換。如果宏configUSE_TIME_SLICING設置爲0,RTOS調度器仍然總是運行處於最高優先級的就緒任務,但是當RTOS 系統節拍中斷髮生時,相同優先級的多個任務之間不再進行任務切換。

  3. configUSE_NEWLIB_REENTRANT(V7.5.0新增)
    如果宏configUSE_NEWLIB_REENTRANT設置爲1,每一個創建的任務會分配一個newlib(一個嵌入式C庫)reent結構。

  4. configENABLE_BACKWARD_COMPATIBILITY
    頭文件FreeRTOS.h包含一系列#define宏定義,用來映射版本V8.0.0和V8.0.0之前版本的數據類型名字。這些宏可以確保RTOS內核升級到V8.0.0或以上版本時,之前的應用代碼不用做任何修改。在FreeRTOSConfig.h文件中設置宏configENABLE_BACKWARD_COMPATIBILITY爲0會去掉這些宏定義,並且需要用戶確認升級之前的應用沒有用到這些名字。

  5. configNUM_THREAD_LOCAL_STORAGE_POINTERS

  • 設置每個任務的線程本地存儲指針數組大小。
  • 線程本地存儲允許應用程序在任務的控制塊中存儲一些值,每個任務都有自己獨立的儲存空間,宏configNUM_THREAD_LOCAL_STORAGE_POINTERS指定每個任務線程本地存儲指針數組的大小。API函數vTaskSetThreadLocalStoragePointer()用於向指針數組中寫入值,API函數pvTaskGetThreadLocalStoragePointer()用於從指針數組中讀取值。
  • 比如,許多庫函數都包含一個叫做errno的全局變量。某些庫函數使用errno返回庫函數錯誤信息,應用程序檢查這個全局變量來確定發生了那些錯誤。在單線程程序中,將errno定義成全局變量是可以的,但是在多線程應用中,每個線程(任務)必須具有自己獨有的errno值,否則,一個任務可能會讀取到另一個任務的errno值。
  • FreeRTOS提供了一個靈活的機制,使得應用程序可以使用線程本地存儲指針來讀寫線程本地存儲。具體參見後續文章《FreeRTOS系列第12篇—FreeRTOS任務應用函數》。
  1. configGENERATE_RUN_TIME_STATS
  • 設置宏configGENERATE_RUN_TIME_STATS爲1使能運行時間統計功能。一旦設置爲1,則下面兩個宏必須被定義:
    1. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():用戶程序需要提供一個基準時鐘函數,函數完成初始化基準時鐘功能,這個函數要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上。這是因爲運行時間統計需要一個比系統節拍中斷頻率還要高分辨率的基準定時器,否則,統計可能不精確。基準定時器中斷頻率要比統節拍中斷快10~100倍。基準定時器中斷頻率越快,統計越精準,但能統計的運行時間也越短(比如,基準定時器10ms中斷一次,8位無符號整形變量可以計到2.55秒,但如果是1秒中斷一次,8位無符號整形變量可以統計到255秒)。
    2. portGET_RUN_TIME_COUNTER_VALUE():用戶程序需要提供一個返回基準時鐘當前“時間”的函數,這個函數要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。
  • 舉一個例子,假如我們配置了一個定時器,每500us中斷一次。在定時器中斷服務例程中簡單的使長整形變量ulHighFrequencyTimerTicks自增。那麼上面提到兩個宏定義如下(可以在FreeRTOSConfig.h中添加):
extern volatile unsigned longulHighFrequencyTimerTicks;
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )
#define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks
  1. configUSE_CO_ROUTINES
  • 設置成1表示使用協程,0表示不使用協程。如果使用協程,必須在工程中包含croutine.c文件。
  • 注:協程(Co-routines)主要用於資源發非常受限的嵌入式系統(RAM非常少),通常不會用於32位微處理器。
  • 在當前嵌入式硬件環境下,不建議使用協程,FreeRTOS的開發者早已經停止開發協程。
  1. configMAX_CO_ROUTINE_PRIORITIES
    應用程序協程(Co-routines)的有效優先級數目,任何數目的協程都可以共享一個優先級。使用協程可以單獨的分配給任務優先級。見configMAX_PRIORITIES。

  2. configUSE_TIMERS
    設置成1使用軟件定時器,爲0不使用軟件定時器功能。詳細描述見FreeRTOS software timers。

  3. configTIMER_TASK_PRIORITY
    設置軟件定時器服務/守護進程的優先級。詳細描述見FreeRTOS software timers 。

  4. configTIMER_QUEUE_LENGTH
    設置軟件定時器命令隊列的長度。詳細描述見FreeRTOS software timers。

  5. configTIMER_TASK_STACK_DEPTH
    設置軟件定時器服務/守護進程任務的堆棧深度,詳細描述見FreeRTOS software timers 。

  6. configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY

  • 這是移植和應用FreeRTOS出錯最多的地方,所以需要打起精神仔細讀懂。
  • Cortex-M3、PIC24、dsPIC、PIC32、SuperH和RX600硬件設備需要設置宏configKERNEL_INTERRUPT_PRIORITY;PIC32、RX600和Cortex-M硬件設備需要設置宏configMAX_SYSCALL_INTERRUPT_PRIORITY。
  • configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY,這兩個宏是等價的,後者是前者的新名字,用於更新的移植層代碼。
  • 注意下面的描述中,在中斷服務例程中僅可以調用以“FromISR”結尾的API函數。
    1. 僅需要設置configKERNEL_INTERRUPT_PRIORITY的硬件設備(也就是宏configMAX_SYSCALL_INTERRUPT_PRIORITY不會用到):configKERNEL_INTERRUPT_PRIORITY用來設置RTOS內核自己的中斷優先級。調用API函數的中斷必須運行在這個優先級;不調用API函數的中斷,可以運行在更高的優先級,所以這些中斷不會被因RTOS內核活動而延時。
    2. configKERNEL_INTERRUPT_PRIORITY和configMAX_SYSCALL_INTERRUPT_PRIORITY都需要設置的硬件設備:configKERNEL_INTERRUPT_PRIORITY用來設置RTOS內核自己的中斷優先級。因爲RTOS內核中斷不允許搶佔用戶使用的中斷,因此這個宏一般定義爲硬件最低優先級。configMAX_SYSCALL_INTERRUPT_PRIORITY用來設置可以在中斷服務程序中安全調用FreeRTOS API函數的最高中斷優先級。優先級小於等於這個宏所代表的優先級時,程序可以在中斷服務程序中安全的調用FreeRTOS API函數;如果優先級大於這個宏所代表的優先級,表示FreeRTOS無法禁止這個中斷,在這個中斷服務程序中絕不可以調用任何API函數。
  • 通過設置configMAX_SYSCALL_INTERRUPT_PRIORITY的優先級級別高於configKERNEL_INTERRUPT_PRIORITY可以實現完整的中斷嵌套模式。這意味着FreeRTOS內核不能完全禁止中斷,即使在臨界區。此外,這對於分段內核架構的微處理器是有利的。請注意,當一個新中斷髮生後,某些微處理器架構會(在硬件上)禁止中斷,這意味着從硬件響應中斷到FreeRTOS重新使能中斷之間的這段短時間內,中斷是不可避免的被禁止的。
  • 不調用API的中斷可以運行在比configMAX_SYSCALL_INTERRUPT_PRIORITY高的優先級,這些級別的中斷不會被FreeRTOS禁止,因此不會因爲執行RTOS內核而被延時。
  • 例如:假如一個微控制器有8箇中斷優先級別:0表示最低優先級,7表示最高優先級(Cortex-M3和Cortex-M4內核優先數和優先級別正好與之相反,後續文章會專門介紹它們)。當兩個配置選項分別爲4和0時,下圖描述了每一個優先級別可以和不可做的事件:
    • configMAX_SYSCALL_INTERRUPT_PRIORITY=4
    • configKERNEL_INTERRUPT_PRIORITY=0
      在這裏插入圖片描述
  • 這些配置參數允許非常靈活的中斷處理:
    • 在系統中可以像其它任務一樣爲中斷處理任務分配優先級。這些任務通過一個相應中斷喚醒。中斷服務例程(ISR)內容應儘可能的精簡—僅用於更新數據然後喚醒高優先級任務。ISR退出後,直接運行被喚醒的任務,因此中斷處理(根據中斷獲取的數據來進行的相應處理)在時間上是連續的,就像ISR在完成這些工作。這樣做的好處是當中斷處理任務執行時,所有中斷都可以處在使能狀態。
    • 中斷、中斷服務例程(ISR)和中斷處理任務是三碼事:當中斷來臨時會進入中斷服務例程,中斷服務例程做必要的數據收集(更新),之後喚醒高優先級任務。這個高優先級任務在中斷服務例程結束後立即執行,它可能是其它任務也可能是中斷處理任務,如果是中斷處理任務,那麼就可以根據中斷服務例程中收集的數據做相應處理。
    • configMAX_SYSCALL_INTERRUPT_PRIORITY接口有着更深一層的意義:在優先級介於RTOS內核中斷優先級(等於configKERNEL_INTERRUPT_PRIORITY)和configMAX_SYSCALL_INTERRUPT_PRIORITY之間的中斷允許全嵌套中斷模式並允許調用API函數。大於configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷優先級絕不會因爲執行RTOS內核而延時。
    • 運行在大於configMAX_SYSCALL_INTERRUPT_PRIORITY的優先級中斷是不會被RTOS內核所屏蔽的,因此也不受RTOS內核功能影響。這主要用於非常高的實時需求中。比如執行電機轉向。但是,這類中斷的中斷服務例程中絕不可以調用FreeRTOS的API函數。
    • 爲了使用這個方案,應用程序要必須符合以下規則:調用FreeRTOS API函數的任何中斷,都必須和RTOS內核處於同一優先級(由宏configKERNEL_INTERRUPT_PRIORITY設置),或者小於等於宏configMAX_SYSCALL_INTERRUPT_PRIORITY定義的優先級。
  1. configASSERT
  • 斷言,調試時可以檢查傳入的參數是否合法。FreeRTOS內核代碼的關鍵點都會調用configASSERT( x )函數,如果參數x爲0,則會拋出一個錯誤。這個錯誤很可能是傳遞給FreeRTOS API函數的無效參數引起的。定義configASSERT()有助於調試時發現錯誤,但是,定義configASSERT()也會增大應用程序代碼量,增大運行時間。推薦在開發階段使用這個斷言宏。

     舉一個例子,我們想把非法參數所在的文件名和代碼行數打印出來,可以先定義一個函數vAssertCalled,該函數有兩個參數,分別接收觸發configASSERT宏的文件名和該宏所在行,然後通過顯示屏或者串口輸出。代碼如下:
    

    #define configASSERT( ( x ) ) if( ( x ) == 0 )vAssertCalled( FILE, LINE )

     這裏__FILE__和__LINE__是大多數編譯器預定義的宏,分別表示代碼所在的文件名(字符串格式)和行數(整形)。
    
     這個例子雖然看起來很簡單,但由於要把整形__LINE__轉換成字符串再顯示,在效率和實現上,都不能讓人滿意。我們可以使用C標準庫assert的實現方法,這樣函數vAssertCalled只需要接收一個字符串形式的參數(推薦仔細研讀下面的代碼並理解其中的技巧):
    

    #define STR(x) VAL(x)
    #define VAL(x) #x
    #define configASSERT(x) ((x)?(void) 0 :xAssertCalld(FILE “:” STR(LINE) " " #x"\n"))

     這裏稍微講解一下,由於內置宏__LINE__是整數型的而不是字符串型,把它轉化成字符串需要一個額外的處理層。宏STR和和宏VAL正是用來輔助完成這個轉化。宏STR用來把整形行號替換掉__LINE__,宏VAL用來把這個整形行號字符串化。忽略宏STR和VAL中的任何一個,只能得到字符串”__LINE__”,這不是我們想要的。
    
     這裏使用三目運算符’?:’來代替參數判斷if語句,這樣可以接受任何參數或表達式,代碼也更緊湊,更重要的是代碼優化度更高,因爲如果參數恆爲真,在編譯階段就可以去掉不必要的輸出語句。
    
  1. INCLUDE Parameters
  • 以“INCLUDE”起始的宏允許用戶不編譯那些應用程序不需要的實時內核組件(函數),這可以確保在你的嵌入式系統中RTOS佔用最少的ROM和RAM。
  • 每個宏以這樣的形式出現:
    INCLUDE_FunctionName
  • 在這裏FunctionName表示一個你可以控制是否編譯的API函數。如果你想使用該函數,就將這個宏設置成1,如果不想使用,就將這個宏設置成0。比如,對於API函數vTaskDelete():
#define INCLUDE_vTaskDelete    1
  • 表示希望使用vTaskDelete(),允許編譯器編譯該函數
#define INCLUDE_vTaskDelete    0
 
  • 表示禁止編譯器編譯該函數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章