建造個(gè)網(wǎng)站花多少錢b2b外鏈
目錄
- 1.概念
- 2.事件組的操作
- 3.事件組相關(guān)函數(shù)
- 3.1 創(chuàng)建
- 3.2 刪除
- 3.3 設(shè)置事件
- 3.4 等待事件
- 3.5 同步點(diǎn)
- 4.示例
- 4.1 等待事件
- 4.2 同步點(diǎn)
- 5.內(nèi)部源碼
- 5.1 結(jié)構(gòu)體
- 5.2 等待事件
- 5.3 設(shè)置事件
- 5.4 為什么不是關(guān)閉中斷
- 疑問
- 疑問1
- 疑問2
- 疑問3
1.概念
事件組可以簡(jiǎn)單地認(rèn)為是一個(gè)整數(shù),每一位代表一個(gè)獨(dú)立的事件。程序員可以為每一位事件賦予不同的含義,例如:
- Bit0:表示串口是否就緒
- Bit1:表示按鍵是否被按下等等。
當(dāng)某個(gè)位的值為1時(shí),表示該事件已發(fā)生;為0則表示事件未發(fā)生。
事件組使用一個(gè)整數(shù)來(lái)存儲(chǔ)所有事件。
該整數(shù)的高位部分(8位或更多)由內(nèi)核保留,供內(nèi)部使用。
事件組可用的位數(shù)取決于配置參數(shù) configUSE_16_BIT_TICKS:
- 如果 configUSE_16_BIT_TICKS 為1,則表示處理器使用16位計(jì)時(shí)器,此時(shí)事件組是16位的,低8位用于表示事件;
- 如果 configUSE_16_BIT_TICKS 為0,則使用32位計(jì)時(shí)器,事件組就是32位的,其中低24位用于表示事件。
一個(gè)或多個(gè)任務(wù)、甚至 ISR 都可以設(shè)置(寫入)事件位,也可以讀取這些事件。
不同任務(wù)可以同時(shí)等待同一個(gè)事件組中的不同位或組合位發(fā)生變化。
2.事件組的操作
- 喚醒行為(廣播效果):
-
- 隊(duì)列、信號(hào)量: 當(dāng)事件發(fā)生時(shí),通常只喚醒一個(gè)等待該資源的任務(wù)。
- 事件組: 當(dāng)滿足等待條件的事件發(fā)生時(shí),所有等待該條件的任務(wù)都會(huì)被喚醒。這種機(jī)制具有“廣播”效果,適合多個(gè)任務(wù)需要同時(shí)知道事件發(fā)生的情況。
- 事件清除(消耗型與非消耗型):
-
- 隊(duì)列、信號(hào)量: 數(shù)據(jù)或計(jì)數(shù)值被讀取后,就被消耗掉。
- 事件組: 當(dāng)一個(gè)任務(wù)等待事件組時(shí),可以選擇在獲得事件后是否清除對(duì)應(yīng)的事件位。也就是說(shuō),任務(wù)獲得事件時(shí)可以選擇“保留”事件(允許后續(xù)任務(wù)繼續(xù)看到該事件)或“清除”事件(將事件位復(fù)位為0)。
假設(shè)系統(tǒng)中有兩個(gè)任務(wù),一個(gè)任務(wù)(任務(wù)A)等待“串口就緒”事件(Bit0),另一個(gè)任務(wù)(任務(wù)B)等待“按鍵按下”事件(Bit1)。
- 當(dāng)外部中斷或另一個(gè)任務(wù)檢測(cè)到串口就緒時(shí),會(huì)設(shè)置事件組中 Bit0 為1;任務(wù)A立即被喚醒,檢查到事件成立后,可以選擇清除 Bit0 或保留它。
- 同樣,當(dāng)檢測(cè)到按鍵按下時(shí),會(huì)設(shè)置 Bit1 為1;任務(wù)B被喚醒并執(zhí)行相應(yīng)處理。
此外,如果有任務(wù)需要同時(shí)等待這兩個(gè)事件中的任一一個(gè)發(fā)生,則可以用“或”關(guān)系等待;如果要求必須同時(shí)滿足兩個(gè)事件才能繼續(xù)操作,則設(shè)置為“與”關(guān)系等待。
3.事件組相關(guān)函數(shù)
3.1 創(chuàng)建
動(dòng)態(tài)創(chuàng)建:
EventGroupHandle_t xEventGroupCreate(void);
- 此函數(shù)在內(nèi)部會(huì)動(dòng)態(tài)分配內(nèi)存來(lái)存儲(chǔ)事件組的數(shù)據(jù)結(jié)構(gòu)。
- 創(chuàng)建成功后返回一個(gè)非 NULL 的事件組句柄;否則返回 NULL。
- 使用動(dòng)態(tài)創(chuàng)建的事件組適合內(nèi)存充足的系統(tǒng),且方便使用。
靜態(tài)創(chuàng)建:
EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t *pxEventGroupBuffer);
- 靜態(tài)創(chuàng)建要求用戶先定義一個(gè)
StaticEventGroup_t
類型的變量(內(nèi)存緩沖區(qū)),并將其地址傳入。 - 該函數(shù)不使用動(dòng)態(tài)內(nèi)存分配,而是利用用戶提供的內(nèi)存來(lái)存儲(chǔ)事件組結(jié)構(gòu)。
- 成功返回非 NULL 的句柄,適用于對(duì)內(nèi)存管理要求嚴(yán)格或不希望使用動(dòng)態(tài)分配的系統(tǒng)。
3.2 刪除
當(dāng)不再需要事件組時(shí),可以通過(guò)刪除事件組來(lái)回收內(nèi)存(僅適用于動(dòng)態(tài)創(chuàng)建的事件組)。
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
- 刪除指定的事件組,釋放由動(dòng)態(tài)分配內(nèi)存創(chuàng)建的事件組所占用的資源。
- 靜態(tài)創(chuàng)建的事件組的內(nèi)存由用戶管理,因此調(diào)用此函數(shù)不會(huì)釋放用戶提供的內(nèi)存。
3.3 設(shè)置事件
設(shè)置事件組的操作即是將某個(gè)位或多個(gè)位置為1,表示對(duì)應(yīng)的事件已發(fā)生。事件的寫操作有兩種版本:任務(wù)中使用和 ISR 中使用。
在任務(wù)中設(shè)置事件:
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet);
- 參數(shù)
xEventGroup
指定要設(shè)置的事件組。 - 參數(shù)
uxBitsToSet
指定要設(shè)置的位(例如 0x15 表示設(shè)置 bit4、bit2、bit0)。 - 該函數(shù)將事件組中指定的位設(shè)置為1,并返回設(shè)置之前的事件組值(由于其他任務(wù)可能同時(shí)修改,返回值通常用作調(diào)試)。
- 設(shè)置操作會(huì)喚醒所有等待這些位(或這些位組合)的任務(wù),具有廣播效果。
在ISR中設(shè)置事件:
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken);
- 用于在中斷服務(wù)例程中設(shè)置事件組中的位。
- 與任務(wù)中使用的版本類似,但由于在 ISR 中不允許直接阻塞或做較復(fù)雜操作,此函數(shù)不是直接設(shè)置事件組,而是通過(guò)向 FreeRTOS 后臺(tái)任務(wù)(daemon task)發(fā)送隊(duì)列數(shù)據(jù)來(lái)間接設(shè)置事件組。
- 參數(shù)
pxHigherPriorityTaskWoken
用于指示是否有更高優(yōu)先級(jí)任務(wù)因該設(shè)置操作而進(jìn)入就緒狀態(tài)。如果后臺(tái)任務(wù)的優(yōu)先級(jí)高于當(dāng)前中斷任務(wù),則該變量會(huì)被設(shè)置為pdTRUE
。 - 返回值為
pdPASS
表示操作成功。
3.4 等待事件
任務(wù)可以使用事件組等待某一位或多位事件發(fā)生。等待函數(shù)允許你指定等待的條件(“與”或“或”關(guān)系)、是否在退出時(shí)清除事件位以及等待的最長(zhǎng)時(shí)間。
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait);
-
xEventGroup:要等待的事件組句柄。
-
uxBitsToWaitFor:指定位掩碼,指定任務(wù)需要等待哪些位。例如,0x15 表示任務(wù)等待 bit0、bit2 和 bit4。
-
xClearOnExit:
-
pdTRUE
:在退出等待前,自動(dòng)清除 uxBitsToWaitFor 指定的位。pdFALSE
:退出前不清除。- 清除操作在函數(shù)內(nèi)部以原子方式完成,可以避免等待和清除之間被其他任務(wù)打斷。
-
xWaitForAllBits:
-
pdTRUE
:表示任務(wù)需要等待 uxBitsToWaitFor 指定的所有位均為1。pdFALSE
:表示只要其中任意一位為1即可。
-
xTicksToWait:如果所等待的事件未發(fā)生,任務(wù)將阻塞等待指定的 Tick 數(shù)??梢栽O(shè)置為:
-
- 0:不阻塞,立即返回當(dāng)前事件組值。
portMAX_DELAY
:無(wú)限等待。- 其他值:阻塞指定 Tick 數(shù)(通常用
pdMS_TO_TICKS()
轉(zhuǎn)換為 Tick Count)。
-
返回值:
-
- 返回的是事件組當(dāng)前的位值,即在“非阻塞條件成立”時(shí)的事件組值;如果超時(shí)則返回超時(shí)時(shí)刻的事件組值。
xEventGroupWaitBits
返回前一定會(huì)清除事件嗎?
- 參數(shù)
xClearOnExit
為真以及成功等待事件時(shí)才會(huì)清除
3.5 同步點(diǎn)
事件組不僅可以用來(lái)等待單個(gè)或多個(gè)事件,還可以用于實(shí)現(xiàn)多個(gè)任務(wù)的同步。所謂“同步點(diǎn)”,指的是多個(gè)任務(wù)各自完成某個(gè)操作后,需要等待其他任務(wù)也完成,才能共同進(jìn)入下一階段。
EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait);
- uxBitsToSet:每個(gè)任務(wù)調(diào)用該函數(shù)時(shí),會(huì)將自己對(duì)應(yīng)的事件位設(shè)置為1,表示“我已經(jīng)完成了我的部分”。
- uxBitsToWaitFor:任務(wù)需要等待的其他任務(wù)所設(shè)置的位。只有當(dāng)這些位滿足條件時(shí)(通常為所有任務(wù)都完成),該函數(shù)才返回,表示同步點(diǎn)達(dá)成。
- 同步成功返回后,事件組中這些位會(huì)被自動(dòng)清除,以便下次使用。
- xTicksToWait 指定了等待的最長(zhǎng)時(shí)間,超時(shí)則返回。
4.示例
4.1 等待事件
// 全局變量:共享數(shù)據(jù)與句柄static int sum = 0; // 全局變量,用于累加計(jì)算(由Task1更新)
static int dec = 0; // 全局變量,用于累減計(jì)算(由Task2更新)static volatile int flagCalcEnd = 0; // 標(biāo)志計(jì)算是否結(jié)束(本例中未使用,可作擴(kuò)展)
static volatile int flagUARTused = 0; // 標(biāo)志UART是否使用(本例中未使用)static QueueHandle_t xQueueCalcHandle; // 隊(duì)列句柄,用于在任務(wù)間傳遞計(jì)算結(jié)果(數(shù)據(jù)為int類型)
static EventGroupHandle_t xEventGroupCalc; // 事件組句柄,用于同步兩個(gè)任務(wù)的事件(事件組中不同位表示不同任務(wù)已完成操作)/*-----------------------------------------------------------* Task1Function:* 任務(wù)1負(fù)責(zé)執(zhí)行一個(gè)簡(jiǎn)單的累加操作,計(jì)算完成后將計(jì)算結(jié)果發(fā)送到隊(duì)列中,* 同時(shí)設(shè)置事件組中的位0,表示任務(wù)1的操作已完成。*/
void Task1Function(void * param)
{volatile int i = 0;while (1){// 累加操作:從0到99999,每次累加1,更新全局變量sumfor (i = 0; i < 100000; i++)sum++;// 將計(jì)算結(jié)果sum通過(guò)隊(duì)列發(fā)送出去,參數(shù)0表示不阻塞,如果隊(duì)列已滿則丟棄數(shù)據(jù)xQueueSend(xQueueCalcHandle, &sum, 0);/* 設(shè)置事件組中的事件位0 (即Bit0) 為1,表示Task1已完成其累加操作 */xEventGroupSetBits(xEventGroupCalc, (1 << 0));// 由于任務(wù)中沒有延時(shí),因此此循環(huán)會(huì)連續(xù)不斷執(zhí)行;// 在實(shí)際應(yīng)用中可能需要添加vTaskDelay()避免CPU占用過(guò)高。}
}/*-----------------------------------------------------------* Task2Function:* 任務(wù)2負(fù)責(zé)執(zhí)行累減操作,將結(jié)果發(fā)送到同一個(gè)隊(duì)列中,* 并設(shè)置事件組中的位1,表示任務(wù)2的操作已完成。*/
void Task2Function(void * param)
{volatile int i = 0;while (1){// 累減操作:從0到-99999,每次遞減1,更新全局變量decfor (i = 0; i < 100000; i++)dec--;// 將累減后的結(jié)果通過(guò)隊(duì)列發(fā)送出去xQueueSend(xQueueCalcHandle, &dec, 0);/* 設(shè)置事件組中的事件位1 (即Bit1) 為1,表示Task2已完成其累減操作 */xEventGroupSetBits(xEventGroupCalc, (1 << 1));// 同樣,此處可添加適當(dāng)延時(shí)以平衡任務(wù)執(zhí)行頻率}
}/*-----------------------------------------------------------* Task3Function:* 任務(wù)3負(fù)責(zé)等待事件組中的兩個(gè)事件都發(fā)生后,再?gòu)年?duì)列中讀取數(shù)據(jù),* 并打印出讀取到的數(shù)據(jù)。** 等待條件:同時(shí)等待事件組中Bit0和Bit1均為1 (xWaitForAllBits = pdTRUE)* 清除模式:等待成功后自動(dòng)清除這兩位 (xClearOnExit = pdTRUE)* 阻塞時(shí)間:無(wú)限等待 (portMAX_DELAY)*/
void Task3Function(void * param)
{int val1, val2;while (1){/* 等待事件:* 任務(wù)等待事件組中Bit0和Bit1同時(shí)為1。* 當(dāng)兩個(gè)事件都發(fā)生后,會(huì)清除這兩位(因?yàn)閤ClearOnExit設(shè)置為pdTRUE)。*/xEventGroupWaitBits(xEventGroupCalc, (1 << 0) | (1 << 1), pdTRUE, pdTRUE, portMAX_DELAY);// 從隊(duì)列中讀取數(shù)據(jù),首先讀取Task1傳送的累加結(jié)果xQueueReceive(xQueueCalcHandle, &val1, 0);// 再讀取Task2傳送的累減結(jié)果xQueueReceive(xQueueCalcHandle, &val2, 0);// 打印兩任務(wù)傳遞的結(jié)果printf("val1 = %d, val2 = %d\r\n", val1, val2);}
}/*-----------------------------------------------------------* main函數(shù):* 1. 硬件初始化* 2. 創(chuàng)建事件組與隊(duì)列* 3. 創(chuàng)建三個(gè)任務(wù):Task1, Task2, Task3* 4. 啟動(dòng)調(diào)度器,進(jìn)入多任務(wù)調(diào)度狀態(tài)*/
int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endif// 硬件初始化(用戶需根據(jù)具體平臺(tái)實(shí)現(xiàn))prvSetupHardware();printf("Hello, world!\r\n");/* 創(chuàng)建事件組:用于同步Task1和Task2的操作事件 */xEventGroupCalc = xEventGroupCreate();/* 創(chuàng)建隊(duì)列:長(zhǎng)度為2,每個(gè)數(shù)據(jù)項(xiàng)大小為int用于傳遞Task1和Task2的計(jì)算結(jié)果 */xQueueCalcHandle = xQueueCreate(2, sizeof(int));if (xQueueCalcHandle == NULL){printf("can not create queue\r\n");}// 創(chuàng)建任務(wù):Task1進(jìn)行累加,Task2進(jìn)行累減,Task3等待事件并打印數(shù)據(jù)xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);// 啟動(dòng)調(diào)度器,開始任務(wù)調(diào)度vTaskStartScheduler();// 如果調(diào)度器啟動(dòng)失敗(例如內(nèi)存不足),將執(zhí)行到這里return 0;
}
- 事件組 在本例中用于同步兩個(gè)任務(wù)的執(zhí)行。Task1和Task2分別完成計(jì)算后通過(guò)事件組設(shè)置自己的事件位;Task3等待兩個(gè)事件同時(shí)滿足后,再?gòu)年?duì)列中讀取數(shù)據(jù)并打印。
- 隊(duì)列 用于傳遞實(shí)際的計(jì)算結(jié)果數(shù)據(jù),確保數(shù)據(jù)不會(huì)丟失或混淆。
4.2 同步點(diǎn)
三個(gè)任務(wù)分別負(fù)責(zé)準(zhǔn)備演示、設(shè)置設(shè)備和沖咖啡,只有當(dāng)三個(gè)任務(wù)都完成自己的準(zhǔn)備工作后,才能“開會(huì)”。任務(wù)之間通過(guò) xEventGroupSync 來(lái)達(dá)到同步點(diǎn)。
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "stdio.h"/*-----------------------------------------------------------* 模擬場(chǎng)景:會(huì)議開始前的準(zhǔn)備工作同步* 有三個(gè)任務(wù):* - 演示任務(wù)(Presentation):準(zhǔn)備演示文稿* - 設(shè)備任務(wù)(Equipment):設(shè)置會(huì)議設(shè)備* - 咖啡任務(wù)(Coffee):沖咖啡** 每個(gè)任務(wù)完成自己工作后,調(diào)用 xEventGroupSync() 將自己對(duì)應(yīng)的事件位設(shè)置,* 并等待其他任務(wù)也完成。只有所有任務(wù)都完成后,* xEventGroupSync() 才返回,然后各任務(wù)輸出“會(huì)議開始”的信息。*-----------------------------------------------------------*//* 定義事件組中各個(gè)事件位 */
#define PRESENTATION (1 << 0) // Bit0:演示任務(wù)完成
#define EQUIPMENT (1 << 1) // Bit1:設(shè)備任務(wù)完成
#define COFFEE (1 << 2) // Bit2:咖啡任務(wù)完成
#define ALL_EVENTS (PRESENTATION | EQUIPMENT | COFFEE)/* 事件組句柄 */
EventGroupHandle_t xMeetingEventGroup;/* 函數(shù)原型 */
static void vPresentationTask(void *pvParameters);
static void vEquipmentTask(void *pvParameters);
static void vCoffeeTask(void *pvParameters);int main(void)
{/* 硬件初始化,具體實(shí)現(xiàn)視平臺(tái)而定 */prvSetupHardware();printf("System starting: Meeting preparation simulation...\r\n");/* 創(chuàng)建事件組 */xMeetingEventGroup = xEventGroupCreate();if (xMeetingEventGroup != NULL){/* 創(chuàng)建3個(gè)任務(wù),各自負(fù)責(zé)一項(xiàng)準(zhǔn)備工作* 這里傳入的參數(shù)是一個(gè)字符串,用于打印任務(wù)名稱*/xTaskCreate(vPresentationTask, "PresentationTask", 1000, "Presenter", 1, NULL);xTaskCreate(vEquipmentTask, "EquipmentTask", 1000, "Equipment", 1, NULL);xTaskCreate(vCoffeeTask, "CoffeeTask", 1000, "Barista", 1, NULL);/* 啟動(dòng)調(diào)度器 */vTaskStartScheduler();}else{/* 如果事件組創(chuàng)建失敗,則打印錯(cuò)誤信息 */printf("Error: Unable to create event group!\r\n");}/* 如果程序運(yùn)行到這里通常表示內(nèi)存不足等嚴(yán)重錯(cuò)誤 */return 0;
}/*-----------------------------------------------------------* vPresentationTask:* 模擬準(zhǔn)備演示文稿的任務(wù)* 每次準(zhǔn)備完成后,通過(guò) xEventGroupSync() 設(shè)置 PRESENTATION 位,* 并等待所有任務(wù)都完成(ALL_EVENTS)。* 當(dāng)同步點(diǎn)成立后,任務(wù)打印“會(huì)議開始”信息。*-----------------------------------------------------------*/
static void vPresentationTask(void *pvParameters)
{const TickType_t xDelay = pdMS_TO_TICKS(100UL); // 100毫秒延時(shí)int iteration = 0;for (;;){/* 執(zhí)行自己的工作:準(zhǔn)備演示文稿 */printf("%s: Preparing presentation, iteration %d...\r\n", (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到達(dá)同步點(diǎn):* 將 PRESENTATION 位設(shè)置為1,并等待 ALL_EVENTS 中所有位都為1。* xClearOnExit設(shè)置為pdTRUE,表示當(dāng)?shù)却晒笞詣?dòng)清除這些位。* 阻塞等待時(shí)間設(shè)置為 portMAX_DELAY,表示一直等待直到所有任務(wù)都完成。*/xEventGroupSync(xMeetingEventGroup, PRESENTATION, ALL_EVENTS, portMAX_DELAY);/* 同步點(diǎn)成立,所有任務(wù)都完成準(zhǔn)備工作,會(huì)議開始 */printf("%s: Presentation ready. Meeting is starting, iteration %d.\r\n", (char *)pvParameters, iteration);iteration++;vTaskDelay(xDelay);}
}/*-----------------------------------------------------------* vEquipmentTask:* 模擬設(shè)置會(huì)議設(shè)備的任務(wù)* 完成工作后設(shè)置 EQUIPMENT 位,并等待所有任務(wù)同步后打印“會(huì)議開始”信息。*-----------------------------------------------------------*/
static void vEquipmentTask(void *pvParameters)
{const TickType_t xDelay = pdMS_TO_TICKS(100UL); // 100毫秒延時(shí)int iteration = 0;for (;;){/* 執(zhí)行自己的工作:設(shè)置會(huì)議設(shè)備 */printf("%s: Setting up equipment, iteration %d...\r\n", (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到達(dá)同步點(diǎn):* 將 EQUIPMENT 位設(shè)置為1,并等待 ALL_EVENTS 中所有位都為1,* 自動(dòng)清除等待位后繼續(xù)執(zhí)行。*/xEventGroupSync(xMeetingEventGroup, EQUIPMENT, ALL_EVENTS, portMAX_DELAY);/* 同步點(diǎn)成立,會(huì)議開始 */printf("%s: Equipment ready. Meeting is starting, iteration %d.\r\n", (char *)pvParameters, iteration);iteration++;vTaskDelay(xDelay);}
}/*-----------------------------------------------------------* vCoffeeTask:* 模擬沖咖啡的任務(wù)* 完成工作后設(shè)置 COFFEE 位,并等待所有任務(wù)同步后打印“會(huì)議開始”信息。*-----------------------------------------------------------*/
static void vCoffeeTask(void *pvParameters)
{const TickType_t xDelay = pdMS_TO_TICKS(100UL); // 100毫秒延時(shí)int iteration = 0;for (;;){/* 執(zhí)行自己的工作:沖咖啡 */printf("%s: Brewing coffee, iteration %d...\r\n", (char *)pvParameters, iteration);vTaskDelay(xDelay);/* 到達(dá)同步點(diǎn):* 將 COFFEE 位設(shè)置為1,并等待所有任務(wù)都完成(ALL_EVENTS)。*/xEventGroupSync(xMeetingEventGroup, COFFEE, ALL_EVENTS, portMAX_DELAY);/* 同步點(diǎn)成立,會(huì)議開始 */printf("%s: Coffee ready. Meeting is starting, iteration %d.\r\n", (char *)pvParameters, iteration);iteration++;vTaskDelay(xDelay);}
}
5.內(nèi)部源碼
5.1 結(jié)構(gòu)體
/* 事件組結(jié)構(gòu)體定義 */
typedef struct EventGroupDef_t
{/* 當(dāng)前事件位(事件標(biāo)志集合)* 類型 EventBits_t 通常為 uint32_t,最多支持 32 個(gè)獨(dú)立事件位(bit)* 每個(gè)位表示一個(gè)事件狀態(tài)(如 0x00000001 表示位0被置位)* 使用 xEventGroupSetBits() 設(shè)置位,xEventGroupClearBits() 清除位* 任務(wù)通過(guò) xEventGroupWaitBits() 等待特定位的組合* 示例:等待位0和位1同時(shí)置位(uxEventBits & 0x03 == 0x03)*/EventBits_t uxEventBits;/* 等待事件位的任務(wù)列表* 使用 FreeRTOS 的鏈表(List_t)管理所有因等待事件位而阻塞的任務(wù)* 當(dāng)調(diào)用 xEventGroupSetBits() 時(shí),檢查列表中的任務(wù)是否滿足等待條件* 滿足條件的任務(wù)會(huì)被喚醒并移至就緒隊(duì)列*/List_t xTasksWaitingForBits;/* 跟蹤和調(diào)試標(biāo)識(shí)符(僅在啟用 configUSE_TRACE_FACILITY 時(shí)生效)* 每個(gè)事件組創(chuàng)建時(shí)被分配唯一的編號(hào),便于調(diào)試工具(如 Tracealyzer)追蹤* 通過(guò) uxEventGroupGetNumber() 可獲取此標(biāo)識(shí)符*/#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxEventGroupNumber;#endif/* 靜態(tài)分配標(biāo)記(僅在同時(shí)啟用靜態(tài)和動(dòng)態(tài)分配時(shí)生效)* 若事件組通過(guò) xEventGroupCreateStatic() 靜態(tài)創(chuàng)建,此字段設(shè)為 pdTRUE* 防止誤用 vEventGroupDelete() 刪除靜態(tài)分配的事件組(因其內(nèi)存不可釋放)*/#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated;#endif
} EventGroup_t;
5.2 等待事件
在之前的隊(duì)列、信號(hào)量或者是互斥量,實(shí)現(xiàn)互斥訪問都是依靠關(guān)閉中斷。事件組卻不是這樣,而是靠關(guān)閉調(diào)度器。這意味著并不會(huì)在中斷中去使用事件組
來(lái)看看等待事件發(fā)生的函數(shù)xEventGroupWaitBits
:
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait )
{/* 將通用事件組句柄轉(zhuǎn)換為內(nèi)部事件組結(jié)構(gòu)指針 */EventGroup_t * pxEventBits = xEventGroup;/* 用于存儲(chǔ)最終返回的事件位值 */EventBits_t uxReturn;/* 用于記錄調(diào)用者的控制要求,如清除標(biāo)志和“等待所有位”標(biāo)志 */EventBits_t uxControlBits = 0;BaseType_t xWaitConditionMet, xAlreadyYielded;BaseType_t xTimeoutOccurred = pdFALSE;/* 斷言檢查:* 1. xEventGroup 必須有效。* 2. 請(qǐng)求等待的位中不能包含內(nèi)核使用的控制位 (eventEVENT_BITS_CONTROL_BYTES)。* 3. 至少有一位被請(qǐng)求。*/configASSERT( xEventGroup );configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );configASSERT( uxBitsToWaitFor != 0 );/* 如果調(diào)度器處于掛起狀態(tài),則不允許阻塞等待 */#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );}#endif/* 暫停任務(wù)調(diào)度以便原子性操作,不被其他任務(wù)打斷 */vTaskSuspendAll();{/* 讀取當(dāng)前事件組中的事件位 */const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;/* 調(diào)用內(nèi)部函數(shù)檢查是否滿足等待條件:* 如果 xWaitForAllBits 為 pdTRUE,則要求 uxCurrentEventBits 包含所有 uxBitsToWaitFor 指定的位,* 否則只要求其中任一位被置位。*/xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );if( xWaitConditionMet != pdFALSE ){/* 如果條件已經(jīng)滿足:* 1. 直接返回當(dāng)前的事件位。* 2. 同時(shí)將 xTicksToWait 設(shè)置為 0,表示不需要阻塞等待。*/uxReturn = uxCurrentEventBits;xTicksToWait = ( TickType_t ) 0;/* 根據(jù)參數(shù)判斷是否在返回前清除等待的位 */if( xClearOnExit != pdFALSE ){pxEventBits->uxEventBits &= ~uxBitsToWaitFor;}else{mtCOVERAGE_TEST_MARKER();}}else if( xTicksToWait == ( TickType_t ) 0 ){/* 如果等待條件不滿足且不允許阻塞等待,則:* 返回當(dāng)前事件位,同時(shí)標(biāo)記為超時(shí)(未滿足條件) */uxReturn = uxCurrentEventBits;xTimeoutOccurred = pdTRUE;}else{/* 如果條件不滿足且允許阻塞等待,則設(shè)置任務(wù)的等待行為:** uxControlBits 用于記錄以下調(diào)用者要求:* 如果傳入?yún)?shù) xClearOnExit 為 pdTRUE,則在任務(wù)解除阻塞時(shí)清除事件位。* 如果傳入?yún)?shù) xWaitForAllBits 為 pdTRUE,則要求等待所有指定的位。*/if( xClearOnExit != pdFALSE ){uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;}else{mtCOVERAGE_TEST_MARKER();}if( xWaitForAllBits != pdFALSE ){uxControlBits |= eventWAIT_FOR_ALL_BITS;}else{mtCOVERAGE_TEST_MARKER();}/* 將任務(wù)希望等待的事件位和控制標(biāo)志(通過(guò)按位或組合)存儲(chǔ)到任務(wù)的事件列表項(xiàng)中,* 并將任務(wù)放入 pxEventBits->xTasksWaitingForBits 等待列表中,同時(shí)指定最大等待時(shí)間。* 當(dāng)這些事件位滿足等待條件時(shí),內(nèi)核會(huì)解除任務(wù)阻塞。 */vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ),( uxBitsToWaitFor | uxControlBits ),xTicksToWait );/* 雖然在任務(wù)解除阻塞后 uxReturn 會(huì)被重新設(shè)置,但為了防止某些編譯器發(fā)出未初始化變量警告,* 這里先將 uxReturn 初始化為 0 */uxReturn = 0;/* 跟蹤記錄任務(wù)進(jìn)入等待狀態(tài) */traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );}}/* 恢復(fù)任務(wù)調(diào)度,xAlreadyYielded 表示是否有任務(wù)因?yàn)榛謴?fù)調(diào)度而已切換 */xAlreadyYielded = xTaskResumeAll();/* 如果任務(wù)進(jìn)入阻塞等待,即 xTicksToWait 仍不為 0 */if( xTicksToWait != ( TickType_t ) 0 ){/* 如果恢復(fù)調(diào)度后沒有發(fā)生上下文切換,則調(diào)用 yield 進(jìn)行任務(wù)切換 */if( xAlreadyYielded == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}/* 當(dāng)任務(wù)解除阻塞后,等待的事件位會(huì)存儲(chǔ)在任務(wù)的事件列表項(xiàng)中,* 調(diào)用 uxTaskResetEventItemValue() 獲取解除阻塞時(shí)設(shè)置的事件位。* 此時(shí)可能是因?yàn)橹付ǖ氖录槐恢梦欢獬枞?#xff0c;也可能是因?yàn)榈却瑫r(shí)解除阻塞。 */uxReturn = uxTaskResetEventItemValue();/* 檢查返回值中是否包含 eventUNBLOCKED_DUE_TO_BIT_SET 標(biāo)志,* 如果沒有該標(biāo)志,則說(shuō)明任務(wù)是因超時(shí)而解除阻塞 */if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ){/* 進(jìn)入臨界區(qū)保護(hù)對(duì)事件組的訪問 */taskENTER_CRITICAL();{/* 將返回值設(shè)置為當(dāng)前的事件組事件位 */uxReturn = pxEventBits->uxEventBits;/* 由于任務(wù)解除阻塞后,事件組的位可能已被更新,* 再次檢查等待條件,如果滿足,則根據(jù) xClearOnExit 參數(shù)決定是否清除事件位 */if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ){if( xClearOnExit != pdFALSE ){pxEventBits->uxEventBits &= ~uxBitsToWaitFor;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}/* 標(biāo)記此次解除阻塞是由于超時(shí)而非事件位被設(shè)置 */xTimeoutOccurred = pdTRUE;}taskEXIT_CRITICAL();}else{/* 如果任務(wù)解除阻塞原因是事件位被置位,則無(wú)需超時(shí)處理 */}/* 清除返回值中可能設(shè)置的控制位,確保返回值只包含實(shí)際的事件位 */uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;}/* 跟蹤記錄等待結(jié)束事件,傳入事件組句柄、等待的位和是否超時(shí) */traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );/* 防止當(dāng)跟蹤宏未使用時(shí)產(chǎn)生編譯器警告 */( void ) xTimeoutOccurred;/* 返回最終的事件位,表示任務(wù)等待結(jié)束時(shí)事件組的狀態(tài) */return uxReturn;
}
無(wú)非就是內(nèi)部調(diào)用prvTestWaitCondition
,檢查是否滿足等待條件。
然后根據(jù)其返回值判斷:
- 不滿足的話,根據(jù)用戶要求來(lái)決定是否阻塞,如果阻塞的話就調(diào)用
vTaskPlaceOnUnorderedEventList
將當(dāng)前任務(wù)放到任務(wù)等待鏈表當(dāng)中,其實(shí)現(xiàn)如下,已經(jīng)標(biāo)注了詳細(xì)的代碼
void vTaskPlaceOnUnorderedEventList( List_t * pxEventList,const TickType_t xItemValue,const TickType_t xTicksToWait )
{/* 1. 檢查傳入的事件列表指針是否有效 */configASSERT( pxEventList );/* 2. 斷言要求調(diào)度器必須處于掛起狀態(tài)。* 這保證了本函數(shù)在調(diào)用期間不會(huì)發(fā)生任務(wù)切換,* 從而確保對(duì)當(dāng)前任務(wù)的事件列表項(xiàng)和事件列表的操作具有原子性。* 該函數(shù)主要用于事件組實(shí)現(xiàn),要求在調(diào)度器掛起時(shí)調(diào)用。 */configASSERT( uxSchedulerSuspended != 0 );/* 3. 設(shè)置當(dāng)前任務(wù)的事件列表項(xiàng)的值。* 將傳入的 xItemValue 與標(biāo)志 taskEVENT_LIST_ITEM_VALUE_IN_USE 進(jìn)行按位或運(yùn)算,* 表示該事件列表項(xiàng)正被使用,并存儲(chǔ)指定的值。* 注意:這里使用 pxCurrentTCB->xEventListItem,代表當(dāng)前任務(wù)控制塊(TCB)的事件列表項(xiàng),* 并且由于當(dāng)前任務(wù)處于阻塞狀態(tài),其他中斷不會(huì)訪問其事件列表項(xiàng)。 */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );/* 4. 將當(dāng)前任務(wù)的事件列表項(xiàng)插入到指定的事件列表(pxEventList)的末尾。* 這樣,任務(wù)就被放入等待列表中,等待相關(guān)事件發(fā)生。* 由于該事件列表屬于事件組實(shí)現(xiàn),且中斷不會(huì)直接訪問事件組,* 所以這里的插入操作是安全的。 */listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) );/* 5. 將當(dāng)前任務(wù)放入延時(shí)列表中,等待 xTicksToWait 指定的時(shí)鐘節(jié)拍數(shù)。* 該函數(shù) prvAddCurrentTaskToDelayedList() 將當(dāng)前任務(wù)設(shè)置為延時(shí)狀態(tài),* 在超時(shí)時(shí)間到達(dá)或等待的事件條件滿足時(shí)將任務(wù)從延時(shí)列表中移除。* 參數(shù) pdTRUE 通常表示任務(wù)阻塞后任務(wù)狀態(tài)被標(biāo)記為等待事件。 */prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
}
-
xEventGroupWaitBits
函數(shù)的下半部分則是一些解除阻塞后的一些處理,分兩者情況 -
- 超時(shí)的話,事件組的位可能已被更新,再次檢查等待條件,如果滿足,則根據(jù) xClearOnExit 參數(shù)決定是否清除事件位
- 不超時(shí)而是被
xEventGroupSetBits
設(shè)置了等待位滿足條件而退出阻塞的話,則不做處理
5.3 設(shè)置事件
來(lái)看看xEventGroupSetBits
是怎么去設(shè)置事件,讓阻塞等待事件的任務(wù)被喚醒,進(jìn)行運(yùn)行。
其實(shí)沒看之前也能猜一下,是不是也是將等待事件的任務(wù)從任務(wù)等待鏈表中取出放到就緒任務(wù)等待鏈表呢???
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet )
{/* 定義用于遍歷等待任務(wù)的列表項(xiàng)指針 */ListItem_t * pxListItem, * pxNext;/* 定義事件列表的結(jié)束標(biāo)記指針,用于判斷遍歷是否結(jié)束 */ListItem_t const * pxListEnd;/* 定義指向事件組中等待任務(wù)列表的指針 */List_t const * pxList;/* 記錄待清除的事件位,初始為 0 */EventBits_t uxBitsToClear = 0;/* 存儲(chǔ)每個(gè)任務(wù)等待的事件位(不包含控制位) */EventBits_t uxBitsWaitedFor;/* 存儲(chǔ)每個(gè)任務(wù)等待條件中的控制位(如等待全部位、退出時(shí)清除位) */EventBits_t uxControlBits;/* 將傳入的事件組句柄轉(zhuǎn)換為內(nèi)部使用的事件組結(jié)構(gòu)指針 */EventGroup_t * pxEventBits = xEventGroup;/* 標(biāo)記是否找到滿足等待條件的任務(wù) */BaseType_t xMatchFound = pdFALSE;/* 斷言檢查:* 1. xEventGroup 必須有效。* 2. 要設(shè)置的位不能包含內(nèi)核保留的控制位(eventEVENT_BITS_CONTROL_BYTES)。*/configASSERT( xEventGroup );configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );/* 獲取等待該事件組的任務(wù)列表 */pxList = &( pxEventBits->xTasksWaitingForBits );/* 獲取該列表的結(jié)束標(biāo)記,用于遍歷判斷 */pxListEnd = listGET_END_MARKER( pxList ); /* 此處使用 mini list 作為結(jié)束標(biāo)記,節(jié)省內(nèi)存 *//* 暫停任務(wù)調(diào)度,確保下面對(duì)事件組狀態(tài)的修改原子性 */vTaskSuspendAll();{/* 跟蹤記錄事件組設(shè)置位操作(用于調(diào)試或性能分析) */traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );/* 從事件組等待列表的頭部開始遍歷 */pxListItem = listGET_HEAD_ENTRY( pxList );/* 1. 設(shè)置指定的事件位:* 使用按位或操作將 uxBitsToSet 加入當(dāng)前事件組的事件位中 */pxEventBits->uxEventBits |= uxBitsToSet;/* 2. 遍歷等待列表,檢查是否有任務(wù)等待的事件條件已滿足 */while( pxListItem != pxListEnd ){/* 保存下一個(gè)列表項(xiàng),因?yàn)楫?dāng)前列表項(xiàng)可能被移除 */pxNext = listGET_NEXT( pxListItem );/* 取得該等待任務(wù)所要求的事件位和控制信息 */uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );/* 初始化匹配標(biāo)志為未匹配 */xMatchFound = pdFALSE;/* 3. 分離等待的事件位和控制位:* 控制位在 eventEVENT_BITS_CONTROL_BYTES 中,包含等待全部位和退出時(shí)清除位等標(biāo)志。* 剩下的位就是該任務(wù)真正等待的事件位。 */uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;/* 4. 判斷當(dāng)前事件組的事件位是否滿足任務(wù)等待條件:* 如果任務(wù)不要求等待所有位(xWaitForAllBits == pdFALSE),只需有任一位匹配即可。* 如果任務(wù)要求等待所有位(xWaitForAllBits == pdTRUE),則必須所有位都匹配。 */if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 ){/* 如果任一位匹配,則滿足條件 */if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 ){xMatchFound = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor ){/* 如果要求所有位匹配,且當(dāng)前事件位包含所有這些位,則滿足條件 */xMatchFound = pdTRUE;}else{/* 等待條件不滿足:對(duì)于要求所有位匹配的情況,并非所有等待位都已置位 */}/* 5. 如果任務(wù)等待的條件滿足,則進(jìn)行解除阻塞操作 */if( xMatchFound != pdFALSE ){/* 判斷是否在任務(wù)解除阻塞時(shí)需要清除等待的位:* 如果控制位中設(shè)置了 eventCLEAR_EVENTS_ON_EXIT_BIT,則需要在退出時(shí)清除這些位 */if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ){/* 累加這些等待的位到待清除變量中 */uxBitsToClear |= uxBitsWaitedFor;}else{mtCOVERAGE_TEST_MARKER();}/* 將當(dāng)前任務(wù)的等待條件(以及當(dāng)前事件組的事件位狀態(tài))寫入任務(wù)的事件列表項(xiàng)中,* 并通過(guò)設(shè)置 eventUNBLOCKED_DUE_TO_BIT_SET 標(biāo)志,表明該任務(wù)解除阻塞的原因是等待位匹配成功,* 而非超時(shí)。隨后,調(diào)用 vTaskRemoveFromUnorderedEventList 將該任務(wù)從等待列表中移除,* 并將任務(wù)放入就緒或待處理列表中。 */vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );}/* 6. 繼續(xù)遍歷下一個(gè)等待任務(wù)。* 注意:直接使用保存的 pxNext 而不是當(dāng)前 pxListItem->pxNext,* 因?yàn)楫?dāng)前列表項(xiàng)可能已經(jīng)從等待列表中移除。 */pxListItem = pxNext;}/* 7. 在解除阻塞任務(wù)后,如果有任務(wù)要求在退出時(shí)清除它們等待的位,* 則將這些位從事件組中清除。 */pxEventBits->uxEventBits &= ~uxBitsToClear;}/* 恢復(fù)任務(wù)調(diào)度 */( void ) xTaskResumeAll();/* 返回當(dāng)前事件組中的事件位狀態(tài) */return pxEventBits->uxEventBits;
}
可以看出其實(shí)也是差不多的。
5.4 為什么不是關(guān)閉中斷
因?yàn)橹袛嗍遣粫?huì)使用到事件組的,假設(shè)使用事件組,處理事件的時(shí)間是無(wú)法確定的,因?yàn)槭强梢栽O(shè)置多個(gè)事件的,這就會(huì)導(dǎo)致中斷的執(zhí)行程序的時(shí)間無(wú)法確定,違背了freeRTOS中關(guān)于中斷程序的要求:越早結(jié)束越好。
但是實(shí)際上確實(shí)是有這么一個(gè)函數(shù):xEventGroupSetBitsFromISR
,可以在中斷程序中去設(shè)置事件,但是實(shí)際上它其實(shí)并不是在中斷程序中去設(shè)置事件的,而是交由定時(shí)器任務(wù)去設(shè)置的,可以來(lái)看一下這個(gè)函數(shù)的內(nèi)部:
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t * pxHigherPriorityTaskWoken ){BaseType_t xReturn;traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */return xReturn;}
繼續(xù)進(jìn)入xTimerPendFunctionCallFromISR
函數(shù)查看,這里需要注意一下傳入的參數(shù):函數(shù)vEventGroupSetBitsCallback
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend,void * pvParameter1,uint32_t ulParameter2,BaseType_t * pxHigherPriorityTaskWoken ){DaemonTaskMessage_t xMessage;BaseType_t xReturn;/* Complete the message with the function parameters and post it to the* daemon task. */xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR;xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend;xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1;xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2;xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn );return xReturn;}
實(shí)際上就是將帶有vEventGroupSetBitsCallback()
的xMessage
結(jié)構(gòu)體存放進(jìn)定時(shí)器隊(duì)列xTimerQueue
那么既然存放到定時(shí)器任務(wù)的隊(duì)列中,那定時(shí)器也肯定是會(huì)去取出,然后執(zhí)行該vEventGroupSetBitsCallback
事件設(shè)置函數(shù)。
所以說(shuō)實(shí)際上在中斷程序去執(zhí)行這個(gè)設(shè)置事件函數(shù)xEventGroupSetBitsFromISR
,實(shí)際上卻還是在任務(wù)中去操作的
疑問
疑問1
使用事件組,可以適用于哪些場(chǎng)景?
- 某個(gè)事件
- 若干個(gè)事件中的某個(gè)事件
- 若干個(gè)事件中的所有事件
等待的事件中,它們要么是或的關(guān)系,要么是與的關(guān)系。也就是可以等待若干個(gè)事件中的任一個(gè),可以等待若干個(gè)事件中的所有。不能在若于個(gè)事件中指定某些事件。
疑問2
事件組能進(jìn)行數(shù)據(jù)的傳輸或是保存嗎?
- 不行,要想實(shí)現(xiàn)不同任務(wù)之間的數(shù)據(jù)的傳輸或是保存,可以借助隊(duì)列等
疑問3
為什么變量的定義,必須放在函數(shù)開頭那里?不能放在代碼之后?
- Keil默認(rèn)支持的C語(yǔ)言標(biāo)準(zhǔn)是C89,必須這樣做
- 如果是C99的話,變量的定義可以放在任何地方
可以在Keil中指定使用C99標(biāo)準(zhǔn):