(1)实验平台:

普中Hi3861鸿蒙物联网WIFI套件https://item.taobao.com/item.htm?id=829136021914(2)资料下载:普中科技-各型号产品资料下载链接


        回想一下在裸机编程中是不是经常用到标记变量呢?它用来标志某个事件的发生情况,然后在循环中判断事件是否发生,如果是等待多个事件的话,还会通过多个标记进行判断,如 if((xxx_flag)&&(xxx_flag))。当然,更加有效的用法就使用变量的某些位进行标志,比如 bit0 表示事件0,bit1 表示事件1,当两个事件都发生的时候,就通过判断对应的标记位是否被置1,如:if(0x03==(xxx_flag&0x03))。然而在操作系统中,也是可以使用事件作为同步的处理,并且实现阻塞机制,操作系统中的事件是一种数据结构而非标记变量。本章分为如下几部分内容:

 

10.1 实验介绍

10.1.1 事件介绍

10.1.1.1 事件基本概念

10.1.1.2 事件运行机制

10.1.1.3 事件使用说明

10.1.2 实验目的

10.1.3 常用 API 函数

10.2 硬件设计

10.3 软件设计

10.4 实验现象


10.1 实验介绍

10.1.1 事件介绍

10.1.1.1 事件基本概念

        事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输。一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。事件集合用 32 位无符号整型变量来表示,每一位代表一个事件。事件的第 25 位为保留位,不可以进行位设置。

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

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

        ●事件读超时机制。

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

10.1.1.2 事件运行机制

        任务可以根据事件类型(事件掩码)uwEventMask 读取单个或者多个事件,事件读取成功后,如果读取模式设置为 LOS_WAITMODE_CLR 则会清除已读取到的事件类型,反之不会清除已读到的事件类型。用户可以选择读取事件的模式,读取事件类型中的所有事件或者是任意事件。

        任务/中断可以写入指定的事件类型(事件掩码),设置事件集合的某些位为 1,系统支持写入多个事件类型,写事件成功可能会触发任务调度。

        清除事件时,根据入事件控制块和待清除的事件类型,对事件对应位进行清0 操作。

        事件控制块中有一个 32 位的变量 uwEventID,可以称之为事件集合,它用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0 表示该事件类型未发生、1 表示该事件类型已经发生),一共 31 种事件类型(第25 位保留),如下所示:

        事件唤醒机制,当任务因为等待某个或者多个事件发生而进入阻塞态,当事件发生的时候会被唤醒,其过程如下:

        任务 1 对事件 3 或事件 5 感兴趣(逻辑或 LOS_WAITMODE_OR),当发生其中的某一个事件都会被唤醒,并且执行相应操作。而任务2 对事件3 与事件5 感兴趣(逻辑与 LOS_WAITMODE_AND),当且仅当事件3 与事件5 都发生的时候,任务 2 才会被唤醒,如果只有其中一个事件发生,那么任务2 还是会继续等待事件发生。如果在读事件函数中设置了清除事件位LOS_WAITMODE_CLR,那么当任务 2 被唤醒后,系统会把事件 3 和事件 5 的事件位清零。

10.1.1.3 事件使用说明

        ●进行事件读写操作时,事件的第 25 位为保留位,不可以进行位设置。

        ●对同一事件反复写入,算作一次写入。

        ●执行 LOS_EventRead()时,其中的事件类型掩码等于等待的所有事件类型掩码按位与(LOS_WAITMODE_AND)或是按位或(LOS_WAITMODE_OR)的结果。

10.1.2 实验目的

        创建 2 个任务,任务 1 间隔 1S 设置一个事件标志,共设置3 个事件,任务2等待接收完 3 个事件标志后执行。

10.1.3 常用 API 函数

        (1)osEventFlagsNew

        功能:创建并初始化事件标志对象。

        参数:指向事件标志属性结构体的指针(可选,通常传递NULL 以使用默认属性)。

        返回值:返回事件标志对象的 ID,如果创建失败则返回NULL。

        用途:用于在任务间同步和通信之前,首先创建一个事件标志对象。

        (2)osEventFlagsDelete

        功能:删除事件标志对象。

        参数:事件标志对象的 ID。

        返回值:通常返回状态码,表示操作是否成功。

        用途:在不再需要事件标志对象时,释放其占用的资源。

        (3)osEventFlagsGetName

        功能:获取事件标志对象的名称(如果设置了的话)。

        参数:事件标志对象的 ID。

        返回值:返回事件标志对象的名称,或者在没有设置名称时返回NULL 或空字符串。

        用途:用于调试或日志记录,帮助识别特定的事件标志对象。

        (4)osEventFlagsSet

        功能:设置指定的事件标志。

        参数:事件标志对象的 ID 和要设置的事件标志位(一个或多个)。

        返回值:通常返回状态码或设置前的事件标志状态。

        用途:用于通知其他任务(或线程)某个或某些事件已经发生。

        (5)osEventFlagsClear

        功能:清除指定的事件标志。

        参数:事件标志对象的 ID 和要清除的事件标志位(一个或多个)。

        返回值:通常返回状态码或清除前的事件标志状态。

        用途:用于重置事件标志,以便再次使用。

        (6)osEventFlagsGet

        功能:获取当前的事件标志状态。

        参数:事件标志对象的 ID。

        返回值:返回当前的事件标志状态。

        用途:用于查询当前的事件标志状态,以确定是否有特定的事件已经发生。

        (7)osEventFlagsWait

        功能:等待一个或多个事件标志变为信号状态。

        参数:事件标志对象的 ID、要等待的事件标志位、等待选项(如等待任意/全部标志)和超时时间。

        返回值:返回等待结束时的事件标志状态或超时状态。

        用途:用于阻塞当前任务(或线程),直到指定的事件标志变为信号状态,或者超时发生。

