FreeRTOS 計(jì)數(shù)信號(hào)量
計(jì)數(shù)信號(hào)量是FreeRTOS 任務(wù)間的同步和資源共享機(jī)制,計(jì)數(shù)信號(hào)量的源碼實(shí)現(xiàn)是基于消息隊(duì)列實(shí)現(xiàn)的。
1 信號(hào)量的概念及其作用
????使用信號(hào)量的最初目的是為了給共享資源建立一個(gè)標(biāo)志,該標(biāo)志表示該共享資源被占用情況。這樣,當(dāng)一個(gè)任務(wù)在訪問共享資源之前,就可以先對(duì)這個(gè)標(biāo)志進(jìn)行查詢,從而在了解資源被占用的情況之后,再來決定自己的行為。
????實(shí)際的應(yīng)用中,信號(hào)量的作用又該如何體現(xiàn)呢?
????比如有?30 臺(tái)電腦的機(jī)房,我們就可以創(chuàng)建信號(hào)量的初始化值是 30,表示 30 個(gè)可用資源。
????另外我們要求一個(gè)同學(xué)使用一臺(tái)電腦,每有一個(gè)同學(xué)使用一臺(tái)電腦,信號(hào)量的數(shù)值就減一,直到 30 臺(tái)電腦都被占用,此時(shí)信號(hào)量的數(shù)值就是 0。如果此時(shí)還有幾個(gè)同學(xué)沒有電腦可以使用,那么這幾個(gè)同學(xué)就得等待,直到有同學(xué)離開。
????有一個(gè)同學(xué)離開,那么信號(hào)量的數(shù)值就加 1,有兩個(gè)就加 2,依此類推。剛才沒有電腦用的同學(xué)此時(shí)就有電腦可以用了,有幾個(gè)同學(xué)用,信號(hào)量就減幾,直到再次沒有電腦可以用,這么一個(gè)過程就是使用信號(hào)量來管理共享資源的過程。?
????平時(shí)使用信號(hào)量主要實(shí)現(xiàn)以下兩個(gè)功能:
◆ 兩個(gè)任務(wù)之間或者中斷函數(shù)跟任務(wù)之間的同步功能,這個(gè)和前面章節(jié)講解的事件標(biāo)志組是類似的。其實(shí)就是共享資源(信號(hào)量)為 1 的時(shí)候。
◆ 多個(gè)共享資源的管理,就像上面舉的機(jī)房上機(jī)的例子。 針對(duì)這兩種功能,F(xiàn)reeRTOS 分別提供了二值信號(hào)量和計(jì)數(shù)信號(hào)量,其中二值信號(hào)量可以理解成計(jì)數(shù)信號(hào)量的一種特殊形式,即初始化為僅有一個(gè)資源可以使用,只不過 FreeRTOS 對(duì)這兩種都提供了 API 函數(shù),而像 RTX,uCOS-II 和 III 是僅提供了一個(gè)信號(hào)量功能,設(shè)置不同的初始值就可以分別實(shí)現(xiàn)二值信 號(hào)量和計(jì)數(shù)信號(hào)量。當(dāng)然,F(xiàn)reeRTOS 使用計(jì)數(shù)信號(hào)量也能夠?qū)崿F(xiàn)同樣的效果。
????
2 FreeRTOS 任務(wù)間計(jì)數(shù)信號(hào)量的實(shí)現(xiàn)
????任務(wù)間信號(hào)量的實(shí)現(xiàn)是指各個(gè)任務(wù)之間使用信號(hào)量實(shí)現(xiàn)任務(wù)的同步或者資源共享功能。下面的框圖來說明一下 FreeRTOS 計(jì)數(shù)信號(hào)量的實(shí)現(xiàn):

