关于RTOS的全部真相。 第十六条 讯号



本文将研究信号,它们是Nucleus SE中任务之间最简单的交互机制。 它们提供了一种在任务之间传输简单消息的低成本方法。


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

使用信号


信号与所有其他类型的内核对象不同,因为它们不是自治的:信号与任务相关联,没有任务就无法存在。 如果将应用程序配置为使用信号,则每个任务都有一组八个信号标志。

任何任务都可以为另一个任务设置信号。 信号只能由信号所有者读取。 在读取期间,信号被重置。 任务无法读取或重置其他任务的信号。

Nucleus RTOS有一个工具,允许任务分配在另一个任务设置一个或多个信号标志时运行的功能。 这有点让人想起中断例程。 Nucleus SE不支持此功能;此处,任务应明确请求信号标志。

信号设定


与大多数Nucleus SE对象一样,信号调整由nuse_config.h中#define指令确定。 主要参数是NUSE_SIGNAL_SUPPORT ,它激活功能支持(对于应用程序中的所有任务)。 指示信号数量的问题不值得:为每个任务分配8个标志。

设置此启用参数可以用作主信号激活器。 这提供了具有适当大小的良好定义的数据结构。 另外,此选项激活API设置。

激活API调用


Nucleus SE中的每个API函数(实用程序调用) 都由nuse_config.h中#define指令激活。 对于信号,这些包括:

NUSE_SIGNALS_SEND NUSE_SIGNALS_RECEIVE 

默认情况下,它们设置为FALSE ,从而禁用每个服务调用并防止实现它们的代码被打开。 要在应用程序中配置信号,您需要选择必要的API调用并将相应的指令设置为TRUE

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

 #define NUSE_SIGNAL_SUPPORT FALSE /* Enables support for signals */ #define NUSE_SIGNALS_SEND FALSE /* Service call enabler */ #define NUSE_SIGNALS_RECEIVE FALSE /* Service call enabler */ 

禁用信号支持的激活的API功能将导致编译错误。 如果您的代码使用尚未激活的API调用,则会发生布局错误,因为实现代码未包含在应用程序中。 当然,包含两个API函数在某种程度上是不必要的,因为在没有这些API的情况下激活信号支持没有意义。 添加了激活器以与其他Nucleus SE功能兼容。

通话信号


Nucleus RTOS支持四个与信号相关的开销调用,这些调用提供以下功能:

  • 向给定任务发送信号。 Nucleus SE在NUSE_Signals_Send()函数中实现。
  • 接收信号。 Nucleus SE在NUSE_Signals_Receive()函数中实现。
  • 信号处理程序注册。 在Nucleus SE中未实现。
  • 打开/关闭(控制)信号。 在Nucleus SE中未实现。

这些挑战的实现方式将在下面详细讨论。

信令和接收服务


可以对一组任务信号执行的基本操作是发送数据(可以由任何任务执行)和读取数据(因此,清除数据只能由所有者任务执行)。 Nucleus RTOS和Nucleus SE为这些操作提供了两个基本的API调用,下面将对其进行介绍。

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

 #define b00000000 ((U8) 0x00) #define b00000001 ((U8) 0x01) #define b00000010 ((U8) 0x02) #define b00000011 ((U8) 0x03) #define b00000100 ((U8) 0x04) #define b00000101 ((U8) 0x05) 

发送信号


任何任务都可以将信号发送到应用程序中的任何其他任务。 发送信号涉及设置一个或多个信号标志。 这是一个OR操作,不会影响先前设置的标志。

调用以将信号发送到Nucleus RTOS
服务电话原型:
状态NU_Send_Signals(NU_TASK *任务,UNSIGNED信号);

参数:

task-指向设置信号标志所属的任务控制单元的指针;
信号 -设置的信号标志的值。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_TASK-指向任务的无效指针;

呼叫发送信号至Nucleus SE
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

STATUS_NUSE_Signals_Send(NUSE_TASK任务,U8信号);

参数:

任务 -设置的信号标志所属的任务的索引(ID);
信号 -设置的信号标志的值。

返回值:

NUSE_SUCCESS-服务调用已成功完成;
NUSE_INVALID_TASK-无效的任务索引。

