亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial Internet of Things >> 嵌入式

队列:介绍和基本服务


查看 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_SUSPENDNU_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_SUSPENDNUSE_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 退出 .


嵌入式

  1. 凸轮锁及其工作原理简介
  2. 吊环螺钉及其工作原理简介
  3. 扣眼及其工作原理简介
  4. 不锈钢简介及其制造方法
  5. C# 基本输入和输出
  6. 邮箱:介绍和基本服务
  7. 信号量:实用服务和数据结构
  8. 信号量:介绍和基本服务
  9. 事件标志组:实用服务和数据结构
  10. 事件标志组:介绍和基本服务
  11. 表面磨削服务:工艺和精度
  12. 加工中心还是车削中心?比较与介绍