参考:
- 【野火】物联网操作系统 LiteOS 开发实战指南
- Huawei LiteOS | 中文网
6. 事件
6.1 基本概念
6.1.1 概念
事件
是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输- 与信号量不同的是:
- 可以实现一对多的同步
- 可以实现多对多的同步
- 事件集合用32位无符号整型变量
uwEventID
来表示,每一位代表一个事件 - 任务通过创建事件控制块来实现对事件的触发和等待操作,任务通过“
逻辑与
”或“逻辑或
”与一个事件或多个事件建立关联,形成一个事件集合
(事件组),事件的”逻辑或
“也称为独立型同步,事件的“逻辑与
”也称为关联型同步
6.1.2 事件特点:
- 事件不与任务关联,事件相互独立,一个32位的变量,用于标识该任务发生的事件类型,其中每一位表示一种事件类型(
0
表示该事件类型未发生,1
表示该事件类型已经发生),一共31种时间类型(第25位保留) - 事件仅用于同步,不提供数据传输功能
- 事件无排他性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次
- 允许多个任务对同一事件进行读写操作
- 支持事件等待超时机制
6.1.3 事件控制块
typedef struct tagEvent
{
UINT32 uwEventID; /**标识发生的事件类型位,事件掩码*/
LOS_DL_LIST stEventList; /**读取事件任务链表*/
} EVENT_CB_S, *PEVENT_CB_S;
-
uwEventID
用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共31种事件类型,第25位系统保留 -
还使用一个链表
stEventList
来记录等待事件的任务,所有在等待此事件的任务均会被挂载在事件阻塞列表stEventList
上
6.1.4 事件读取模式
在读事件(获取事件)时,可以选择读取模式,来选择用户感兴趣的事件,读取模式如下:
- 所有事件,
LOS_WAITMODE_AND
(逻辑与):读取掩码中所有事件类型,只有读取的所有事件类型都发生了,才能读取成功 - 任一事件,
LOS_WAITMODE_OR
(逻辑或):读取掩码中任一事件类型,读取的事件中任意一种事件类型发生了,就可以读取成功了 - 清除事件,
LOS_WAITMODE_CLR
:下面两种方法表示事件读取成功后,对应事件类型位会自动清除LOS_WAITMODE_AND | LOS_WAITMODE_CLR
LOS_WAITMODE_OR| LOS_WAITMODE_CLR
6.2 应用场景
-
适用于事件类型的通信,无数据传输
- 我们可以用事件来做标志位,判断某些事件是否发生了,然后根据结果进行处理
- 某些场合,可能需要多个事件发生了才能进行下一步操作,譬如危险机器的启动,需要检查各项指标,只有指标全部达到了才能启动,但是检查指标的过程需要时间,需要系统事件来做统一的等待
-
相比于裸机编程的全局变量进行事件标志,LiteOS采用的事件通信机制具有下面的优点:
- 允许多个任务对同一事件进行读写操作,保护机制不需要我们关心
- 可以有效的解决中断服务程序和任务之间的同步问题
- 支持事件等待超时机制
- 事件发生可以立即唤醒等待中的任务,而不需要用户轮询查询
-
事件
能够在一定程度上代替信号量
,用于任务与任务,中断与任务间的同步,但与信号量不同的是:- 事件的发送操作是不可累计的,而信号量的释放动作时可以累计的
- 事件接收任务可以等待多种事件,信号量只能识别单一同步动作,而不能同时等待多个时间的同步
- 各个事件可以分别发送或一起发送给事件对象
6.3 事件的运作机制
6.3.1 读事件
- 可以根据入参事件掩码类型
uwEventMask
读取时间的单个或多个事件类型 - 事件读取成功后,如果设置
LOS_WAITMODE_CLR
则会清除已读取到的事件类型,反之需要显示地清除 - 读取事件时,用户可以通过入参选择读取
所有事件
还是任意事件
6.3.2 写事件
- 对指定事件写入指定的事件类型(事件掩码),设置事件集合的某些位为1
- 可以一次同时写多个事件类型,写事件成功会触发任务调度
6.3.3 清除事件
- 根据
入参事件ID
和待清除的事件类型
,对事件对应位进行清0操作
6.3.4 事件唤醒
- 当任务因为等待某个或者多个事件发生而进入阻塞态,当事件发生的时候会被唤醒,如下图所示:
6.4 实际开发
注意:需要包含头文件
“los_event.h
”
6.4.1 功能函数
Huawei LiteOS系统中的事件模块为用户提供下面几个接口。
功能分类 | 接口名 | 描述 |
---|---|---|
事件初始化 | LOS_EventInit | 初始化一个事件控制块 |
读事件 | LOS_EventRead | 读取指定事件类型,超时时间为相对时间:单位为Tick |
写事件 | LOS_EventWrite | 写指定的事件类型 |
清除事件 | LOS_EventClear | 清除指定的事件类型 |
校验事件掩码 | LOS_EventPoll | 根据用户传入的事件值、事件掩码及校验模式,返回用户传入的事件是否符合预期 |
销毁事件 | LOS_EventDestroy | 销毁指定的事件控制块 |
-
事件初始化函数
UINT32 LOS_EventInit(PEVENT_CB_S pstEventCB)// pstEventCB 事件控制块
使用时,需要注意,先创建一个事件控制块,传入的是事件控制块地址
-
事件销毁函数,用于事件只用一次的场合
UINT32 LOS_EventDestory(PEVENT_CB_S pstEventCB)// pstEventCB 事件控制块
使用时,需要注意,先创建一个事件控制块,传入的是事件控制块地址
-
写入指定事件函数,用于写入事件中指定的位,当被置位后,阻塞在该位上的任务将会被解锁,简单来讲:就是设置我们自己定义的事件标志位为1,并且看看有没有任务在等待这个事件,有的话就唤醒它
UINT32 LOS_EventWrite(PEVENT_CB_S pstEventCB, //事件控制块 UINT32 uwEvents) //用户指定的事件标志
一般操作我们都是用宏定义来实现
#define EVENT (0x01 << x)
,<< x
表示写入事件集合的bit x
,然后传入写事件函数中 -
读指定事件函数,读取事件组中的一个或多个事件发生标志, 当要读取的事件标志位没有被置位时任务将进入阻塞等待状态
UINT32 LOS_EventRead(PEVENT_CB_S pstEventCB, // 事件控制块 UINT32 uwEventMask, // 用户定义的事件掩码 UINT32 uwMode, // 事件读取模式 UINT32 uwTimeOut) // 超时时间
简单过程为:
- 调用这个函数接口时,系统首先根据用户指定参数和接收模式来判断任务要等待的事件是否发生
- 如果已经发生,则根据参数
uwMode
来决定是否清除事件的相应标志位,并且返回事件的值,但是这个值不是一个稳定的值,所以在等待到对应事件的时候,还需要我们判断事件是否与任务需要的一致 - 如果事件没有发生,则把任务添加到事件阻塞列表中,把任务感兴趣的时间标志值和等待模式记录下来,直到事件发生或等待时间超时
- 读取模式可以分为三种,见6.1.4,如果没有设置自动清除,则需要我们进行手动清除
-
清除指定事件函数,如果再获取事件的时候没有将对应的标志位清除,那么就需要我们显示地调用清除函数进行清除
UINT32 LOS_EventClear(PEVENT_CB_S pstEventCB, // 事件控制块 UINT32 uwEvents) // 用户指定的事件标志
- 注意:清除的时候,需要对事件进行取反之后再传递进来
6.4.2 开发流程
- 调用事件初始化
LOS_EventInit
接口,初始化事件等待队列 - 写事件
LOS_EventWrite
,配置事件掩码类型 - 读事件
LOS_EventRead
,可以选择读取模式 - 清除事件
LOS_EventClear
,清除指定事件类型
6.4.3 注意事项
- 系统初始化之前不能调用读写事件接口
- 在中断中,可以对事件对象进行写操作,但不能读操作
- 在锁任务调度状态下,禁止任务阻塞与读事件
LOS_EventClear
入参值是:要清除的指定事件类型的反码(~uwEvents
)- 事件掩码的第25位不能使用,原因是为了区别
LOS_EventRead
接口返回的是事件还是错误码