自学内容网 自学内容网

ESP32移植Openharmony设备开发---(5)Event事件

Event事件

官方文档:OpenAtom OpenHarmony

基本概念:

事件(Event)是一种任务间的通信机制,可用于任务间的同步操作。事件的特点是:

任务间的事件同步,可以一对多,也可以多对多。一对多表示一个任务可以等待多个事件,多对多表示多个任务可以等待多个事件。但是一次写事件最多触发一个任务从阻塞中醒来。

事件读超时机制。

只做任务间同步,不传输具体数据。

根据Liteos-m事件的特性,我们可以用事件来做标志位,判断某些事件是否发生了,然后根据结果做处理,作用类似于我们在裸机编写中的标志位,我们通常把全局变量作为标志位,来进行信息的传输。但使用全局变量作为标志位的话,就需要任务中轮询查看事件是否发送,这对CPU资源是一种极大的浪费,因此在操作系统当中使用事件这种通信机制就能解决以上问题。

在一些特定的场合,需要多个事件发生才能进行下一步的操作,比如一些复杂的机器,需要各项指标的检测,当有一个指标不通过便无法启动该机器,但是指标的检测不可能在同一时间全部检测完,检测需要分先后顺序,当一个指标检测通过就发送一个事件,而触发机器启动的条件就是所有指标都通过也就是需要集齐所有的指标发送的事件,那么机器才允许启动。

事件也可在一定程度上替代信号量的工作,用于任务与任务,任务与中断之间的同步,一个任务或中断服务例程发送一个事件给事件对象,而后等待的任务被唤醒并对相应的事件进行处理。与信号量有所区别的是,信号量是一个信号量对应一个任务,而事件可多个事件对应一个任务或多个任务,同时按照任务等待的参数,可选择是“逻辑或”触发还 是“逻辑与”触发。这个特性也是信号量等所不具备的,信号量只能识别单一同步动作, 而不能同时等待多个事件的同步。

事件运作原理

事件初始化:创建一个事件控制块,该控制块维护一个已处理的事件集合,以及等待特定事件的任务链表。

写事件:会向事件控制块写入指定的事件,事件控制块更新事件集合,并遍历任务链表,根据任务等待具体条件满足情况决定是否唤醒相关任务。

读事件:如果读取的事件已存在时,会直接同步返回。其他情况会根据超时时间以及事件触发情况,来决定返回时机:等待的事件条件在超时时间耗尽之前到达,阻塞任务会被直接唤醒,否则超时时间耗尽该任务才会被唤醒。

读事件条件满足与否取决于入参eventMask和mode,eventMask即需要关注的事件类型掩码。mode是具体处理方式,分以下三种情况:

  • LOS_WAITMODE_AND:逻辑与,基于接口传入的事件类型掩码eventMask,只有这些事件都已经发生才能读取成功,否则该任务将阻塞等待或者返回错误码。
  • LOS_WAITMODE_OR:逻辑或,基于接口传入的事件类型掩码eventMask,只要这些事件中有任一种事件发生就可以读取成功,否则该任务将阻塞等待或者返回错误码。
  • LOS_WAITMODE_CLR:这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或 LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。

事件清零:根据指定掩码,去对事件控制块的事件集合进行清零操作。当掩码为0时,表示将事件集合全部清零。当掩码为0xffff时,表示不清除任何事件,保持事件集合原状。

事件销毁:销毁指定的事件控制块。

BUILD.gn

# Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//kernel/liteos_m/liteos.gni")

module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name){
    sources = [
        "os_event_example.c"
    ]

    include_dirs = [
        "//kernel/liteos_m/kernel/include"
    ]
}

LOS_Event_example.c

/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "los_task.h"
#include "los_event.h"
#include "ohos_run.h"

#define FLAGS_MSK1 0x00000001U
#define FLAGS_MSK2 0x00000002U
#define DELAY_100_TICKS 100U
#define TASK_STACK_SIZE 4096
#define TASK_PRIO   5
EVENT_CB_S g_event;

// 发送事件
void OS_Thread_EventSender(UINT32 argument)
{
    (void)argument;
    while (1) {
        if (LOS_EventWrite(&g_event, FLAGS_MSK1) == LOS_OK) {
            printf("write eventFlags %d success.\n", FLAGS_MSK1);
        }
        LOS_TaskDelay(DELAY_100_TICKS);

        if (LOS_EventWrite(&g_event, FLAGS_MSK1 | FLAGS_MSK2) == LOS_OK) {
            printf("write eventFlags %d success.\n", FLAGS_MSK1 | FLAGS_MSK2);
        }

        LOS_TaskDelay(DELAY_100_TICKS);
    }
}

// 接收事件
void OS_Thread_EventReceiverOR(UINT32 argument)
{
    (void)argument;
    UINT32 flags;
    while (1) {
        flags = LOS_EventRead(&g_event, FLAGS_MSK1 | FLAGS_MSK2, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, 0xFFFFFFFFU);
        printf("EventReceiverOR wait event! Receive Flags is %d\n", flags);
    }
}

