STM32 FreeRTOS (二)消息队列

文章详细介绍了FreeRTOS操作系统中基于消息队列的任务间通信机制,包括队列的创建、数据存储、读写操作以及阻塞策略。消息队列作为一种异步通信方式,支持任务间的FIFO通信,并能在无数据或满载时进行阻塞和唤醒操作。同时,文中提到了相关API的使用方法,如创建、删除队列以及发送和接收消息的函数。
摘要由CSDN通过智能技术生成
    • 消息队列简介

基于 FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权限的程序。这些独立的任务之间的通讯与同步一般都是基于操作系统提供的IPC通讯机制,而FreeRTOS 中所有的通信与同步机制都是基于队列实现的。

消息队列是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传送信息,实现了任务接收来自其他任务或中断的不固定长度的消息。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务,用户还可以指定挂起的任务时间;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息,消息队列是一种异步的通信方式。

队列特性

1.数据存储

队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。

通常情况下,队列被作为 FIFO(先进先出)缓冲区使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。

往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把队列中的数据拷贝删除。

2.读阻塞

当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。

由于队列可以被多个任务读取,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列数据有效。这种情况下,一旦队列数据有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。

3.写阻塞

与读阻塞想反,任务也可以在写队列时指定一个阻塞超时时间。这个时间是当被写队列已满时,任务进入阻塞态以等待队列空间有效的最长时间。

由于队列可以被多个任务写入,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列空间有效。这种情况下,一旦队列空间有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。

消息队列的工作流程

主要调用的API

1.发送消息(入队)

任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满或者允许覆盖入队, FreeRTOS 会将消息拷贝到消息队列队尾,否则,会根据用户指定的阻塞超时时间进行阻塞,在这段时间中,如果队列一直不允许入队,该任务将保持阻塞状态以等待队列允许入队。当其它任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转为就绪态。当任务等待的时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。

发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,发送的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。

下面是消息队列的发送API接口,函数中有FromISR则表明在中断中使用的。

2.消息队列读取(出队)

任务调用接收函数收取队列消息, 函数首先判断当前队列是否有未读消息, 如果没有, 则会判断参数 xTicksToWait, 决定直接返回函数还是阻塞等待。

如果队列中有消息未读, 首先会把待读的消息复制到传进来的指针所指内, 然后判断函数参数 xJustPeeking == pdFALSE的时候, 符合的话, 说明这个函数读取了数据, 需要把被读取的数据做出队处理, 如果不是, 则只是查看一下(peek),只是返回数据,但是不会把数据清除。

对于正常读取数据的操作, 清除数据后队列会空出空位, 所以查看队列中的等待列表中是否有任务等发送数据而被挂起, 有的话恢复一个任务就绪, 并根据优先级判断是否需要出进行任务切换。

对于只是查看数据的, 由于没有清除数据, 所以没有空间新空出,不需要检查发送等待链表, 但是会检查接收等待链表, 如果有任务挂起会切换其到就绪并判断是否需要切换。

    • 工程创建

1.RCC和时钟树的配置

2.时基设置

3.FreeTROS的设置

开启FreeRTOS,v1与v2版本不同,一般选用v1即可

在 Config parameters 进行具体参数配置。

这里一般默认就好了

然后就是创建任务:

在 Tasks and Queues 进行配置。

创建队列Queue

在 Tasks and Queues 进行配置。

创建一个消息队列TestQueue,

  • Queue Name: 队列名称
    Queue Size: 队列能够存储的最大单元数目,即队列深度
    Queue Size: 队列中数据单元的长度,以字节为单位
    Allocation: 分配方式:Dynamic 动态内存创建
    Buffer Name: 缓冲区名称
    Buffer Size: 缓冲区大小
    Conrol Block Name: 控制块名称

    • 相关API讲解

    • 库函数

队列创建:xQueueCreate()
  • 参数:

uxQueueLength 队列可同时容纳的最大项目数 。

uxItemSize 存储队列中的每个数据项所需的大小(以字节为单位)。

数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 必须大小相同。

  • 返回值

如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。

  • 用法示例:

队列删除:vQueueDelete()
  • 函数功能:删除队列 — 释放分配用于存储放置在队列中的项目的所有内存。

  • 参数:xQueue 要删除的队列的句柄。

向队列写消息:xQueueSend()
  • 函数功能:向队列中写入消息

  • 参数:

xQueue

队列的句柄,数据项将发布到此队列。

pvItemToQueue

指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。

xTicksToWait

如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。时间在 tick 周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

  • 返回值:如果成功发布项目,返回 pdTRUE,否则返回 pdFALSE。

  • 用法示例:

  • 备注:如果要从尾部添加消息,使用xQueueSendFromISR();用法和xQueueSend()一样.

