如何让自己做的博客网站上线,网站商城首页怎么做吸引人,网站建设网络营销平台: 云搜系统,wordpress iframe插件摘要(From AI): 这篇博客详细介绍了 FreeRTOS 中的事件组和任务通知机制#xff0c;讲解了事件组如何通过位操作实现任务间的同步与通信#xff0c;以及任务如何通过通知机制进行阻塞解除和数据传递。博客提供了多个代码示例#xff0c;展示了如何使用事件组和任务通知在多任…摘要(From AI): 这篇博客详细介绍了 FreeRTOS 中的事件组和任务通知机制讲解了事件组如何通过位操作实现任务间的同步与通信以及任务如何通过通知机制进行阻塞解除和数据传递。博客提供了多个代码示例展示了如何使用事件组和任务通知在多任务环境中实现任务同步特别适用于任务间的依赖关系和信号传递
前言本文档是本人在依照B站UPMichael_ee的视频教程进行学习时所做的学习笔记可能存在疏漏和错误如有发现敬请指正。 文章目录 Event GroupEvent Group WaitxEventGroupCreate()xEventGroupSetBits()xEventGroupWaitBits()Example Code:Event Group Synchronization with Multiple Tasks Event Group SyncxEventGroupSync()Example Code:Event Group Synchronization NotificationNotification SyncxTaskNotifyGive()ulTaskNotifyTake()Example Code:Simple Task Notification in FreeRTOS Notification ValuexTaskNotify()xTaskNotifyWait()Example Code:Task Notification with Conditional Actions Based on Values 参考资料 Michael_ee 视频教程 freeRTOS官网 espressif 在线文档 Event Group
事件组是一种同步机制用于任务之间的通信。它们允许任务设置、清除和等待多个事件的组合
每个事件组有多个位任务可以操作这些位来表示不同的状态或事件。
关键功能
位操作事件组可以被看作是一个二进制位的集合任务可以对这些位进行设置、清除和等待同步机制任务可以等待事件组中的某些位变为设定状态例如位为1这样可以使任务在等待某些事件发生时暂停执行直到事件发生多任务通信事件组可以在多个任务之间传递信息
使用场景
在多个任务之间传递控制信号或数据标志实现任务之间的依赖关系如任务A完成某项工作后任务B才可以执行
Event Group Wait
xEventGroupCreate()
创建一个新的事件组,并返回可以引用创建的事件组的句柄
事件组包含的标志位或位的数量依赖于 configUSE_16_BIT_TICKS 配置项 如果 configUSE_16_BIT_TICKS 1则事件组有 8 位标志位 如果 configUSE_16_BIT_TICKS 0则事件组有 24 位标志位 配置文件路径(v5.3.1)idf\v5.3.1\esp-idf\components\freertos\config\include\freertos
#include FreeRTOS.h
#include event_groups.hEventGroupHandle_t xEventGroupCreate( void );返回值
EventGroupHandle_t创建了事件组返回的值是创建的事件组的句柄
NULL无法创建事件组因为可用的 FreeRTOS 堆内存不足
xEventGroupSetBits()
在RTOS事件组中设置位
这个函数不能从中断中调用
#include FreeRTOS.h
#include event_groups.hEventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );参数
xEventGroup需要设置 bit 的事件组
uxBitsToSet一个按位的值表示要在事件组中设置的一个或多个位 通过设置不同的二进制值来指定要等待的位 如果想等待 bit 0 和 bit 2 设置uxBitsToWaitFor 应该是 0x05即 00000101如果想等待 bit 0、bit 1 和 bit 2 设置uxBitsToWaitFor 应该是 0x07即 00000111 可以根据需求组合多个位来构造不同的掩码值
返回值
EventBits_t事件组中各位bits在调用 xEventGroupSetBits() 函数返回时的状态
可能被改变状态的情况 自动清除xClearBitsOnExit 参数 当调用 xEventGroupSetBits() 设置位后可能有任务正在等待这些位通过 xEventGroupWaitBits()如果等待任务设置了 xClearBitsOnExit 参数为 pdTRUE则这些位在任务被唤醒时会自动被清除在 xEventGroupSetBits() 返回时返回值中的位可能已经被清除 高优先级 Task 清除位 如果设置事件位后有更高优先级的任务因这些位的设置从阻塞状态切换为就绪状态Ready并立即执行它可能会修改事件组的值
在 xEventGroupSetBits() 返回时返回值可能反映的是任务执行后事件组的状态而不是立即设置位后的状态
xEventGroupWaitBits()
读取RTOS事件组中的位可选择进入阻塞状态带超时以等待一个位或一组位被设置
这个函数不能从中断中调用
#include FreeRTOS.h
#include event_groups.hEventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait );参数
xEventGroup需要测试查看bit 的事件组
uxBitsToWaitFor一个位运算值用于指定在事件组中要等待的位
不能被设置为0
xClearOnExit设置是否清除事件 pdTRUE 如果设置为 pdTRUE那么在函数返回时事件组中由 uxBitsToWaitFor 指定的那些位会被清除即设置为 0前提是函数返回的原因不是超时这通常用于在检测到某些事件发生后自动清除事件状态避免其他任务误判这些事件仍然有效。 pdFALSE 如果设置为 pdFALSE事件组中的位不会被清除即便函数成功返回。这种方式适用于需要让其他任务也能检测到这些位的场景
xWaitForAllBits决定任务等待位时的逻辑条件是 逻辑 AND等待所有指定的位都被设置还是 逻辑 OR只需等待任意一个指定的位被设置 pdTRUE逻辑 AND 函数会等待事件组中的所有指定位都被设置为 1如果所有位在等待时间内都被设置函数返回如果等待时间到期xTicksToWait 超时函数返回超时结果 pdFALSE逻辑 OR 函数会等待事件组中的任意一个指定位被设置为 1如果任意一位在等待时间内被设置函数立即返回如果等待时间到期且没有任何位被设置函数返回超时结果
xTicksToWait等待一个/全部 bit 的最大时间
返回值
EventBits_t事件组的当前值这个值表示函数返回时事件组中哪些位bits是被设置1的
Example Code:Event Group Synchronization with Multiple Tasks
#include stdio.h
#include inttypes.h
#include sdkconfig.h
#include freertos/FreeRTOS.h
#include freertos/task.h
#include esp_chip_info.h
#include esp_flash.h
#include esp_system.h#include freeRTOS/event_groups.hEventGroupHandle_t eventGroup;#define BIT_0 (1 0)
#define BIT_4 (1 4)void Task1(void *pvParam)
{printf(Task1 is running\n);while (true){printf(Task1 is begin to wait\n);// xEventGroupWaitBits(eventGroup, BIT_0 | BIT_4, pdTRUE, pdFALSE, portMAX_DELAY);// // 检测第一位和第四位是否被设置如果设置则唤醒Task1// // 检测完成后第一位和第四位将被清除// printf(BIT_0 or BIT_4 is set, Task1 is woken up\n);xEventGroupWaitBits(eventGroup, BIT_0 | BIT_4, pdTRUE, pdTRUE, portMAX_DELAY);printf(BIT_0 or BIT_4 is set, Task1 is woken up\n);vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task2(void *pvParam)
{printf(Task2 is running\n);vTaskDelay(pdMS_TO_TICKS(1000));while (true){printf(Task2 is begin to set bit0\n);xEventGroupSetBits(eventGroup, BIT_0);vTaskDelay(pdMS_TO_TICKS(5000));printf(Task2 is begin to set bit4\n);xEventGroupSetBits(eventGroup, BIT_4);vTaskDelay(pdMS_TO_TICKS(5000));}
}void app_main(void)
{eventGroup xEventGroupCreate(); // 创建事件组if (eventGroup NULL){printf(Event group creation failed\n);}else{vTaskSuspendAll();xTaskCreatePinnedToCore(Task1, Task1, 2048, NULL, 1, NULL, 0);xTaskCreatePinnedToCore(Task2, Task2, 2048, NULL, 1, NULL, 0);xTaskResumeAll();}
}
Event Group Sync
wait和sync的不同
wait
等待事件组的 Task设为 waitTask 在进入 wait 状态后等待设置事件组的 Task设为 setTask对事件组进行设置waitTask 在检测到事件组满足要求后继续运行setTask 在调用xEventGroupSetBits()后不阻塞继续运行
sync
setTask 在设置事件组的目标位后进入阻塞状态等待其它 setTask 对事件组进行设置当满足各 setTask 对事件组的要求后所有进入阻塞状态的 setTask 同时进入运行状态
即 setTask 在设置事件组之后也在 wait 事件组
xEventGroupSync()
在事件组中设置位然后等待在同一事件组中设置位的组合
此功能通常用于同步多个任务通常称为任务集合其中每个任务在继续之前必须等待其他任务到达同步点
如果uxBitsToWaitFor参数指定的位被设置或在该时间内被设置则该函数将在其时间到期之前返回这种情况下由uxBitsToWaitFor指定的所有位将在函数返回之前自动清除
这个函数不能从中断中调用
#include FreeRTOS.h
#include event_groups.hEventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait );参数
xEventGroup需要设置 bit 的时间组
uxBitsToSet一个位运算值用于指定在事件组中要设置的位
uxBitsToWaitFor一个位运算值用于指定在事件组中要等待的位
xTicksToWait等待 bits 的最大时间
返回值
EventBits_t表示事件组的状态具体包括以下两种情况 等待的位被设置 如果 xEventGroupSync() 返回是因为所有等待的位被设置则返回值是事件组中这些位在被清除前的状态 超时到期 如果 xEventGroupSync() 返回是因为超时时间到期则可能并非所有等待的位都被设置返回值表示超时时事件组的状态
Example Code:Event Group Synchronization
#include stdio.h
#include inttypes.h
#include sdkconfig.h
#include freertos/FreeRTOS.h
#include freertos/task.h
#include esp_chip_info.h
#include esp_flash.h
#include esp_system.h#include freeRTOS/event_groups.hEventGroupHandle_t eventGroup;#define BIT_0 (1 0)
#define BIT_1 (1 1)
#define BIT_2 (1 2)
#define ALL_SYNC_BITS (BIT_0 | BIT_1 | BIT_2)void Task0(void *pvParam)
{printf(Task0 is running\n);while (true){vTaskDelay(pdMS_TO_TICKS(1000));printf(Task0 set BIT_0\n);xEventGroupSync(eventGroup, BIT_0, ALL_SYNC_BITS, portMAX_DELAY); // 设置 BIT_0进入同步等待printf(Task0 sync\n);vTaskDelay(pdMS_TO_TICKS(5000));}
}void Task1(void *pvParam)
{printf(Task1 is running\n);while (true){vTaskDelay(pdMS_TO_TICKS(3000));printf(Task1 set BIT_1\n);xEventGroupSync(eventGroup, BIT_1, ALL_SYNC_BITS, portMAX_DELAY);printf(Task1 sync\n);vTaskDelay(pdMS_TO_TICKS(5000));}
}void Task2(void *pvParam)
{printf(Task2 is running\n);while (true){vTaskDelay(pdMS_TO_TICKS(6000));printf(Task2 set BIT_2\n);xEventGroupSync(eventGroup, BIT_2, ALL_SYNC_BITS, portMAX_DELAY);printf(Task2 sync\n);vTaskDelay(pdMS_TO_TICKS(5000));}
}void app_main(void)
{eventGroup xEventGroupCreate(); // 创建事件组if (eventGroup NULL){printf(Event group creation failed\n);}else{vTaskSuspendAll();xTaskCreatePinnedToCore(Task0, Task0, 2048, NULL, 1, NULL, 0);xTaskCreatePinnedToCore(Task1, Task1, 2048, NULL, 1, NULL, 0);xTaskCreatePinnedToCore(Task2, Task2, 2048, NULL, 1, NULL, 0);xTaskResumeAll();}
}Notification
每个任务都有一个 32 位的通知值该值在任务创建时初始
任务通知是直接发送给任务的事件它可以解除接收任务的阻塞并可选择更新接收任务的通知值
通知值有两种用法按位、增量
Notification Sync
xTaskNotifyGive()
使目标任务的通知值递增
RTOS 任务通知功能在默认情况下是启用的并且可以从构建中排除每个任务节省8字节通过在 FreeRTOSConfig.h 设置configUSE_TASK_NOTIFICATIONS为0
#include FreeRTOS.h
#include task.hBaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );参数
xTaskToNotify被通知的任务的句柄其通知值递增增量用法
返回值
总是返回pdPASS
ulTaskNotifyTake()
任务可以使用 ulTaskNotifyTake() 来选择性地阻塞等待通知值变为非零在任务的通知值不为零时返回
在退出时可以选择将通知值清零此时通知值类似于二值信号量或将通知值递减此时通知值更像计数信号量
#include FreeRTOS.h
#include task.huint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );参数
xClearCountOnExit pdFALSE 每次成功调用后通知值减 1类似计数信号量的效果 pdTRUE 每次成功调用后通知值重置为 0类似二值信号量的效果
xTicksToWait等待通知的最大时间
返回值
任务的通知值在被递减或清除之前的值
Example Code:Simple Task Notification in FreeRTOS
#include stdio.h
#include inttypes.h
#include sdkconfig.h
#include freertos/FreeRTOS.h
#include freertos/task.h
#include esp_chip_info.h
#include esp_flash.h
#include esp_system.h#include freeRTOS/event_groups.hTaskHandle_t task0Handle NULL;
TaskHandle_t task1Handle NULL;void Task0(void *pvParam)
{printf(Task0 is running\n);while (true){printf(Task0 is waitting for notification\n);ulTaskNotifyTake(pdTRUE, portMAX_DELAY);printf(Task0 got notification\n); // Task0 等待 Task1 的通知vTaskDelay(pdMS_TO_TICKS(1000));}
}void Task1(void *pvParam)
{printf(Task1 is running\n);vTaskDelay(pdMS_TO_TICKS(5000));while (true){printf(Task1 is sending notification\n);xTaskNotifyGive(task0Handle); // Task1 通知 Task0vTaskDelay(pdMS_TO_TICKS(5000));}
}void app_main(void)
{vTaskSuspendAll();xTaskCreatePinnedToCore(Task0, Task0, 2048, NULL, 1, task0Handle, 0);xTaskCreatePinnedToCore(Task1, Task1, 2048, NULL, 1, task1Handle, 0);xTaskResumeAll();
}
Notification Value
xTaskNotify()
用于直接向任务发送事件并解除阻塞并可选地以以下方式之一更新接收任务的通知值
将一个 32 位的数字写入通知值增加一个增量通知值设置一个或多个通知值保持通知值不变
#include FreeRTOS.h
#include task.hBaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );参数
xTaskToNotify被通知的 Task 句柄
ulValue用于更新被通知任务的通知值如何解释 ulValue 取决于 eAction 参数的值
eAction
eNoAction任务被通知但通知值不变eSetBits任务的通知值与 ulValue 进行按位或(or) 操作eIncrement任务的通知值加 1eSetValueWithOverwrite任务的通知值被无条件设置为 ulValue即使之前已经有通知eSetValueWithoutOverwrite如果任务已经有通知待处理则通知值不会被改变xTaskNotify() 将返回 pdFAIL如果任务没有待处理的通知则其通知值会被设置为 ulValue
返回值
在除eSetValueWithoutOverwrite所有其他情况下返回 pdPASS
xTaskNotifyWait()
如果接收任务已经被阻塞并等待通知当一个通知到达时接收任务将从阻塞状态移除并清除通知
#include FreeRTOS.h
#include task.hBaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );参数
ulBitsToClearOnEntry
在调用 xTaskNotifyWait() 时通知值中的某些位会在函数进入时被清除如果 ulBitsToClearOnEntry 设置为 0x01则任务通知值中的第 0 位会在函数进入时被清除如果设置为 0xffffffffULONG_MAX则通知值的所有位都会被清除相当于将通知值重置为 0注意仅当调用时没有挂起的通知时清除操作才会执行
ulBitsToClearOnExit
在接收到通知后在函数退出前通知值中的某些位会被清除如果设置为 0xffffffffULONG_MAX则通知值的所有位都会被清除
pulNotificationValue
用于将任务的通知值传递给调用者保存的是 在清除ulBitsToClearOnExit的位之前的通知值如果不需要获取通知值可以将其设置为 NULL
xTicksToWait最大等待时间
返回值
pdTRUE接收到了通知或在调用时通知已挂起pdFALSE在等待超时时间内没有接收到通知
Example Code:Task Notification with Conditional Actions Based on Values
#include stdio.h
#include inttypes.h
#include sdkconfig.h
#include freertos/FreeRTOS.h
#include freertos/task.h
#include esp_chip_info.h
#include esp_flash.h
#include esp_system.h#include freeRTOS/event_groups.hTaskHandle_t task0Handle NULL;
TaskHandle_t task1Handle NULL;void Task0(void *pvParam)
{printf(Task0 is running\n);uint32_t notifiedValue 0;while (true){xTaskNotifyWait(0x00, 0xffffffff, notifiedValue, portMAX_DELAY);if (notifiedValue 0x00000001) // 当接收到的通知值为 0x01 时执行相应操作{printf(Task0 get notification: bit_0\n);}else if (notifiedValue 0x00000002){printf(Task0 get notification: bit_1\n);}else if (notifiedValue 0x00000004){printf(Task0 get notification: bit_2\n);}else{printf(Task0 get notification: unknown\n);}vTaskDelay(pdMS_TO_TICKS(3000));}
}void Task1(void *pvParam)
{printf(Task1 is running\n);vTaskDelay(5000 / portTICK_PERIOD_MS);while (true){printf(Task1 is sending notification\n);xTaskNotify(task0Handle, 0x01, eSetValueWithOverwrite); // 发送 bit_0覆盖之前的值vTaskDelay(5000 / portTICK_PERIOD_MS);xTaskNotify(task0Handle, 0x02, eSetValueWithOverwrite);vTaskDelay(5000 / portTICK_PERIOD_MS);xTaskNotify(task0Handle, 0x03, eSetValueWithOverwrite);vTaskDelay(5000 / portTICK_PERIOD_MS);xTaskNotify(task0Handle, 0x04, eSetValueWithOverwrite);vTaskDelay(5000 / portTICK_PERIOD_MS);}
}void app_main(void)
{vTaskSuspendAll();xTaskCreatePinnedToCore(Task0, Task0, 2048, NULL, 1, task0Handle, 0);xTaskCreatePinnedToCore(Task1, Task1, 2048, NULL, 1, task1Handle, 0);xTaskResumeAll();
}