FreeRtos开发之事件实现多任务间的同步

1、事件的概念

事件是实现任务与任务或任务与中断间通信的机制,用于同步,无数据传输。只用于通信,数据传输可以使用队列
与信号量不同的是,事件可以实现一对多、多对多的同步,即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
FreeRTOS提供的事件具有如下特点:
① 事件相互独立,一个32位的事件集合(EventBitst类型的变量,实际可用于表示事件的只有低24位)用于标识该任务发生的事件类型其中每一位表示一种事件类型(0表示该事件类型未发生,1表示该事件类型已经发生),一共有 24种事件类型。
②事件仅用于同步,不提供数据传输功能。
③ 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读取),等效于只设置一次。
④允许多个任务对同一事件进行读写操作。
⑤ 支持事件等待超时机制。
在FreeRTOS 事件中,获取每个事件时,用户可以选择感兴趣的事件,并且选择读取事件信息标记。它有3个属性,分别是逻辑与、逻辑或以及是否清除标记。当任务等待事件同步时,可以通过任务感兴趣的事件位和事件信息标记来判断当前接收的事件是否满足要求,如果满足,则说明任务等到对应的事件,系统将唤醒等待的任务;否则,任务会根据用户指定的阻塞超时时间继续等待下去。

2、事件的应用

FrecRTOS的事件用于任务与任务或任务与中断间的同步。为什么不直接用变量呢?那样岂不是更有效率?
若是在裸机编程中,用全局变量是最有效的方法,但是在操作系统中,使用全局变量就要考虑以下问题了:①如何对全局变量进行保护?如何处理多任务同时对它进行访问的情况?②)如何让内核对事件进行有效管理?
如果使用全局变量,就需要在任务中轮询查看事件是否发送,这会造成CPU 资源的浪费,此外,用户还需要自己去实现等待超时机制。
所以,在操作系统中最好还是使用系统提供的通信机制,简单、方便、实用。
在某些场合,可能需要多个事件发生后才能进行下一步操作,比如一些危险机器的启动,需要检查各项指标,当指标不达标时就无法启动。但是检查各个指标时,不会立刻检测完毕,所以需要事件来做统一的等待。当所有的事件都完成了,那么机器才允许启动,这只是事件的应用之一
事件可用于多种场合,能够在一定程度上替代信号量,用于任务与任务间、中断与任务间的同步。一个任务或中断服务例程发送一个事件给事件对象,而后等待的任务被唤醒并对相应的事件进行处理。但是事件与信号量不同的是,事件的发送操作是不可累计的,而信号量的释放动作是可累计的。事件的另外一个特性是,接收任务可等待多种事件,即多个事件对应一个任务或多个任务。同时按照任务等待的参数,可选择是“逻辑或“触发还是"罗辑与"触发。这个特性也是信号量等所不具备的,信号量只能识别单一同步动作,而不能同时等待多个事件的同步。
各个事件可分别发送或一起发送给事件对象,而任务可以等待多个事件,任务仅对感兴趣的事件进行关注。当有它们感兴趣的事件发生并且符合条件时,任务将被唤醒并进行后续的处理动作。

3、FreeRTOS 任务间事件标志组的实现

任务间事件标志组的实现是指各个任务之间使用事件标志组实现任务的同步机制。
下面的框图说明FreeRTOS 事件标志的实现:
在这里插入图片描述
运行条件:
创建 2 个任务:Task1 和 Task2
运行过程描述如下:任务 Task1 运行过程中调用函数 xEventGroupWaitBits,等待事件标志位被设置,任务 Task1 由运行态进入到阻塞态。任务 Task2 设置 Task1 等待的事件标志,任务 Task1 由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态上面就是一个简单的 FreeRTOS 任务间事件标志通信过程。

4、FreeRTOS 中断方式事件标志组的实现

FreeRTOS 中断方式事件标志组的实现是指中断函数和 FreeRTOS 任务之间使用事件标志。
下面的框图说明FreeRTOS 事件标志的实现:
在这里插入图片描述运行条件:
创建一个任务和一个串口接收中断运行过程描述如下:
任务 Task1 运行过程中调用函数 xEventGroupWaitBits,等待事件标志位被设置,任务 Task1 由运行态进入到阻塞态Task1 阻塞的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中设置 Task1等待的事件标志,任务 Task1 由阻塞态进入到就绪态,在调度器的作用下由就绪态又进入到运行态上面就是一个简单的 FreeRTOS 中断方式事件标志通信过程。
实际应用中,中断方式的消息机制要注意以下三个问题:
中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应。
实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效地保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行。中断服务程序中一定要调用专用于中断的事件标志设置函数,即以 FromISR 结尾的函数。

5、事件的API函数

使用事件的典型流程如下

  1. 创建事件组
  2. 置位事件组
  3. 等待事件组
  4. 删除事件组

1、事件组的句柄定义

//定义事件组句柄
EventGroupHandle_t MyEvent0lHandle = NULL;

2、创建事件组
函数原型:

EventGroupHandle txEventGroupCreate( void );

函数描述:
函数 xEventGroupCreate 用于创建事件标志组。
返回值,如果创建成功,此函数返回事件标志组的句柄,如果 FreeRTOSConfig.h 文件中定义的 heap空间不足会返回 NULL
示例:

