一、概念与应用
1.1概念
事件是实现任务与任务或任务与中断间 通信的机制,用于同步,无数据传输。(注意与二值信号量区分)
与信号量不同的是,事件可以实现一对多、多对多的同步,即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
实际上,事件就是事件组中的最小单元——bit
1.2特点
-
事件仅用于同步,不提供数据传输
-
事件相互独立,一个32位的事件集合(EventBitst类型的变量,实际可用于表示事件的只有低24位)用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生,1表示该事件类型已经发生),一共有 24种事件类型
-
事件的公共性——允许多个任务对同一事件进行读写操作
-
支持事件等待超时机制
-
多次向任务设置同一事件(如果任务还未来得及读取),等效于只设置一次
FreeRTOS 中,获取每个事件时,用户可以选择感兴趣的事件,并且选择读取事件信息标记。它有3个属性,分别是逻辑与、逻辑或以及是否清除标记。当任务等待事件同步时,可以通过任务感兴趣的事件位和事件信息标记来判断当前接收的事件是否满足要求,如果满足,则说明任务等到对应的事件,系统将唤醒等待的任务;否则,任务会根据用户指定的阻塞超时时间继续等待下去
1.3应用
似乎和全局变量很像?而且用全局变量更有效率?
确实,如果是在裸机编程中,用全局变量是最有效的方法,但那是不得已,比如:
①如何对全局变量进行保护?如何处理多任务同时对它进行访问的情况?
②如何让内核对事件进行有效管理?
使用全局变量,不可避免得需要在任务中轮询查看事件是否发送,这会造成CPU 资源的浪费,此外,用户还需要自己去实现等待超时机制。
操作系统给我们提供了丰富的组件,当然要善于运用
某些场合,可能需要多个事件发生后才能进行下一步操作,比如一些危险机器的启动,需要检查各项指标,当指标不达标时就无法启动。但是检查各个指标时,不会立刻检测完毕,所以需要事件来做统一的等待。当所有的事件都完成了,那么机器才允许启动,这只是事件的应用之一
事件可用于多种场合,能够在一定程度上替代信号量,用于任务与任务间、中断与任务间的同步。一个任务或中断服务例程发送一个事件给事件对象,而后等待的任务被唤醒并对相应的事件进行处理。但是事件与信号量不同的是,事件的发送操作是不可累计的,而信号量的释放动作是可累计的。此外,接收任务可等待多种事件,即多个事件对应一个任务或多个任务。同时按照任务等待的参数,可选择是“逻辑或”触发还是“逻辑与”触发。这个特性也是信号量等所不具备的,信号量相当于单一的同步动作,而不能让任务同时等待多个事件的同步。
各个事件可分别发送或一起发送给事件对象,而任务可以等待多个事件,任务仅对感兴趣的事件进行关注。当有它们感兴趣的事件发生并且符合条件时,任务将被唤醒并进行后续的处理动作。
二、事件组API
注意!!STM32cubeMX不提供此接口,需要自行在代码中创建等一系列的操作
2.1创建事件组
#include "FreeRTOS.h"
#include "event_groups.h"
/*创建一个事件组*/
EventGroupHandle_t xEventGroupCreat(void)
return: NULL 创建失败
any other value 创建成功返回的事件组句柄
matters need attention:None
2.2任务中设置bit
#include "FreeRTOS.h"
#include "event_groups.h"
/*任务中 设置事件组的 bit位*/
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
params:xEventGroup 事件组句柄
uxBitsToSet 24bit可设置的事件组 如 0x08 表示第3位有效 0x09表示第3位和第0位有效
return: any value :返回调用此函数后事件组位的值 整个24位的
matters need attention:
用户通过参数 uxBitsToSet 设置的标志位并不一定会保留到此函数的返回值中,以下是两种特殊情况:
a. 调用此函数时,遇到一个更高优先级的任务就绪,同时它也修改了事件标志,导致此函数的修改出现问题
b. 修改是成功了,但触发高优先级任务后被函数xEventGroupWaitBits 清除掉了
2.3中断中设置bit
#include "FreeRTOS.h"
#include "event_groups.h"
/*中断中 设置事件组的 bit位 操作系统中不允许中断中进行上下文切换,在中断中会向守护任务发送一条消息 由守护任务进行调度*/
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet
BaseType_t *pxHigherPriorityTaskWoken)
params:xEventGroup 事件组句柄
uxBitsToSet 24bits 可设置的事件组 如 0x08 表示第3位有效 0x09表示第3位和第0位有效
pxHigherPriorityTaskWoken NULL即可
return: pdPASS 成功
pdFAIL 发送消息到守护任务失败
matters need attention:
在config.h中把三个宏置1
#define INCLUDE_xEventGroupSetBitsFromISR
#define configUSE_TIMERS
#define INCLUDE_xTimerPendFunctionCall(这个就是守护任务,内核自动创建,它收到消息会执行事件的置位操作,同时也为了不在临界段中执行此不确定操作,将临界段改成由调度锁来完成。这样不确定性操作在中断服务程序和临界段中执行的问题就都得到解决了)
守护任务就是针对中断中不允许上下文切换产生的, 并且和软件定时器Timer有关
实际应用中注意:
中断函数执行得越快越好,防止低优先级中断异常得不到响应;
不要在中断中进行消息处理(wait)可以在中断中发送消息通知任务,去任务中进行消息的处理,当然该任务应设置高优先级
2.4任务中获取事件组整体值
#include "FreeRTOS.h"
#include "event_groups.h"
/*任务中 获取24位事件组的值*/
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)
params:xEventGroup 事件组句柄
return: any value :返回调用此函数后整个24位的事件组的值
matters need attention:None
2.5中断中获取事件组整体值
#include "FreeRTOS.h"
#include "event_groups.h"
/*中断中 获取24位事件组的值*/
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup)
params:xEventGroup 事件组句柄
return: any value :返回调用此函数后整个24位的事件组的值
matters need attention:None
方便中断中使用,因为需要向守护任务发送消息
2.6等待bit触发
#include "FreeRTOS.h"
#include "event_groups.h"
/*等待事件组位的触发 感兴趣的那一位*/
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor
const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits
TickType_t xTicksToWait)
params:xEventGroup 事件组句柄
uxBitsToWaitFor 要触发等待的位
xClearOnExit 触发完毕后是否需要清除标志 pdTRUE/pdFALSE //方便其他任务使用就选择不清除
xWaitForAllBits 逻辑与pdTRUE 逻辑或pdFALSE
xTicksToWait 阻塞等待时间
return: any value :已经触发的事件标志位,可用此做事件标志的判断
matters need attention:None
2.7多任务同步(等待bit)
#include "FreeRTOS.h"
#include "event_groups.h"
/*用于同步任务集合(就是多个任务) 其中每个任务必须等待其他任务到达同步点才能继续 顺序同步!!!*/
EventBits_t xEventGroupSync(const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet
const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait)
params: uxEventGroup 事件组句柄
uxBitsToSet 要设置的标志位
uxBitsToWaitFor 要触发等待的位
xTicksToWait 阻塞等待时间
return: any value :已经触发的事件标志位,可用此做事件标志的判断
matters need attention:None
这里的同步指的是顺序同步,即:任务一到达同步点,触发判断任务二是否到达同步点,依次类推,当所有任务都达到同步点