使用信號量來同步的話,任務只能與單個事務或任務進行同步,有時候某個任務可能會需要與多個事件或任務進行同步,此時信號量就無能為力了,FreeRTOS為此提供了一個可選的解決方法,那就是事件標志組。
0x01 事件標志組
事件標志組的數據類型為EventGroupHandle_t,當configUSE_16_BIT_TICKS為1的時候,事件標志組可以存儲8個事件位,當configUSE_16_BIT_TICKS設置為0的時候,可以存儲24個事件位。事件0存在在bit0上,EventGroupHandle_t變量的位1就是事件1。
0x02 創建事件標志組
FreeRTOS提供了兩個用于創建事件標志組的函數
- xEventGroupCreate():使用動態方法創建事件標志組
- xEventGroupCreateStatic():使用靜態方法創建事件標志組
EventGroupHandle_t xEventGroupCreate( void );
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer );
pxEventGroupBuffer :保存事件標志組結構體
xEventGroupCreate所需要的內存通過動態內存管理方法分配,當configUSE_16_BIT_TICKS為1的時候,事件標志組可以存儲8個事件位(bit0-bit7),當configUSE_16_BIT_TICKS設置為0的時候,可以存儲24個事件位(bit0-bit23)。
xEventGroupCreateStatic所需要的內存用戶自己分配
0x03 設置事件位
FreeRTOS提供了4個函數用來設置事件標志組中事件位,事件位的設置包括清零和置1兩種操作
-
xEventGroupClearBits():將指定的事件位清零,用在任務中
-
xEventGroupClearBitsFromISR():將指定的事件位清零,用在中斷服務中
-
xEventGroupSetBits():將指定的事件位置1,用在任務中
-
xEventGroupSetBitsFromISR():將指定的事件位置1,用在中斷中
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
- EventGroupHandle_t xEventGroup:要操作的時間標志組的句柄
- EventBits_t uxBitsToClear:要清零的事件位,比如要清除bit3的話就設置0x08,可以同時清除多個位,如設置0x09,就同時清除bit3和bit0
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
- EventGroupHandle_t xEventGroup:要操作的時間標志組的句柄
- EventBits_t uxBitsToClear:要清零的事件位,比如要清除bit3的話就設置0x08,可以同時清除多個位,如設置0x09,就同時清除bit3和bit0
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
- EventGroupHandle_t xEventGroup:要操作的事件標志組的句柄
- EventBits_t uxBitsToSet:指定要置1的事件位,比如要置bit3為1的話就設置0x08,可以同時設置多個位,如設置0x09,就同時置bit3和bit0為1
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
- EventGroupHandle_t xEventGroup:要操作的事件標志組的句柄
- EventBits_t uxBitsToSet:指定要置1的事件位,比如要置bit3為1的話就設置0x08,可以同時設置多個位,如設置0x09,就同時置bit3和bit0為1
- BaseType_t *pxHigherPriorityTaskWoken:標記退出此函數以后是否進行任務切換,傳入pdTRUE在退出中斷函數之前一定進行一次任務切換
0x04 獲取事件標志組值
FreeRTOS提供了兩個API函數用來查詢事件標志組值。
- xEventGroupGetBits():獲取當前事件標志組的值(各個事件位的值),用在任務中
- xEventGroupGetBitsFromISR():獲取當前事件標志組的值,用在中斷服務函數中
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
- EventGroupHandle_t xEventGroup :要獲取的事件標志組的句柄
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
- EventGroupHandle_t xEventGroup :要獲取的事件標志組的句柄
0x05 等待指定的事件位
某個任務可能需要與多個事件進行同步,那么這個任務就需要等待并判斷多個事件位,FreeRTOS提供xEventGroupWaitBits()完成這個功能。如果任務要等待的事件位還沒有準備好,任務就會進入阻塞態。
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
- EventGroupHandle_t xEventGroup:指定要等待的時間標志組
- EventBits_t uxBitsToWaitFor:指定要等待的事件位,比如要等待bit0和bit2的時間此參數就是0x05
- const BaseType_t xClearOnExit:此參數為pdTRUE的話,那么在退出此函數之前由參數uxBitsToWaitFor指定的這些事件位就會清零,如果設置為pdFALSE的話,這些事件位就不會改變。
- const BaseType_t xWaitForAllBits:此參數如果設置為pdTRUE的話,當uxBitsToWaitFor所設置的這些事件為都置1,或者指定的阻塞時間到的時候xEventGroupWaitBits函數才會返回,當設置為pdFALSE的話,只要uxBitsToWaitFor所設置的這些事件位其中的任意一個置1,或者指定的阻塞時間到的話,函數xEventGroupWaitBits就會返回
- TickType_t xTicksToWait :設置阻塞時間,單位為節拍數
返回值:
返回當前所等待事件位置1以后的事件標志組的值,或者阻塞時間到,根據這個值,我們就知道哪些事件位置1了,如果函數因為阻塞時間到返回的話,那么這個返回值就不代表任何的含義。
實驗
設計四個任務:
start_task:用來創建其他三個任務和事件標志組
eventsetbit_task:讀取按鍵值,根據不同的按鍵值將事件標志組中相應的事件位置1,用來模擬事件的發生
eventgroup_task:同時等待事件標志組中的多個事件位,當這些事件位都置1的話就將其顯示到LCD上,并且也通過串口打印出來
start_task代碼:
void start_task(void *pvParameter)
{taskENTER_CRITICAL();EventGroup_Handler=xEventGroupCreate();xTaskCreate(eventsetbit_task,"eventsetbit_task",256,NULL,2,&EventSetBit_Handler);xTaskCreate(eventgroup_task,"eventgroup_task",256,NULL,4,&EventGp_Handler);xTaskCreate(eventquery_task,"eventquery_task",256,NULL,3,&EventQuery_Handler);vTaskDelete(StartTask_Handler);taskEXIT_CRITICAL();
}
創建三個任務和一個事件狀態標志組
void eventsetbit_task(void *pvParameter)
{u8 key;while(1){if(EventGroup_Handler!=NULL){key = KEY_Scan(0);switch(key){case KEY1_PRES:xEventGroupSetBits(EventGroup_Handler,2);break;case WKUP_PRES:xEventGroupSetBits(EventGroup_Handler,4);break;}}vTaskDelay(10);}
}
eventsetbit_task檢查KEY1和KEY_UP有無按下,KEY1按下將bit1設置為1,KEY_UP按下將bit2設置為1
//中斷服務函數
void EXTI3_IRQHandler(void)
{BaseType_t Result,xHigherPriorityTaskWoken;delay_xms(50); //消抖if(KEY0==0){Result=xEventGroupSetBitsFromISR(EventGroup_Handler,EVENTBIT_0,&xHigherPriorityTaskWoken);if(Result!=pdFAIL){portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}}__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中斷標志位
}
kEY0按下將bit0值1
eventgroup_task檢查bit0、bit1、bit2是否都置為1了
void eventgroup_task(void *pvParameter)
{u8 num;EventBits_t EventValue;while(1){if(EventGroup_Handler!=NULL){EventValue=xEventGroupWaitBits(EventGroup_Handler,7,pdTRUE,pdTRUE,portMAX_DELAY);printf("事件標志組的值:%d\r\n",EventValue);LCD_ShowxNum(174,110,EventValue,1,16,0);num++;LED1 = !LED1;LCD_Fill(6,131,233,313,lcd_discolor[num%14]);}else{vTaskDelay(10);}}}
查詢當前的事件標志組的值,然后顯示出來
void eventquery_task(void *pvParameter)
{u8 num=0;EventBits_t NewValue,LastValue;while(1){if(EventGroup_Handler!=NULL){NewValue=xEventGroupGetBits(EventGroup_Handler);if(NewValue!=LastValue){LastValue = NewValue;printf("事件標志組的值: %d\r\n",NewValue);LCD_ShowxNum(174,110,NewValue,1,16,0);}}num++;if(num==0){num=0;LED0 = !LED0;}vTaskDelay(50);}
}