【FreeRTOS基础入门】事件组与同步点


前言

FreeRTOS是一个广泛应用于嵌入式系统的实时操作系统内核,提供了丰富的功能和组件,以帮助开发者构建可靠的嵌入式应用程序。其中,事件组(Event Groups)是一种重要的机制,用于任务之间的事件通知和同步。本文将重点介绍FreeRTOS中事件组的基础知识,以帮助初学者更好地理解和应用这一功能。

在嵌入式系统中,多个任务可能需要协同工作,彼此之间需要进行事件通知和同步。事件组提供了一种有效的方式来管理和触发事件,从而实现任务之间的同步与通信。通过学习事件组的概念、使用方法以及实际应用场景,开发者可以更好地设计和实现复杂的嵌入式系统。


一、事件组是什么

案例

学校组织秋游,组长在等待:

  • 张三:我到了
  • 李四:我到了
  • 王五:我到了
    组长说:好,大家都到齐了,出发!
    秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的。
    在这个日常生活场景中:
  • 出发:要等待这 3 个人都到齐,他们是"与"的关系
  • 交报告:只需等待这 3 人中的任何一个,他们是"或"的关系
    在 FreeRTOS 中,可以使用事件组(event group)来解决这些问题。

1.1 事件组的概念

在这里插入图片描述

事件组可以简单的认为就是一个整数,你可以去设置整数的某一位为1代表有事件,设置0为无事件。
你可以去等待一个位或者多个位
下面的为事件组结构体:

typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;
	List_t xTasksWaitingForBits;		/*< List of tasks waiting for a bit to be set. */

	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;
	#endif

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
	#endif
} EventGroup_t;

在这里插入图片描述

其中:uxEventBits成员为事件组,xTasksWaitingForBits用来存储等待事件的任务

事件组用一个整数来表示,其中的高 8 位留给内核使用,只能用其他的位来表示事件。
那么这个整数是多少位的?
 如果 configUSE_16_BIT_TICKS 是 1,那么这个整数就是 16 位的,低 8 位
用来表示事件
 如果 configUSE_16_BIT_TICKS 是 0,那么这个整数就是 32 位的,低 24 位
用来表示事件
 configUSE_16_BIT_TICKS 是用来表示 Tick Count 的,怎么会影响事件组?
这只是基于效率来考虑
 如果 configUSE_16_BIT_TICKS 是 1,就表示该处理器使用 16 位更高效,所
以事件组也使用 16 位
 如果 configUSE_16_BIT_TICKS 是 0,就表示该处理器使用 32 位更高效,所
以事件组也使用 32 位

二、事件组的操作

2.1 事件组与其他实现同步与互斥方法的区别

事件组和队列、信号量等不太一样,主要集中在 2 个地方:
 唤醒谁?
 队列、信号量:事件发生时,只会唤醒一个任务
 事件组:事件发生时,会唤醒所有符号条件的任务,简单地说它有"广播"的
作用
 是否清除事件?
 队列、信号量:是消耗型的资源,队列的数据被读走就没了;信号量被获取后就减少了
在这里插入图片描述

 事件组:被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件以上图为列,事件组的常规操作如下:
 先创建事件组
 任务 C、D 等待事件:
 等待什么事件?可以等待某一位、某些位中的任意一个,也可以等待多位。
简单地说就是"或"、"与"的关系。
 得到事件时,要不要清除?可选择清除、不清除。
 任务 A、B 产生事件:设置事件组里的某一位、某些位

2.2 创建事件组

我们可以使用下面这个函数进行动态的创建事件组

EventGroupHandle_t xEventGroupCreate( void );

他返回的是一个事件组的handle

我们也可以加个static表示要创建静态的事件组

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );

你只需要提供事件组的Buffer即可。

2.3 删除事件组

我们可以使用下面这个函数删除一个事件组:

void vEventGroupDelete( EventGroupHandle_t xEventGroup );

参数为要删除事件组的handle

2.4 设置事件

在任务中,你可以使用下面这个函数进行设置某一位:

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
 const EventBits_t uxBitsToSet );

