消息队列在RTOS的应用

47 篇文章 5 订阅
11 篇文章 0 订阅

传说互联网应用有两大利器,一个是缓存,另一个就是消息队列。 一直相对消息队列做一下梳理,希望早日另有成文。 一叶知秋,实际上消息队列在嵌入式系统中同样有着广泛的应用。 近来致力于IoT和智能硬件,现学习一下消息队列在RTOS中的应用场景。

RTOS是一个管理CPU的软件, 即微处理单元(MPU) , 还可能管理高效的DSP。 大多数 RTOS 内核是用 c 语言编写的, 同时需要用汇编语言编写一小部分代码来适应不同的 CPU 架构。一个 RTOS 内核为开发者提供了许多有用的服务, 如多任务处理、中断管理、通过消息队列、信号量、资源管理、时间管理、内存分区管理等等。

应用程序或者最终地产品基本上会划分为多个任务, 每个任务负责应用程序的一部分。 一个任务就是一个简单的程序, 它有自己的 CPU使用时间。 根据任务的重要性, 每个任务都被赋予了优先级。

RTOS中的消息队列

图1 消息队列是用于将内容传递给任务的内核对象

如图1所示, 消息队列是一个核心对象(即数据结构) , 通过这个对象, 消息从中断服务例程(ISR)或任务发送到另一个任务。 应用程序可以有任意数量的消息队列, 每一个都有自己的目的和用途。 例如, 消息队列可以用来将从通信接口 ISR 接收到的数据包传递给一个任务, 而 ISR 则负责处理数据包。 另一个队列可以用来将内容传递给显示任务, 该任务将负责正确地更新显示的内容。

消息通常是指向包含实际消息存储区域的void指针。 这些指针可以指向任何东西, 甚至可以指向接收任务执行的函数。 因此, 它的实际含义取决于应用程序。 每个消息队列都可以根据它将保存的存储量进行配置。 消息队列可以配置为保存单个消息或者 n 个消息。 队列的大小取决于应用程序以及消费者的处理速度。

如果一个任务等待一条消息, 并且队列中没有消息, 则该任务将被挂起, 直到有消息发送到队列中。 等待任务不消耗 CPU 时间, RTOS 可以运行其他任务。 如图1所示, 挂起的任务可以指定超时。 如果在指定时间内未收到消息, 则当该任务成为最高优先任务时, 将允许任务恢复执行(即 unblock)。 当任务执行时, 基本上会被告知它恢复的原因是因为超时。

消息队列通常作为先入先出(FIFO)实现, 这意味着收到的第一个消息将是从队列中提取的第一个消息。 然而, 有些内核允许发送被认为比其他内核更重要的消息, 从而排在队列的首位。 换句话说, 在"先入先出"顺序中, 使该消息成为任务提取的第一条信息。

消息队列的另一个重要方面是, 消息本身需要保持从发送到处理的时间范围。 这意味着不能将指针传递给栈变量等等。 为了将消息保持在作用域中, 通常会填充一个结构, 并从这些消息池中获取, 如图2所示。 


图2 消息池的存储区域

发送消息的 ISR 或任务将从池中获取结构, 填充结构, 并将指针指向队列的结构。 接收任务将从队列中提取指针, 处理结构, 完成后将结构返回到池中。 当然, 发送方和接收方都需要使用相同的池, 除非数据结构中的字段指示使用了哪个池。

在 RTOS 中的许多消息队列实现中, 如队列已满, 则发送到队列的消息将被丢弃。 通常这不是一个问题, 应用程序的逻辑可以从这种情况中恢复。 然而, 如图3所示, 实现一个发送任务会发送消息的机制是相当容易的:

图3 如果队列已满, 则阻止发送

  1. 计数信号的初始化值与队列可接受的最大条目数相对应

  2. 发送任务在允许将消息发送到队列之前检查信号量。 如果信号量值为零, 则发送方等待。

  3. 如果值为非零, 则信号量减少, 发送方将消息发送到队列中

  4. 消息的接收方像往常一样将消息队列分隔开来

  5. 当收到消息时, 接收这将指针从队列中取出并向信号量发出信号, 表示队列中的条目已被释放

