FreeRTOS 的信號旗標 ( Semaphore ) 和互斥旗標 ( Mutex ) 是用於在多任務平臺下的同步、資源管理和保護資源免遭破壞的內核物件。
☛ 什麼是信號旗標?
在 FreeRTOS 中的任務優先順序中,高優先順序的任務會搶佔低優先順序的任務,因此在執行高優先順序的任務時,低優先順序的任務可能會發生數據損壞的情況。 因為該任務尚未執行,並且數據不斷從感測器傳到該任務,這會導致數據丟失和整個應用程式故障。 因此,需要保護資源免受數據丟失的影響,而信號旗標在這裡起著重要的作用。
信號旗標 ( Semaphore ) 是一種信令機制,其中處於等待狀態的任務由另一個任務發出信號以執行。 換句話說,當 task1 完成工作時,它將顯示一個標誌或將標誌加 1,然後另一個任務 task2 接收到該標誌,表明它現在可以執行其工作。 當 task2 完成工作后,該標誌將減少 1。
因此,從根本上講,它是一種 ” 給予 ” 和 ” 接受 ” 機制,而信號旗標是一個整數變數,用於同步對資源的訪問。
信號旗標類型
FreeRTOS 的信號旗標有兩種類型,分別為:
⑴ 二進位信號旗標 ( Binary Semaphore )
它具有兩個整數值 0和 1。 它與長度為 1 的佇列有些相似。 例如,我們有兩個任務 task1 和 task2。 task1 將數據發送到 task2,因此 task2 繼續檢查佇列項是否為 1,然後它可以讀取數據,否則必須等待直到變為 1。 在獲取數據之後,task2 遞減佇列並將其設為 0,這意味著 task1 再次可以將數據發送到 task2。
⑵ 計數信號旗標 ( Counting Semaphore )
它的值大於 0,並且可以認為長度大於 1 的佇列。 該信號機用於計數事件。 在這種使用情況下,事件處理程式將在事件發生時 ” 給出 ” 信號旗標 ( 增加信號旗標計數值 ),處理程式任務將在每次處理事件時 ” 接收 ” 信號旗標 ( 減少信號旗標計數值 )。
因此,計數值為發生的事件數與已處理的事件數之差。
如何在 FreeRTOS 中使用信號旗標
FreeRTOS 支援不同的 API,用於創建信號旗標、獲取信號旗標和提供信號旗標。
現在,同一內核物件可以有兩種類型的 API。 如果必須從 ISR 提供信號機,則無法使用常規信號旗標 API。 您應該使用受中斷保護的 API。
使用二進位信號旗標,因為它易於理解和實現。 由於此處使用了中斷功能,因此您需要在 ISR 函數中使用受中斷保護的 API。 當我們說用中斷同步任務時,這意味著在 ISR 之後立即將任務置於 ” 運行 ” 狀態。
⑴ 創建信號旗標
要使用任何內核對象,我們必須首先創建。 要創建二進位信號旗標,請使用 vSemaphoreCreateBinary()。 該 API 不帶任何參數,並返回 SemaphoreHandle_t 類型的變數。 範例如下:
創建一個全域變數名稱 sema_v 來存儲信號旗標。
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary();
⑵ 提供信號旗標
為了提供信號旗標,有兩個 API 函數,一種用於中斷,另一種用於常規任務。
① xSemaphoreGive():此 API 在創建信號旗標時僅接受一個參數,即上述信號旗標的變數名,如 sema_v。 可以從要同步的任何常規任務中調用它。
② xSemaphoreGiveFromISR():該函數是 xSemaphoreGive() 的受中斷保護的 API 函數。 當需要同步 ISR 和正常任務時,應從 ISR 函數中使用 xSemaphoreGiveFromISR()。
⑶ 獲取信號旗標
要獲取信號旗標,請使用 API 函數 xSemaphoreTake()。 該 API 帶有兩個參數。
① xSemaphore:信號旗標的名稱。
② xTicksToWait:這是任務在 ” 阻塞 ” 狀態下等待信號旗標變為可用的最長時間。
xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
☛ 什麼是互斥旗標?
信號旗標是一種信號傳遞機制,與互斥旗標不同,互斥旗標 ( Mutex ) 是一種鎖定機制,與具有獨立的增量和遞減功能的信號旗標不同,但在互斥旗標中,該功能本身具有並給出。 這是一種避免共用資源損壞的技術。
為了保護共用資源,可以為資源分配一個令牌卡 ( 互斥體 )。 擁有此卡的人都可以訪問其他資源。 其他人應該等到卡歸還。 這樣,只有一種資源可以訪問任務,而其他資源則等待機會。 藉由下面的範例來瞭解 FreeRTOS 中的互斥旗標:
這裡我們有三個任務,一個任務是在 LCD 上列印數據,第二個任務是將 LDR 資料發送到 LCD 任務,最後一個任務是在 LCD 上發送溫度數據。 因此,這裡有兩個任務共用同一個資源,即 LCD。 如果 LDR 任務和溫度任務同時發送數據,則數據之一可能已損壞或丟失。
因此,為了保護數據丟失,我們需要為 task1 鎖定 LCD 資源,直到它完成顯示任務為止。 然後 LCD 任務將解鎖,之後 task2 才可以執行其工作。 下圖中為互斥旗標和信號旗標的工作情況。
如何在 FreeRTOS 中使用互斥旗標
互斥旗標也可以與信號旗標相同地使用。 首先,創建它,然後使用相應的 API 進行使用。
⑴ 創建互斥旗標
要創建一個互斥旗標,請使用 xSemaphoreCreateMutex()。 顧名思義,互斥鎖是一種二進位信號旗標。 它們用於不同的上下文和目的。 二進位信號旗標用於同步任務,而互斥旗標用於保護共享資源。
該 API 沒有任何參數,並返回 SemaphoreHandle_t 類型的變數。 如果無法創建互斥鎖,則x SemaphoreCreateMutex() 傳回 NULL。
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex();
⑵ 獲取互斥旗標
當任務想要訪問資源時,它將通過使用 xSemaphoreTake() 來獲取互斥物件。 它與二進位信號旗標相同。 它還需要兩個參數。
① xSemaphore:將使用的互斥物件的名稱。
② xTicksToWait:這是任務在 ” 阻止 ” 狀態下等待 Mutex 可用的最長時間。
⑶ 提供互斥旗標
訪問共享資源后,任務應返回互斥鎖,以便其他任務可以訪問它。 xSemaphoreGive() 用於將互斥旗標返回。 xSemaphoreGive() 函數僅採用一個參數,即在給出的互斥旗標。