運(yùn)行條件:
◆ 創(chuàng)建 2 個(gè)任務(wù) Task1 和 Task2。?
◆ 創(chuàng)建計(jì)數(shù)信號(hào)量可用資源為 1。
運(yùn)行過程描述如下:?
◆ 任務(wù) Task1 運(yùn)行過程中調(diào)用函數(shù) xSemaphoreTake 獲取信號(hào)量資源,
????如果信號(hào)量沒有被任務(wù) Task2 占用,Task1 將直接獲取資源;
????如果信號(hào)量被 Task2 占用,任務(wù) Task1 將由運(yùn)行態(tài)轉(zhuǎn)到阻塞狀態(tài), 等待資源可用。一旦獲取了資源并使用完畢后會(huì)通過函數(shù) xSemaphoreGive 釋放掉資源。?
◆ 任務(wù) Task2 運(yùn)行過程中調(diào)用函數(shù) xSemaphoreTake 獲取信號(hào)量資源,
????如果信號(hào)量沒有被任務(wù) Task1 占用,Task2 將直接獲取資源。
????如果信號(hào)量被 Task1 占用,任務(wù) Task2 將由運(yùn)行態(tài)轉(zhuǎn)到阻塞狀態(tài), 等待資源可用。一旦獲取了資源并使用完畢后會(huì)通過函數(shù) xSemaphoreGive 釋放掉資源。
3 FreeRTOS 中斷方式計(jì)數(shù)信號(hào)量的實(shí)現(xiàn)
?????中斷方式信號(hào)量的實(shí)現(xiàn)是指中斷函數(shù)和 FreeRTOS 任務(wù)之間使用信號(hào)量。信號(hào)量的中斷方 式主要是用于實(shí)現(xiàn)任務(wù)同步,如下的框圖來說明一下 FreeRTOS 中斷方式信號(hào)量的實(shí)現(xiàn):

