以下內容轉載自安富萊電子:http://forum.armfly.com/forum.php
1 、信 號 量
1.1 信號量的概念及其作用
信號量(semaphores)是 20 世紀 60 年代中期 Edgser Dijkstra 發明的。使用信號量的最初目的是為了給共享資源建立一個標志,該標志表示該共享資源被占用情況。這樣,當一個任務在訪問共享資源之前,就可以先對這個標志進行查詢,從而在了解資源被占用的情況之后,再來決定自己的行為。
實際的應用中,信號量的作用又該如何體現呢?比如有個 30 人的電腦機房,我們就可以創建信號量的初始化值是 30,表示 30 個可用資源,不理解的初學者表示信號量還有初始值?是的,信號量說白了就是共享資源的數量。另外我們要求一個同學使用一台電腦,這樣每有一個同學使用一台電腦,那么信號量的數值就減一,直到 30 台電腦都被占用,此時信號量的數值就是 0。如果此時還有幾個同學沒有電腦可以使用,那么這幾個同學就得等待,直到有同學離開。有一個同學離開,那么信號量的數值就加 1,有兩個就加 2,依此類推。剛才沒有電腦用的同學此時就有電腦可以用了,有幾個同學用,信號量就減幾,直到再次沒有電腦可以用,這么一個過程就是使用信號量來管理共享資源的過程。
平時使用信號量主要實現以下兩個功能:
兩個任務之間或者中斷函數跟任務之間的同步功能,這個和前面章節講解的事件標志組是類似的。其實就是共享資源為 1 的時候。
多個共享資源的管理,就像上面舉的機房上機的例子。
針對這兩種功能,FreeRTOS 分別提供了二值信號量和計數信號量,其中二值信號量可以理解成計數信號量的一種特殊形式,即初始化為僅有一個資源可以使用,只不過 FreeRTOS 對這兩種都提供了 API函數,而像 RTX,uCOS-II 和 III 是僅提供了一個信號量功能,設置不同的初始值就可以分別實現二值信號量和計數信號量。當然,FreeRTOS 使用計數信號量也能夠實現同樣的效果。
另外,為什么叫二值信號量呢?因為信號量資源被獲取了,信號量值就是 0,信號量資源被釋放,信號量值就是 1,把這種只有 0和 1 兩種情況的信號量稱之為二值信號量。
實際上信號量還有很多其它用法,而且極具挑戰性,可以大大的開拓大家的視野,有興趣的可以閱讀一下《The Little Book Of Semaphores》,作者是 Allen B. Downy。
1.2 FreeRTOS 任務間計數信號量的實現
任務間信號量的實現是指各個任務之間使用信號量實現任務的同步或者資源共享功能。下面我們通過如下的框圖來說明一下 FreeRTOS 計數信號量的實現,讓大家有一個形象的認識。
運行條件:
創建 2 個任務 Task1 和 Task2。
創建計數信號量可用資源為 1。
運行過程描述如下:
任務 Task1 運行過程中調用函數 xSemaphoreTake 獲取信號量資源,如果信號量沒有被任務 Task2占用,Task1 將直接獲取資源。如果信號量被 Task2 占用,任務 Task1 將由運行態轉到阻塞狀態,等待資源可用。一旦獲取了資源並使用完畢后會通過函數 xSemaphoreGive 釋放掉資源。
任務 Task2 運行過程中調用函數 xSemaphoreTake 獲取信號量資源,如果信號量沒有被任務 Task1占用,Task2 將直接獲取資源。如果信號量被 Task1 占用,任務 Task2 將由運行態轉到阻塞狀態,等待資源可用。一旦獲取了資源並使用完畢后會通過函數 xSemaphoreGive 釋放掉資源。
上面就是一個簡單的 FreeRTOS 任務間計數信號量的使用過程。
1.3 FreeRTOS 中斷方式計數信號量的實現
FreeRTOS 中斷方式信號量的實現是指中斷函數和 FreeRTOS 任務之間使用信號量。信號量的中斷方式主要是用於實現任務同步,與前面章節講解事件標志組中斷方式是一樣的。
下面我們通過如下的框圖來說明一下 FreeRTOS 中斷方式信號量的實現,讓大家有一個形象的認識。
運行條件:
創建一個任務 Task1 和一個串口接收中斷。
信號量的初始值為 0,串口中斷調用函數 xSemaphoreGiveFromISR 釋放信號量,任務 Task1 調用函數 xSemaphoreTake 獲取信號量資源。
運行過程描述如下:
任務 Task1 運行過程中調用函數 xSemaphoreTake,由於信號量的初始值是 0,沒有信號量資源可用,任務 Task1 由運行態進入到阻塞態。
Task1 阻塞的情況下,串口接收到數據進入到了串口中斷服務程序,在串口中斷服務程序中調用函數xSemaphoreGiveFromISR 釋放信號量資源,信號量數值加 1,此時信號量計數值為 1,任務 Task1由阻塞態進入到就緒態,在調度器的作用下由就緒態又進入到運行態,任務 Task1 獲得信號量后,信號量數值減 1,此時信號量計數值又變成了 0。
再次循環執行時,任務 Task1 調用函數 xSemaphoreTake 由於沒有資源可用再次進入到阻塞態,等待串口釋放信號量資源,如此往復循環。
上面就是一個簡單的 FreeRTOS 中斷方式信號量同步過程。實際應用中,中斷方式的消息機制要注意以下四個問題:
中斷函數的執行時間越短越好,防止其它低於這個中斷優先級的異常不能得到及時響應。
實際應用中,建議不要在中斷中實現消息處理,用戶可以在中斷服務程序里面發送消息通知任務,在任務中實現消息處理,這樣可以有效地保證中斷服務程序的實時響應。同時此任務也需要設置為高優先級,以便退出中斷函數后任務可以得到及時執行。
中斷服務程序中一定要調用專用於中斷的信號量設置函數,即以 FromISR 結尾的函數。
在操作系統中實現中斷服務程序與裸機編程的區別。
如果 FreeRTOS 工程的中斷函數中沒有調用 FreeRTOS 的信號量 API 函數,與裸機編程是一樣的。
如果 FreeRTOS 工程的中斷函數中調用了 FreeRTOS 的信號量 API 函數,退出的時候要檢測是否有高優先級任務就緒,如果有就緒的,需要在退出中斷后進行任務切換,這點與裸機編程稍有區別。
另外強烈推薦用戶將 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407,F429的 NVIC 優先級分組設置為 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這樣中斷優先級的管理將非常方便。
用戶要在 FreeRTOS 多任務開啟前就設置好優先級分組,一旦設置好切記不可再修改。
1.4 FreeRTOS 任務間二值信號量的實現
任務間信號量的實現是指各個任務之間使用信號量實現任務的同步或者資源共享功能。下面我們通過如下的框圖來說明一下 FreeRTOS 二值信號量的實現,讓大家有一個形象的認識。
上面就是一個簡單的 FreeRTOS 任務間二值信號量的使用過程。
2 、計 數 信 號API 函 數
使用如下 18 個函數可以實現 FreeRTOS 的信號量(含計數信號量,二值信號量和互斥信號):
xSemaphoreCreateBinary()
xSemaphoreCreateBinaryStatic()
vSemaphoreCreateBinary()
xSemaphoreCreateCounting()
xSemaphoreCreateCountingStatic()
xSemaphoreCreateMutex()
xSemaphoreCreateMutexStatic()
xSem'CreateRecursiveMutex()
xSem'CreateRecursiveMutexStatic()
vSemaphoreDelete()
xSemaphoreGetMutexHolder()
uxSemaphoreGetCount()
xSemaphoreTake()
xSemaphoreTakeFromISR()
xSemaphoreTakeRecursive()
xSemaphoreGive()
xSemaphoreGiveRecursive()
xSemaphoreGiveFromISR()
關於這 18 個函數的講解及其使用方法可以看 FreeRTOS 在線版手冊:
2.1 函數 xSemaphoreCreateCounting
使用舉例:
static SemaphoreHandle_t xSemaphore = NULL; /* ********************************************************************************************************* * 函 數 名: AppObjCreate * 功能說明: 創建任務通信機制 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 初始化有 1 個可用資源,當前可用資源為 0,此時計數信號量的功能等同二值信號量 */ xSemaphore = xSemaphoreCreateCounting(1, 0); if(xSemaphore == NULL) { /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */ } }
2.2 函數 xSemaphoreGive
2.3 函數 xSemaphoreGiveFromISR
使用舉例:
static SemaphoreHandle_t xSemaphore = NULL; /* ********************************************************************************************************* * 函 數 名: TIM2_IRQHandler * 功能說明: 定時器中斷。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void TIM2_IRQHandler (void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* 中斷消息處理,此處省略 */ …… /* 發送同步信號 */ xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken); /* 如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中斷后切到當前最高優先級任務執行 */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken); }
2.4 函數 xSemaphoreTake
未完待續~~~(實驗任務間通信,中斷方式)