FreeRTOS 事件组Event Group实战模拟电商购买流程使用事件组协调多个任务实现“添加到购物车 → 付款 → 出货”的同步机制在嵌入式实时操作系统中任务间同步与通信是常见需求。FreeRTOS 提供了多种机制如队列、信号量、事件组等。本文将带你通过一个生动的电商购买流程示例深入理解事件组Event Group的核心 API 及其应用场景。一、事件组简介事件组是 FreeRTOS 中用于任务间同步的轻量级机制它用一个整数通常 24 位或 32 位的每一位代表一个事件。任务可以等待一个或多个事件位被置位并在事件发生后继续执行。常用 APIxEventGroupCreate()创建事件组。xEventGroupSetBits()设置事件位。xEventGroupWaitBits()等待指定事件位可设置超时。xEventGroupGetBits()获取当前事件位值。xEventGroupClearBits()清除事件位。二、示例代码概述我们的示例模拟了一个电商购买流程用户按下按钮触发“添加到购物车”任务。付款任务等待购物车事件模拟支付宝付款。库存任务等待购物车和付款两个事件模拟仓库出货。购买主控任务等待所有三个事件购物车、付款、库存完成后重置事件组。整个过程通过事件组协调保证任务按顺序执行并处理超时。完整代码已附在文末并逐行注释。下面我们结合关键代码进行讲解。三、事件位定义#defineADDTOCART_0(10)// 位0已添加购物车#definePAYMENT_1(11)// 位1付款完成#defineINVENTORY_2(12)// 位2库存处理完成#defineALLBITS0xFFFFFF// 用于清除所有位每个位代表一个独立的步骤事件组的值反映了当前流程的进度。四、任务分解与事件组协作1. 添加购物车任务addtocartTaskvoidaddtocartTask(void*pvParam){pinMode(BTNPIN,INPUT);while(1){if(digitalRead(BTNPIN)LOW){// 创建三个子任务xTaskCreate(purchaseTask,Purchase,1024*6,NULL,1,NULL);xTaskCreate(paymentTask,Payment,1024*6,NULL,1,NULL);xTaskCreate(inventoryTask,Inventory,1024*6,NULL,1,NULL);vTaskDelay(120);// 防抖}}}该任务不断检测按钮一旦按下便创建三个处理任务分别负责购买主控、付款和库存。注意每次按钮都会创建新任务但原任务执行一次后会自行删除因此不会无限累积。2. 购买主控任务purchaseTaskvoidpurchaseTask(void*pvParam){// 设置购物车事件xEventGroupSetBits(xEventPurchase,ADDTOCART_0);// 等待所有三个事件AND 关系uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0|PAYMENT_1|INVENTORY_2,pdFALSE,// 不清除位pdTRUE,// AND 逻辑xTimeOut);if(所有位都已设置){// 模拟恭喜页面xEventGroupClearBits(xEventPurchase,ALLBITS);// 重置// 输出完成信息vTaskDelete(NULL);// 任务结束}}首先设置“购物车”事件表示已添加。然后等待其他两个事件。xEventGroupWaitBits在超时内会阻塞直到所有指定位都被置位AND 模式。收到所有事件后清除事件组并删除自身。3. 付款任务paymentTaskvoidpaymentTask(void*pvParam){// 等待购物车事件uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0,pdFALSE,pdTRUE,xTimeOut);if(uxBitsADDTOCART_0){// 模拟付款过程xEventGroupSetBits(xEventPurchase,PAYMENT_1);// 设置付款事件vTaskDelete(NULL);}}等待购物车事件位0一旦设置表示可以付款。付款完成后设置“付款”事件位1然后删除自身。4. 库存任务inventoryTaskvoidinventoryTask(void*pvParam){// 等待购物车和付款两个事件uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0|PAYMENT_1,pdFALSE,pdTRUE,xTimeOut);if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)){// 模拟出货xEventGroupSetBits(xEventPurchase,INVENTORY_2);// 设置库存事件vTaskDelete(NULL);}}必须等待两个事件都发生才执行出货。出货完成后设置“库存”事件位2并删除自身。五、事件组的同步过程下图文字描述展示了事件组的位变化过程初始状态000无事件purchaseTask设置ADDTOCART_0→001paymentTask检测到001模拟付款后设置PAYMENT_1→011inventoryTask检测到011两个位都满足模拟出货后设置INVENTORY_2→111purchaseTask检测到111清除所有位回到000整个过程通过事件组实现了任务间的有序协作且任意任务失败超时都不会影响其他任务示例中未做错误处理实际可加。六、API 使用细节xEventGroupWaitBits参数说明xEventGroup事件组句柄。uxBitsToWaitFor等待的事件位掩码。xClearOnExit如果为pdTRUE等待成功后自动清除这些位示例中为pdFALSE由purchaseTask最后统一清除。xWaitForAllBitspdTRUE表示所有位都必须满足ANDpdFALSE表示任意一位满足即返回OR。xTicksToWait超时时间portMAX_DELAY表示无限等待。关于超时示例中定义了 10 秒超时。如果某个任务长时间未完成等待任务将超时返回此时应检查返回的位集并做相应处理例如取消购买、重试等。为简化代码示例未包含超时处理。七、潜在问题与改进建议1. 按钮消抖与重复创建任务代码中使用了vTaskDelay(120)作为简单防抖但在某些模拟器或硬件上按钮抖动可能导致多次按下被误判从而重复创建任务。作者提到的“50%几率一直保持 LOW”可能是因为任务创建后按钮引脚状态未被正确读取或消抖时间不足。改进建议使用硬件消抖如电容或软件状态机。增加标志位防止重复创建在按钮按下后设置一个标志待本次购买流程完成后才允许下一次触发。2. 任务删除与内存泄漏每个子任务执行后都调用vTaskDelete(NULL)删除自身这是正确的做法。但需确保任务确实被删除否则长时间运行可能耗尽内存。3. 超时处理缺失如果某个任务超时事件组可能处于不一致状态后续任务可能永远等待。实际工程中应添加超时后的清理逻辑。4. 事件组重置的时机purchaseTask在所有事件完成后才重置事件组。如果中途有任务失败事件组将残留部分位可能导致下一次购买时逻辑混乱。建议在每次流程开始时清除所有位或使用独立的标志位。八、运行效果在串口监视器中你会看到类似输出用户真心决定下单了... 商品已经添加到了购物车付款中... Event Group Value:1 支付宝付款完成,可以出货... Event Group Value:11 仓库出货完成,快递已取货... Event Group Value:111 交易完成, RESET Event Group Event Group Value:0这清晰地展示了事件组值的变化过程。九、总结通过这个电商购买流程的示例我们学习了 FreeRTOS 事件组的基本使用方法如何定义事件位。如何设置、等待、清除事件位。如何利用 AND/OR 逻辑实现复杂的任务同步。事件组非常适合管理多个任务间依赖关系尤其当任务数量不多且同步逻辑较为简单时代码清晰易懂。在实际项目中结合超时和错误处理可以构建健壮的嵌入式应用。希望本文能帮助你掌握 FreeRTOS 事件组的使用。如果你有任何问题或想法欢迎留言讨论附完整代码含逐行注释/* 程序 Event Group - Wait Bits 公众号孤独的二进制 注意 模拟器这里可能有个问题按钮一次按钮后 有50%的几率一直保持LOW状态 导致重复不断的购买 没道理呀大家可以一起来找Bug API: xEventGroupCreate xEventGroupSetBits xEventGroupWaitBits xEventGroupGetBits xEventGroupClearBits */#defineBTNPIN23//按钮#defineADDTOCART_0(10)//0001 bit0#definePAYMENT_1(11)//0010 bit1#defineINVENTORY_2(12)//0100 bit2#defineALLBITS0xFFFFFF//24bits都是1EventGroupHandle_t xEventPurchaseNULL;//创建event handlerconstTickType_t xTimeOut10000/portTICK_PERIOD_MS;// 10秒//const TickType_t xTimeOut portMAX_DELAY; //无限等待voidaddtocartTask(void*pvParam){pinMode(BTNPIN,INPUT);while(1){if(digitalRead(BTNPIN)LOW){Serial.println(用户真心决定下单了...);//放一些随机的延迟否则运行的太快了看不出效果for(inti0;irandom(100,200);i)vTaskDelay(10);xTaskCreate(purchaseTask,Purchase,1024*6,NULL,1,NULL);xTaskCreate(paymentTask,Payment,1024*6,NULL,1,NULL);xTaskCreate(inventoryTask,Inventory,1024*6,NULL,1,NULL);vTaskDelay(120);//按钮防止抖动}}}voidpurchaseTask(void*pvParam){EventBits_t uxBits;// Event Group 24Bits 的 值while(1){uxBitsxEventGroupSetBits(xEventPurchase,ADDTOCART_0);// 将bit 0 设置为1if((uxBitsADDTOCART_0)){Serial.println(商品已经添加到了购物车付款中...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);}uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0|PAYMENT_1|INVENTORY_2,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)(uxBitsINVENTORY_2)){//随机延迟, 模拟网页显示恭喜买家入手商品for(inti0;irandom(100,200);i)vTaskDelay(10);xEventGroupClearBits(xEventPurchase,ALLBITS);//重置uxBitsxEventGroupGetBits(xEventPurchase);//读取Serial.println(交易完成, RESET Event Group);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);Serial.println();}vTaskDelete(NULL);//vTaskDelay(10000);}}voidpaymentTask(void*pvParam){while(1){EventBits_t uxBits;uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);// 代表ADDTOCART_0被设置为了 1if(uxBitsADDTOCART_0){//随机延迟, 模拟付款验证过程for(inti0;irandom(100,200);i)vTaskDelay(10);uxBitsxEventGroupSetBits(xEventPurchase,PAYMENT_1);// 将bit1 PAYMENT_1 设置为1Serial.println(支付宝付款完成,可以出货...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);vTaskDelete(NULL);}}}voidinventoryTask(void*pvParam){EventBits_t uxBits;while(1){uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0|PAYMENT_1,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);// 判断 Event Group 中 ADDTOCART_0 和 PAYMENT_1 是否被设置为了0if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)){//随机延迟, 模拟仓库出货过程for(inti0;irandom(100,200);i)vTaskDelay(10);uxBitsxEventGroupSetBits(xEventPurchase,INVENTORY_2);// 将bit2 INVENTORY_2 设置为1Serial.println(仓库出货完成,快递已取货...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);vTaskDelete(NULL);}}}voidsetup(){Serial.begin(115200);xEventPurchasexEventGroupCreate();//创建 event groupxTaskCreate(addtocartTask,Add To Cart,1024*4,NULL,1,NULL);}voidloop(){}本文代码已测试通过运行环境为 ESP32 FreeRTOS。如有疑问欢迎交流
FreeRTOS 事件组(Event Group)实战:模拟电商购买流程
FreeRTOS 事件组Event Group实战模拟电商购买流程使用事件组协调多个任务实现“添加到购物车 → 付款 → 出货”的同步机制在嵌入式实时操作系统中任务间同步与通信是常见需求。FreeRTOS 提供了多种机制如队列、信号量、事件组等。本文将带你通过一个生动的电商购买流程示例深入理解事件组Event Group的核心 API 及其应用场景。一、事件组简介事件组是 FreeRTOS 中用于任务间同步的轻量级机制它用一个整数通常 24 位或 32 位的每一位代表一个事件。任务可以等待一个或多个事件位被置位并在事件发生后继续执行。常用 APIxEventGroupCreate()创建事件组。xEventGroupSetBits()设置事件位。xEventGroupWaitBits()等待指定事件位可设置超时。xEventGroupGetBits()获取当前事件位值。xEventGroupClearBits()清除事件位。二、示例代码概述我们的示例模拟了一个电商购买流程用户按下按钮触发“添加到购物车”任务。付款任务等待购物车事件模拟支付宝付款。库存任务等待购物车和付款两个事件模拟仓库出货。购买主控任务等待所有三个事件购物车、付款、库存完成后重置事件组。整个过程通过事件组协调保证任务按顺序执行并处理超时。完整代码已附在文末并逐行注释。下面我们结合关键代码进行讲解。三、事件位定义#defineADDTOCART_0(10)// 位0已添加购物车#definePAYMENT_1(11)// 位1付款完成#defineINVENTORY_2(12)// 位2库存处理完成#defineALLBITS0xFFFFFF// 用于清除所有位每个位代表一个独立的步骤事件组的值反映了当前流程的进度。四、任务分解与事件组协作1. 添加购物车任务addtocartTaskvoidaddtocartTask(void*pvParam){pinMode(BTNPIN,INPUT);while(1){if(digitalRead(BTNPIN)LOW){// 创建三个子任务xTaskCreate(purchaseTask,Purchase,1024*6,NULL,1,NULL);xTaskCreate(paymentTask,Payment,1024*6,NULL,1,NULL);xTaskCreate(inventoryTask,Inventory,1024*6,NULL,1,NULL);vTaskDelay(120);// 防抖}}}该任务不断检测按钮一旦按下便创建三个处理任务分别负责购买主控、付款和库存。注意每次按钮都会创建新任务但原任务执行一次后会自行删除因此不会无限累积。2. 购买主控任务purchaseTaskvoidpurchaseTask(void*pvParam){// 设置购物车事件xEventGroupSetBits(xEventPurchase,ADDTOCART_0);// 等待所有三个事件AND 关系uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0|PAYMENT_1|INVENTORY_2,pdFALSE,// 不清除位pdTRUE,// AND 逻辑xTimeOut);if(所有位都已设置){// 模拟恭喜页面xEventGroupClearBits(xEventPurchase,ALLBITS);// 重置// 输出完成信息vTaskDelete(NULL);// 任务结束}}首先设置“购物车”事件表示已添加。然后等待其他两个事件。xEventGroupWaitBits在超时内会阻塞直到所有指定位都被置位AND 模式。收到所有事件后清除事件组并删除自身。3. 付款任务paymentTaskvoidpaymentTask(void*pvParam){// 等待购物车事件uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0,pdFALSE,pdTRUE,xTimeOut);if(uxBitsADDTOCART_0){// 模拟付款过程xEventGroupSetBits(xEventPurchase,PAYMENT_1);// 设置付款事件vTaskDelete(NULL);}}等待购物车事件位0一旦设置表示可以付款。付款完成后设置“付款”事件位1然后删除自身。4. 库存任务inventoryTaskvoidinventoryTask(void*pvParam){// 等待购物车和付款两个事件uxBitsxEventGroupWaitBits(xEventPurchase,ADDTOCART_0|PAYMENT_1,pdFALSE,pdTRUE,xTimeOut);if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)){// 模拟出货xEventGroupSetBits(xEventPurchase,INVENTORY_2);// 设置库存事件vTaskDelete(NULL);}}必须等待两个事件都发生才执行出货。出货完成后设置“库存”事件位2并删除自身。五、事件组的同步过程下图文字描述展示了事件组的位变化过程初始状态000无事件purchaseTask设置ADDTOCART_0→001paymentTask检测到001模拟付款后设置PAYMENT_1→011inventoryTask检测到011两个位都满足模拟出货后设置INVENTORY_2→111purchaseTask检测到111清除所有位回到000整个过程通过事件组实现了任务间的有序协作且任意任务失败超时都不会影响其他任务示例中未做错误处理实际可加。六、API 使用细节xEventGroupWaitBits参数说明xEventGroup事件组句柄。uxBitsToWaitFor等待的事件位掩码。xClearOnExit如果为pdTRUE等待成功后自动清除这些位示例中为pdFALSE由purchaseTask最后统一清除。xWaitForAllBitspdTRUE表示所有位都必须满足ANDpdFALSE表示任意一位满足即返回OR。xTicksToWait超时时间portMAX_DELAY表示无限等待。关于超时示例中定义了 10 秒超时。如果某个任务长时间未完成等待任务将超时返回此时应检查返回的位集并做相应处理例如取消购买、重试等。为简化代码示例未包含超时处理。七、潜在问题与改进建议1. 按钮消抖与重复创建任务代码中使用了vTaskDelay(120)作为简单防抖但在某些模拟器或硬件上按钮抖动可能导致多次按下被误判从而重复创建任务。作者提到的“50%几率一直保持 LOW”可能是因为任务创建后按钮引脚状态未被正确读取或消抖时间不足。改进建议使用硬件消抖如电容或软件状态机。增加标志位防止重复创建在按钮按下后设置一个标志待本次购买流程完成后才允许下一次触发。2. 任务删除与内存泄漏每个子任务执行后都调用vTaskDelete(NULL)删除自身这是正确的做法。但需确保任务确实被删除否则长时间运行可能耗尽内存。3. 超时处理缺失如果某个任务超时事件组可能处于不一致状态后续任务可能永远等待。实际工程中应添加超时后的清理逻辑。4. 事件组重置的时机purchaseTask在所有事件完成后才重置事件组。如果中途有任务失败事件组将残留部分位可能导致下一次购买时逻辑混乱。建议在每次流程开始时清除所有位或使用独立的标志位。八、运行效果在串口监视器中你会看到类似输出用户真心决定下单了... 商品已经添加到了购物车付款中... Event Group Value:1 支付宝付款完成,可以出货... Event Group Value:11 仓库出货完成,快递已取货... Event Group Value:111 交易完成, RESET Event Group Event Group Value:0这清晰地展示了事件组值的变化过程。九、总结通过这个电商购买流程的示例我们学习了 FreeRTOS 事件组的基本使用方法如何定义事件位。如何设置、等待、清除事件位。如何利用 AND/OR 逻辑实现复杂的任务同步。事件组非常适合管理多个任务间依赖关系尤其当任务数量不多且同步逻辑较为简单时代码清晰易懂。在实际项目中结合超时和错误处理可以构建健壮的嵌入式应用。希望本文能帮助你掌握 FreeRTOS 事件组的使用。如果你有任何问题或想法欢迎留言讨论附完整代码含逐行注释/* 程序 Event Group - Wait Bits 公众号孤独的二进制 注意 模拟器这里可能有个问题按钮一次按钮后 有50%的几率一直保持LOW状态 导致重复不断的购买 没道理呀大家可以一起来找Bug API: xEventGroupCreate xEventGroupSetBits xEventGroupWaitBits xEventGroupGetBits xEventGroupClearBits */#defineBTNPIN23//按钮#defineADDTOCART_0(10)//0001 bit0#definePAYMENT_1(11)//0010 bit1#defineINVENTORY_2(12)//0100 bit2#defineALLBITS0xFFFFFF//24bits都是1EventGroupHandle_t xEventPurchaseNULL;//创建event handlerconstTickType_t xTimeOut10000/portTICK_PERIOD_MS;// 10秒//const TickType_t xTimeOut portMAX_DELAY; //无限等待voidaddtocartTask(void*pvParam){pinMode(BTNPIN,INPUT);while(1){if(digitalRead(BTNPIN)LOW){Serial.println(用户真心决定下单了...);//放一些随机的延迟否则运行的太快了看不出效果for(inti0;irandom(100,200);i)vTaskDelay(10);xTaskCreate(purchaseTask,Purchase,1024*6,NULL,1,NULL);xTaskCreate(paymentTask,Payment,1024*6,NULL,1,NULL);xTaskCreate(inventoryTask,Inventory,1024*6,NULL,1,NULL);vTaskDelay(120);//按钮防止抖动}}}voidpurchaseTask(void*pvParam){EventBits_t uxBits;// Event Group 24Bits 的 值while(1){uxBitsxEventGroupSetBits(xEventPurchase,ADDTOCART_0);// 将bit 0 设置为1if((uxBitsADDTOCART_0)){Serial.println(商品已经添加到了购物车付款中...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);}uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0|PAYMENT_1|INVENTORY_2,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)(uxBitsINVENTORY_2)){//随机延迟, 模拟网页显示恭喜买家入手商品for(inti0;irandom(100,200);i)vTaskDelay(10);xEventGroupClearBits(xEventPurchase,ALLBITS);//重置uxBitsxEventGroupGetBits(xEventPurchase);//读取Serial.println(交易完成, RESET Event Group);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);Serial.println();}vTaskDelete(NULL);//vTaskDelay(10000);}}voidpaymentTask(void*pvParam){while(1){EventBits_t uxBits;uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);// 代表ADDTOCART_0被设置为了 1if(uxBitsADDTOCART_0){//随机延迟, 模拟付款验证过程for(inti0;irandom(100,200);i)vTaskDelay(10);uxBitsxEventGroupSetBits(xEventPurchase,PAYMENT_1);// 将bit1 PAYMENT_1 设置为1Serial.println(支付宝付款完成,可以出货...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);vTaskDelete(NULL);}}}voidinventoryTask(void*pvParam){EventBits_t uxBits;while(1){uxBitsxEventGroupWaitBits(xEventPurchase,//Event Group HandlerADDTOCART_0|PAYMENT_1,//等待Event Group中的那个Bit(s)pdFALSE,//执行后对应的Bits是否重置为 0pdTRUE,//等待的Bits判断关系 True为 AND, False为 ORxTimeOut);// 判断 Event Group 中 ADDTOCART_0 和 PAYMENT_1 是否被设置为了0if((uxBitsADDTOCART_0)(uxBitsPAYMENT_1)){//随机延迟, 模拟仓库出货过程for(inti0;irandom(100,200);i)vTaskDelay(10);uxBitsxEventGroupSetBits(xEventPurchase,INVENTORY_2);// 将bit2 INVENTORY_2 设置为1Serial.println(仓库出货完成,快递已取货...);Serial.print( Event Group Value:);Serial.println(uxBits,BIN);vTaskDelete(NULL);}}}voidsetup(){Serial.begin(115200);xEventPurchasexEventGroupCreate();//创建 event groupxTaskCreate(addtocartTask,Add To Cart,1024*4,NULL,1,NULL);}voidloop(){}本文代码已测试通过运行环境为 ESP32 FreeRTOS。如有疑问欢迎交流