关于RTOS的全部真相。 第22条 邮箱:辅助服务和数据结构



本文将继续回顾从系列“ RTOS的真相”的上一篇文章开始的邮箱。

该系列中的先前文章:

第21条。 邮箱:简介和基本服务
第20条 信号量:辅助服务和数据结构
第十九条 信号灯:简介和基本服务
第十八条 事件标志组:助手服务和数据结构
第十七条 事件标志组:简介和基本服务
第十六条 讯号
第十五条 内存分区:服务和数据结构
第十四条 内存部分:简介和基本服务
第十三条 任务数据结构和不受支持的API调用
第十二条 任务处理服务
第11条 任务:API的配置和介绍
第10条 计划程序:高级功能和上下文保留
第9条。 调度程序:实施
第8条 Nucleus SE:内部设计和部署
第7条 Nucleus SE:简介
第6条。 其他RTOS服务
第5条 任务交互和同步
第4条 任务,上下文切换和中断
第3条 任务与计划
第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。


辅助邮箱服务


Nucleus RTOS有四个API调用,它们提供与邮箱相关的辅助功能:转储邮箱,检索有关邮箱的信息,检索应用程序中邮箱的数量以及检索指向应用程序中所有邮箱的指针。 这些功能的前三个在Nucleus SE中实现。

邮箱重置


此API服务调用将邮箱重置为其初始的未使用状态。 存储在邮箱中的邮件将丢失。 邮箱上挂起的所有任务都将以返回码NUSE_MAILBOX_WAS_RESET恢复。

调用以重置Nucleus RTOS中的邮箱
服务电话原型:
状态NU_Reset_Mailbox(NU_MAILBOX *邮箱);

参数:
邮箱 -指向邮箱控制单元的指针。

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_MAILBOX-无效的邮箱指针。

致电以重置Nucleus SE中的邮箱
该API服务调用支持Nucleus RTOS API的核心功能。

服务电话原型:
状态NUSE_Mailbox_Reset(NUSE_MAILBOX邮箱);

参数:
邮箱 -转储邮箱的索引(ID)。

返回值:
NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_MAILBOX-无效的邮箱索引。

在Nucleus SE中实施邮箱重置
使用条件编译选择NUSE_Mailbox_Reset函数代码的变体(在检查参数之后),具体取决于是否激活了对阻止(暂停)任务的支持。 我们将考虑这两个选项。

如果未激活锁,则此API函数的代码非常简单。 通过将NUSE_Mailbox_Status []参数设置为FALSE,可以将邮箱标记为未使用。

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

