

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# FreeRTOS 核心基礎
<a name="dev-guide-freertos-kernel"></a>

FreeRTOS 核心是一種即時作業系統，可支援許多架構。它的基礎知識非常適合用於建置嵌入式微控制器應用程式。它提供的功能如下：
+ 多工排程器。
+ 多個記憶體配置選項 (包括建立完全靜態配置系統的能力)。
+ 任務間的協調基本功能，包括任務通知、訊息佇列、多種旗號類型，以及串流及訊息緩衝區。
+ 支援多核心微控制器上的對稱多處理 (SMP)。

FreeRTOS 核心絕不會執行不具確定性的操作 (例如查核連結清單、處於關鍵區段內部，或是插斷)。FreeRTOS 核心包含高效率的軟體計時器實作，除非計時器需要服務，否則便不會使用任何 CPU 時間。封鎖的任務不需要耗費時間的定期服務。直達任務通知允許快速任務訊號，幾乎不會造成任何 RAM 額外負荷。它們可用於大多數任務間和interrupt-to-task訊號案例。

FreeRTOS 核心小型、簡易且易於使用。典型 RTOS 核心二進位映像的範圍介於 4000 到 9000 位元組之間。

如需 FreeRTOS 核心up-to-date文件，請參閱 [ FreeRTOS.org](https://freertos.org/)。FreeRTOS.org 提供多種使用 FreeRTOS 核心的詳細教學和指南，包括 [FreeRTOS FreeRTOS 核心快速入門指南](https://freertos.org/Documentation/01-FreeRTOS-quick-start/01-Beginners-guide/02-Quick-start-guide)，以及 *FreeRTOS 文件*中更深入[的 RTOS 實作](https://freertos.org/Documentation/02-Kernel/05-RTOS-implementation-tutorial/01-RTOS-implementation)。

# FreeRTOS 核心排程器
<a name="freertos-kernel-scheduler"></a>

使用 RTOS 內嵌應用程式的結構可以視為一組獨立任務。每個任務都會在其自身的內容中執行，無須依存於其他任務。應用程式中在任何時間點上都只會有一個執行中的應用程式。即時 RTOS 排程器會決定每個任務執行的時機。每個任務都會取得一個自己的堆疊。當任務切換出去以讓另一個任務執行時，任務的執行內容會儲存到任務堆疊，使其可在相同任務於稍後切換回來繼續執行時進行還原。

為了提供具確定性的即時行為，FreeRTOS 任務排程器允許指派嚴格的優先順序給任務。RTOS 能確保可執行的最高優先順序任務獲得處理時間。具有相同優先順序的任務若同時執行，便需要共享處理時間。FreeRTOS 也會建立閒置任務，只在其他任務尚未準備好執行的期間執行。

# 核心記憶體配置
<a name="kernel-memory-allocation"></a>

每次建立任務、佇列或其他 RTOS 物件時，RTOS 核心都需要 RAM。RAM 可根據以下方式進行配置：
+ 於編譯時間靜態配置。
+ 由 RTOS API 物件建立函數從 RTOS 堆積動態配置。

動態建立 RTOS 物件時，基於多個原因，不一定適合使用標準 C 程式庫 `malloc()` 和 `free()` 函數：
+ 它們可能無法用於內嵌系統。
+ 它們會佔用寶貴的程式碼空間。
+ 它們通常並非安全執行緒。
+ 它們不具確定性。

基於這些原因，FreeRTOS 會將記憶體配置 API 保存在其可攜式 layer 中。可攜式 layer 位於實作核心 RTOS 功能的來源檔案外部，因此您可以提供應用程式限定實作給您正在開發的即時系統。當 RTOS 核心需要 RAM 時，它會呼叫 `pvPortMalloc()` 而非 `malloc()`()。釋放 RAM 時，RTOS 核心會呼叫 `vPortFree()` 而非 `free()`。

# 管理應用程式記憶體
<a name="application-memory-management"></a>

當應用程式需要記憶體時，它們可以從 FreeRTOS 堆積進行配置。FreeRTOS 提供數種堆積管理方案，其複雜度及功能各不相同。您也可以提供自己的堆積實作。

FreeRTOS 核心包含五種堆積實作：

**`heap_1`**  
為最簡易的實作。不允許釋放記憶體。

**`heap_2`**  
允許釋放記憶體，但不會聯合相鄰的可用區塊。

**`heap_3`**  
包裝標準 `malloc()` 及 `free()` 以確保執行緒的安全。

**`heap_4`**  
聯合相鄰的可用區塊，避免分散。包含絕對地址置放選項。

**`heap_5`**  
與 heap\$14 相似。可延伸堆積，跨越多個不相鄰的記憶體區域。

# 任務間協調
<a name="inter-task-coordination"></a>

本節包含 FreeRTOS 基本功能的相關資訊。

**Topics**
+ [佇列](#inter-task-queues)
+ [旗號與 Mutex](#inter-task-semaphones)
+ [直達任務通知](#direct-task-notifications)
+ [串流緩衝區](#rtos-stream-buffer)
+ [訊息緩衝區](#rtos-message-buffer)

## 佇列
<a name="inter-task-queues"></a>

佇列是任務間通訊的主要形式。它們可用於在任務間及插斷與任務間傳送訊息。在大多數的情況下，它們會用來做為安全執行緒的先進先出 (FIFO) 緩衝區。新的資料會傳送到佇列後方。(資料也可以傳送到佇列前方。) 訊息會以複製的方式透過佇列傳送，即表示資料 (可為指向大型緩衝區的指標) 本身會複製到佇列中，而非僅只是將參考存放到資料內。

佇列 API 允許指定封鎖時間。當任務嘗試從空白佇列讀取時，任務會置放到封鎖狀態中，直到資料在佇列上可供使用，或是超過封鎖時間。處於封鎖狀態中的任務不會使用任何 CPU 時間，可讓其他任務執行。同樣的，當任務嘗試寫入填滿的佇列時，任務會置放到封鎖狀態中，直到佇列中有可用的空間，或是超過封鎖時間。若在相同佇列上有超過一個處於封鎖狀態的任務，則具有最高優先順序的任務會最先解除封鎖。

其他 FreeRTOS 基本功能 (例如直達任務通知和串流及訊息緩衝區) 可在許多常見設計案例中，提供除佇列之外的輕量替代項目。

## 旗號與 Mutex
<a name="inter-task-semaphones"></a>

FreeRTOS 核心可提供二進位旗號、計數旗號，以及適用於互斥及同步處理的 mutex。

二進位旗號只能擁有兩個值。它們是實作同步處理 (任務間或任務與插斷間) 的好選擇。計數旗號可接收兩個以上的值。它們可讓許多任務共享資源或執行更複雜的同步處理操作。

Mutex 是二進位旗號，包含優先順序繼承機制。這表示若具有高優先順序的任務在嘗試取得由低優先順序任務保有的 mutex 時遭到封鎖，保有該字符任務的優先順序會暫時提升至遭封鎖任務的優先順序。這項機制的目的是為了確保能盡可能縮短較高優先順序任務處於封鎖狀態中的時間，將已發生的優先順序反轉減至最少。

## 直達任務通知
<a name="direct-task-notifications"></a>

任務通知可讓任務與其他任務互動，並和插斷服務常式 (ISR) 進行同步，而無須單獨的通訊物件 (例如旗號)。每個 RTOS 任務都具有一個 32 位元的通知值，用於存放通知的內容 (若有的話)。RTOS 任務通知是一種事件，會直接傳送到能解除鎖定接收端任務的任務，並選擇性更新接收端任務的通知值。

RTOS 任務通知可用來做為除二進位、計數旗號及佇列 (在某些情況下) 之外更快的輕量替代項目。將較於可用於執行相同功能的 FreeRTOS 功能，任務通知在速度及 RAM 使用量上都具有優勢。但是，只有在僅有一個任務能做為事件的收件人時，才能使用任務通知。

## 串流緩衝區
<a name="rtos-stream-buffer"></a>

串流緩衝區可讓來自插斷服務常式的位元組串流傳遞至任務，或是從其中一個任務傳遞至另一個任務。位元組串流可為任意長度，並且不一定要有開始或結束。一次可以寫入任何數量的位元組，並且一次也能讀取任何數量的位元組。您可以透過在專案中加納入 `stream_buffer.c` 來源檔案，來啟用串流緩衝區功能。

串流緩衝區假設只有一個寫入緩衝區的任務或插斷 (寫入者)，並且只有一個從緩衝區讀取的任務或插斷 (讀取者)。寫入者和讀取者為不同的任務或插斷服務常式不會影響安全，但擁有多個寫入者或讀取者則不安全。

串流緩衝區實作會使用直達服務通知。因此，呼叫將呼叫端任務置放到封鎖狀態的串流緩衝區 API 可變更任務的通知狀態及值。

### 傳送資料
<a name="rtos-stream-buffer-send"></a>

`xStreamBufferSend()` 用於在任務中將資料傳送到串流緩衝區。`xStreamBufferSendFromISR()` 則用於在插斷服務常式 (ISR) 中將資料傳送到串流緩衝區。

`xStreamBufferSend()` 允許指定封鎖時間。使用非零的封鎖時間呼叫 `xStreamBufferSend()` 以寫入串流緩衝區，並且緩衝區已填滿時，任務便會置放到封鎖狀態，直到有可用的空間或超過封鎖時間。

`sbSEND_COMPLETED()` 和 `sbSEND_COMPLETED_FROM_ISR()` 為巨集，會在資料寫入串流緩衝區時呼叫 (由 FreeRTOS API 從內部呼叫)。它會取得已更新串流緩衝區的控點。這兩個巨集都會檢查串流緩衝區上是否有正在等待資料的遭封鎖任務；若有的話，便會將任務從封鎖狀態中移除。

您可以透過在 [`FreeRTOSConfig.h`](freertos-config.md) 中提供自己的 `sbSEND_COMPLETED()` 實作，來變更此預設行為。這在使用串流緩衝區來在多核心處理器的核心之間傳遞資料時很有用。在這種案例下，可實作 `sbSEND_COMPLETED()` 來在另一個 CPU 核心內產生插斷，然後插斷的服務常式便能使用 `xStreamBufferSendCompletedFromISR()` API 來檢查正在等待資料的任務，並視需要解除封鎖。

### 接收資料
<a name="rtos-stream-buffer-receive"></a>

`xStreamBufferReceive()` 用於在任務中從串流緩衝區讀取資料。`xStreamBufferReceiveFromISR()` 則用於在插斷服務常式 (ISR) 中從串流緩衝區中讀取資料。

`xStreamBufferReceive()` 允許指定封鎖時間。使用非零的封鎖時間呼叫 `xStreamBufferReceive()` 以從串流緩衝區讀取，並且緩衝區為空白時，任務便會置放到封鎖狀態，直到串流緩衝區中有指定數量的資料可用，或是超過封鎖時間。

解除封鎖任務前串流緩衝區中必須具備的資料數量稱為串流緩衝區的觸發層級。觸發層級為 10 的封鎖任務會在至少有 10 個位元組寫入緩衝區，或是超過任務的封鎖時間時解除封鎖。若在到達觸發層級前超過讀取中任務的封鎖時間，則任務便會接收到任何寫入該緩衝區的資料。任務的觸發層級必須設為介於 1 和串流緩衝區大小之間的值。串流緩衝區的觸發層級會在呼叫 `xStreamBufferCreate()` 時設定。您可透過呼叫 `xStreamBufferSetTriggerLevel()` 來變更該層級。

`sbRECEIVE_COMPLETED()` 和 `sbRECEIVE_COMPLETED_FROM_ISR()` 為巨集，會在從串流緩衝區讀取資料時呼叫 (由 FreeRTOS API 從內部呼叫)。巨集會檢查串流緩衝區上是否有正在等待可用空間的遭封鎖任務；若有的話，便會將任務從封鎖狀態中移除。您可以透過在 [`FreeRTOSConfig.h`](freertos-config.md) 中提供替代實作，來變更 `sbRECEIVE_COMPLETED()` 的預設行為。

## 訊息緩衝區
<a name="rtos-message-buffer"></a>

訊息緩衝區可讓來自插斷服務常式的可變長度離散訊息傳遞至任務，或是從其中一個任務傳遞至另一個任務。例如，長度為 10、20 和 123 位元組的訊息全部都可寫入相同的訊息緩衝區，並從從該緩衝區讀取。10 位元組的訊息只能以 10 位元組訊息的方式讀取，而無法以個別位元組進行讀取。訊息緩衝區是建置在串流緩衝區實作上。您可以透過在專案中加入 `stream_buffer.c` 原始檔案，來啟用訊息緩衝區功能。

訊息緩衝區假設只有一個寫入緩衝區的任務或插斷 (寫入者)，並且只有一個從緩衝區讀取的任務或插斷 (讀取者)。寫入者和讀取者為不同的任務或插斷服務常式不會影響安全，但擁有多個寫入者或讀取者則不安全。

訊息緩衝區實作會使用直達任務通知。因此，呼叫將呼叫端任務置放到封鎖狀態的串流緩衝區 API 可變更任務的通知狀態及值。

為啟用訊息緩衝區來處理不同大小的訊息，每個訊息的長度都會在寫入訊息本身之前寫入訊息緩衝區。長度會存放在類型為 `size_t` 的變數中，通常在 32 位元組架構上的大小為 4 位元組。因此，寫入 10 位元組的訊息到訊息緩衝區，實際使用的緩衝區空間為 14 位元組。同樣的，寫入 100 位元組的訊息到訊息緩衝區，實際使用的緩衝區空間為 104 位元組。

### 傳送資料
<a name="rtos-message-buffer-send"></a>

`xMessageBufferSend()` 用於從任務中將資料傳送到訊息緩衝區。`xMessageBufferSendFromISR()` 則用於從插斷服務常式 (ISR) 中將資料傳送到訊息緩衝區。

`xMessageBufferSend()` 允許指定封鎖時間。使用非零的封鎖時間呼叫 `xMessageBufferSend()` 以寫入訊息緩衝區，並且緩衝區已填滿時，任務便會置放到封鎖狀態，直到訊息緩衝區中有可用的空間或超過封鎖時間。

`sbSEND_COMPLETED()` 和 `sbSEND_COMPLETED_FROM_ISR()` 為巨集，會在資料寫入串流緩衝區時呼叫 (由 FreeRTOS API 從內部呼叫)。它會接收一個參數，該參數為已更新串流緩衝區的控點。這兩個巨集都會檢查串流緩衝區上是否有正在等待資料的遭封鎖任務；若有的話，它們便會將任務從封鎖狀態中移除。

您可以透過在 [`FreeRTOSConfig.h`](freertos-config.md) 中提供自己的 `sbSEND_COMPLETED()` 實作，來變更此預設行為。這在使用串流緩衝區來在多核心處理器的核心之間傳遞資料時很有用。在這種案例下，可實作 `sbSEND_COMPLETED()` 來在另一個 CPU 核心內產生插斷，然後插斷的服務常式便能使用 `xStreamBufferSendCompletedFromISR()` API 來檢查正在等待資料的任務，並視需要解除封鎖。

### 接收資料
<a name="rtos-message-buffer-receive"></a>

`xMessageBufferReceive()` 用於在任務中從訊息緩衝區讀取資料。`xMessageBufferReceiveFromISR()` 則用於在插斷服務常式 (ISR) 中從訊息緩衝區讀取資料。`xMessageBufferReceive()` 則允許指定封鎖時間。使用非零的封鎖時間呼叫 `xMessageBufferReceive()` 以從訊息緩衝區讀取，並且緩衝區為空白時，任務便會置放到封鎖狀態，直到有可用的資料或超過封鎖時間。

`sbRECEIVE_COMPLETED()` 和 `sbRECEIVE_COMPLETED_FROM_ISR()` 為巨集，會在從串流緩衝區讀取資料時呼叫 (由 FreeRTOS API 從內部呼叫)。巨集會檢查串流緩衝區上是否有正在等待可用空間的遭封鎖任務；若有的話，便會將任務從封鎖狀態中移除。您可以透過在 [`FreeRTOSConfig.h`](freertos-config.md) 中提供替代實作，來變更 `sbRECEIVE_COMPLETED()` 的預設行為。

# 對稱多處理 (SMP) 支援
<a name="smp-support"></a>

[FreeRTOS 核心中的 SMP 支援](https://freertos.org/symmetric-multiprocessing-introduction.html)可讓 FreeRTOS 核心的一個執行個體跨多個相同的處理器核心排程任務。核心架構必須相同，並共用相同的記憶體。

FreeRTOS [ APIs](https://freertos.org/symmetric-multiprocessing-introduction.html#smp-specific-apis) 在單一核心和 SMP 版本之間基本上保持不變。因此，針對 FreeRTOS 單核心版本撰寫的應用程式應與 SMP 版本編譯，且最少或完全不費力。不過，可能存在一些功能問題，因為對於單一核心應用程式而言，某些假設可能不再適用於多核心應用程式。

一個常見的假設是，在執行較高優先順序的任務時，無法執行較低優先順序的任務。雖然在單一核心系統上是如此，但多核心系統不再是如此，因為多個任務可以同時執行。如果應用程式依賴相對任務優先順序來提供相互排除，則可能會在多核心環境中觀察到非預期的結果。

另一個常見的假設是，ISRs 無法與彼此或與其他任務同時執行。這在多核心環境中不再是如此。應用程式寫入器需要確保適當的相互排除，同時存取任務和 ISRs之間共用的資料。

# 軟體計時器
<a name="software-timers"></a>

軟體計時器允許在未來指定的時間執行函數。由計時器執行的函數稱為計時器的*回撥函數*。啟動的計時器和所執行回撥函數之間的時間稱為計時器的*期間*。FreeRTOS 核心提供高效率的軟體計時器實作，因為：
+ 它不會從插斷內容中執行計時器回撥函數。
+ 除非計時器確實已過期，否則它便不會使用任何處理時間。
+ 它不會將任何處理額外負荷新增到刻度插斷。
+ 它不會在停用插斷時查核任何連結清單結構。

# 低電力支援
<a name="low-power-support"></a>

與大多數的內嵌作業系統相似，FreeRTOS 核心會使用硬體計時器產生定期刻度插斷，用來測量時間。一般硬體計時器實作的省電受限於必須定期離開，然後再重新進入低電力狀態來處理刻度插斷。若刻度插斷的頻率過高，每一刻度進入及離開低電力狀態所使用的能源及時間，便會超過除最輕度省電模式之外所有模式潛在可節省的電力。

為了解決這項限制，FreeRTOS 包含了一個適用於低電力應用程式的無刻度計時器模式。FreeRTOS 無刻度閒置模式會在閒置期間停止定期刻度插斷 (即沒有可執行應用程式任務的期間)，然後在重新啟動刻度插斷時對 RTOS 刻度計數值進行修正調整。停止刻度插斷可讓微控制器保持在深度省電狀態，直到發生插斷或 RTOS 核心將任務轉換到準備就緒狀態時為止。

# 設定核心 (FreeRTOSConfig.h)
<a name="freertos-config"></a>

您可以使用 `FreeRTOSConfig.h` 標頭檔案設定特定主機板和應用程式的 FreeRTOS 核心。每個建置在核心上的應用程式都必須在其前置處理器包含路徑中具有 `FreeRTOSConfig.h` 標頭檔案。`FreeRTOSConfig.h` 是應用程式特定項目，應該放在應用程式目錄底下，而不是在任一個 FreeRTOS 核心原始程式碼目錄底下。

FreeRTOS 示範和測試應用程式`FreeRTOSConfig.h`的檔案位於 `freertos/vendors/vendor/boards/board/aws_demos/config_files/FreeRTOSConfig.h`和 `freertos/vendors/vendor/boards/board/aws_tests/config_files/FreeRTOSConfig.h`。

如需可在 `FreeRTOSConfig.h` 中指定的可用配置參數清單，請參閱 [FreeRTOS.org](https://www.freertos.org/a00110.html)。