【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 10 章 事件
本章介绍Hi3861鸿蒙的事件机制实验。
(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 事件基本概念
事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输。一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。事件集合用 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 个事件标志后执行。
更多推荐
所有评论(0)