参数二怎么填写?
他的定义是这样的:typedef TickType_t EventBits_t;类型就是uint32_t,如果你设置的是32位的话
我们可以来让他的某一个位为1,代表这个位有事件了
比如我们想让第5位就这样:1<<5,如果想要两个就使用1 << 5 | 1 << 4,使用或运算符

如果你需要在中断中使用,函数名称只需要加个ISR

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet, 
BaseType_t *pxHigherPriorityTaskWoken );

他的参数3用来告诉调用者,在设置事件组位的过程中是否有更高优先级的任务被唤醒了。
因此,当你调用 xEventGroupSetBitsFromISR 函数时,如果有更高优先级的任务被唤醒了,那么 pxHigherPriorityTaskWoken 参数会被设置为非零值;如果没有更高优先级的任务被唤醒,那么 pxHigherPriorityTaskWoken 参数会被设置为零。这样,在 ISR 结束后,FreeRTOS 就知道是否需要进行任务切换。

2.5 等待事件

我们可以使用下面这个函数来等待某个事件:

EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
										const EventBits_t uxBitsToWaitFor,
										const BaseType_t xClearOnExit,
										const BaseType_t xWaitForAllBits,
										const TickType_t xTicksToWait );

参数2意义为等待哪些位。参数3为是否在这个函数结束时清楚等待位。参数4为,如果你的参数2等待了多个位,比如0和5,是否要等待这两个位全部有了事件在唤醒任务,还是任意一个都行,他们可以填pdTRUE或者pdFLASE

2.6 示例代码

#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"

// 定义事件组句柄
EventGroupHandle_t xEventGroup;

// 定义事件组位
#define BIT_0 (1 << 0)
#define BIT_1 (1 << 1)

// 任务1,等待事件组位 BIT_0
void vTask1(void *pvParameters) {
    while (1) {
        // 等待事件组位 BIT_0,设置 clear_on_exit 标志位,表示等待后清除
        EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);
        if (uxBits & BIT_0) {
            // 收到 BIT_0 位的信号
            // 执行相应操作...
        }
    }
}

// 任务2,等待事件组位 BIT_1
void vTask2(void *pvParameters) {
    while (1) {
        // 等待事件组位 BIT_1,设置 clear_on_exit 标志位,表示等待后清除
        EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, BIT_1, pdTRUE, pdFALSE, portMAX_DELAY);
        if (uxBits & BIT_1) {
            // 收到 BIT_1 位的信号
            // 执行相应操作...
        }
    }
}

// 主函数
int main(void) {
    // 创建事件组
    xEventGroup = xEventGroupCreate();

    // 创建任务1,优先级较高
    xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

    // 创建任务2,优先级较低
    xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    // 启动调度器
    vTaskStartScheduler();

    // 如果调度器启动失败,进行错误处理
    for (;;);

    return 0;
}

三、同步点

3.1 同步点是什么

有一个事情需要多个任务协同,比如:
 任务 A:炒菜
 任务 B:买酒
 任务 C:摆台
 A、B、C 做好自己的事后,还要等别人做完;大家一起做完,才可开饭
同步点就是如果你做好了,你需要等待其他和你一起干这个事情的任务,如果没有做完就接着做,直到做完,当需要同步的任务都ok了,就进行下一步的执行,少一个没做完都不行

3.2 设置同步点

我们可以使用下面这个函数设置一个同步点:

EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
									const EventBits_t uxBitsToSet,
									const EventBits_t uxBitsToWaitFor,
									TickType_t xTicksToWait );

参数2为你要设置哪些事件,表示你完成了这个事件
参数3表示你要等待哪些事件完成。
当你设置的参数3事件都ok之后,他就会往下走


总结

事件组作为FreeRTOS的一个重要特性,为嵌入式系统的开发提供了强大的事件管理机制。通过使用事件组,开发者可以轻松地实现任务之间的事件通知和同步,提高系统的灵活性和效率。在实际应用中,合理利用事件组可以简化任务之间的协作逻辑,使系统更加可靠和稳定。希望本文能够为初学者提供一个清晰的入门指南,让大家更好地利用FreeRTOS的事件组功能进行嵌入式开发。

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人才程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值