FreeRTOS学习笔记(八)事件


前言

  本章我们将展开事件的学习,着重比对和信号量的异同及优劣。


一、事件

1.1 事件的引入

  前面我们学习了使用信号量来完成同步,但是使用信号量来同步的话任务只能与单个的事件或任务进行同步。有时候某个任务可能会需要与多个事件或任务进行同步,此时信号量就无能为力了。
  事件是一种实现任务间通信的机制,多用于实现多任务之间的同步,但只能进行事件类型的通信,不能通过数据。它可以实现一对多,多对多的通信,即一个任务可以等待一(多)个事件发生;也可以是一(多)个事件都发生后唤醒任务。事件无排队性,多次向任务设置同一事件等同于只设置一次,且支持等待超时机制
  每个事件在获取的时候,用户可以选择感兴趣的事件,并选择读取事件信息标记,它有三个属性“逻辑与”“逻辑或”以及“是否清楚标记”。当任务等待事件同步时,可以通过任务感兴趣的事件位和信息标记位去判断事件是否符合要求,若满足则任务等待到对应事件去,反之任务会感觉超时时间继续等待。

1.2 事件位和事件组

  在FreeRTOS中,事件位(Event bits)用来表明某个事件是否发生,事件位通常用作事件标志。32位的每一位都可以单独表示一个事件,例如,第1位代表某个特定的事件,第2位代表另一个事件。任务可以通过设置、清除或者等待某个(或多个)特定的事件标志来实现同步。
  事件标志组(Event Groups)是一种轻量级的任务间同步机制,允许多个任务通过事件标志进行通信或协调。事件标志组提供了一种可以通过多个标志位表示任务状态或事件发生情况的方式,任务可以等待一个或多个标志位被设置。它也是一个 32 位的整数,[23:0]位的每一个位可以单独作为一个标志位,每个标志位可以是 0 或 1,分别代表某个事件未发生或已发生。

二、事件的实现

2.1 相关API函数

  FreeRTOS提供了一组API函数来操作事件标志组,主要包括:
xEventGroupCreate( )
  此函数用于创建一个事件标志组,所需要的内存通过动态内存管理方法分配。事件标志组可用的 bit 数和最大延时值portMAX_DELAY取决于event_groups的configUSE_16_BIT_TICKS宏定义 ,具体如下:

  • 1:16 位模式下,事件标志组有8个可用的位[7:0],最大延时为 0xffff,即 65535;
  • 0:32 位模式下,事件标志组有 24 个可用的位[23:0],最大延时为 0xffffffff,即 4,294,967,295。
// 创建事件标志组
EventGroupHandle_t xEventGroupCreate( void );

xEventGroupWaitBits( )
  某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标志),使用函数 xEventGroupWaitBits()可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置 1 或清零)的话任务就会进入阻塞态,直到阻塞时间到达或者所等待的事件位准备好。

// 用于等待特定的事件标志被设置
EventBits_t xEventGroupWaitBits(
  EventGroupHandle_t xEventGroup,
  const EventBits_t uxBitsToWaitFor,
  const BaseType_t xClearOnExit,
  const BaseType_t xWaitForAllBits,
  TickType_t xTicksToWait );
  • 参数说明:
    • xEventGroup:标志组句柄
    • uxBitsToWaitFor:等待的标志位
    • xClearOnExit:是否在退出时清除等待的标志位
    • xWaitForAllBits:等待所有指定的标志位都被设置,还是只等待任意一个标志位被设置
    • xTicksToWait:等待时间

xEventGroupSetBits( )xEventGroupSetBitsFromISR( )
  这两个函数的作用都是设置指定的事件位为 1,但xEventGroupSetBits( )函数只能用在任务中,xEventGroupSetBitsFromISR( )用于中断服务函数,此函数原型如
下:

// 设置指定的标志位,通常用于通知任务某个事件发生
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
  • 参数:
    • xEventGroup: 要操作的事件标志组的句柄
    • uxBitsToClear: 指定要置 1 的事件位,比如要将 bit3 值 1 的话就设置为 0X08,如设置为 0X09 的话就是同时将 bit3 和 bit0 置 1
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, 
	const EventBits_t uxBitsToSet, BaseType_t * pxHigherPriorityTaskWoken );
  • 参数:
    • xEventGroup: 要操作的事件标志组的句柄
    • uxBitsToClear: 指定要置 1 的事件位
    • pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换
  • 返回值:
    • 成功:pdPASS
    • 失败:pdFALSE

  值得注意的是,我们还可以使用类似#define EVENT (0x01<<X)的宏定义进行置位,这种方式与使用 xEventGroupSetBits() 函数是配合使用的。通过宏定义事件位可以方便地指定要在事件标志组中设置的位,当调用 xEventGroupSetBits() 时,可以使用这些宏来明确需要设置哪些事件位。
xEventGroupClearBits( )xEventGroupClearBitsFromISR( )
  将事件标志组中的指定事件位清零,和置1的两个函数一样分别用于任务和中断。

// 清除指定的标志位,常用于复位某个事件状态
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
  • 参数:
    • xEventGroup: 要操作的事件标志组的句柄
    • uxBitsToClear: 要清零的事件位
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet );
  • 参数:
    • xEventGroup: 要操作的事件标志组的句柄
    • uxBitsToClear: 指定要置 1 的事件位
  • 返回值:
    • 成功:pdPASS
    • 失败:pdFALSE

