1. 程式人生 > >FreeRTOS 事件標誌組

FreeRTOS 事件標誌組

func ear rtos 內核 stm32f407 rto 指定 ora mage

為什麽要使用事件標誌
事件標誌組是實現多任務同步的有效機制之一。也許有不理解的初學者會問采用事件標誌組多麻煩,
搞個全局變量不是更簡單?其實不然,在裸機編程時,使用全局變量的確比較方便,但是在加上 RTOS 後
就是另一種情況了。 使用全局變量相比事件標誌組主要有如下三個問題:
? 使用事件標誌組可以讓 RTOS 內核有效地管理任務,而全局變量是無法做到的,任務的超時等機制需
要用戶自己去實現。
? 使用了全局變量就要防止多任務的訪問沖突,而使用事件標誌組則處理好了這個問題,用戶無需擔心。
? 使用事件標誌組可以有效地解決中斷服務程序和任務之間的同步問題。
FreeRTOS 任務間事件標誌組的實現
任務間事件標誌組的實現是指各個任務之間使用事件標誌組實現任務的通信或者同步機制。
下面我們來說說 FreeRTOS 中事件標誌的實現,根據用戶在 FreeRTOSConfig.h 文件中的配置:
? #define configUSE_16_BIT_TICKS 1
配置宏定義 configUSE_16_BIT_TICKS 為 1 時,每創建一個事件標誌組,用戶可以使用的事件標誌是
8 個。
? #define configUSE_16_BIT_TICKS 0

配置宏定義 configUSE_16_BIT_TICKS 為 0 時,每創建一個事件標誌組,用戶可以使用的事件標誌是24 個。
上面說的 8 個和 24 個事件標誌應該怎麽理解呢?其實就是定義了一個 16 位變量,僅使用了低 8bit
或者定義了一個 32 位變量,僅使用了低 24bit。每一個 bit 用 0 和 1 兩種狀態來代表事件標誌。 反映到
FreeRTOS 上就是將事件標誌存儲到了 EventBits_t 類型的變量中, 這個變量又是怎麽回事呢?定義如下:
技術分享

由上面定義可以看出,TickType_t 數據類型可以是 16 位數或者 32 位數,這樣就跟上面剛剛說的
configUSE_16_BIT_TICKS 宏定義呼應上了。教程配套的例子都是配置宏定義 configUSE_16_BIT_TICKS
為 0, 即用戶每創建一個事件標誌組, 有 24 個標誌可以設置。 如下圖所示,這裏僅使用 bit0, bit1 和 bit2。

技術分享

下面我們通過如下的框圖來說明一下 FreeRTOS 事件標誌的實現,讓大家有一個形象的認識。

技術分享

FreeRTOS 中斷方式事件標誌組的實現
FreeRTOS 中斷方式事件標誌組的實現是指中斷函數和 FreeRTOS 任務之間使用事件標誌。 下面我們
通過如下的框圖來說明一下 FreeRTOS 事件標誌的實現,讓大家有一個形象的認識。

技術分享

? 任務 Task1 運行過程中調用函數 xEventGroupWaitBits,等待事件標誌位被設置,任務 Task1 由運
行態進入到阻塞態。
? Task1 阻塞的情況下,串口接收到數據進入到了串口中斷服務程序,在串口中斷服務程序中設置 Task1
等待的事件標誌,任務 Task1 由阻塞態進入到就緒態,在調度器的作用下由就緒態又進入到運行態。


上面就是一個簡單的 FreeRTOS 中斷方式事件標誌通信過程。 實際應用中,中斷方式的消息機制要註意以
下四個問題:
? 中斷函數的執行時間越短越好,防止其它低於這個中斷優先級的異常不能得到及時響應。
? 實際應用中,建議不要在中斷中實現消息處理,用戶可以在中斷服務程序裏面發送消息通知任務,在
任務中實現消息處理,這樣可以有效地保證中斷服務程序的實時響應。同時此任務也需要設置為高優
先級,以便退出中斷函數後任務可以得到及時執行。
? 中斷服務程序中一定要調用專用於中斷的事件標誌設置函數,即以 FromISR 結尾的函數。
? 在操作系統中實現中斷服務程序與裸機編程的區別。
? 如果 FreeRTOS 工程的中斷函數中沒有調用 FreeRTOS 的事件標誌組 API 函數,與裸機編程是
一樣的。
? 如果 FreeRTOS 工程的中斷函數中調用了 FreeRTOS 的事件標誌組的 API 函數,退出的時候要
檢測是否有高優先級任務就緒,如果有就緒的,需要在退出中斷後進行任務切換,這點跟裸機編
程稍有區別,詳見 實驗例程說明(中斷方式):
? 另外強烈推薦用戶將 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407, F429
的 NVIC 優先級分組設置為 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這樣中
斷優先級的管理將非常方便。
? 用戶要在 FreeRTOS 多任務開啟前就設置好優先級分組,一旦設置好切記不可再修改。

