全部文档
概述 硬件支持 快速开发指南 内核 驱动 通用组件 专业组件 常见问题

事件


简介

事件也是任务间同步的一种机制,如果任务需要等待某种特定的条件才能继续往下执行,就可以用事件实现。和信号量不同的是,事件可以通过“逻辑或”或者“逻辑与”将多个事件关联起来,形成事件集合,任务等待该事件集合满足条件之后,才会被唤醒,“逻辑或”指任务等到集合中的任意一个事件即可被唤醒,而“逻辑与”需要等到集合中的所有事件才会被唤醒;事件发送无法累积,如果多次向任务发送同一事件,且任务还没有清除该事件,则该事件只被视作发送1次,而信号量释放可以累积,每次释放资源数就会加1。

事件是通过一个32位无符号整形数据表示,其中每一个bit代表一个事件,1代表事件发生,0代表事件未发生,如下图


事件实现原理

事件也是基于阻塞队列实现,任务接收事件,但是事件未发生,就会导致任务阻塞,并且任务被放到阻塞队列,当另一个任务发送阻塞任务需要的事件时,阻塞任务被唤醒,并放到就绪队列放到,如下图:

图中(1),任务1先运行

图中(2),任务1接收事件,由于事件未发生,任务阻塞

图中(3),任务1被放到阻塞队列

图中(4),任务2运行

图中(5),任务2发送事件

图中(6),任务1被唤醒,放到就绪队列

图中(7),任务1运行

事件实现中断和任务的同步

事件仅可以用于任务间的同步,还可以用于中断和任务间的同步。例如,某个任务需要等待某个特定的条件,而这个条件需要中断程序触发,就可以采用事件的方法,下图描述了中断和任务通过事件同步的过程:

图中(1),任务运行

图中(2),任务接收事件,由于事件未发生,任务阻塞

图中(3),任务被放到阻塞队列

图中(4),中断程序运行

图中(5),中断发送事件

图中(6),任务被唤醒,放到就绪队列

图中(7),任务运行


重要定义及数据结构

事件宏定义

宏定义了阻塞任务的唤醒顺序和事件选项配置。

#define OS_EVENT_WAKE_TYPE_PRIO     0x55
#define OS_EVENT_WAKE_TYPE_FIFO     0xAA

#define OS_EVENT_OPTION_AND         0x00000001
#define OS_EVENT_OPTION_OR          0x00000002
#define OS_EVENT_OPTION_CLEAR       0x00000004
#define OS_EVENT_OPTION_MASK        0x00000007
事件宏 说明
OS_EVENT_WAKE_TYPE_PRIO 按优先级唤醒
OS_EVENT_WAKE_TYPE_FIFO 按FIFO唤醒
OS_EVENT_OPTION_AND 任务在接收到所有的事件时才会被唤醒
OS_EVENT_OPTION_OR 任务在接收到任一事件就会被唤醒
OS_EVENT_OPTION_CLEAR 任务接收到事件后会清除相应事件
OS_EVENT_OPTION_MASK 选项掩码

事件控制块结构体

struct os_event
{
    os_list_node_t  task_list_head;         /* Block task list head */
    os_list_node_t  resource_node;          /* Node in resource list */

    os_uint32_t     set;                    /* Event set. */
    os_uint8_t      object_inited;          /* If mutex object is inited, value is OS_KOBJ_INITED */
    os_uint8_t      object_alloc_type;      /* Indicates whether memory is allocated dynamically or statically,
                                               value is OS_KOBJ_ALLOC_TYPE_STATIC or OS_KOBJ_ALLOC_TYPE_DYNAMIC */
    os_uint8_t      wake_type;              /* The type to wake up blocking tasks, value is OS_EVENT_WAKE_TYPE_PRIO
                                               or OS_EVENT_WAKE_TYPE_FIFO */
    char            name[OS_NAME_MAX + 1];  /* Mutex name */
};
事件控制块成员变量 说明
task_list_head 任务阻塞队列头,任务监听事件未发生时将其阻塞在该队列上
resource_node 资源管理节点,通过该节点将创建的事件挂载到gs_os_event_resource_list_head上
set 已发生事件集
object_inited 初始化状态,0x55表示已经初始化,0xAA表示已经去初始化,其他值为未初始化
object_alloc_type 事件类型,0为静态事件,1为动态事件
wake_type 阻塞任务唤醒方式,0x55表示按优先级唤醒,0xAA表示按FIFO唤醒。可以通过属性设置接口进行设置
name 事件名字,名字长度不能大于OS_NAME_MAX

API列表

接口 说明
os_event_init 以静态方式初始化事件,事件对象的内存空间由使用者提供
os_event_deinit 去初始化事件,与os_event_init()配合使用
os_event_create 以动态方式创建并初始化事件,事件对象的内存空间采用动态申请的方式获得
os_event_destroy 销毁事件,与os_event_create()配合使用
os_event_send 发送事件
os_event_recv 接收事件,若暂时没有满足条件的事件,且设定了超时时间,则当前任务会阻塞
os_event_clear 清除事件控制块中特定的事件集
os_event_get 查询事件控制块中已发生的事件集
os_event_set_wake_type 设置阻塞任务的唤醒类型

