关于RTOS的全部真相。 第26条。 渠道:辅助服务和数据结构



在本文中,我们将继续考虑数据传输通道。

渠道支持服务


Nucleus RTOS具有四个API调用,这些API提供与通道相关的辅助功能:重置通道,获取通道信息,获取应用程序中通道的数量以及获取指向应用程序中所有通道的指针。 前三个功能在Nucleus SE中实现。

该系列中的先前文章:

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

频道重设


此API调用将通道重置为其原始的未使用状态。 存储在其中的所有消息都将丢失。 通道上暂停的所有任务都将使用返回码NUSE_PIPE_WAS_RESET恢复。

Nucleus RTOS中的频道重置调用

服务电话原型:

状态NU_Reset_Pipe(NU_PIPE *管道);

参数:

pipe-指向用户定义的通道控制块的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_PIPE-无效的通道指针。

Nucleus SE中的通道重置挑战

该API服务调用支持Nucleus RTOS API的核心功能。

服务电话原型:

STATUS NUSE_Pipe_Reset(NUSE_PIPE管道);

参数:

pipe删除的管道的索引(ID)。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_PIPE-无效的频道索引。

Nucleus SE中的通道重置实现

NUSE_Pipe_Reset()函数的代码(检查参数之后)非常简单。 通道开始和结束索引以及通道中的消息计数器都设置为0。

如果激活了任务锁定,则其他代码负责恢复挂起的任务:

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

通道上挂起的每个任务都被分配为状态“就绪”,返回码为NUSE_PIPE_WAS_RESET 。 完成此过程后,如果使用优先级调度程序,则会调用NUSE_Reschedule()函数,因为可以准备执行一个或多个具有高优先级的任务。

频道资讯


该服务呼叫返回频道信息。 Nucleus SE中此调用的实现与Nucleus RTOS的不同之处在于它返回的信息较少。 这是因为Nucleus SE不支持对象命名,可变长度消息和任务暂停顺序,并且可以禁用任务暂停。

在Nucleus RTOS中调用频道信息
服务电话原型:

状态NU_Pipe_Information(NU_PIPE *管道,CHAR *名称,VOID **起始地址,UNSIGNED * pipe_size,UNSIGNED *可用,UNSIGNED *消息,OPTION * message_type,UNSIGNED * message_size,OPTION *悬浮类型,UNSIGNED * task_waask **;第一个

参数:

pipe-指向用户提供的通道控制块的指针;
name-指向通道消息名称的8个字符区域的指针;
start_address-指向一个指针的指针,将在该指针中写入通道数据区域的起始地址;
pipe_size-指向变量的指针,该变量用于存储通道中的字节总数;
available-指向变量的指针,用于存储通道中的可用字节数;
messages-指向变量的指针,该变量用于存储通道中的消息数;
message_type-指向变量的指针,该变量用于存储通道支持的消息类型。 它可以采用值NU_FIXED_SIZENU_VARIABLE_SIZE
message_size-指向变量的指针,该变量用于存储每个通道消息中的字节数。 如果通道支持可变长度的消息,则此数字为最大消息大小;
pause_type-指向用于存储挂起任务类型的变量的指针。 它可以采用值NU_FIFONU_PRIORITY ;
task_waiting-指向变量的指针,该变量用于存储在此通道上暂停的任务数;
first_task-指向第一个已暂停任务的指针的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_PIPE-无效的通道指针。

在Nucleus SE中呼叫频道信息
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Pipe_Information(NUSE_PIPE管道,ADDR *起始地址,U8 * pipe_size,U8 *可用,U8 *消息,U8 * message_size,U8 * task_waiting,NUSE_TASK * first_task);

参数:

pipe-通道的索引,有关其的信息被请求;
start_address-指向类型为ADDR的变量的指针,用于存储通道数据区开始的地址;
pipe_size-指向类型为U8的变量的指针,用于存储通道可以接收的消息总数;
可用 -指向类型为U8的变量的指针,用于存储通道中剩余可用空间的消息数;
messages-指向类型U8的变量的指针,用于存储通道中当前的消息数;
message_size-指向类型为U8的变量的指针,用于存储此通道处理的消息的大小;
task_waiting-指向一个变量的指针,该变量用于存储在此通道上暂停的任务数(如果禁用了任务暂停, 则不返回任何内容);
first_task-指向NUSE_TASK类型的变量的指针,该变量将获取第一个挂起任务的索引(如果禁用任务挂起, 则不返回任何内容)。

返回值:

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

在Nucleus SE中实现频道信息

实现此API调用非常简单:

 *start_address = NUSE_Pipe_Data[pipe]; *pipe_size = NUSE_Pipe_Size[pipe]; *available = NUSE_Pipe_Size[pipe] - NUSE_Pipe_Items[pipe]; *messages = NUSE_Pipe_Items[pipe]; *message_size = NUSE_Pipe_Message_Size[pipe]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Pipe_Blocking_Count[pipe]; if (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif 

该函数返回通道的状态。 然后,如果激活了任务锁,则将返回未决任务的数量和第一个任务的索引(否则,这两个参数将设置为0)。

获取频道数


该服务调用返回在应用程序中配置的通道数。 在Nucleus RTOS中,该值可能会随时间变化,并且返回值将指示当前通道数。 在Nucleus SE中,返回值是在构建阶段设置的,无法更改。

在Nucleus RTOS中调用通道计数器
服务电话原型:

UNSIGNED NU_Established_Pipes(VOID);

参数:
缺席。

返回值:
在系统中创建的通道数。

在Nucleus SE中呼叫频道计数器
该实用程序调用支持Nucleus RTOS API的核心功能

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

参数:
缺席。

返回值:
在应用程序中配置的通道数。

在Nucleus SE中实现通道计数器
此API调用的实现非常简单: 返回 #define NUSE_PIPE_NUMBER符号的值。

资料结构


通道使用六个或七个数据结构(位于RAM或ROM中),这些数据结构(与其他Nucleus SE对象一样)是一组表,表的大小和数量与已配置通道的数量及其参数相对应。

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

RAM中的内核数据


该数据具有以下结构:

NUSE_Pipe_Head []U8类型的指针数组,每个配置的通道都有一个条目,并指示消息通道的开始。 用作NUSE_Pipe_Data []中的地址索引(请参见下文)。
NUSE_Pipe_Tail []是一个U8阵列,每个配置的通道都有一个条目,并指向消息通道的末尾。 用作NUSE_Pipe_Data []中的地址索引(请参见下文)。
NUSE_Pipe_Items []是类型为U8的数组,每个配置的通道都有一个条目,并且是该通道中当前消息数的计数器。 该数据是多余的,因为可以通过通道的起始索引和结束索引获得该值,但是计数器的存在简化了代码。
NUSE_Pipe_Blocking_Count [] -此类型为U8的数组包含每个通道上已阻止任务数的计数器。 仅当激活了任务锁支持时才创建此数组。

当Nucleus SE启动时,所有这些数据结构都由NUSE_Init_Pipe()函数以零初始化。 这是合乎逻辑的,因为所有通道都创建为空(未使用)。 以下文章之一将完整描述Nucleus SE的启动过程。

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

 RAM U8 NUSE_Pipe_Head[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Tail[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Items[NUSE_PIPE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Pipe_Blocking_Count[NUSE_PIPE_NUMBER]; #endif 

RAM用户数据


用户有责任在RAM中提供一个数据区域来存储每个已组态通道的数据。 此区域的大小应包含U8类型的数组,所有通道消息都将适合其中。

ROM数据


该数据的结构如下:

NUSE_Pipe_Data []ADDR类型的数组,每个配置的通道都有一条记录,并指示每个通道的数据区域(请参见上面的“ RAM中的用户数据”部分)。
NUSE_Pipe_Size []是类型为U8的数组,每个配置的通道都有一个条目,并显示每个通道可以容纳的消息数。
NUSE_Pipe_Message_Size []U8类型的数组,每个配置的通道都有一个记录,并显示可以放置在每个通道上的消息大小(以字节为单位)。

这些数据结构在nuse_config.c文件中声明和初始化(静态),因此:

 ROM ADDR *NUSE_Pipe_Data[NUSE_PIPE_NUMBER] = { /* addresses of pipe data areas ------ */ }; ROM U8 NUSE_Pipe_Size[NUSE_PIPE_NUMBER] = { /* pipe sizes ------ */ }; ROM U8 NUSE_Pipe_Message_Size[NUSE_PIPE_NUMBER] = { /* pipe message sizes ------ */ }; 

通道记忆


像所有其他Nucleus SE核心对象一样,通道所需的内存量是可以预测的。

可以按以下方式计算应用程序中所有通道的ROM中的数据量(以字节为单位):

NUSE_PIPE_NUMBER *(sizeof(ADDR)+ 2)

激活任务后,所有应用程序通道在RAM中的内核数据量(以字节为单位)可以计算如下:

NUSE_PIPE_NUMBER * 4

否则:

NUSE_PIPE_NUMBER * 3

具有管道索引的通道的RAM中的用户数据量(以字节为单位):

NUSE_Pipe_Size [管道] * NUSE_Pipe_Message_Size [管道]

未实现的API调用


Nucleus SE中未实现四个Nucleus RTOS API服务调用。

频道创建


此API调用创建一个通道。 Nucleus SE不需要此功能,因为通道是静态创建的。

服务电话原型:

状态NU_Create_Pipe(NU_PIPE *管道,CHAR *名称,VOID *起始地址,UNSIGNED pipe_size,OPTION message_type,UNSIGNED message_size,OPTIONsuspant_type);

参数:

pipe-指向用户提供的通道控制块的指针;它将在其他API调用中用作主通道激活器;
name-指向以七个字符组成的通道名称的指针,该名称以0结尾;
start_address-通道起始地址;
pipe_size-通道中的字节总数;
message_type-通道支持的消息类型。 它可以采用值NU_FIXED_SIZENU_VARIABLE_SIZE ;
message_size-如果通道支持固定长度的消息,则此参数指示每个消息的确切大小。 否则,如果通道支持可变长度消息,则此值为最大消息大小;否则,为0。
suspend_type-指示通道上任务挂起的类型。 它可以采用值NU_FIFONU_PRIORITY (分别为FIFO调度程序和PRIORITY调度程序)。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_PIPE-指向通道控制单元的空指针( NULL ),或者该控制单元已被使用;
NU_INVALID_MEMORY-start_address中指定了不正确的数据区域;
NU_INVALID_MESSAGE-无效的message_type参数;
NU_INVALID_SIZE-消息大小大于通道大小,或者通道或消息大小为零;或者
NU_INVALID_SUSPEND-无效的suspend_type参数。

删除频道


此API调用将删除以前创建的频道。 Nucleus SE不需要它,因为通道是静态创建的,无法删除。

服务电话原型:
状态NU_Delete_Pipe(NU_PIPE *管道);

参数:
pipe-指向通道控制块的指针。

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_PIPE-无效的通道指针。

通道指针


此API调用将建立指向系统中所有通道的指针的顺序列表。 在Nucleus SE中,由于通道是由简单的索引而不是指针标识的,因此是不必要的,因此,这种功能将是多余的。

服务电话原型:
UNSIGNED NU_Pipe_Pointers(NU_PIPE **指针列表,UNSIGNED maximum_pointers);

参数:
pointer_list-指向NU_PIPE指针数组的指针。 该数组将填充指向系统中先前创建的通道的指针。
最大指针 -数组中指针的最大数量。

返回值:
数组中NU_PIPE指针的数量

广播到频道


此API调用将消息传递给所有等待来自特定通道的消息的任务。 在Nucleus SE中,此功能尚未实现,因为它增加了冗余的复杂性。

服务电话原型:
状态NU_Broadcast_To_Pipe(NU_PIPE *管道,VOID *消息,未签名的大小,未签名的挂起);

参数:
pipe-指向通道控制块的指针;
message-指向已发送消息的指针;
size-消息中UNSIGNED数据元素的数量。 如果通道支持可变长度消息,则此参数必须等于或小于通道支持的消息的大小。 如果通道支持定长消息,则此参数必须等于通道支持的消息大小;
暂停 -指示如果通道已满,是否暂停调用任务。 它可以是NU_NO_SUSPENDNU_SUSPEND或超时值。

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_PIPE-指向通道的无效指针;
NU_INVALID_POINTER-消息的空指针( NULL );
NU_INVALID_SIZE-指定的消息大小与创建通道时指定的消息大小不兼容;
NU_INVALID_SUSPEND-尝试从与任务无关的线程挂起;
NU_PIPE_FULL-通道中没有足够的空间来存储消息;
NU_TIMEOUT-通道仍然满,即使在指定的超时时间到期后也是如此;
NU_PIPE_DELETED-任务挂起时删除了通道;
NU_PIPE_RESET —任务挂起时重置了通道。

兼容Nucleus RTOS


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

对象标识符


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

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

邮件大小和类型


在Nucleus RTOS中,可以将通道配置为处理由任意数量的字节组成的消息,就像Nucleus SE一样。 Nucleus RTOS还支持可变长度消息通道,在创建时仅为其指定最大消息大小。 Nucleus SE不支持可变长度消息。

频道大小


在Nucleus SE中,由于所有变量和常量均为U8类型,因此每个通道的最大消息数为256。 Nucleuts RTOS没有这种限制。

未实现的API调用


Nucleus RTOS支持十个通道开销。 其中有四个未在Nucleus SE中实现。 可以在本文前面的“未实现的API调用”部分中找到这些调用的详细说明以及做出此决定的原因。

在下面的文章中,我们将考虑系统时间。

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

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


All Articles