事件標誌組 API 函數
使用如下 11 個函數可以實現 FreeRTOS 的事件標誌組:
? xEventGroupCreate()
? xEventGroupCreateStatic()
? vEventGroupDelete()
? xEventGroupWaitBits()
? xEventGroupSetBits()
? xEventGroupSetBitsFromISR()
? xEventGroupClearBits()
? xEventGroupClearBitsFromISR()
? xEventGroupGetBits()
? xEventGroupGetBitsFromISR()
這裏我們重點的說以下 4 個函數:
? xEventGroupCreate()
? xEventGroupWaitBits()
? xEventGroupSetBits()
? xEventGroupSetBitsFromISR()

函數 xEventGroupCreate
函數原型:
EventGroupHandle_t xEventGroupCreate( void );
函數描述:
函數 xEventGroupCreate 用於創建事件標誌組。
? 返回值,如果創建成功, 此函數返回事件標誌組的句柄,如果 FreeRTOSConfig.h 文件中定義的 heap
空間不足會返回 NULL
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) )
技術分享

函數 xEventGroupSetBits
函數原型:
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, /* 事件標誌組句柄 */
const EventBits_t uxBitsToSet ); /* 事件標誌位設置 */
函數描述:
函數 xEventGroupSetBits 用於設置指定的事件標誌位為 1。
? 第 1 個參數是事件標誌組句柄。
? 第 2 個參數表示 24 個可設置的事件標誌位,EventBits_t 是定義的 32 位變量,
低 24 位用於事件標誌設置。變量 uxBitsToSet 的低 24 位的某個位設置為 1,那麽被設置的
事件標誌組的相應位就設置為 1。 變量 uxBitsToSet 設置為 0 的位對事件標誌相應位沒有影響。比
如設置變量 uxBitsToSet = 0x0003 就表示將事件標誌的位 0 和位 1 設置為 1,其余位沒有變化。

? 返回當前的事件標誌組數值。

使用這個函數要註意以下問題:
1. 使用前一定要保證事件標誌組已經通過函數 xEventGroupCreate 創建了。
2. 此函數是用於任務代碼中調用的,故不可以在中斷服務程序中調用此函數,中斷服務程序中使用的是
xEventGroupSetBitsFromISR
3. 用戶通過參數 uxBitsToSet 設置的標誌位並不一定會保留到此函數的返回值中,下面舉兩種情況:
a. 調用此函數的過程中,其它高優先級的任務就緒了,並且也修改了事件標誌,此函數返回的事件
標誌位會發生變化。
b. 調用此函數的任務是一個低優先級任務,通過此函數設置了事件標誌後,讓一個等待此事件標誌
的高優先級任務就緒了,會立即切換到高優先級任務去執行,相應的事件標誌位會被函數
xEventGroupWaitBits 清除掉,等從高優先級任務返回到低優先級任務後,函數xEventGroupSetBits 的返回值已經被修改。