xEventGroupGetBits( )xEventGroupGetBitsFromISR( )
  我们可以通过 FreeRTOS 提供的 API 函数来查询事件标准组值,FreeRTOS 一共提供了两个这样的 API 函数:

// 用于任务获取当前事件标志组的值
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )
  • 参数:
    • xEventGroup: 要获取的事件标志组的句柄
// 用于中断获取当前事件标志组的值
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
  • 参数:
    • xEventGroup: 要获取的事件标志组的句柄。

2.2 实现流程

  1. 宏定义的设置:事件标志组的实现过程中有一些关键的宏定义,这些定义用于配置事件标志组和任务调度相关的操作。
      EventBits_t: 表示事件标志的存储类型,实际上是 TickType_t 类型,由configUSE_16_BIT_TICKS 设置(前面提到过),具体定义如下:
typedef TickType_t EventBits_t;

  xEventGroupSetBits: 用于设置事件标志的宏,用于触发指定的事件位:

// 这里 uxBitsToSet 是需要设置的事件位。
#define xEventGroupSetBits( xEventGroup, uxBitsToSet ) \
    xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, NULL )
  1. 创建事件标志组:创建一个事件标志组使用 xEventGroupCreate() 函数。此函数会在内存中分配一个事件标志组的数据结构,并初始化为全 0 的事件位。
  2. 设置事件位:要设置某些事件位,可以使用 xEventGroupSetBits() 函数。此函数将指定的事件位设置为 1,同时检查是否有任何任务正等待这些位,并唤醒它们。
  3. 等待事件位:任务可以通过 xEventGroupWaitBits() 函数等待某些事件位被设置。该函数会将调用任务置为阻塞状态,直到指定的事件位发生变化。
  4. 清除事件位:利用xEventGroupClearBits() 函数用于手动清除某些事件位。

免责声明:本文参考了网上公开资料,仅用于学习交流,若有错误或侵权请联系笔者。

### FreeRTOS 事件处理机制 FreeRTOS 中的事件主要用于事件类型的通讯,无数据传输。这意味着可以利用事件作为标志位来判断某些事件是否已经发生并据此做出相应的处理[^1]。 #### 使用场景对比分析 在裸机编程环境中,使用全局变量作为标志位是一种常见做法;然而,在引入操作系统的情况下,这种方法存在诸多弊端: - **资源消耗**:任务需不断轮询检查全局变量状态,造成不必要的 CPU 占用。 - **并发冲突**:多任务环境下难以确保对共享资源的安全访问。 - **复杂度增加**:开发者还需自行实现诸如等待超时等功能[^3]。 相比之下,采用 FreeRTOS 提供的事件机制能够简化开发流程,提高程序健壮性和效率。 #### 多重条件下的协调工作 对于需要依赖多项前置条件才能继续执行的情况(例如设备初始化过程中涉及多个参数校验),可以通过组合不同事件的方式轻松达成目标——只有当所有预期中的事件都被触发后才会放行后续动作。 ### 技术细节与实践指南 为了支持更灵活的任务间协作模式,特别是针对那些可能涉及到同多个对象交互的需求,FreeRTOS 引入了「事件标志组」的概念。这一特性允许单个接收方监听来自若干源头的消息集合,并依据预设逻辑作出响应[^4]。 以下是基于 C 语言的一个简易实例展示如何创建和运用此类结构体成员完成基本功能: ```c // 定义必要的头文件包含语句以及配置选项设定 #include "FreeRTOS.h" #include "event_groups.h" EventGroupHandle_t xCreatedEventGroup; void setup_events(void *pvParameters){ // 创建一个新的事件群组 xCreatedEventGroup = xEventGroupCreate(); while(1){ // 模拟外部输入改变导致特定事件被激活的过程... vTaskDelay(pdMS_TO_TICKS(500)); if(/* some condition */){ // 设置某项具体事务对应的标记位 xEventGroupSetBits(xCreatedEventGroup, bit_0); } if(/* another condition */){ // 设定另一项关联事项的状态指示符 xEventGroupSetBits(xCreatedEventGroup, bit_1); } } } void wait_for_events(void *pvParameters){ EventBits_t uxReturn; while (1) { // 阻塞直到指定的一系列事件均已完成为止 uxReturn = xEventGroupWaitBits( xCreatedEventGroup, bit_0 | bit_1, /* 等待这两者 */ pdTRUE, /* 自动清除匹配上的bit*/ pdFALSE, /* 不强制要求全部满足即可返回 */ portMAX_DELAY ); /* 无限期等待 */ if((uxReturn & (bit_0|bit_1)) == (bit_0|bit_1)){ // 当两个事件都已准备好时采取行动 printf("Both events occurred.\n"); // 清除相关联的事件标记以便下次循环正常使用 xEventGroupClearBits(xCreatedEventGroup, bit_0 | bit_1 ); }else{ // 如果有其他未预见的情形发生,则在此处添加额外处理分支 } } } ``` 此代码片段展示了怎样定义、初始化一个 `EventGroup` 类型的对象,并通过调用 API 接口函数对其进行操作以达到跨线程间的高效沟通目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值