关于RTOS的全部真相。 第十七条 事件标志组:简介和基本服务



在前面的一篇文章(第5章)中已经提到了事件标志组。 在Nucleus SE中,它们类似于信号,但更灵活。 它们提供了一种低成本且灵活的方式来在任务之间传输简单消息。


该系列中的先前文章:
第十六条 讯号
第十五条 内存分区:服务和数据结构
第十四条 内存部分:简介和基本服务
第十三条 任务数据结构和不受支持的API调用
第十二条 任务处理服务
第11条 任务:API的配置和介绍
第10条 计划程序:高级功能和上下文保留
第9条。 调度程序:实施
第8条 Nucleus SE:内部设计和部署
第7条 Nucleus SE:简介
第6条。 其他RTOS服务
第5条 任务交互和同步
第4条 任务,上下文切换和中断
第3条 任务与计划
第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。


使用事件标志


在Nucleus SE中,事件标记是在构建阶段定义的。 应用程序中事件标志组的最大数量为16。如果未定义事件标志组,则与事件标志组的数据结构和服务调用相关的代码将不包含在应用程序中。

事件标志组-一组八个位的标志,对其进行访问进行调节,以便多个任务可以安全地使用一个标志。 一个任务可以设置或清除事件标志的任意组合。 另一个任务是随时读取一组标志,它也可以等待特定的标志序列(通过轮询或暂停)。

配置事件标志组


事件标志组数


与大多数Nucleus SE对象一样,事件标志组的配置由nuse_config.h中#define指令指定。 主要参数是NUSE_EVENT_GROUP_NUMBER ,它确定将在应用程序中定义多少组事件标志。 默认情况下,此参数设置为0(即,不使用事件标志组),并且可以具有最大为16的任何值。不正确的值将导致编译错误,该错误将通过检入nuse_config_check.h来生成(由nuse_config.c启用)。 ,这意味着它将使用此模块进行编译),因此,# error指令将起作用。 选择一个非零值将用作事件标志组的主要激活器。 定义数据结构时使用此参数,其大小取决于其值(以下文章中有更多关于此的内容)。 此外,非零值会激活API设置。

激活API调用


Nucleus SE中的每个API函数(实用程序调用) 都由nuse_config.h中#define指令激活。 对于事件标志组,这些标志包括:
NUSE_EVENT_GROUP_SET
NUSE_EVENT_GROUP_RETRIEVE
NUSE_EVENT_GROUP_INFORMATION
NUSE_EVENT_GROUP_COUNT

默认情况下,它们设置为FALSE ,从而禁用每个服务调用并阻止包含实现它们的代码。 要配置事件标志组,您需要选择必要的API调用并将相应的伪指令设置为TRUE

以下是默认nuse_config.h文件的摘录。

 #define NUSE_EVENT_GROUP_NUMBER 0 /* Number of event groups in the system - 0-16 */ #define NUSE_EVENT_GROUP_SET FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_RETRIEVE FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_INFORMATION FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_COUNT FALSE /* Service call enabler */ 

如果应用程序中没有事件标志组,则激活的API函数将导致编译错误(始终启用的NUSE_Event_Group_Count()除外)。 如果您的代码使用尚未激活的API调用,则会发生布局错误,因为实现代码未包含在应用程序中。

事件调用实用程序调用


Nucleus RTOS支持七个实用程序调用,这些调用提供以下功能:

  • 设置事件标志。 Nucleus SE在NUSE_Event_Group_Set()函数中实现。
  • 读取事件标志。 在Nucleus SE中,在NUSE_Event_Group_Retrieve()中实现
  • 提供有关特定事件标志组的信息。 在Nucleus SE中,在NUSE_Event_Group_Information()中实现
  • 返回应用程序中当前配置的事件标志组的数量。 在Nucleus SE中,在NUSE_Event_Group_Count()中实现
  • 向应用程序添加新的事件标志组。 未实施Nucleus SE。
  • 从应用程序中删除一组事件标志。 未实施Nucleus SE。
  • 返回指向应用程序中所有事件标志组的指针。 未实施Nucleus SE。

这些开销调用中的每一个的实现将在下面详细讨论。