os_event_init

该函数以静态方式初始化事件,事件对象的内存空间由使用者提供,函数原型如下:

os_err_t os_event_init(os_event_t *event, const char *name);
参数 说明
event 事件控制块,由用户提供,并指向对应的事件控制块内存地址
name 事件名字,其最大长度由OS_NAME_MAX 宏指定,多余部分会被自动截掉
返回 说明
OS_EOK 初始化事件成功
OS_EINVAL 无效参数

os_event_deinit

该函数用于去初始化事件,与os_event_init()配合使用,函数原型如下:

os_err_t os_event_deinit(os_event_t *event);
参数 说明
event 事件控制块
返回 说明
OS_EOK 去初始化事件成功

os_event_create

该函数以动态方式创建并初始化事件,事件对象的内存空间采用动态申请的方式获得,函数原型如下:

os_event_t *os_event_create(const char *name);
参数 说明
name 事件名字,其最大长度由OS_NAME_MAX 宏指定,多余部分会被自动截掉
返回 说明
非OS_NULL 事件创建成功
OS_NULL 事件创建失败

os_event_destroy

该函数用于销毁事件,与os_event_create()配合使用,函数原型如下:

os_err_t os_event_destroy(os_event_t *event);
参数 说明
event 事件控制块
返回 说明
OS_EOK 事件销毁成功

os_event_send

该函数用于发送事件,函数原型如下:

os_err_t os_event_send(os_event_t *event, os_uint32_t set);
参数 说明
event 事件控制块
set 待发送的事件集,事件集不为0
返回 说明
OS_EOK 事件发送成功

os_event_recv

该函数用于接收事件,若暂时没有满足条件的事件,且设定了超时时间,则当前任务会阻塞,函数原型如下:

os_err_t os_event_recv(os_event_t  *event,
                       os_uint32_t  interested_set,
                       os_uint32_t  option,
                       os_tick_t    timeout,
                       os_uint32_t *recved_set);
参数 说明
event 事件控制块
interested_set 接收感兴趣事件集合
option 选项,可取值OS_EVENT_OPTION_AND和OS_EVENT_OPTION_OR;若取值OS_EVENT_OPTION_AND,任务在接收到所有的事件时才会被唤醒;若取值OS_EVENT_OPTION_OR,任务在接收到任一事件就会被唤醒;OS_EVENT_OPTION_AND或者OS_EVENT_OPTION_OR可与OS_EVENT_OPTION_CLEAR取“逻辑或”,表明接收到事件后会清除相应事件
timeout 事件暂时获取不到时的等待超时时间。若为OS_NO_WAIT,则等待时间为0;若为OS_WAIT_FOREVER,则永久等待直到获取到事件;若为其它值,则等待timeout时间或者获取到事件为止,并且其他值时timeout必须小于OS_TICK_MAX / 2
recved_set 接收到的事件集合
返回 说明
OS_EOK 事件接收成功
OS_EEMPTY 不等待且未接收到事件
OS_ETIMEOUT 等待超时未接收到事件
OS_EINVAL option参数错误
OS_ERROR 其它错误

os_event_clear

该函数用于清除事件控制块中特定的事件集,函数原型如下:

os_err_t os_event_clear(os_event_t *event, os_uint32_t interested_clear);
参数 说明
event 事件控制块
interested_clear 待清除的事件集
返回 说明
OS_EOK 清除事件集成功

os_event_get

该函数用于查询事件控制块中已发生的事件集,函数原型如下:

os_int32_t os_event_get(os_event_t *event);
参数 说明
event 事件控制块
返回 说明
os_int32_t 已发生的事件集

os_event_set_wake_type

该函数用于设置阻塞任务的唤醒类型,函数原型如下:

os_err_t os_event_set_wake_type(os_event_t *event, os_uint8_t wake_type);
参数 说明
event 事件控制块
wake_type OS_EVENT_WAKE_TYPE_PRIO为设置唤醒阻塞任务的类型为按优先级唤醒(事件创建后默认为使用此方式),OS_EVENT_WAKE_TYPE_FIFO 为设置唤醒阻塞任务的类型为先进先出唤醒
返回 说明
OS_EOK 设置唤醒阻塞任务类型成功
OS_EBUSY 设置唤醒阻塞任务类型失败

配置选项

OneOS在使用事件时提供了功能裁剪的配置,具体配置如下图所示:

配置项 说明
Enable event flag 使能事件功能,如果不使能该功能,事件相关的源代码就不会编译,默认使能

使用示例

静态事件使用示例

本例用静态的方式初始化了一个事件对象,接收事件的任务采用“逻辑或”的方式接收多个事件,只要接收到一个事件,就会被唤醒

#include <oneos_config.h>
#include <dlog.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <os_event.h>

#define TEST_TAG        "TEST"
#define TASK_STACK_SIZE 1024
#define TASK_PRIORITY   15