如图所示, 这个机制只适用于两个任务, 因为 ISR 不允许在信号量上分配信号。

消息队列的典型用法

图4显示了消息队列的不同用法:

1-4. 消息队列通常用于从 ISR 发送消息或将任务发送到另一个任务, 如前面所讨论的那样

5.但是, 如果消息符合指针的大小, 则不必发送实际消息及分配存储区域。 例如, 如果一个32位指针, 那么可以将模拟转换器(ADC)从一个12位 ADC 读取到一个指针, 并通过消息队列发送,只要接收这知道将值返回整数即可, 这是完全合法的

6-7 一个任务如果知道这些消息将不会发送给它的,可以使用超时机制在一定的时间内延迟自己。 在这种情况下, 一个能够保存单个条目的队列就足够了。 事实上, 如果另一个任务或 ISR 发送消息, 那么延迟将被终止, 这可能也是想要实现的行为。

8 消息队列可以作为一个信号量来简单地向事件发生的任务发出信号。 在这种情况下, 信息可以是任何东西。 队列的大小取决于应用程序需要缓冲多少信号。

9-10 消息队列也可以用作二进制信号量或用于资源共享的计数信号量。 对于二进制信号量, 队列将包含单个消息, 并在队列中放置消息(任何值)。 为了访问资源, 可以在队列上分配一个任务。 如果队列中有消息, 则该任务将获得对资源的访问。 一旦使用了资源, 队列就会被发布, 从而根据需要放弃资源, 供其他任务使用。 同样的机制也适用于使用 n 个资源实现计数信号, 队列将预先填入n个虚拟消息。

11.消息实际上可以用来模拟事件标志, 其中32位指针大小的变量中,每一位可以代表一个事件

12.消息队列可以用来实现栈结构,这基本上是 LIFO 机制的另一个用途。

总而言之,消息队列有许多不同方式的使用场景。 事实上, 通过消息队列,可以编写相当复杂的应用程序。 使用消息队列可以减少代码的大小, 可以被模拟(信号、时间延迟和事件标志)许多其他的服务。

编译自http://www.embedded-computing.com/dev-tools-and-os/the-many-uses-of-rtos-message-queues


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Keil RTX RTOS 提供了消息队列的支持。下面是使用消息队列的基本步骤: 1. 定义消息队列 消息队列可以是全局变量或局部变量。定义消息队列时需要指定消息队列的大小和消息的数据类型,例如: ``` #define MSGQUEUE_SIZE 16 osMessageQueueId_t msgQueue; uint32_t msgQueueBuffer[MSGQUEUE_SIZE]; const osMessageQueueAttr_t msgQueueAttr = { .name = "MsgQueue" }; msgQueue = osMessageQueueNew(MSGQUEUE_SIZE, sizeof(uint32_t), &msgQueueAttr); ``` 上面的代码定义了一个大小为 16 的消息队列,用于存储 uint32_t 类型的消息。消息队列的属性可以通过 osMessageQueueAttr_t 结构体来设置。 2. 发送消息到消息队列 发送消息时需要将消息数据写入到消息队列中,例如: ``` uint32_t msgData = 123; osStatus_t status = osMessageQueuePut(msgQueue, &msgData, 0, osWaitForever); if (status != osOK) { // 发送消息失败 } ``` 上面的代码将值为 123 的消息发送到消息队列中。osMessageQueuePut() 函数的第一个参数是消息队列 ID,第二个参数是指向消息数据的指针,第三个参数是消息的优先级,第四个参数是超时时间。 3. 接收消息 接收消息时需要从消息队列中读取消息数据,例如: ``` uint32_t msgData; osStatus_t status = osMessageQueueGet(msgQueue, &msgData, NULL, osWaitForever); if (status != osOK) { // 接收消息失败 } ``` 上面的代码从消息队列中读取一个消息,并将消息数据存储到 msgData 变量中。osMessageQueueGet() 函数的第一个参数是消息队列 ID,第二个参数是指向消息数据的指针,第三个参数是指向消息优先级的指针,第四个参数是超时时间。 以上就是使用 Keil RTX RTOS 消息队列的基本步骤。当然,在实际应用中,还需要根据具体的需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值