1. 程式人生 > >RTX——第14章 信號量

RTX——第14章 信號量

建議 fonts 共享 打印 nvic 都是 接收 空閑 size

本章節開始講解 RTX 的另一個重要的任務間的同步和資源共享機制,信號量。

信號量有3種用途:

1) 表達事件的發生次數或者已發生事件的數量。

2) 表達資源可用性,例如有一臺打印機,信號量值為1表示打印機空閑, 為0表示打印機被占用。這是資源可用量的一個特例,也可以說,信號量值為1表示有1臺打印機空閑,為0表示無打印機空閑。

3) 表達資源可用量,例如有10個串口,信號量值用於表達空閑串口數量

實際的應用中,信號量的作用又該如何體現呢?比如有個 30 人的電腦機房,我們就可以創建信號量
的初始化值是 30,表示 30 個可用資源,不理解的初學者表示信號量還有初始值?是的,信號量說白了就
是共享資源的數量。 另外我們要求一個同學使用一臺電腦,這樣每有一個同學使用一臺電腦,那麽信號量
的數值就減一,直到 30 臺電腦都被占用,此時信號量的數值就是 0。 如果此時還有幾個同學沒有電腦可
以使用,那麽這幾個同學就得等待,直到有同學離開,有一個同學離開,那麽信號量的數值就加 1,有兩
個就加 2,依次類推。剛才沒有電腦用的同學此時就有電腦可以用了,有幾個同學用,信號量就減幾,直
到再次沒有電腦可以用,這麽一個過程就是使用信號量來管理共享資源的過程。
平時使用信號量主要實現以下兩個功能:
? 兩個任務或者中斷函數跟任務之間的同步功能,這個和上章節講解的事件標誌組是類似的。其實就是
共享資源為 1 的時候。
? 多個共享資源的管理,就像上面舉的機房上機的例子。

RTX 任務間信號量的實現

技術分享

運行條件:
? 創建 2 個任務 Task1 和 Task2。
? 創建信號量可用資源為 1。
運行過程描述如下:
? 任務 Task1 運行過程中調用函數 os_sem_wait 獲取信號量資源,如果信號量沒有被任務 Task2 占用,
Task1 將直接獲取資源。 如果信號被 Task2 占用,任務 Task1 將由運行態轉到掛起狀態,等待資源
可用。一旦獲取了資源並使用完畢後會通過函數 os_sem_send 釋放掉資源。
? 任務 Task2 運行過程中調用函數 os_sem_wait 獲取信號量資源,如果信號量沒有被任務 Task2 占用,
Task1 將直接獲取資源。 如果信號被 Task2 占用,任務 Task1 將由運行態轉到掛起狀態,等待資源


可以。一旦獲取了資源並使用完畢後會通過函數 os_sem_send 釋放掉資源。
上面就是一個簡單 RTX 任務間信號量的使用過程。

RTX 中斷方式信號量的實現
RTX 中斷方式信號量的實現是指中斷函數和 RTX 任務之間使用信號量。 信號量的中斷方式主要是用
於實現任務同步,與上個章節講解事件標誌組中斷方式是一樣的。
下面我們通過如下的框圖來說明一下 RTX 中斷方式信號量的實現,讓大家有一個形象的認識。
技術分享

運行條件:
? 創建 1 個任務 Task1 和一個串口接收中斷。
? 信號量的初始值為 0,串口中斷調用函數 isr_sem_send 釋放信號量,任務 Task1 調用函數
os_sem_wait 獲取信號量資源。


運行過程描述如下:
? 任務 Task1 運行過程中調用函數 os_sem_wait,由於信號量的初始值是 0,沒有信號量資源可用,任
務 Task1 由運行態進入到掛起態。
? Task1 掛起的情況下,串口接收到數據進入到了串口中斷服務程序,在串口中斷服務程序中調用函數
isr_sem_send 釋放信號量資源,信號量數值加 1,此時信號量計數值為 1,任務 Task1 由掛起態進
入到就緒態,在調度器的作用下由就緒態又進入到運行態,任務 Task1 獲得信號量後,信號量數值減
1,此時信號量計數值又變成了 0。
? 再次循環執行時,任務 Task1 調用函數 os_sem_wait 由於沒有資源可用再次進入到掛起態,等待串
口釋放信號量資源,如此往復循環。
上面就是一個簡單 RTX 中斷方式信號量同步過程。 實際應用中,中斷方式的消息機制切記註意以下四個個
問題:

