队列:介绍和基本服务
查看 RTOS Revealed 系列
队列在之前的文章中介绍过。与邮箱相比,它们提供了一种在任务之间传递简单消息的更灵活的方式。
使用队列
在 Nucleus SE 中,队列是在构建时配置的。一个应用程序最多可以配置 16 个队列。如果未配置队列,则应用程序中不包含属于队列的数据结构或服务调用代码。
队列只是一组存储位置,每个位置都足够容纳 ADDR 类型的单个数据项 ,对其的访问受到控制,以便多个任务可以安全地使用它。任务可以重复写入队列,直到所有位置都已满。任务可以从队列中读取,数据通常以先进先出 (FIFO) 的方式接收。尝试发送到完整队列或从空队列读取可能会导致错误或任务暂停,具体取决于 API 调用中选择的选项和 Nucleus SE 配置。
队列和管道
Nucleus SE 还支持管道,这在之前的文章中也有介绍,并在以后的文章中详细介绍。队列和管道之间的主要区别在于消息大小。队列携带包含单个 ADDR 的消息 – 这些通常是指针。管道承载任意字节长的消息;应用程序中每个管道的大小是固定的,并在配置时设置。
配置队列
队列数量
与 Nucleus SE 的大多数方面一样,队列的配置主要由 #define 控制 nuse_config.h 中的语句 .关键设置是 NUSE_QUEUE_NUMBER ,这决定了为应用程序配置了多少队列。默认设置为 0(即没有使用队列),您可以将其设置为最多 16 的任何值。错误的值将导致编译时错误,这是由 nuse_config_check.h<中的测试生成的/b> (这包含在 nuse_config.c 中 因此使用此模块编译)导致 #error 正在编译的语句。
选择非零值是队列的“主启用”。这导致一些数据结构被相应地定义和调整大小,下一篇文章将详细介绍。它还激活 API 启用设置。
API 启用
Nucleus SE 中的每个 API 函数(服务调用)都有一个启用 #define nuse_config.h 中的符号 .对于队列,这些是:
NUSE_QUEUE_SEND
NUSE_QUEUE_RECEIVE
NUSE_QUEUE_JAM
NUSE_QUEUE_RESET
NUSE_QUEUE_INFORMATION
NUSE_QUEUE_COUNT
默认情况下,所有这些都设置为 FALSE ,从而禁用每个服务调用并禁止包含任何实现代码。要为应用程序配置队列,您需要选择要使用的 API 调用并将其启用符号设置为 TRUE .
这是默认 nuse_config.h 文件的摘录。
#define NUSE_QUEUE_NUMBER 0 /* 队列数量
系统 - 0-16 * /
/ *服务呼叫使能* /
的#define FALSE NUSE_QUEUE_SEND
的#define FALSE NUSE_QUEUE_RECEIVE
的#define FALSE NUSE_QUEUE_JAM
的#define NUSE_QUEUE_RESET FALSE
#define NUSE_QUEUE_INFORMATION FALSE
#define NUSE_QUEUE_COUNT FALSE
如果启用了队列 API 功能并且未配置任何队列(NUSE_Queue_Count() 除外),则会导致编译时错误 这总是被允许的)。如果您的代码使用未启用的 API 调用,则会导致链接时间错误,因为应用程序中不会包含任何实现代码。
队列服务调用
Nucleus RTOS 支持 10 个属于队列的服务调用,提供以下功能:
向队列发送消息。由 NUSE_Queue_Send() 实现 在 Nucleus SE 中。
从队列接收消息。由 NUSE_Queue_Receive() 实现 在 Nucleus SE 中。
将消息发送到队列的前面。由 NUSE_Queue_Jam() 实现 在 Nucleus SE 中。
将队列恢复到未使用状态,没有任务挂起(重置)。由 NUSE_Queue_Reset() 实现 在 Nucleus SE 中。
提供指定队列的信息。由 NUSE_Queue_Information() 实现 在 Nucleus SE 中。
返回为应用程序(当前)配置了多少队列的计数。由 NUSE_Queue_Count() 实现 在 Nucleus SE 中。
向应用程序添加一个新队列(创建)。未在 Nucleus SE 中实现。
从应用程序中移除队列(删除)。未在 Nucleus SE 中实现。
返回指向应用程序中所有队列(当前)的指针。未在 Nucleus SE 中实现。
向队列中所有挂起的任务(广播)发送消息。未在 Nucleus SE 中实现。
详细检查了每个服务调用的实现。
队列写入和读取服务
可以在队列上执行的基本操作是向其中写入数据——有时称为发送 – 并从中读取数据 – 这也称为接收 .也可以将数据写入队列的前端——这也被称为阻塞 . Nucleus RTOS 和 Nucleus SE 分别为这些操作提供了三个基本的 API 调用,这里将对其进行讨论。
写入队列
用于写入队列的 Nucleus RTOS API 调用非常灵活,如果操作无法立即完成,您可以无限期挂起或超时挂起;即您尝试写入一个完整的队列。 Nucleus SE 提供相同的服务,只是任务挂起是可选的,并且没有实现超时。
Nucleus RTOS 还提供了一种向队列广播的功能,但 Nucleus SE 不支持这种功能。在下一篇文章中未实现的 API 中对其进行了描述。
用于发送到队列的 Nucleus RTOS API 调用
服务调用原型:
STATUS NU_Send_To_Queue(NU_QUEUE *queue, VOID *message,
未签名大小,未签名挂起);
参数:
队列 – 指向用户提供的队列控制块的指针
消息 – 指向要发送的消息的指针
尺寸 – UNSIGNED 的数量 消息中的数据元素。如果队列支持变长消息,则该参数必须等于或小于队列支持的消息大小。如果队列支持固定大小的消息,该参数必须与队列支持的消息大小完全一致。
暂停 – 任务暂停规范;可能是 NU_NO_SUSPEND 或 NU_SUSPEND 或超时值
退货:
NU_SUCCESS – 通话成功
NU_INVALID_QUEUE – 队列指针无效
NU_INVALID_POINTER – 消息指针为NULL
NU_INVALID_SIZE – 消息大小与队列支持的消息大小不兼容
NU_INVALID_SUSPEND – 尝试从非任务线程挂起
NU_QUEUE_FULL – 队列已满,未指定挂起
NU_TIMEOUT – 即使挂起指定的超时值,队列仍然是满的
NU_QUEUE_DELETED – 任务挂起时队列被删除
NU_QUEUE_RESET – 任务挂起时队列被重置
用于发送到队列的 Nucleus SE API 调用
此 API 调用支持 Nucleus RTOS API 的关键功能。
服务调用原型:
STATUS NUSE_Queue_Send(NUSE_QUEUE queue, ADDR *message,
U8暂停);
参数:
队列 – 要使用的队列的索引(ID)
消息 – 指向要发送的消息的指针,它是 ADDR 类型的单个变量
暂停 – 任务暂停规范;可能是 NUSE_NO_SUSPEND 或 NUSE_SUSPEND
退货:
NUSE_SUCCESS – 通话成功
NUSE_INVALID_QUEUE – 队列索引无效
NUSE_INVALID_POINTER – 消息指针为NULL
NUSE_INVALID_SUSPEND – 尝试从非任务线程挂起或未启用阻塞 API 调用
NUSE_QUEUE_FULL – 队列已满,未指定挂起
NUSE_QUEUE_WAS_RESET – 任务挂起时队列被重置
队列 ASend 的 Nucleus SE 实现
NUSE_Queue_Send() 的大部分代码 API 函数 - 参数检查后 - 由条件编译选择,取决于是否启用了对阻塞(任务挂起)API 调用的支持。我们将在此处分别查看这两种变体。
如果没有开启阻塞,这个 API 调用的代码很简单:
if (NUSE_Queue_Items[queue] ==NUSE_Queue_Size[queue]) /* 队列已满 */{ return_value =NUSE_QUEUE_FULL;}else /* 队列元素可用 */{ NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] =*信息; if (NUSE_Queue_Head[queue] ==NUSE_Queue_Size[queue]) { NUSE_Queue_Head[queue] =0; } NUSE_Queue_Items[队列]++; return_value =NUSE_SUCCESS;}
该函数只是检查队列中是否有空间并使用 NUSE_Queue_Head[] index 将消息存储在队列的数据区中。
启用阻塞后,代码变得更加复杂:
do{ if (NUSE_Queue_Items[queue] ==NUSE_Queue_Size[queue]) /* 队列已满 */ { if (suspend ==NUSE_NO_SUSPEND) { return_value =NUSE_QUEUE_FULL; } else { /* 阻塞任务 */ NUSE_Queue_Blocking_Count[queue]++; NUSE_Suspend_Task(NUSE_Task_Active, (queue <<4) | NUSE_QUEUE_SUSPEND); return_value =NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value !=NUSE_SUCCESS) { 暂停 =NUSE_NO_SUSPEND; } } } else { /* 队列元素可用 */ NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] =*message; if (NUSE_Queue_Head[queue] ==NUSE_Queue_Size[queue]) { NUSE_Queue_Head[queue] =0; } NUSE_Queue_Items[队列]++; if (NUSE_Queue_Blocking_Count[queue] !=0) { U8 索引; /* 检查这个队列上是否有任务被阻塞 */ NUSE_Queue_Blocking_Count[queue]--; for (index=0; index对代码的一些解释可能有用:
代码包含在 do...while 中 循环,当参数 suspend 的值为 NUSE_SUSPEND 时继续 .
如果队列已满并且挂起 设置为 NUSE_NO_SUSPEND ,API 调用以 NUSE_QUEUE_FULL 退出 .如果挂起设置为 NUSE_SUSPEND ,任务暂停。返回时(即任务被唤醒时),如果返回值为 NUSE_SUCCESS ,表示任务被唤醒,因为一条消息被读取(而不是队列的重置)代码循环回到顶部。
如果队列未满,则使用 NUSE_Queue_Head[] 存储提供的消息 将消息存储在队列的数据区中的索引。检查队列中是否有任何任务被挂起(等待接收)。如果有任何任务在等待,则唤醒第一个任务。 暂停 变量设置为 NUSE_NO_SUSPEND 并且 API 调用以 NUSE_SUCCESS 退出 .
嵌入式