值得注意的是,Nucleus RTOS或Nucleus SE中都没有复位功能。 这是有意完成的。 复位功能暗示了标志的特殊状态的普遍性。 对于事件标志组,唯一的“特殊”状态是重置所有标志,这可以使用NUSE_Event_Group_Set()完成

服务呼叫以设置和读取事件标志组


可以对一组事件标志执行的基本操作是设置一个或多个标志的值,以及读取当前标志值。 Nucleus RTOS和Nucleus SE为这些操作提供了四个基本的API调用。

由于事件标志是位,因此最好将它们可视化为二进制数。 由于历史上标准C不支持二进制常数的表示(仅八进制和十六进制),因此Nucleus SE具有有用的头文件nuse_binary.h ,其中包含针对所有256个8位值的#define字符,例如b01010101

设置事件标志


Nucleus RTOS API实用程序的标记调用非常灵活,它允许您使用ANDOR运算来设置和清除标记值。 Nucleus SE提供了类似的功能,但任务暂停是可选的。

调用以在Nucleus RTOS中设置标志
服务电话原型:

状态NU_Set_Events(NU_EVENT_GROUP *组,UNSIGNED event_flags,OPTION操作);

参数:

group-指向一组事件标志的用户提供的控制块的指针;
event_flags-标志组的位掩码的值;
operation-要执行的操作NU_OR (用于设置标志)或NU_AND (用于清除标志)。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_GROUP-指向一组事件标志的无效指针;
NU_INVALID_OPERATION-指定的操作不同于NU_ORNU_AND

调用以在Nucleus SE中设置标志
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

STATUS NUSE_Event_Group_Set(NUSE_EVENT_GROUP组,U8 event_flags,OPTION操作);

参数:

group-设置/清除标志的事件组的索引(ID);
event_flags-一组标志的最大比特值;
operation-要执行的操作NUSE_OR (用于设置标志)或NUSE_AND (用于清除标志)。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_GROUP-一组事件标志的无效索引;
NUSE_INVALID_OPERATION-指定的操作不同于NUSE_ORNUSE_AND

在Nucleus SE中实现事件标志的安装
NUSE_Event_Group_Set() API函数的初始代码是通用的(检查参数后),无论是否激活了对阻塞调用(任务挂起)的API支持。 逻辑很简单:

 NUSE_CS_Enter(); if (operation == NUSE_OR) { NUSE_Event_Group_Data[group] |= event_flags; } else /* NUSE_AND */ { NUSE_Event_Group_Data[group] &= event_flags; } 

event_flags位掩码 (使用ANDOR操作)叠加在所选事件标志组的值上。

剩余的代码仅在激活任务锁定时启用:

 #if NUSE_BLOCKING_ENABLE while (NUSE_Event_Group_Blocking_Count[group] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this event group */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_EVENT_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == group)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Event_Group_Blocking_Count[group]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

如果从该组标志中暂停了任何任务(用于读取),它们将继续执行。 当他们有机会继续执行时(取决于调度程序),他们可以确定是否满足恢复条件(请参阅读取事件标志)。

读取事件标志


Nucleus RTOS API实用程序读取的调用非常灵活,如果无法立即完成操作(例如,如果您尝试读取不代表当前状态的特定事件标志序列),则可以无限期地暂停任务或以特定的超时暂停任务。 Nucleus SE提供了相同的功能,仅任务暂停是可选的,并且未实现超时。

在Nucleus RTOS中挑战标志
服务电话原型:

状态NU_Retrieve_Events(NU_EVENT_GROUP *组,未签名的请求事件,选项操作,未签名*检索的事件,未签名挂起);

参数:

group-指向一组事件标志的用户提供的控制块的指针;
request_events-定义要读取的标志的位掩码;
操作 -有四个操作可用: NU_ANDNU_AND_CONSUMENU_ORNU_OR_CONSUMENU_ANDNU_AND_CONSUME操作指示所有请求的标志都是必需的。 NU_ORNU_OR_CONSUME操作指示一个或多个请求的标志已足够。 成功请求后, CONSUME参数将自动清除现有标志。
resolved_events-读取事件标志的值的存储指针;
暂停 -暂停任务的规范; 可以采用NU_NO_SUSPENDNU_SUSPEND值,或者以系统计时器的滴答度为单位的超时值(从1到4,294,967,293)。