? 中斷函數的執行時間越短越好,防止其它低於這個中斷優先級的異常不能得到及時響應
? 實際應用中,建議不要在中斷中實現消息處理,用戶可以在中斷服務程序裏面發送消息通知任務,在
任務中實現消息處理,這樣可以有效的保證中斷服務程序的實時響應。同時此任務也需要設置為高優
先級,以便退出中斷函數後任務可以得到及時執行。
? 中斷服務程序中一定要調用專用於中斷的信號量設置函數 isr_sem_send。
? 在 RTX 操作系統中實現中斷函數和裸機編程是一樣的。
? 另外強烈推薦用戶將 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407,429 的
NVIC 優先級分組設置為 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這樣中斷
優先級的管理將非常方便。
? 用戶要在 RTX 多任務開啟前就設置好優先級分組,一旦設置好切記不可再修改。
信號量 API 函數
使用如下 4 個函數可以實現 RTX 的信號量:
? os_sem_init
? os_sem_send
? isr_sem_send
? os_sem_wait
技術分享

函數 os_sem_init
函數原型:
void os_sem_init (
OS_ID semaphore, /* os_sem 類型變量 */
U16 token_count ); /* 信號量初始值 */
函數描述:
函數 os_sem_init 用於信號量的初始化並設置初始值
? 第 1 個參數填寫數據類型為 OS_SEM 的變量,同時也作為 ID 標識
? 第 2 個參數是信號量初始值,也就是可用資源個數。
使用舉例:

技術分享

細心的讀者可能發現,上面的初始化函數為什麽不是這麽寫:os_sem_init(semaphore, 0),而是在變量
semaphore 前面加一個取地址符&,實際上這兩種寫法都是可以的,RTX 的手冊裏面默認都是前面加上
個取地址符,大家使用的時候也可以都加上,方便區分。
為什麽兩種寫法都可以呢,因為 OS_SEM 是這麽定義的:typedef U32 OS_SEM[2],對應上面的函
數舉例就是 U32 semaphore[2],定義了一個 32 位數組,數組裏面有兩個元素。 所以 os_sem_init 的第
一個參數填 semaphore 或者&semaphore 都是這個數組的首地址。

還有就是,明明函數的第一個參數是OS_ID類型,為什麽我們要定義一個OS_SEM類型的呢?這裏是因為OS_ID是void *類型的,我是怎麽知道要使用OS_SEM類型來初始化這個函數的呢?手冊這樣告訴我的:

技術分享

函數 os_sem_send
函數原型:
OS_RESULT os_sem_send (
OS_ID semaphore ); /* OS_SEM 類型變量 */
函數描述:
函數 os_sem_send 用於釋放信號量,調用後信號量計數值加 1。
? 第 1 個參數參數填寫數據類型為 OS_SEM 的變量,同時也作為 ID 標識。
? 返回值永遠是 OS_R_OK。
使用這個函數要註意以下問題:
1. 使用此函數前一定要調用函數 os_sem_init 進行初始化。

技術分享

函數 isr_sem_send
函數原型:
void isr_sem_send (
OS_ID semaphore ); /* OS_SEM 類型變量 */

函數描述:
函數 isr_sem_send 用於釋放信號量,調用後信號量計數值加 1。
? 第 1 個參數參數填寫數據類型為 OS_SEM 的變量,同時也作為 ID 標識。
使用這個函數要註意以下問題:
1. 使用此函數前一定要調用函數 os_sem_init 進行初始化。
技術分享

函數 os_sem_wait
函數原型:
OS_RESULT os_sem_wait (
OS_ID semaphore, /* OS_SEM 類型變量 */
U16 timeout ); /* 超時時間設置 */
函數描述:

函數 os_sem_wait 用於獲取信號量,如果當前的信號量計數值大於 0,那麽調用函數 os_sem_wait 後可
以成功獲取信號量,並將信號量的計數值減 1。如果信號量計數值等於 0,調用此函數的任務將由運行態
轉到掛起態,等待信號量資源可用,也就是等待信號量計數值大於 0。
? 第 1 個參數參數填寫數據類型為 OS_SEM 的變量,同時也作為 ID 標識。
? 第 2 個參數表示設置的等待時間,範圍 0-0xFFFF,當參數設置為 0-0xFFFE 時,表示等待這麽多個
時鐘節拍,參數設置為 0xFFFF 時表示無限等待直到有信號量資源可用。
? 函數返回 OS_R_SEM 表示函數設置的超時時間範圍內收到信號量可用資源。
函數返回 OS_R_TMO 表示超時。
函數返回 OS_R_OK 表示無需等待,立即獲得可用信號量資源。
使用這個函數要註意以下問題:
1. 使用此函數前一定要調用函數 os_sem_init 進行初始化。

使用舉例:

技術分享

代碼練習場:

技術分享

技術分享

技術分享

這裏的信號量初始化為0,按鍵一下就發送一次,發送完畢並接收之後,就又變成了0.

串口打印:

技術分享

RTX——第14章 信號量