//定义事件组句柄
EventGroupHandle_t MyEvent0lHandle = NULL;
//创建事件组
MyEvent01Handle = xEventGroupCreate();
if(MyEvent01Handle == NULL)
	printf("创建事件组失败\r\n\r\n");
else
	printf("创建事件组成功\r\n\r\n");

3、删除事件组
函数原型:

vEventGroupDelete(EventGroupHandle_t xEventGroup);

函数描述:
函数 vEventGroupDelete可用于删除事件组。
4、任务内置位事件组
函数原型:

EventBils_t xEvenlGroupSetBits(EventGroupllandle_t xEventGroup,/* 事件标志组句柄 */
					const EventBitst uxBitsToSct):/*件标志位设置*

函数描述:
函数 xEventGroupSetBits 用于设置指定的事件标志位为 1。
第 1 个参数是事件标志组句柄。第 2个参数表示 24 个可设置的事件标志位,EventBits_t是定义的 32 位变量,低 24 位用于事件标志设置。变量 uxBitsToSet 的低 24位的某个位设置为1,那么被设置的事件标志组的相应位就设置为1。变量 uxBitsToSet 设置为0 的位对事件标志相应位没有影响。比如设置变量 uxBitsToSet = 0x0003 就表示将事件标志的位 0和位1设置为 1,其余位没有变化。最低四位为 0011 = 0x3
返回当前的事件标志组数值。
使用这个函数要注意以下问题:
1.使用前一定要保证事件标志组已经通过函数 xEventGroupCreate 创建了
2.此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xEventGroupSetBitsFromISR
3.用户通过参数 uxBitsToSet 设置的标志位并不一定会保留到此函数的返回值中,下面举两种情况:
a.调用此函数的过程中,其它高优先级的任务就绪了,并且也修改了事件标志,此函数返回的事件标志位会发生变化。
b.调用此函数的任务是一个低优先级任务,通过此函数设置了事件标志后,让一个等待此事件标志的高优先级任务就绪了,会立即切换到高优先级任务去执行,相应的事件标志位会被函数xEventGroupWaitBits 清除掉,等从高优先级任务返回到低优先级任务后,函数xEventGroupSetBits 的返回值已经被修改。

使用举例:

#define KEY2_EVENT	(EventBits_t)(0x0001 << 0) //设置事件掩码位0
#define KEY3_EVENT	(EventBits_t)(0x0001 << 8) //设置事件掩码位8
#define KEY4_EVENT	(EventBits_t)(0x0001 << 16) //设置事件掩码位16


//KEY3处理
if(KEY.KeyCode == KEY3)
{
   
   
	//触发按键3事件
	printf("触发按键3事件\r\n");
	xEventGroupSetBits(MyEvent01Handle,KEY3_EVENT);
}

5、中断内置位事件组
函数原型:

BaseType_t xFvenGroupSelBilsFromISR(EventGroupHandle_t xEvenlGroup,/* 事件标志纵句柄 */
				const EventBils_t uxBitsToSet,/*事件标志位设置 */
				BaseType_t *pxHigherPriorilyTaskWoken);/*高优先级任务是否被唤醒的状态保存 */

函数描述:
函数 xEventGroupSetBits 用于设置指定的事件标志位为 1.
◆第1 个参数是事件标志组句柄。
第2个参数表示 24 个可设置的事件标志位,EventBitst是定义的 32 位变量,低,24 位用于事件标志设置。变量 uxBitsToSet 的低 24位的某个位设置为1,那么被设置的事件标志组的相应位就设置为 1。变量 uxBitsToSet 设置为0的位对事件标志相应位没有影响。比如设置变量 uxBitsToSet =0x0003 就表示将事件标志的位 0和位1 设置为 1,其余位没有变化。第3个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 PdTRUE说明有高优先级任务要执行,否则没有。
返回值,如果消息成功发送给守护任务(就是FreeRTOS 的定时器服务任务)返回dPASS,否则返回 DdFAIL,另外守护任务中的消息队列满了也会返回 pdFALL。
使用这个函数要注意以下问题:1.使用前一定要保证事件标志已经通过函数 xEventGroupCreate 创建了。同时要在 FreeRTOSConfig.h文件中使能如下三个宏定义:

#defineiNCLUDE xEventGroupSetBitFromiSR 1
#define configUSE TIMERS 1
#define lNCLUDE xTimerPendFunctionCall 1

2.函数 xEventGroupSetBitsFromlSR 是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xEventGroupSetBits.
3.函数 xEventGroupSetBitsFromISR 对事件标志组的操作是不确定性操作,因为不知道当前有多少个任务在等待此事件标志。而FreeRTOS在中断服务程序中执行,就通过此函数给FreeRTOS 的守护任务(就是不允许在中断服务程序和临界段中执行不确定性操作。为了F护任务中执行事件标志的置位操作。同时也为了不在临界段中执行此不确定操FreeRTOS 的定时器服务任务,内核自动创建的)发送消息,在作,将临界段改成由调度锁来完成。这样不确定性操作在中断服务程序和临界段中执行的问题就都得到解决了,4.由于函数 xEventGroupSetBitsFromlSR 对事件标志的置位操作是在守护任务里面执行的,如果想让置位操作立即生效,即让等此事件标志事件标志组的所有其它任务。的任务能够得到及时执行,需要设置守护任务的优先级高于使用此
使用举例:

void HAL_UART_RxCpltCallback(UART HandleTypeDef *huart
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值