

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

# 任務間協調
<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()` 的預設行為。