// 接收事件
void OS_Thread_EventReceiverAND(UINT32 argument)
{
    (void)argument;
    UINT32 flags;
    while (1) {
        flags = LOS_EventRead(&g_event, FLAGS_MSK1 | FLAGS_MSK2, LOS_WAITMODE_AND | LOS_WAITMODE_CLR, 0xFFFFFFFFU);
        printf("EventReceiverAND wait event! Receive Flags is %d\n", flags);
    }
}

// 事件测试Example
static void OS_Event_example(void)
{
    if (LOS_EventInit(&g_event) != LOS_OK) {
        printf("Failed to create EventFlags!\n");
        return;
    }
    LOS_TaskLock();

    TSK_INIT_PARAM_S attr = {0};
    attr.pfnTaskEntry = (TSK_ENTRY_FUNC)OS_Thread_EventSender;
    attr.uwStackSize = TASK_STACK_SIZE;
    attr.usTaskPrio = TASK_PRIO;
    attr.pcName = "Thread_EventSender";
    UINT32 taskIDEventSender;
    if (LOS_TaskCreate(&taskIDEventSender, &attr) != LOS_OK) {
        printf("Failed to create Thread_EventSender!\n");
        return;
    }
    attr.pcName = "Thread_EventReceiverOR";
    attr.pfnTaskEntry = (TSK_ENTRY_FUNC)OS_Thread_EventReceiverOR;
    UINT32 taskIDEventRecvOR;
    if (LOS_TaskCreate(&taskIDEventRecvOR, &attr) != LOS_OK) {
        printf("Failed to create Thread_EventReceiverOR!\n");
        return;
    }
    attr.pcName = "Thread_EventReceiverAND";
    attr.pfnTaskEntry = (TSK_ENTRY_FUNC)OS_Thread_EventReceiverAND;
    UINT32 taskIDEventRecvAND;
    if (LOS_TaskCreate(&taskIDEventRecvAND, &attr) != LOS_OK) {
        printf("Failed to create Thread_EventReceiverAND!\n");
        return;
    }

    LOS_TaskUnlock();
}

OHOS_APP_RUN(OS_Event_example);

代码分析:

创建三个线程OS_Thread_EventSender,OS_Thread_EventReceiverOR,OS_Thread_EventReceiverAND;线程OS_Thread_EventSender通过对g_event_flags_id写入不同的事件,分别实现其于线程OS_Thread_EventReceiverOR和OS_Thread_EventReceiverAND之间的同步:
线程OS_Thread_EventReceiverOR调用读事件进入阻塞状态,直到线程OS_Thread_EventSender写入事件0x00000001U;
线程OS_Thread_EventReceiverAND调用读事件进入阻塞状态,直到线程OS_Thread_EventSender写入事件0x00000001U和0x00000002U;

编译并烧录

修改工作文件夹级的BUILD.gn文件,将编译的文件夹指定为新建的004_LOS_Event

在源码根目录下使用hb工具对写好的代码进行编译

选择mini级系统

同理 产品选择esp公司下的esp32

选择完毕后在源码根目录下执行hb build -f 进行编译

编译完成后会有如下界面,并且编译后的代码固件位于:out\esp32\esp32

验证结果

打开串口工具->选择COM5->打开串口

按下ESP32开发板上的EN键,即可观察到实验现象:

LOS_EventInit()
UINT32 LOS_EventInit(PEVENT_CB_S eventCB);

描述:

创建了一个新的事件标志对象,用于跨线程发送事件,eventCB是指向要初始化的事件控制块的指针。可以在RTOS启动(调用 LOS_Start)之前安全地调用该函数,但不能在内核初始化 (调用 LOS_KernelInit)之前调用该函数。

注意 :不能在中断服务调用该函数

参数:

名字

描述

eventCB

输入/输出,指向要初始化的事件控制块的指针

LOS_EventWrite()
UINT32 LOS_EventWrite(PEVENT_CB_S eventCB, UINT32 events);

描述:
将传入的事件掩码指定的事件events写入eventCB指向的事件控制块。

注意 :不能在中断服务调用该函数

参数:

名字

描述

eventCB

事件控制块, 由调用LOS_EventInit初始化得到

events

事件掩码指定的事件

LOS_EventRead()
UINT32 LOS_EventRead(PEVENT_CB_S eventCB, UINT32 eventMask, UINT32 mode, UINT32 timeOut);

描述:
读事件(等待事件),任务会根据timeOut(单位:tick)进行阻塞等待;
未读取到事件时,返回值为0;
正常读取到事件时,返回正值(事件发生的集合);
其他情况返回特定错误码。

注意 :如果参数timeout设置为0,可以从中断服务例程调用

参数:

名字

描述

eventCB

事件控制块, 由调用LOS_EventInit初始化得到

eventMask

事件类型掩码

mode

指定标记选项LOS_WAITMODE_AND、逻辑与 LOS_WAITMODE_OR、逻辑或 LOS_WAITMODE_CLR,这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用

timeout

超时时间,0表示不超时


原文地址:https://blog.csdn.net/2301_77659368/article/details/142849067

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!