運(yùn)行條件:
◆ 創(chuàng)建一個(gè)任務(wù) Task1 和一個(gè)串口接收中斷。?
◆ 信號(hào)量的初始值為 0,串口中斷調(diào)用函數(shù) xSemaphoreGiveFromISR 釋放信號(hào)量,
????任務(wù) Task1 調(diào)用函數(shù) xSemaphoreTake 獲取信號(hào)量資源。
運(yùn)行過程描述如下:
◆ 任務(wù) Task1 運(yùn)行過程中調(diào)用函數(shù) xSemaphoreTake,由于信號(hào)量的初始值是 0,沒有信號(hào)量資源可用,任務(wù) Task1 由運(yùn)行態(tài)進(jìn)入到阻塞態(tài)。?
◆ Task1 阻塞的情況下,串口接收到數(shù)據(jù)進(jìn)入到了串口中斷服務(wù)程序,在串口中斷服務(wù)程序中調(diào)用函數(shù) xSemaphoreGiveFromISR 釋放信號(hào)量資源,信號(hào)量數(shù)值加 1,此時(shí)信號(hào)量計(jì)數(shù)值為 1,任務(wù) Task1 由阻塞態(tài)進(jìn)入到就緒態(tài),在調(diào)度器的作用下由就緒態(tài)又進(jìn)入到運(yùn)行態(tài),任務(wù) Task1 獲得信號(hào)量后,信號(hào)量數(shù)值減 1,此時(shí)信號(hào)量計(jì)數(shù)值又變成了 0。?
◆ 再次循環(huán)執(zhí)行時(shí),任務(wù) Task1 調(diào)用函數(shù) xSemaphoreTake 由于沒有資源可用再次進(jìn)入到阻塞態(tài),等待串口釋放信號(hào)量資源,如此往復(fù)循環(huán)。
重點(diǎn)的了解以下 4 個(gè)函數(shù):
◆ xSemaphoreCreateCounting ()?
◆ xSemaphoreGive ()?
◆ xSemaphoreGiveFromISR ()?
◆ xSemaphoreTake ()
函數(shù) xSemaphoreCreateCounting 用于創(chuàng)建計(jì)數(shù)信號(hào)量。
◆ 第 1 個(gè)參數(shù)是設(shè)置此計(jì)數(shù)信號(hào)量支持的最大計(jì)數(shù)值。
◆ 第 2 個(gè)參數(shù)是設(shè)置計(jì)數(shù)信號(hào)量的初始值。
◆ 返回值,如果創(chuàng)建成功會(huì)返回消息隊(duì)列的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足, 無法為此消息隊(duì)列提供所需的空間會(huì)返回 NULL。
使用這個(gè)函數(shù)要注意以下問題:
1. 此函數(shù)是基函數(shù) xQueueCreateCountingSemaphore 實(shí)現(xiàn)的:
#define ????xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )???????? \ xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
函數(shù) xQueueCreateCountingSemaphore 的實(shí)現(xiàn)是基于消息隊(duì)列函數(shù) xQueueGenericCreate 實(shí)現(xiàn)的。
2. 使用此函數(shù)要在 FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_COUNTING_SEMAPHORES ????1
舉例:
函數(shù) xSemaphoreGive 用于在任務(wù)代碼中釋放信號(hào)量。
◆ 第 1 個(gè)參數(shù)是信號(hào)量句柄。
◆ 返回值,如果信號(hào)量釋放成功返回 pdTRUE,否則返回 pdFALSE,因?yàn)橛?jì)數(shù)信號(hào)量的實(shí)現(xiàn)是基于消息隊(duì)列,返回失敗的主要原因是消息隊(duì)列已經(jīng)滿了。
使用這個(gè)函數(shù)要注意以下問題:?
1. 此函數(shù)是基于消息隊(duì)列函數(shù) xQueueGenericSend 實(shí)現(xiàn)的:
#define xSemaphoreGive( xSemaphore ) ????????\?
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, \ queueSEND_TO_BACK )?
2. 此函數(shù)是用于任務(wù)代碼中調(diào)用的,故不可以在中斷服務(wù)程序中調(diào)用此函數(shù),中斷服務(wù)程序中使用的是 xSemaphoreGiveFromISR。?
3. 使用此函數(shù)前,一定要保證用函數(shù) xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting()創(chuàng)建了信號(hào)量。
4. 此函數(shù)不支持使用 xSemaphoreCreateRecursiveMutex()創(chuàng)建的信號(hào)量。
舉例:
函數(shù) xSemaphoreGiveFromISR 用于中斷服務(wù)程序中釋放信號(hào)量。
◆ 第 1 個(gè)參數(shù)是信號(hào)量句柄。?
◆ 第2 個(gè)參數(shù)用于保存是否有高優(yōu)先級(jí)任務(wù)準(zhǔn)備就緒。如果函數(shù)執(zhí)行完畢后,此參數(shù)的數(shù)值是 pdTRUE, 說明有高優(yōu)先級(jí)任務(wù)要執(zhí)行,否則沒有。?
◆ 返回值,如果信號(hào)量釋放成功返回 pdTRUE,否則返回 errQUEUE_FULL。
舉例:
函數(shù) xSemaphoreTake 用于在任務(wù)代碼中獲取信號(hào)量
◆ 第 1 個(gè)參數(shù)是信號(hào)量句柄。?
◆ 第 2 個(gè)參數(shù)是沒有信號(hào)量可用時(shí),等待信號(hào)量可用的最大等待時(shí)間,單位系統(tǒng)時(shí)鐘節(jié)拍。
◆ 返回值,如果創(chuàng)建成功會(huì)獲取信號(hào)量返回 pdTRUE,否則返回 pdFALSE。
使用這個(gè)函數(shù)要注意以下問題:
1. 此函數(shù)是用于任務(wù)代碼中調(diào)用的,故不可以在中斷服務(wù)程序中調(diào)用此函數(shù),中斷服務(wù)程序使用的是 xSemaphoreTakeFromISR。?
2. 如果消息隊(duì)列為空且第 2 個(gè)參數(shù)為 0,那么此函數(shù)會(huì)立即返回。
3. 如果用戶將 FreeRTOSConfig.h 文件中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第 2 個(gè)參數(shù)配 置為 portMAX_DELAY,那么此函數(shù)會(huì)永久等待直到信號(hào)量可用。
舉例:
????