#define EVENT_FLAG_0 (1 << 0)
#define EVENT_FLAG_1 (1 << 1)
#define EVENT_FLAG_2 (1 << 2)

static os_event_t event_static;

void task_entry(void *para)
{
    os_uint32_t recv_event;

    while (1)
    {
        if (os_event_recv(&event_static, (EVENT_FLAG_0 | EVENT_FLAG_1 | EVENT_FLAG_2),
                          OS_EVENT_OPTION_OR | OS_EVENT_OPTION_CLEAR,
                          OS_WAIT_FOREVER, &recv_event) == OS_EOK)
        {
            LOG_W(TEST_TAG, "task: OR recv event:0x%x", recv_event);
        }
    }
}

void event_static_sample(void)
{
    os_task_t *task = OS_NULL;

    os_event_init(&event_static, "event_static");

    task = os_task_create("task_event",
                           task_entry,
                           OS_NULL,
                           TASK_STACK_SIZE,
                           TASK_PRIORITY);
    if (task)
    {
        os_task_startup(task);
    }

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_0);
    os_event_send(&event_static, EVENT_FLAG_0);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_1);
    os_event_send(&event_static, EVENT_FLAG_1);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_2);
    os_event_send(&event_static, EVENT_FLAG_2);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_0);
    os_event_send(&event_static, EVENT_FLAG_0);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_1);
    os_event_send(&event_static, EVENT_FLAG_1);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_static_sample: send event:0x%x", EVENT_FLAG_2);
    os_event_send(&event_static, EVENT_FLAG_2);
    os_task_msleep(400);
}

SH_CMD_EXPORT(static_event, event_static_sample, "test staitc event");

运行结果如下:

sh>static_event
W/TEST: event_static_sample: send event:0x1
W/TEST: task: OR recv event:0x1
W/TEST: event_static_sample: send event:0x2
W/TEST: task: OR recv event:0x2
W/TEST: event_static_sample: send event:0x4
W/TEST: task: OR recv event:0x4
W/TEST: event_static_sample: send event:0x1
W/TEST: task: OR recv event:0x1
W/TEST: event_static_sample: send event:0x2
W/TEST: task: OR recv event:0x2
W/TEST: event_static_sample: send event:0x4
W/TEST: task: OR recv event:0x4

动态事件使用示例

本例用动态的方式创建并初始化了一个事件对象,接收事件的任务采用“逻辑与”的方式接收多个事件,在多个事件都接收到之后,才会被唤醒

#include <oneos_config.h>
#include <dlog.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <os_event.h>

#define TEST_TAG        "TEST"
#define TASK_STACK_SIZE 1024
#define TASK_PRIORITY   15

#define EVENT_FLAG_0 (1 << 0)
#define EVENT_FLAG_1 (1 << 1)
#define EVENT_FLAG_2 (1 << 2)

static os_event_t *event_dynamic = OS_NULL;

void task_entry(void *para)
{
    os_uint32_t recv_event;

    while (1)
    {
        if (os_event_recv(event_dynamic, (EVENT_FLAG_0 | EVENT_FLAG_1 | EVENT_FLAG_2),
                          OS_EVENT_OPTION_AND | OS_EVENT_OPTION_CLEAR,
                          OS_WAIT_FOREVER, &recv_event) == OS_EOK)
        {
            LOG_W(TEST_TAG, "task: AND recv event:0x%x", recv_event);
        }
    }
}

void event_dynamic_sample(void)
{
    os_task_t *task = OS_NULL;

    event_dynamic = os_event_create("event_dynamic");
    if (!event_dynamic)
    {
        LOG_E(TEST_TAG, "event_dynamic_sample: event create err");
        return;
    }

    task = os_task_create("task_event",
                           task_entry,
                           OS_NULL,
                           TASK_STACK_SIZE,
                           TASK_PRIORITY);

    if (task)
    {
        os_task_startup(task);
    }

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_0);
    os_event_send(event_dynamic, EVENT_FLAG_0);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_1);
    os_event_send(event_dynamic, EVENT_FLAG_1);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_2);
    os_event_send(event_dynamic, EVENT_FLAG_2);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_0);
    os_event_send(event_dynamic, EVENT_FLAG_0);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_1);
    os_event_send(event_dynamic, EVENT_FLAG_1);
    os_task_msleep(400);

    LOG_W(TEST_TAG, "event_dynamic_sample: send event:0x%x", EVENT_FLAG_2);
    os_event_send(event_dynamic, EVENT_FLAG_2);
    os_task_msleep(400);
}

SH_CMD_EXPORT(dynamic_event, event_dynamic_sample, "test dynamic event");

运行结果如下:

sh>dynamic_event
W/TEST: event_dynamic_sample: send event:0x1
W/TEST: event_dynamic_sample: send event:0x2
W/TEST: event_dynamic_sample: send event:0x4
W/TEST: task: AND recv event:0x7
W/TEST: event_dynamic_sample: send event:0x1
W/TEST: event_dynamic_sample: send event:0x2
W/TEST: event_dynamic_sample: send event:0x4
W/TEST: task: AND recv event:0x7

results matching ""

    No results matching ""