while (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this mailbox */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_MAILBOX_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Mailbox_Blocking_Count[mailbox]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

邮箱被重置为“空”状态。

邮箱中的每个挂起任务都被分配为状态“就绪”,返回码为NUSE_MAILBOX_WAS_RESET 。 完成此过程后,如果使用优先级调度程序, 则会进行NUSE_Reschedule()服务调用,因为一个或多个优先级较高的任务可能已准备就绪,并等待执行权限。

检索邮箱信息


该服务呼叫提供了一组邮箱信息。 Nucleus SE中此调用的实现与Nucleus RTOS的不同之处在于它返回的信息较少,因为不支持对象命名和暂停顺序,并且可以禁用任务暂停。

致电Nucleus RTOS邮箱信息
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:
状态NU_Mailbox_Information(NU_MAILBOX *邮箱,CHAR *名称,OPTION *暂挂类型,DATA_ELEMENT * message_present,UNSIGNED *任务等待中,NU_TASK ** first_task);

参数:

邮箱 -指向邮箱控制单元的指针;
name-指向邮箱名称的8个字符的指针。 终止的空字节也包含在该区域中。
suspend_type-指向存储任务挂起类型的变量的指针。 它可以采用值NU_FIFONU_PRIORITY ;
message_present-指向变量的指针,该变量将采用值NU_TRUENU_FALSE ,具体取决于邮箱是否已满;
task_waiting-指向变量的指针,该变量将接收此邮箱中挂起的任务数;
first_task-指向任务指针的指针,该指针将指向第一个已暂停的任务。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_MAILBOX-无效的邮箱指针。

致电Nucleus SE邮箱信息
该API服务调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Mailbox_Information(NUSE_MAILBOX邮箱,U8 * message_present,U8 * task_waiting,NUSE_TASK * first_task);

参数:

邮箱 -有关其请求信息的邮箱的索引;
message_present-指向变量的指针,该变量的值为TRUEFALSE ,具体取决于邮箱是否已满;
task_waiting-指向一个变量的指针,该变量将使用此邮箱上已挂起的任务数(如果禁用了任务挂起,则不会返回任何内容);
first_task-指向NUSE_TASK类型的变量的指针,该变量将获取第一个挂起任务的索引(如果禁用了任务挂起, 则不返回任何内容)。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_MAILBOX-无效的邮箱索引;
NUSE_INVALID_POINTER-一个或多个指针参数不正确。

在Nucleus SE中实现邮箱信息

该API调用的实现非常简单:

 *message_present = NUSE_Mailbox_Status[mailbox]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Mailbox_Blocking_Count[mailbox]; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif return NUSE_SUCCESS; 

该函数返回邮箱的状态。 然后,如果激活了对块任务的服务调用,则将返回挂起任务的数量和第一个任务的索引(否则,这些参数将设置为0)。

获取邮箱数


该实用程序调用返回应用程序中的邮箱数。 在Nucleus RTOS中,其数量可能会随时间变化,并且返回值将显示当前邮箱的数量,而在Nucleus SE中,邮箱的数量是在构建阶段设置的,无法更改。

在Nucleus RTOS中调用邮箱计数器
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:
UNSIGNED NU_Established_Mailboxes(VOID);

参数:
缺席。

返回值:
在应用程序中创建的邮箱数。

在Nucleus SE中调用邮箱计数器
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:
U8 NUSE_Mailbox_Count(无效);

参数:
缺席。

返回值:
应用程序中已配置邮箱的数量。

在Nucleus SE中实现邮箱计数器
此API调用的实现非常简单: 返回 #define NUSE_MAILBOX_NUMBER指令的值。

资料结构


邮箱使用两个或三个数据结构数组(所有这些数组都位于RAM中),与其他Nucleus SE对象一样,它们是一组表,其大小取决于配置的邮箱数量及其参数。

我强烈建议应用程序代码不要直接访问这些数据结构,而是通过提供的API函数引用它们。 这样可以避免与Nucleus SE的未来版本不兼容以及不必要的副作用,并且还简化了将应用程序移植到Nucleus RTOS的过程。 以下是数据结构的详细概述,以更好地了解服务调用代码的工作方式和调试。

RAM数据


该数据具有以下结构:

NUSE_Mailbox_Data []-ADDR类型的数组,每个配置的邮箱都有一个条目,它存储邮箱数据。
NUSE_Mailbox_Status []是一个U8阵列,每个配置的邮箱都有一个条目,它监视邮箱的使用。 非零值( TRUE )表示邮箱已满。
NUSE_Mailbox_Blocking_Count []-U8类型的数组,它包含每个邮箱的阻止任务计数器。 仅当激活了API调用阻止功能时才创建此数组。

Nucleus SE启动时,这些数据结构在NUSE_Init_Mailbox()函数中以零初始化。 这是合乎逻辑的,因为每个邮箱都是空的(未使用)。

以下是nuse_init.c文件中这些数据结构的定义。

 RAM ADDR NUSE_Mailbox_Data[NUSE_MAILBOX_NUMBER]; RAM U8 NUSE_Mailbox_Status[NUSE_MAILBOX_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Mailbox_Blocking_Count[NUSE_MAILBOX_NUMBER]; #endif 

ROM数据


对于邮箱的实现,不使用ROM中的数据。

邮箱的内存量


与所有Nucleus SE内核对象一样,邮箱所需的内存量是事先已知的。

应用程序中所有邮箱的ROM中数据的存储量为0。

可以通过以下方式计算应用程序中所有邮箱(已激活API调用以阻止任务)中RAM中的数据量(以字节为单位):
NUSE_MAILBOX_NUMBER *(sizeof(ADDR)+2)

否则:
NUSE_MAILBOX_NUMBER *(sizeof(ADDR)+1)

未实现的API调用


Nucleus SE中未实现可以在Nucleus RTOS中找到的四个实用程序调用。

创建邮箱


此API服务调用将创建一个邮箱。 Nucleus SE不需要它,因为邮箱是静态创建的。

服务电话原型:
状态NU_Create_Mailbox(NU_MAILBOX *邮箱,CHAR *名称,UNSIGNED OPTIONsuspend_type);

参数:

邮箱 -指向用户提供的邮箱控制单元的指针; 用于管理其他API调用中的邮箱
name-指向邮箱的7个字符的名称,其终止字节为零;
suspend_type-指示在邮箱上挂起任务的原理。 它可以采用值NU_FIFONU_PRIORITY ,分别表示FIFO的原理(先进先出)或任务暂停的优先级。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_MAILBOX-指向邮箱控制单元的空指针( NULL ),或者该指针已在使用中;
NU_INVALID_SUSPEND-无效的suspend_type参数。

删除邮箱


此API服务调用将删除以前创建的邮箱。 Nucleus SE不需要此功能,因为邮箱是静态创建的,无法删除。

服务电话原型:

状态NU_Delete_Mailbox(NU_MAILBOX *邮箱);

参数:

邮箱 -指向邮箱控制单元的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_MAILBOX-无效的邮箱指针。

邮箱指针


该API调用构成了指向系统上所有邮箱的指针的顺序列表。 Nucleus SE不需要它,因为邮箱是由简单索引而不是指针标识的。

服务电话原型:

UNSIGNED NU_Mailbox_Pointers(NU_MAILBOX **指针列表,UNSIGNED maximum_pointers);

参数:

pointer_list-指针数组NU_MAILBOX的指针; 该数组将填充指向系统中创建的邮箱的指针;
maximum_pointers-数组中的最大指针数。

返回值:

数组中NU_MAILBOX指针的数量。

在邮箱中记录一条消息以传递给所有收件人


此服务调用向正在等待来自特定邮箱的消息的所有任务发送消息。 在Nucleus SE中,未实现此实用程序调用,因为这会增加不必要的复杂性。

服务电话原型:

状态NU_Broadcast_To_Mailbox(NU_MAILBOX *邮箱,VOID *消息,未签名挂起);

参数:

邮箱 -指向邮箱控制单元的指针;
message-指向已发送消息的指针;
挂起 -指示是否已挂起呼叫任务(如果邮箱已包含消息); 可以是NU_NO_SUSPENDNU_SUSPEND或超时值。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_MAILBOX-无效的邮箱指针;
NU_INVALID_POINTER-消息的空指针( NULL );
NU_INVALID_SUSPEND-尝试从与任务无关的线程挂起;
NU_MAILBOX_FULL-邮箱已经包含一条消息;
NU_TIMEOUT-超时到期后,邮箱仍然满;
NU_MAILBOX_DELETED-任务挂起时删除了邮箱。
NU_MAILBOX_RESET-挂起任务时重置邮箱。

兼容Nucleus RTOS


与所有其他Nucleus SE对象一样,我的目标是最大程度地提高与Nucleus RTOS的应用程序代码兼容性。 邮箱也不例外,从用户的角度来看,邮箱的实现方式与Nucleus RTOS中的实现方式相同。 还有一定的不兼容性,考虑到这样的事实,因此代码在所需的内存量方面将变得更加易于理解和更有效,因此我认为这是可以接受的。 否则,可以将Nucleus RTOS API调用直接移植到Nucleus SE。

对象标识符


在Nucleus RTOS中,所有对象均由特定类型的数据结构(控制单元)描述。 指向该控制单元的指针用作邮箱的标识符。 我决定在Nucleus SE中,需要一种不同的方法来有效地使用内存:所有内核对象都由RAM和/或ROM中的一组表描述。 这些表的大小由每种类型的已配置对象的数量确定。 特定对象的标识符是此表中的索引。 因此,我将NUSE_MAILBOX定义为与U8等效,此类型的变量(而非指针)用作邮箱的标识符。 如果将代码从Nucleus SE移植到Nucleus RTOS,反之亦然,则这种轻微的不兼容性很容易处理。 通常,除了移动和存储之外,不对对象标识符执行任何操作。

Nucleus RTOS还支持邮箱命名。 这些名称仅用于调试。 我从Nucleus SE中排除了它们以节省内存。

邮件大小和类型


在Nucleus RTOS中,邮箱消息由四个32位字组成。 在Nucleus SE中,我决定将此值减小为ADDR类型的一个变量。 此更改可节省大量内存并减少任务执行时间。 它还建议邮箱的通常用法是将信息从一个任务转发到另一个任务。 在将应用程序移植到Nucleus RTOS时,这种不兼容不会造成大问题。 如果需要其他消息格式,则可以修改Nucleus SE。

未实现的API调用


Nucleus RTOS支持九个邮箱办公室呼叫。 其中有四个未在Nucleus SE中实现。 上面介绍了这些挑战的详细信息以及将其排除在Nucleus SE之外的原因。

下一篇文章将介绍队列。

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

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


All Articles