函數 xEventGroupSetBitsFromISR
函數原型:
BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup, /* 事件標誌組句柄 */
const EventBits_t uxBitsToSet, /* 事件標誌位設置 */
BaseType_t *pxHigherPriorityTaskWoken ); /* 高優先級任務是否被喚醒的狀態保存 */
函數描述:
函數 xEventGroupSetBitsFromISR,用於設置指定的事件標誌位為 1。(中斷方式)
? 第 1 個參數是事件標誌組句柄。
? 第 2 個參數表示 24 個可設置的事件標誌位,EventBits_t 是定義的 32 位變量(詳解 18.1.2 小節說
明),低 24 位用於事件標誌設置。變量 uxBitsToSet 的低 24 位的某個位設置為 1,那麽被設置的
事件標誌組的相應位就設置為 1。 變量 uxBitsToSet 設置為 0 的位對事件標誌相應位沒有影響。比
如設置變量 uxBitsToSet = 0x0003 就表示將事件標誌的位 0 和位 1 設置為 1,其余位沒有變化。
? 第 3 個參數用於保存是否有高優先級任務準備就緒。如果函數執行完畢後,此參數的數值是 pdTRUE,
說明有高優先級任務要執行,否則沒有。
? 返回值,如果消息成功發送給 daemon 任務(就是 FreeRTOS 的定時器任務)返回 pdPASS,否則
返回 pdFAIL,另外 daemon 任務中的消息隊列滿了也會返回 pdFAIL。
使用這個函數要註意以下問題:
1. 使用前一定要保證事件標誌已經通過函數 xEventGroupCreate 創建了。同時要在 FreeRTOSConfig.h
文件中使能如下三個宏定義:
#define INCLUDE_xEventGroupSetBitFromISR 1
#define configUSE_TIMERS 1
#define INCLUDE_xTimerPendFunctionCall 1
2. 函數 xEventGroupSetBitsFromISR 是用於中斷服務程序中調用的,故不可以在任務代碼中調用此函
數,任務代碼中使用的是 xEventGroupSetBits。
3. 函數 xEventGroupSetBitsFromISR 對事件標誌組的操作是不確定性操作,因為不知道當前有多少個
任務在等待此事件標誌。而 FreeRTOS 不允許在中斷服務程序和臨界段中執行不確定性操作。 為了不
在中斷服務程序中執行,就通過此函數給 FreeRTOS 的 daemon 任務(就是 FreeRTOS 的定時器任
務)發送消息,在 daemon 任務中執行事件標誌的置位操作。 同時也為了不在臨界段中執行此不確定
操作,將臨界段改成由調度鎖來完成。這樣不確定性操作在中斷服務程序和臨界段中執行的問題就都
得到解決了。
4. 由於函數 xEventGroupSetBitsFromISR 對事件標誌的置位操作是在 daemon 任務裏面執行的,如果
想讓置位操作立即生效,即讓等此事件標誌的任務能夠得到及時執行,需要設置 daemon 任務的優先
級高於使用此事件標誌組的所有其它任務。
5. 通過下面的使用舉例重點看一下函數 xEventGroupSetBitsFromISR 第三個參數的規範用法,初學者務
必要註意。
技術分享

技術分享

函數 xEventGroupWaitBits
函數原型:
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup, /* 事件標誌組句柄 */
const EventBits_t uxBitsToWaitFor, /* 等待被設置的事件標誌位 */
const BaseType_t xClearOnExit, /* 選擇是否清零被置位的事件標誌位 */
const BaseType_t xWaitForAllBits, /* 選擇是否等待所有標誌位都被設置 */
TickType_t xTicksToWait ); /* 設置等待時間 */
函數描述:
函數 xEventGroupWaitBits 等待事件標誌被設置。
? 第 1 個參數是事件標誌組句柄。
? 第 2 個參數表示等待 24 個事件標誌位中的指定標誌,EventBits_t 是定義的 32 位變量(詳解 18.1.2
小節說明),低 24 位用於事件標誌設置。比如設置變量 uxBitsToWaitFor = 0x0003 就表示等待事
件標誌的位 0 和位 1 設置為 1。 此參數切不可設置為 0。
? 第 3 個參數選擇是否清除已經被置位的事件標誌,如果這個參數設置為 pdTRUE,且函數
xEventGroupWaitBits 在參數 xTicksToWait 設置的溢出時間內返回,那麽相應被設置的事件標誌
位會被清零。 如果這個參數設置為 pdFALSE,對已經被設置的事件標誌位沒有影響。
? 第 4 個參數選擇是否等待所有的標誌位都被設置,如果這個參數設置為 pdTRUE,要等待第 2 個參
數 uxBitsToWaitFor 所指定的標誌位全部被置 1,函數才可以返回。當然,超出了在參數
xTicksToWait 設置的溢出時間也是會返回的。如果這個參數設置為 pdFALSE,第 2 個參數
uxBitsToWaitFor 所指定的任何標誌位被置 1,函數都會返回,超出溢出時間也會返回。
? 第 5 個參數設置等待時間,單位時鐘節拍周期。 如果設置為 portMAX_DELAY,表示永久等待。
? 返回值,由於設置的時間超時或者指定的事件標誌位被置 1,導致函數退出時返回的事件標誌組數值。
使用這個函數要註意以下問題:
1. 此函數切不可在中斷服務程序中調用。
2. 這裏再著重說明下這個函數的返回值,通過返回值用戶可以檢測是哪個事件標誌位被置 1 了。
? 如果由於設置的等待時間超時,函數的返回值可會有部分事件標誌位被置 1。
? 如果由於指定的事件標誌位被置1而返回, 並且設置了這個函數的參數xClearOnExit為pdTRUE,
那麽此函數的返回值是清零前的事件標誌組數值。
另外,調用此函數的任務在離開阻塞狀態到退出函數 xEventGroupWaitBits 之間這段時間,如果一個
高優先級的任務搶占執行了,並且修改了事件標誌位,那麽此函數的返回值會跟當前的事件標誌組數
值不同 。


FreeRTOS 事件標誌組