向队列读消息:xQueueReceive()
  • 函数功能:用于从一个队列中接收消息,并把接收的消息从队列中删除。

  • 参数

返回值:如果接收消息成功,返回 pdTRUE,否则返回 pdFALSE。

查询队列:

* 1.返回队列中可用数据的个数

UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );

* 2.返回队列中可用空间的个数

UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

    • HAL函数

队列ID:

备注:队列ID。例如,对osMessageCreate的调用返回。可用作参数到osMessageDelete以删除队列。

队列创建:
  • 函数功能:使用动态内存的方式创建一个新的队列。

  • 参数: 1.queue_def: 引用由osMessageQDef定义的队列

2.thread_id: 线程ID或NULL

  • 返回值:成功返回任务ID,失败返回0

队列删除:
  • 函数功能:队列删除函数是根据消息队列ID直接删除的,删除之后这个消息队列的所有信息都会被系统回收清空,而且不能再次使用这个消息队列了。

  • 参数: queue_id: 消息队列ID,表示的是要删除哪个想队列

  • 返回值:错误码

  • 示例如下:

向队列发送消息:
  • 函数功能:用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以引用的形式。可用在中断服务程序中。

  • 参数:

  1. queue_id: 目标队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

  1. info: 发送数据的指针。其指向将要复制到目标队列中的数据单元。由于在创建队列时设置了队列中数据单元的长度,所以会从该指针指向的空间复制对应长度的数据到队列的存储区域。

  1. millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。

超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
  • 返回值:错误码

向队列接收消息:(拷贝形式,会删除队列的消息)
  • 函数功能:

用于从一个队列中接收消息并把消息从队列中删除。接收的消息是以拷贝的形式进行的,所以我们必须提供一个足够大空间的缓冲区。具体能够拷贝多少数据到缓冲区,这个在队列创建的时候已经设定。可用在中断服务程序中。

  • 参数:

  1. queue_id: 被读队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

  1. millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。

  • 返回值:错误码

向队列接收消息:(不从队列中删出接收到的单元)
  • 函数功能:

从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。osMessagePeek() 从队列首接收到数据后,不会修改队列中的数据,也不会改变数据在队列中的存储序顺。可用在中断服务程序中。

  • 参数:

  1. queue_id: 被读队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

  1. millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。

  • 返回值:错误码

查询队列有效单元个数:
  • 函数功能:

从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。osMessagePeek() 从队列首接收到数据后,不会修改队列中的数据,也不会改变数据在队列中的存储序顺。可用在中断服务程序中。

  • 参数: queue_id: 目标队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

  • 返回值:当前队列中保存的数据单元个数。返回 0 表明队列为空

    • 实验例程

创建两个任务,一个消息队列:

发送消息队列任务:

接收消息队列任务:

创建消息队列:

实验效果:

按下按键一就向消息队列发送消息,同时拷贝接收,利用串口打印出来。

消息队列FreeRTOS 中一种常用的线程间通信机制,它允许一个线程向另一个线程发送消息,并且可以在接收消息的线程中进行相应的处理。在 STM32 上使用 FreeRTOS消息队列需要以下步骤: 1. 创建消息队列:使用 `xQueueCreate()` 函数创建一个消息队列,该函数返回一个指向队列的句柄。例如: ```c xQueueHandle msgQueue = xQueueCreate(10, sizeof(uint32_t)); ``` 上述代码创建了一个容量为 10 的队列,用于存储 `uint32_t` 类型的消息。 2. 发送消息:使用 `xQueueSend()` 函数向队列发送消息。例如: ```c uint32_t msg = 42; xQueueSend(msgQueue, &msg, portMAX_DELAY); ``` 上述代码将值为 42 的消息发送到 `msgQueue` 队列中。`portMAX_DELAY` 参数表示如果队列已满,发送操作将一直阻塞,直到有空间可用。 3. 接收消息:使用 `xQueueReceive()` 函数从队列接收消息。例如: ```c uint32_t receivedMsg; xQueueReceive(msgQueue, &receivedMsg, portMAX_DELAY); ``` 上述代码将从 `msgQueue` 队列中接收一个消息,并将其存储在 `receivedMsg` 变量中。`portMAX_DELAY` 参数表示如果队列为空,接收操作将一直阻塞,直到有消息可用。 需要注意的是,在使用消息队列时,需要确保发送和接收消息的线程都已经创建,并且消息队列的句柄在两个线程中均可访问。 以上就是在 STM32 上使用 FreeRTOS 消息队列的简要介绍。如果有更多关于消息队列的问题,可以继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值