概述
文章對事件組的,應用場景,運作機制,以及事件的創(chuàng)建,刪除,等待,置位,同步等操作
文章目錄
概述
一、事件標志組簡介
1、事件位(事件標志)
2、事件組
3、事件標志組和事件位的數(shù)據類型
二、事件的應用場景
三、事件運作機制
四、事件控制塊
?五、事件組函數(shù)
1.事件創(chuàng)建函數(shù) xEventGroupCreate()
2.事件刪除函數(shù) vEventGroupDelete()
3.事件組置位函數(shù) xEventGroupSetBits()(任務)
4.等待事件函數(shù) xEventGroupWaitBits()
5. xEventGroupClearBits()與 xEventGroupClearBitsFromISR()
?六、事件實驗
七、實驗現(xiàn)象
一、事件標志組簡介
1、事件位(事件標志)
事件位用來表明某個事件是否發(fā)生,事件位通常用作事件標志,比如下面的幾個例子:
● 當收到一條消息并且把這條消息處理掉以后就可以將某個位(標志)置 1,當隊列中沒有 消息需要處理的時候就可以將這個位(標志)置 0。
● 當把隊列中的消息通過網絡發(fā)送輸出以后就可以將某個位(標志)置 1,當沒有數(shù)據需要 從網絡發(fā)送出去的話就將這個位(標志)置 0。
● 現(xiàn)在需要向網絡中發(fā)送一個心跳信息,將某個位(標志)置 1?,F(xiàn)在不需要向網絡中發(fā)送 心跳信息,這個位(標志)置 0。
2、事件組
一個事件組就是一組的事件位,事件組中的事件位通過位編號來訪問,同樣,以上面列出 的三個例子為例:
● 事件標志組的 bit0 表示隊列中的消息是否處理掉。
● 事件標志組的 bit1 表示是否有消息需要從網絡中發(fā)送出去。
● 事件標志組的 bit2 表示現(xiàn)在是否需要向網絡發(fā)送心跳信息。
3、事件標志組和事件位的數(shù)據類型
事件標志組的數(shù)據類型為 EventGroupHandle_t,當 configUSE_16_BIT_TICKS 為 1 的時候 事件標志組可以存儲 8 個事件位,當 configUSE_16_BIT_TICKS 為 0 的時候事件標志組存儲 24 個事件位。
事件標志組中的所有事件位都存儲在一個無符號的 EventBits_t 類型的變量中,EventBits_t 在 event_groups.h 中有如下定義:
typedef TickType_t EventBits_t;
數(shù)據類型 TickType_t 在文件 portmacro.h 中有如下定義:
#if( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#define portTICK_TYPE_IS_ATOMIC 1
#endif
可以看出當 configUSE_16_BIT_TICKS 為 0 的時候 TickType_t 是個 32 位的數(shù)據類型,因 此 EventBits_t 也是個 32 位的數(shù)據類型。EventBits_t 類型的變量可以存儲 24 個事件位,另外的 那高 8 位有其他用。事件位 0 存放在這個變量的 bit0 上,變量的 bit1 就是事件位 1,以此類推。 對于 STM32 來說一個事件標志組最多可以存儲 24 個事件位,如圖1所示
圖1 事件標志組和事件位
二、事件的應用場景
? ? ? FreeRTOS 的事件用于事件類型的通訊,無數(shù)據傳輸,也就是說,我們可以用事件來做 標志位,判斷某些事件是否發(fā)生了,然后根據結果做處理,那很多人又會問了,為什么我 不直接用變量做標志呢,豈不是更好更有效率?非也非也,若是在裸機編程中,用全局變 量是最為有效的方法,這點我不否認,但是在操作系統(tǒng)中,使用全局變量就要考慮以下問 題了:
?如何對全局變量進行保護呢,如何處理多任務同時對它進行訪問?
? ? ? 如何讓內核對事件進行有效管理呢?使用全局變量的話,就需要在任務中輪詢查 看事件是否發(fā)送,這簡直就是在浪費 CPU 資源啊,還有等待超時機制,使用全局 變量的話需要用戶自己去實現(xiàn)。
所以,在操作系統(tǒng)中,還是使用操作系統(tǒng)給我們提供的通信機制就好了,簡單方便還 實用。
? ? ? ?在某些場合,可能需要多個時間發(fā)生了才能進行下一步操作,比如一些危險機器的啟 動,需要檢查各項指標,當指標不達標的時候,無法啟動,但是檢查各個指標的時候,不 能一下子檢測完畢啊,所以,需要事件來做統(tǒng)一的等待,當所有的事件都完成了,那么機 器才允許啟動,這只是事件的其中一個應用。
? ? ? ?事件可使用于多種場合,它能夠在一定程度上替代信號量,用于任務與任務間,中斷 與任務間的同步。一個任務或中斷服務例程發(fā)送一個事件給事件對象,而后等待的任務被 喚醒并對相應的事件進行處理。但是它與信號量不同的是,事件的發(fā)送操作是不可累計的, 而信號量的釋放動作是可累計的。事件另外一個特性是,接收任務可等待多種事件,即多 個事件對應一個任務或多個任務。同時按照任務等待的參數(shù),可選擇是“邏輯或”觸發(fā)還 是“邏輯與”觸發(fā)。這個特性也是信號量等所不具備的,信號量只能識別單一同步動作, 而不能同時等待多個事件的同步。
? ? ? ? 各個事件可分別發(fā)送或一起發(fā)送給事件對象,而任務可以等待多個事件,任務僅對感 興趣的事件進行關注。當有它們感興趣的事件發(fā)生時并且符合感興趣的條件,任務將被喚 醒并進行后續(xù)的處理動作。
三、事件運作機制
? ? ? 接收事件時,可以根據感興趣的參事件類型接收事件的單個或者多個事件類型。事件 接收成功后,必須使用 xClearOnExit 選項來清除已接收到的事件類型,否則不會清除已接收 到的 事件 ,這樣就需要用戶顯式清除事?位。
? ? ? 用戶可 以自 定義 通過 傳入參 數(shù) xWaitForAllBits 選擇讀取模式,是等待所有感興趣的事件還是等待感興趣的任意一個事件。?
? ? ?設置事件時,對指定事件寫入指定的事件類型,設置事件集合的對應事件位為 1,可 以一次同時寫多個事件類型,設置事件成功可能會觸發(fā)任務調度。?
? ? ? ?清除事件時,根據入參數(shù)事件句柄和待清除的事件類型,對事件對應位進行清 0 操作。 事件不與任務相關聯(lián),事件相互獨立,一個 32位的變量(事件集合,實際用于表示事 件的只有 24 位),用于標識該任務發(fā)生的事件類型,其中每一位表示一種事件類型(0 表 示該事件類型未發(fā)生、1表示該事件類型已經發(fā)生),一共 24種事件類型具體見圖2
圖2事件集合 set(一個 32 位的變量)
? ? ?事件喚醒機制,當任務因為等待某個或者多個事件發(fā)生而進入阻塞態(tài),當事件發(fā)生的 時候會被喚醒,其過程具體見圖3
圖3? 事件喚醒任務示意圖
? ? ? 任務 1 對事件 3 或事件 5 感興趣(邏輯或),當發(fā)生其中的某一個事件都會被喚醒, 并且執(zhí)行相應操作。而任務 2 對事件 3 與事件 5 感興趣(邏輯與),當且僅當事件 3 與事 件 5 都發(fā)生的時候,任務 2 才會被喚醒,如果只有一個其中一個事件發(fā)生,那么任務還是 會繼續(xù)等待事件發(fā)生。如果接在收事件函數(shù)中設置了清除事件位 xClearOnExit,那么當任 務喚醒后將把事件 3 和事件 5 的事件標志清零,否則事件標志將依然存在。
四、事件控制塊
? ? ? ?事件標志組存儲在一個 EventBits_t 類型的變量中,該變量在事件組結構體中定義,具 體見代碼清單 20-1 加粗部分。如果宏 configUSE_16_BIT_TICKS 定義為 1,那么變量 uxEventBits 就 是 16 位 的 , 其 中 有 8 個 位 用來存儲 事 件 組 , 如 果 宏 configUSE_16_BIT_TICKS 定義為 0,那么變量 uxEventBits 就是 32 位的,其中有 24 個位 用來存儲事件組,每一位代表一個事件的發(fā)生與否,利用邏輯或、邏輯與等實現(xiàn)不同事件 的不同喚醒處理。在 STM32 中,uxEventBits 是 32 位的,所以我們有 24 個位用來實現(xiàn)事件 組。除了事件標志組變量之外,F(xiàn)reeRTOS 還使用了一個鏈表來記錄等待事件的任務,所有 在等待此事件的任務均會被掛載在等待事件列表 xTasksWaitingForBits。
typedef struct xEventGroupDefinition {
EventBits_t uxEventBits;
List_t xTasksWaitingForBits;
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxEventGroupNumber;
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) \
&& ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
} EventGroup_t;
?五、事件組函數(shù)
1.事件創(chuàng)建函數(shù) xEventGroupCreate()
? ? 事件創(chuàng)建函數(shù),顧名思義,就是創(chuàng)建一個事件,與其他內核對象一樣,都是需要先創(chuàng) 建才能使用的資源,F(xiàn)reeRTOS 給我們提供了一個創(chuàng)建事件的函數(shù) xEventGroupCreate(),當 創(chuàng)建一個事件時,系統(tǒng)會首先給我們分配事件控制塊的內存空間,然后對該事件控制塊進 行基本的初始化,創(chuàng)建成功返回事件句柄;創(chuàng)建失敗返回 NULL。所以,在使用創(chuàng)建函數(shù) 之前,我們需要先定義有個事件的句柄。
/* 創(chuàng)建一個事件組,返回它的句柄。
* 此函數(shù)內部會分配事件組結構體
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );
/* 創(chuàng)建一個事件組,返回它的句柄。
* 此函數(shù)無需動態(tài)分配內存,所以需要先有一個StaticEventGroup_t結構體,并傳入它的指針
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );
2.事件刪除函數(shù) vEventGroupDelete()
? ? ?vEventGroupDelete()用于刪除由函數(shù) xEventGroupCreate()創(chuàng)建的事件組,只有被創(chuàng)建 成功的事件才能被刪除,但是需要注意的是該函數(shù)不允許在中斷里面使用。當事件組被刪 除之后,阻塞在該事件組上的任務都會被解鎖,并向等待事件的任務返回事件組的值為 0, 其使用是非常簡單的。
/*
* xEventGroup: 事件組句柄,你要刪除哪個事件組
*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
3.事件組置位函數(shù) xEventGroupSetBits()(任務)
? ? ? ? xEventGroupSetBits()用于置位事件組中指定的位,當位被置位之后,阻塞在該位上的 任務將會被解鎖。使用該函數(shù)接口時,通過參數(shù)指定的事件標志來設定事件的標志位,然 后遍歷等待在事件對象上的事件等待列表,判斷是否有任務的事件激活要求與當前事件對 象標志值匹配,如果有,則喚醒該任務。簡單來說,就是設置我們自己定義的事件標志位 為 1,并且看看有沒有任務在等待這個事件,有的話就喚醒它。
xEventGroupSetBits()函數(shù)說明
xEventGroupSetBits()函數(shù)使用實例
#define KEY1_EVENT (0x01 << 0)//設置事件掩碼的位0
#define KEY2_EVENT (0x01 << 1)//設置事件掩碼的位1
static EventGroupHandle_t Event_Handle =NULL;
Event_Handle = xEventGroupCreate();
if(NULL != Event_Handle)
printf("Event_Handle 事件創(chuàng)建成功!\r\n");
static void KEY_Task(void* parameter)
{
/* 任務都是一個無限循環(huán),不能返回 */
while (1)
{
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) //如果KEY2被單擊
{
printf ( "KEY1被按下\n" );
/* 觸發(fā)一個事件1 */
xEventGroupSetBits(Event_Handle,KEY1_EVENT);
}
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) //如果KEY2被單擊
{
printf ( "KEY2被按下\n" );
/* 觸發(fā)一個事件2 */
xEventGroupSetBits(Event_Handle,KEY2_EVENT);
}
vTaskDelay(20); //每20ms掃描一次
}
}
4.等待事件函數(shù) xEventGroupWaitBits()
? ? ? ?既然標記了事件的發(fā)生,那么我怎么知道他到底有沒有發(fā)生,這也是需要一個函數(shù)來 獲 取 事 件 是 否 已 經 發(fā) 生 , FreeRTOS 提 供 了 一 個 等 待 指 定 事 件 的 函 數(shù) — — xEventGroupWaitBits(),通過這個函數(shù),任務可以知道事件標志組中的哪些位,有什么事 件發(fā)生了,然后通過 “邏輯與”、“邏輯或”等操作對感興趣的事件進行獲取,并且這個 函數(shù)實現(xiàn)了等待超時機制,當且僅當任務等待的事件發(fā)生時,任務才能獲取到事件信息。 在這段時間中,如果事件一直沒發(fā)生,該任務將保持阻塞狀態(tài)以等待事件發(fā)生。當其它任 務或中斷服務程序往其等待的事件設置對應的標志位,該任務將自動由阻塞態(tài)轉為就緒態(tài)。 當任務等待的時間超過了指定的阻塞時間,即使事件還未發(fā)生,任務也會自動從阻塞態(tài)轉 移為就緒態(tài)。這樣子很有效的體現(xiàn)了操作系統(tǒng)的實時性,如果事件正確獲?。ǖ却剑﹦t 返回對應的事件標志位,由用戶判斷再做處理,因為在事件超時的時候也會返回一個不能 確定的事件值,所以需要判斷任務所等待的事件是否真的發(fā)生。
xEventGroupWaitBits()函數(shù)說明?
xEventGroupWaitBits()使用實例
static void LED_Task(void* parameter)
{
EventBits_t r_event; /* 定義一個事件接收變量 */
/* 任務都是一個無限循環(huán),不能返回 */
while (1)
{
/*******************************************************************
* 等待接收事件標志
*
* 如果xClearOnExit設置為pdTRUE,那么在xEventGroupWaitBits()返回之前,
* 如果滿足等待條件(如果函數(shù)返回的原因不是超時),那么在事件組中設置
* 的uxBitsToWaitFor中的任何位都將被清除。
* 如果xClearOnExit設置為pdFALSE,
* 則在調用xEventGroupWaitBits()時,不會更改事件組中設置的位。
*
* xWaitForAllBits如果xWaitForAllBits設置為pdTRUE,則當uxBitsToWaitFor中
* 的所有位都設置或指定的塊時間到期時,xEventGroupWaitBits()才返回。
* 如果xWaitForAllBits設置為pdFALSE,則當設置uxBitsToWaitFor中設置的任何
* 一個位置1 或指定的塊時間到期時,xEventGroupWaitBits()都會返回。
* 阻塞時間由xTicksToWait參數(shù)指定。
*********************************************************/
r_event = xEventGroupWaitBits(Event_Handle, /* 事件對象句柄 */
KEY1_EVENT|KEY2_EVENT,/* 接收線程感興趣的事件 */
pdTRUE, /* 退出時清除事件位 */
pdTRUE, /* 滿足感興趣的所有事件 */
portMAX_DELAY);/* 指定超時事件,一直等 */
if((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT))
{
/* 如果接收完成并且正確 */
printf ( "KEY1與KEY2都按下\n");
LED1_TOGGLE; //LED1 反轉
}
else
printf ( "事件錯誤!\n");
}
}
5. xEventGroupClearBits()與 xEventGroupClearBitsFromISR()
? ? ? ?xEventGroupClearBits()與 xEventGroupClearBitsFromISR()都是用于清除事件組指定的 位,如果在獲取事件的時候沒有將對應的標志位清除,那么就需要用這個函數(shù)來進行顯式 清除,xEventGroupClearBits()函數(shù)不能在中斷中使用,而是由具有中斷保護功能 的 xEventGroupClearBitsFromISR() 來代替,中斷清除事件標志位的操作在守護任務(也叫定 時 器 服 務 任 務 ) 里 面 完 成 。 守 護 進 程 的 優(yōu) 先 級 由 FreeRTOSConfig.h 中 的 宏 configTIMER_TASK_PRIORITY 來定義 。 要 想 使 用 該 函 數(shù) 必 須 把 FreeRTOS/source/event_groups.c 這個 C 文件添加到工程中。
xEventGroupClearBits()與 xEventGroupClearBitsFromISR()函數(shù)說明
?xEventGroupClearBits()函數(shù)使用實例
#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )
void aFunction( EventGroupHandle_t xEventGroup )
{
EventBits_t uxBits;
/* 清楚事件組的 bit 0 and bit 4 */
uxBits = xEventGroupClearBits(
xEventGroup,
BIT_0 | BIT_4 );
if ( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) {
/* 在調用 xEventGroupClearBits()之前 bit0 和 bit4 都置位
但是現(xiàn)在是被清除了*/
} else if ( ( uxBits & BIT_0 ) != 0 ) {
/* 在調用 xEventGroupClearBits()之前 bit0 已經置位
但是現(xiàn)在是被清除了*/
} else if ( ( uxBits & BIT_4 ) != 0 ) {
/* 在調用 xEventGroupClearBits()之前 bit4 已經置位
但是現(xiàn)在是被清除了*/
} else {
/* 在調用 xEventGroupClearBits()之前 bit0 和 bit4 都沒被置位 */
}
}
?六、事件實驗
? ? ? ?事件標志組實驗是在 FreeRTOS 中創(chuàng)建了兩個任務,一個是設置事件任務,一個是等 待事件任務,兩個任務獨立運行,設置事件任務通過檢測按鍵的按下情況設置不同的事件 標志位,等待事件任務則獲取這兩個事件標志位,并且判斷兩個事件是否都發(fā)生,如果是 則輸出相應信息,LED 進行翻轉。等待事件任務的等待時間是 portMAX_DELAY,一直在等待事件的發(fā)生,等待到事件之后清除對應的事件標記位,具體見代碼清單 20-13 加粗部 分。
/* FreeRTOS頭文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/* 開發(fā)板硬件bsp頭文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任務句柄 ********************************/
/*
* 任務句柄是一個指針,用于指向一個任務,當任務創(chuàng)建好之后,它就具有了一個任務句柄
* 以后我們要想操作這個任務都需要通過這個任務句柄,如果是自身的任務操作自己,那么
* 這個句柄可以為NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 創(chuàng)建任務句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task任務句柄 */
static TaskHandle_t KEY_Task_Handle = NULL;/* KEY_Task任務句柄 */
/********************************** 內核對象句柄 *********************************/
/*
* 信號量,消息隊列,事件標志組,軟件定時器這些都屬于內核的對象,要想使用這些內核
* 對象,必須先創(chuàng)建,創(chuàng)建成功之后會返回一個相應的句柄。實際上就是一個指針,后續(xù)我
* 們就可以通過這個句柄操作這些內核對象。
*
* 內核對象說白了就是一種全局的數(shù)據結構,通過這些數(shù)據結構我們可以實現(xiàn)任務間的通信,
* 任務間的事件同步等各種功能。至于這些功能的實現(xiàn)我們是通過調用這些內核對象的函數(shù)
* 來完成的
*
*/
static EventGroupHandle_t Event_Handle =NULL;
/******************************* 全局變量聲明 ************************************/
/*
* 當我們在寫應用程序的時候,可能需要用到一些全局變量。
*/
/******************************* 宏定義 ************************************/
/*
* 當我們在寫應用程序的時候,可能需要用到一些宏定義。
*/
#define KEY1_EVENT (0x01 << 0)//設置事件掩碼的位0
#define KEY2_EVENT (0x01 << 1)//設置事件掩碼的位1
/*
*************************************************************************
* 函數(shù)聲明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于創(chuàng)建任務 */
static void LED_Task(void* pvParameters);/* LED_Task 任務實現(xiàn) */
static void KEY_Task(void* pvParameters);/* KEY_Task 任務實現(xiàn) */
static void BSP_Init(void);/* 用于初始化板載相關資源 */
/*****************************************************************
* @brief 主函數(shù)
* @param 無
* @retval 無
* @note 第一步:開發(fā)板硬件初始化
第二步:創(chuàng)建APP應用任務
第三步:啟動FreeRTOS,開始多任務調度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定義一個創(chuàng)建信息返回值,默認為pdPASS */
/* 開發(fā)板硬件初始化 */
BSP_Init();
printf("這是一個FreeRTOS事件標志組實驗!\n");
/* 創(chuàng)建AppTaskCreate任務 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任務入口函數(shù) */
(const char* )"AppTaskCreate",/* 任務名字 */
(uint16_t )512, /* 任務棧大小 */
(void* )NULL,/* 任務入口函數(shù)參數(shù) */
(UBaseType_t )1, /* 任務的優(yōu)先級 */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任務控制塊指針 */
/* 啟動任務調度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 啟動任務,開啟調度 */
else
return -1;
while(1); /* 正常不會執(zhí)行到這里 */
}
/***********************************************************************
* @ 函數(shù)名 : AppTaskCreate
* @ 功能說明: 為了方便管理,所有的任務創(chuàng)建函數(shù)都放在這個函數(shù)里面
* @ 參數(shù) : 無
* @ 返回值 : 無
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定義一個創(chuàng)建信息返回值,默認為pdPASS */
taskENTER_CRITICAL(); //進入臨界區(qū)
/* 創(chuàng)建 Event_Handle */
Event_Handle = xEventGroupCreate();
if(NULL != Event_Handle)
printf("Event_Handle 事件創(chuàng)建成功!\r\n");
/* 創(chuàng)建LED_Task任務 */
xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任務入口函數(shù) */
(const char* )"LED_Task",/* 任務名字 */
(uint16_t )512, /* 任務棧大小 */
(void* )NULL, /* 任務入口函數(shù)參數(shù) */
(UBaseType_t )2, /* 任務的優(yōu)先級 */
(TaskHandle_t* )&LED_Task_Handle);/* 任務控制塊指針 */
if(pdPASS == xReturn)
printf("創(chuàng)建LED_Task任務成功!\r\n");
/* 創(chuàng)建KEY_Task任務 */
xReturn = xTaskCreate((TaskFunction_t )KEY_Task, /* 任務入口函數(shù) */
(const char* )"KEY_Task",/* 任務名字 */
(uint16_t )512, /* 任務棧大小 */
(void* )NULL,/* 任務入口函數(shù)參數(shù) */
(UBaseType_t )3, /* 任務的優(yōu)先級 */
(TaskHandle_t* )&KEY_Task_Handle);/* 任務控制塊指針 */
if(pdPASS == xReturn)
printf("創(chuàng)建KEY_Task任務成功!\n");
vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務
taskEXIT_CRITICAL(); //退出臨界區(qū)
}
/**********************************************************************
* @ 函數(shù)名 : LED_Task
* @ 功能說明: LED_Task任務主體
* @ 參數(shù) :
* @ 返回值 : 無
********************************************************************/
static void LED_Task(void* parameter)
{
EventBits_t r_event; /* 定義一個事件接收變量 */
/* 任務都是一個無限循環(huán),不能返回 */
while (1)
{
/*******************************************************************
* 等待接收事件標志
*
* 如果xClearOnExit設置為pdTRUE,那么在xEventGroupWaitBits()返回之前,
* 如果滿足等待條件(如果函數(shù)返回的原因不是超時),那么在事件組中設置
* 的uxBitsToWaitFor中的任何位都將被清除。
* 如果xClearOnExit設置為pdFALSE,
* 則在調用xEventGroupWaitBits()時,不會更改事件組中設置的位。
*
* xWaitForAllBits如果xWaitForAllBits設置為pdTRUE,則當uxBitsToWaitFor中
* 的所有位都設置或指定的塊時間到期時,xEventGroupWaitBits()才返回。
* 如果xWaitForAllBits設置為pdFALSE,則當設置uxBitsToWaitFor中設置的任何
* 一個位置1 或指定的塊時間到期時,xEventGroupWaitBits()都會返回。
* 阻塞時間由xTicksToWait參數(shù)指定。
*********************************************************/
r_event = xEventGroupWaitBits(Event_Handle, /* 事件對象句柄 */
KEY1_EVENT|KEY2_EVENT,/* 接收線程感興趣的事件 */
pdTRUE, /* 退出時清除事件位 */
pdTRUE, /* 滿足感興趣的所有事件 */
portMAX_DELAY);/* 指定超時事件,一直等 */
if((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT))
{
/* 如果接收完成并且正確 */
printf ( "KEY1與KEY2都按下\n");
LED1_TOGGLE; //LED1 反轉
}
else
printf ( "事件錯誤!\n");
}
}
/**********************************************************************
* @ 函數(shù)名 : KEY_Task
* @ 功能說明: KEY_Task任務主體
* @ 參數(shù) :
* @ 返回值 : 無
********************************************************************/
static void KEY_Task(void* parameter)
{
/* 任務都是一個無限循環(huán),不能返回 */
while (1)
{
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) //如果KEY2被單擊
{
printf ( "KEY1被按下\n" );
/* 觸發(fā)一個事件1 */
xEventGroupSetBits(Event_Handle,KEY1_EVENT);
}
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) //如果KEY2被單擊
{
printf ( "KEY2被按下\n" );
/* 觸發(fā)一個事件2 */
xEventGroupSetBits(Event_Handle,KEY2_EVENT);
}
vTaskDelay(20); //每20ms掃描一次
}
}
/***********************************************************************
* @ 函數(shù)名 : BSP_Init
* @ 功能說明: 板級外設初始化,所有板子上的初始化均可放在這個函數(shù)里面
* @ 參數(shù) :
* @ 返回值 : 無
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中斷優(yōu)先級分組為4,即4bit都用來表示搶占優(yōu)先級,范圍為:0~15
* 優(yōu)先級分組只需要分組一次即可,以后如果有其他的任務需要用到中斷,
* 都統(tǒng)一用這個優(yōu)先級分組,千萬不要再分組,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按鍵初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/
七、實驗現(xiàn)象
? ? ? ? 程序編譯好,用 USB 線連接電腦和開發(fā)板的 USB 接口(對應絲印為 USB 轉串口), 用?仿真器把配套程序下載到?STM32 開發(fā)板,在電腦上打開串口調試助手,然后復位開發(fā)板就可 以在調試助手中看到串口的打印信息,按下開發(fā)版的 KEY1 按鍵發(fā)送事件 1,按下 KEY2 按鍵發(fā)送事件 2;我們按下 KEY1 與 KEY2 試試,在串口調試助手中可以看到運行結果, 并且當事件 1 與事件 2都發(fā)生的時候,開發(fā)板的 LED 會進行翻轉。文章來源:http://www.zghlxwxcb.cn/news/detail-674022.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-674022.html
到了這里,關于FreeRTOS事件組 基于STM32的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!