在Nucleus SE中实现信令
下面是NUSE_Signals_Send()函数的完整代码:

 STATUS NUSE_Signals_Send(NUSE_TASK task, U8 signals) { #if NUSE_API_PARAMETER_CHECKING if (task >= NUSE_TASK_NUMBER) { return NUSE_INVALID_TASK; } #endif NUSE_CS_Enter(); NUSE_Task_Signal_Flags[task] |= signals; NUSE_CS_Exit(); return NUSE_SUCCESS; } 

代码很简单。 在对参数进行任何验证之后,信号值将对指定任务的信号标志进行“或”运算。 锁定任务不会影响信号。

接收信号


任务只能读取自己的信号标志集。 在读取期间,标志值被重置。

调用以在Nucleus RTOS中接收信号
服务电话原型:
未签名的NU_Receive_Signals(VOID);

参数:无。

返回值:
信号标志值。

调用以在Nucleus SE中接收信号
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:
U8 NUSE_Signals_Receive(void);

参数:无。

返回值:
信号标志值。

核SE信号接收的实现
以下是NUSE_Signals_Receive()函数的完整代码:

 U8 NUSE_Signals_Receive(void) { U8 signals; NUSE_CS_Enter(); Signals = NUSE_Task_Signal_Flags[NUSE_Task_Active]; NUSE_Task_Signal_Flags[NUSE_Task_Active] = 0; NUSE_CS_Exit(); return signals; } 

代码很简单。 标志的值被复制,初始值被重置,并且该复制由API函数返回。 锁定任务不会影响信号。

资料结构


由于信号不是独立的对象,因此内存的使用取决于它们所属的任务。 以下是一些完整理解的信息。 信号使用一个数据结构(在RAM中),该数据结构与其他Nucleus SE对象一样,是一个表,其维数对应于应用程序中的任务数。 仅当启用信号支持时,才使用此数据结构。

我强烈建议应用程序代码不要直接访问此数据结构,而要使用可用的API函数。 这样可以避免与Nucleus SE的未来版本不兼容,产生不必要的副作用,并且还简化了将应用程序移植到Nucleus RTOS的过程。 下面将详细讨论数据结构,以简化对服务调用和调试原理的理解。

放置在RAM中的数据的结构


资料结构:
NUSE_Task_Signal_Flags []-U8类型的数组,每个配置的任务都有一个条目,信号标志存储在此数组中。

加载Nucleus SE时, NUSE_Init_Task()函数会将此数据结构初始化为零。

放在ROM中的数据结构


信号在ROM中缺少数据结构。

用于存储信号数据的内存量


与所有Nucleus SE核心对象一样,信号所需的内存量是可以预测的。

ROM中应用程序中所有信号的数据量为0。

对于应用程序中所有信号,用于在RAM中存储数据的内存量(以字节为单位)等于已配置任务的数量( NUSE_TASK_NUMBER )。 但是实际上,这些数据属于任务,并且在上一篇有关任务的文章中进行了描述。

未实现的API调用


Nucleus SE中未实现两个Nucleus RTOS API信令调用:

信号处理程序注册


该API调用为当前任务设置信号处理过程(功能)。 Nucleus SE不需要此功能,因为不支持信号处理程序。

服务电话原型:
状态NU_Register_Signal_Handler(VOID(* signal_handler)(UNSIGNED));

参数:
signal_handler-接收信号时应调用的函数

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_POINTER-指向信号处理程序的空指针( NULL

控制(激活/停用)信号


该服务为当前任务激活和/或禁用信号。 对于每个任务,有32个信号可用。 每个信号由signal_enable_mask中的一位表示 。 在signal_enable_mask中添加一个位将启用相应的信号,而删除一个位则将其禁用。

服务电话原型:
UNSIGNED NU_Control_Signals(UNSIGNED enable_signal_mask);

参数:
enable_signal_mask-表示正确信号的位模式。

返回值:
遮罩可激活/禁用前一个信号。

兼容Nucleus RTOS


开发Nucleus SE时,我的目标是保持代码级别与Nucleus RTOS兼容。 信号也不例外,从开发人员的角度来看,它们的实现方式与Nucleus RTOS中的实现方式几乎相同。 考虑到最终代码更容易理解并且可以更有效地使用内存,因此我认为有些不兼容是有效的。 否则,Nucleus RTOS API调用几乎可以直接转移到Nucleus SE调用。

信号处理器


在Nucleus SE中,未实现信号处理程序以简化整体结构。

信号可用性和数量


在Nucleus RTOS中,任务可以具有32个信号标志。 在Nucleus SE中,我决定将它们的数量减少到八个,因为这对于简单的应用程序就足够了,并且可以节省RAM资源。 如有必要,可以完全关闭信号。

未实现的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-CN427439/


All Articles