返回值:

NU_SUCCESS-调用成功完成;
NU_NOT_PRESENT-指定的操作没有返回事件(在NU_OR情况下不是单个事件,在NU_AND情况下不是所有事件);
NU_INVALID_GROUP-指向一组事件标志的无效指针;
NU_INVALID_OPERATION-指定的操作不正确;
NU_INVALID_POINTER-指向事件标志存储的空指针(NULL);
NU_INVALID_SUSPEND-尝试从与任务无关的线程中暂停;
NU_TIMEOUT-即使在指定的超时后也未设置所需的事件标志组合;
NU_GROUP_DELETED-任务挂起时删除了事件标志组。

在Nucleus SE中进行标志挑战
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Event_Group_Retrieve(NUSE_EVENT_GROUP组,U8请求的事件,OPTION操作,U8 *检索的事件,U8挂起);

参数:

group-读取的事件标志组的索引(ID);
request_events-定义要读取的标志的位掩码;
操作 -指示所需标志数量的规范: NUSE OR (某些标志)或NUSE AND (所有标志);
resolved_events-指向读取事件标志的实际值的存储的指针(使用NUSE_AND操作这与在request_events参数中传递的相同);
暂停 -暂停任务的规范;它可以采用值NUSE_NO_SUSPENDNUSE_SUSPEND

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_NOT_PRESENT-指定的操作没有返回事件(对于NUSE_OR ,不是单个事件,对于NUSE_AND是不是所有事件);
NUSE_INVALID_GROUP-一组事件标志的无效索引;
NUSE_INVALID_OPERATION-指定的操作不同于NUSE_ORNUSE_AND
NUSE_INVALID_POINTER-指向读取事件标志存储的空指针( NULL );
NUSE_INVALID_SUSPEND-尝试从非任务流暂停或禁用了对阻止API调用的支持。

在Nucleus SE中实现事件标志读取
在条件编译期间,根据是否激活了对阻止(挂起)任务的API调用的支持,选择了API函数代码NUSE_Event_Group_Retrieve()的版本 (检查参数之后)。 让我们分别考虑这两个选项。

如果禁用了锁定,则此API调用的完整代码如下所示:

 temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } 

从指定的事件标志组中选择所需的事件标志。 将该值与所需事件进行比较,同时考虑到AND / OR操作以及返回的结果和所请求标志的立即值。

如果激活了任务锁定,则代码将变得更加复杂:

 do { temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } if (return_value == NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } else { if (suspend == NUSE_SUSPEND) /* block task */ { NUSE_Event_Group_Blocking_Count[group]++; NUSE_Suspend_Task(NUSE_Task_Active, (group << 4) | NUSE_EVENT_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

该代码放置在do ... while循环中 ,该循环suspend参数为NUSE_SUSPEND时起作用

读取请求的事件标志就像调用它们时一样,不会阻塞。 如果读取不成功,并且suspend参数为NUSE_NO_SUSPEND ,则API调用将设置为NUSE_NOT_PRESENT 。 如果suspend参数设置为NUSE_SUSPEND ,任务将暂停。 返回时(任务恢复时),如果返回值为NUSE_SUCCESS ,表明由于已设置或清除了该组中的事件标志而恢复了任务,则循环从头开始,并读取并检查这些标志。 由于没有用于重置事件标志组的API函数,因此这是恢复任务的唯一原因,但是系统上保留NUSE_Task_Blocking_Return []检查过程,以使锁定控制与其他类型的对象兼容。

下面的文章将描述与事件标志组关联的其他API调用,以及它们的数据结构。

关于作者: Colin Walls在电子行业工作了30多年,大部分时间用于固件。 他现在是Mentor Embedded(Mentor Graphics的一个部门)的固件工程师。 Colin Walls经常在会议和研讨会上发表演讲,他撰写了许多技术文章并撰写了两本有关固件的书。 居住在英国。 Colin的专业博客 ,电子邮件:colin_walls@mentor.com。

Source: https://habr.com/ru/post/zh-CN428131/


All Articles