10.2 硬件设计

        本实验为操作系统内核测试,硬件只需 Hi3861,无需其他外围设备。

10.3 软件设计

        将前面章节创建好的工程模板,复制一份,重命名为06_os_event,如下所示:

        (1)修改 demo 文件夹下的 BUILD.gn 文件,如下所示:

        (2)添加工程编译文件路径,如下所示

        (3)修改 template.c 文件,代码如下:

/**
 ****************************************************************************************************
 * @file        template.c
 * @author      普中科技
 * @version     V1.0
 * @date        2024-06-05
 * @brief       LiteOS事件
 * @license     Copyright (c) 2024-2034, 深圳市普中科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:普中-Hi3861
 * 在线视频:https://space.bilibili.com/2146492485
 * 公司网址:www.prechin.cn
 * 购买地址:
 *
 ****************************************************************************************************
 * 实验现象:打开串口助手,任务1间隔1S设置一个事件标志,共设置3个事件,任务2等待接收完3个事件标志后执行
 *
 ****************************************************************************************************
 */

#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"


osThreadId_t Task1_ID; //  任务1 ID
osThreadId_t Task2_ID; //  任务2 ID
osEventFlagsId_t event_ID;  // 事件 ID
uint32_t event1_Flags = 0x00000001U;  // 事件掩码 每一位代表一个事件
uint32_t event2_Flags = 0x00000002U;  // 事件掩码 每一位代表一个事件
uint32_t event3_Flags = 0x00000004U;  // 事件掩码 每一位代表一个事件
#define TASK_STACK_SIZE 1024
#define TASK_DELAY_TIME 1 // s


/**
 * @description: 任务1 用于发送事件
 * @param {*}
 * @return {*}
 */
void Task1(void)
{
    while (1) 
    {
        printf("enter Task 1.......\n");
        osEventFlagsSet(event_ID, event1_Flags);        // 设置事件标记
        printf("send eventFlag1.......\n");
        sleep(TASK_DELAY_TIME); // 1秒
        osEventFlagsSet(event_ID, event2_Flags);        // 设置事件标记
        printf("send eventFlag2.......\n");
        sleep(TASK_DELAY_TIME); // 1秒
        osEventFlagsSet(event_ID, event3_Flags);        // 设置事件标记
        printf("send eventFlag3.......\n");
        sleep(TASK_DELAY_TIME); // 1秒
    }
}
/**
 * @description: 任务2 用于接受事件
 * @param {*}
 * @return {*}
 */
void Task2(void)
{
    uint32_t flags = 0;
    while (1) 
    {
        // 永远等待事件标记触发,当接收到 event1_Flags 和 event2_Flags 和 event3_Flags时才会执行printf函数
        // osFlagsWaitAll :全部事件标志位接收到    osFlagsWaitAny: 任意一个事件标志位接收到
        // 当只有一个事件的时候,事件的类型选择哪个都可以
        flags = osEventFlagsWait(event_ID,  event1_Flags | event2_Flags | event3_Flags, osFlagsWaitAll, osWaitForever);
        printf("receive event is OK\n");        // 事件已经标记
    }
}

/**
 * @description: 初始化并创建任务
 * @param {*}
 * @return {*}
 */
static void template_demo(void)
{
    printf("普中-Hi3861开发板--LiteOS事件\r\n");

    event_ID = osEventFlagsNew(NULL);       // 创建事件
    if (event_ID != NULL) 
    {
        printf("ID = %d, Create event_ID is OK!\n", event_ID);
    }

    osThreadAttr_t taskOptions;
    taskOptions.name = "Task1";              // 任务的名字
    taskOptions.attr_bits = 0;               // 属性位
    taskOptions.cb_mem = NULL;               // 堆空间地址
    taskOptions.cb_size = 0;                 // 堆空间大小
    taskOptions.stack_mem = NULL;            // 栈空间地址
    taskOptions.stack_size = TASK_STACK_SIZE;           // 栈空间大小 单位:字节
    taskOptions.priority = osPriorityNormal; // 任务的优先级

    Task1_ID = osThreadNew((osThreadFunc_t)Task1, NULL, &taskOptions); // 创建任务1
    if (Task1_ID != NULL)
    {
        printf("ID = %d, Create Task1_ID is OK!\n", Task1_ID);
    }

    taskOptions.name = "Task2";                                        // 任务的名字
    taskOptions.priority = osPriorityNormal;                           // 任务的优先级
    Task2_ID = osThreadNew((osThreadFunc_t)Task2, NULL, &taskOptions); // 创建任务2
    if (Task2_ID != NULL) 
    {
        printf("ID = %d, Create Task2_ID is OK!\n", Task2_ID);
    }
}
SYS_RUN(template_demo);

10.4 实验现象

        将程序下载到开发板内(可参考“2.2.5 程序下载运行”章节),打开串口调试助手“\5--开发工具\4-串口调试助手\UartAssist.exe”,波特率设置为115200,实验现象:任务 1 间隔 1S 设置一个事件标志,共设置3 个事件,任务2 等待接收完 3 个事件标志后执行。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