
本文将研究信号,它们是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 #define NUSE_SIGNALS_SEND FALSE #define NUSE_SIGNALS_RECEIVE FALSE
禁用信号支持的激活的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。