浅析Free RTOS中Queue的应用

目录

概述

1 认识Queue

1.1 Queue定义

1.2 FreeRTOS中的Queue

1.3 Queue状态

1.4 Queue内容

1.5 发送和接收Message

1.5.1 发送message

1.5.2 接收Message

2 Queue的特性

2.1 数据存储

2.2 可被多任务存取

2.3 读Queue时阻塞

2.4 写Queue时阻塞

3 使用Queue

3.1 xQueueCreate() 函数

3.2 xQueueSendToBack() 与 xQueueSendToFront()函数

3.3 xQueueReceive()与 xQueuePeek() 函数

3.4 uxQueueMessagesWaiting() API 函数

4 一个案例

4.1 功能描述

4.2 定义Queue的变量

 4.3 创建Queue

4.4 应用Queue发送或者接收message

4.4.1 发送Message

4.4.2 接收Message

4.5 测试

5 结论


源代码下载地址:

stm32-freeRTOS-queue资源-CSDN文库

概述

本文主要介绍Queue的相关知识,包括Queue的定义,发送和接收消息的方式等内容。重点使用Free RTOS中Queue的接口,实现数据在不同task之间的发送和接收的案例,并在板卡上验证该功能。

1 认识Queue

1.1 Queue定义

消息队列是一个类似于缓冲区的对象。通过它,任务和ISR发送和接收消息,实现到数据的同学和同步。消息队列小一个管道。它暂时保存来自发送者的消息,直到有意的接受者准备读这些消息。这个临时缓冲区把发送任务和接收任务隔开,即它必须同时释放发送和接收消息的任务。

创建一个队列,其应该具备这些要素:

1)分配一个相关的队列控制块(QCB)

2)  一个消息队列名

3)一个唯一的ID

4) 存储器缓冲区

5)队列长度

6)最大消息长度

7)一个或者多个任务等待列表

1.2 FreeRTOS中的Queue

FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权限的小程序。这些独立的任务之间很可能会通过相互通信以提供有用的系统功能。FreeRTOS 中所有的通信与同步机制都是基于队列实现的

1.3 Queue状态

发送消息状态:

step -1: 当一个任务发送消息给一个消息队列,消息会直接发送给阻塞的任务

step -2: 阻塞任务进入就绪态或者运行态,此时消息队列为空,发送消息成功

step -3: 如果另外的消息送到相同的队列,而且没有任务在消息队列的任务等待列表中等候,此时消息队列的状态为非空

step -4: 当消息数据达到队列总数是,队列为满,此时队列无法接收任何消息。

1.4 Queue内容

消息队列可以用来接收和发送多种数据,有些消息的数据可能相当长,这种情况下,可使用发送数据指针的方式。

1.5 发送和接收Message

1.5.1 发送message

方式一: 先进先出FIFO次序Queue

方式一: 先进后出LIFO次序Queue

1.5.2 接收Message

方式一: 任务等待类表-先进先出FIFO次序Queue

方式二: 任务等待类表-先进后出LIFO次序Queue

2 Queue的特性

2.1 数据存储

队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。通常情况下,队列被作为 FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把队列中的数据拷贝删除。

2.2 可被多任务存取

队列是具有自己独立权限的内核对象,并不属于或赋予任何任务。所有任务都可以向同一队列写入和读出。一个队列由多方写入是经常的事,但由多方读出倒是很少遇到。

2.3 读Queue时阻塞

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

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

2.4 写Queue时阻塞

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

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

3 使用Queue

3.1 xQueueCreate() 函数

队列在使用前必须先被创建。队列由声明为 xQueueHandle 的变量进行引用。 xQueueCreate()用于创建一个队列,并返回一个 xQueueHandle 句柄以便于对其创建的队列进行引用。当创建队列时, FreeRTOS 从堆空间中分配内存空间。分配的空间用于存储队列数据结构本身以及队列中包含的数据单元。如果内存堆中没有足够的空间来创建队列,xQueueCreate()将返回 NULL。第五章会有关于内存堆管理的更多信息。

xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize );
参数名称描述
uxQueueLength队列能够存储的最大单元数目,即队列深度
uxItemSize队列中数据单元的长度,以字节为单位
返回值描述
NULL表示没有足够的堆空间分配给队列而导致创建失败
非 NULL表示队列创建成功。此返回值应当保存下来,以作为 操作此队列的句柄

3.2 xQueueSendToBack() 与 xQueueSendToFront()函数

1) xQueueSendToBack()用于将数据发送到队列尾;

2) xQueueSendToFront()用于将数据发送到队列首。

3) xQueueSend()完全等同于 xQueueSendToBack()。

但 切 记 不 要 在 中 断 服 务 例 程 中 调 用 xQueueSendToFront() 或xQueueSendToBack()。

中断模式使用的发送消息函数:

1)xQueueSendToFrontFromISR()

2)xQueueSendToBackFromISR()

portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
参数值描述
xQueue目标队列的句柄。这个句柄即是调用 xQueueCreate()创建该队 列时的返回值。
pvItemToQueue发送数据的指针。其指向将要复制到目标队列中的数据单元。 由于在创建队列时设置了队列中数据单元的长度,所以会从该指 针指向的空间复制对应长度的数据到队列的存储区域。
xTicksToWait阻塞超时时间。如果在发送时队列已满,这个时间即是任务处于 阻塞态等待队列空间有效的最长等待时间。如 果 xTicksToWait 设 为 0 , 并 且 队 列 已 满 , 则xQueueSendToFront()与 xQueueSendToBack()均会立即返回。阻塞时间是以系统心跳周期为单位的,所以绝对时间取决于系统心跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转换为毫秒时间单位。如 果 把 xTicksToWait 设 置 为 portMAX_DELAY , 并 且 在FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1,那么阻塞等待将没有超时限制。

返回值介绍

返回值描述
pdPASS返回 pdPASS 只会有一种情况,那就是数据被成功发送到队列<中。如果设定了阻塞超时时间(xTicksToWait 非 0),在函数返回之前任务将被转移到阻塞态以等待队列空间有效—在超时到来前能够将数据成功写入到队列,函数则会返回 pdPASS。
errQUEUE_FULL如 果 由 于 队 列 已 满 而 无 法 将 数 据 写 入 , 则 将 返 errQUEUE_FULL。如果设定了阻塞超时时间( xTicksToWait 非 0),在函数返回之前任务将被转移到阻塞态以等待队列空间有效。但直到超时也没有其它任务或是中断服务例程读取队列而腾出空间,函数则会返回 errQUEUE_FULL。

3.3 xQueueReceive()与 xQueuePeek() 函数

xQueueReceive(): 用于从队列中接收(读取)数据单元。接收到的单元同时会从队列中删除xQueuePeek():也是从从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。 其从队列首接收到数据后,不会修改队列中的数据,也不会改变数据在队列中的存储序顺。

注意:

切记不要在中断服务例程中调用 xQueueRceive()和 xQueuePeek()。

中断模式下使用的接收函数: xQueueReceiveFromISR()

参数介绍

参数值描述
xQueue目标队列的句柄。这个句柄即是调用 xQueueCreate()创建该队 列时的返回值。
pvBuffer接收缓存指针。其指向一段内存区域,用于接收从队列中拷贝来 的数据。数据单元的长度在创建队列时就已经被设定,所以该指针指向的 内存区域大小应当足够保存一个数据单元。
xTicksToWait阻塞超时时间。如果在发送时队列已满,这个时间即是任务处于 阻塞态等待队列空间有效的最长等待时间。如 果 xTicksToWait 设 为 0 , 并 且 队 列 已 满 , 则xQueueSendToFront()与 xQueueSendToBack()均会立即返回。阻塞时间是以系统心跳周期为单位的,所以绝对时间取决于系统心跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转换为毫秒时间单位。如 果 把 xTicksToWait 设 置 为 portMAX_DELAY , 并 且 在FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1,那么阻塞等待将没有超时限制。

 返回值

返回值描述
pdPASS返回 pdPASS 只会有一种情况,那就是数据被成功发送到队列<中。如果设定了阻塞超时时间(xTicksToWait 非 0),在函数返回之前任务将被转移到阻塞态以等待队列空间有效—在超时到来前能够将数据成功写入到队列,函数则会返回 pdPASS。
errQUEUE_FULL如 果 由 于 队 列 已 满 而 无 法 将 数 据 写 入 , 则 将 返 errQUEUE_FULL。如果设定了阻塞超时时间( xTicksToWait 非 0),在函数返回之前任务将被转移到阻塞态以等待队列空间有效。但直到超时也没有其它任务或是中断服务例程读取队列而腾出空间,函数则会返回 errQUEUE_FULL。

3.4 uxQueueMessagesWaiting() API 函数

uxQueueMessagesWaiting(): 用于查询队列中当前有效数据单元个数。

注意:不要在中断服务例程中调用 uxQueueMessagesWaiting()。

中断模式下使用的函数: uxQueueMessagesWaitingFromISR()。

unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );
参数值描述
xQueue被查询队列的句柄。这个句柄即是调用 xQueueCreate()创建该队列时 的返回值。

返回值:

返回值描述
非0当前队列中保存的数据单元个数
0表明队列为空


4 一个案例

4.1 功能描述

使用STM32H7平台,基于Free RTOS平台,创建3个Task, 2个Task发送Message, 一个Task接收Message。

4.2 定义Queue的变量

代码第6行: 定义Queue的属性

代码第7行: 创建Queue ID 变量

代码第9~12行: 定义消息体数据结构

代码第14~17行: 消息体内容

详细代码:

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"const osMessageQueueAttr_t QueueInputSignalAttribute = {.name = "QueueTest"};
osMessageQueueId_t QueueTest;typedef struct {int id;int32_t value;
}Qdata_stru;Qdata_stru queList[2] = {{1, 100},{2, 50},
};

 4.3 创建Queue

使用osMessageQueueNew创建一个Queue, osMessageQueueNew函数在cmsis_os2.c中定义。其函数原型为:

osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr)

参数介绍:

参数名称描述
msg_count整个消息队列的长度
msg_size消息的字节大小
attr属性

创建方法如下:

 详细代码:

void initTask( void )
{int ucQuequeLength= sizeof(Qdata_stru);QueueTest = osMessageQueueNew (10, ucQuequeLength, &QueueInputSignalAttribute);
}

注意:

创建Queue才能实现发送或者接收message

4.4 应用Queue发送或者接收message

4.4.1 发送Message

定义两个Task,在该Task中实现消息发送功能

代码第32行:发送message

代码第33行:  判断message 是否发送成功

代码第47行:发送message

代码第48行:  判断message 是否发送成功

详细代码:

 void mainTask(void *argument)
{  osStatus_t status;	for(;;){status = osMessageQueuePut(QueueTest, &queList[0], NULL,100);if( status != osOK){printf("mainTask osStatus_t = %d \r\n",status);}osDelay(300);}
}void monitorTask(void *argument)
{osStatus_t status;	for(;;){status = osMessageQueuePut(QueueTest, &queList[1], NULL,100);if( status != osOK){printf(" monitorTask osStatus_t = %d \r\n",status);}osDelay(200);}
}

4.4.2 接收Message

定义一个Task,在该Task中仅仅实现接收其他Task发送的消息。

代码第62行: 接收queue消息

代码第64~69行: 根据 message ID解析消息

 详细代码:

void stateTask(void *argument)
{Qdata_stru recv_que;static int cnt = 0;for(;;){if( osOK == osMessageQueueGet(QueueTest, &recv_que, NULL, 100)){if( recv_que.id == 1){printf(" que 1: %d \r\n",recv_que.value);}else if( recv_que.id == 2) {printf(" que 2: %d \r\n",recv_que.value);}}cnt++;if( (cnt %10) == 0){HAL_GPIO_TogglePin(R_STATUS_GPIO_Port, R_STATUS_Pin);}osDelay(1);}
}

4.5 测试

编译代码,下载到板卡中,通过终端查看发送和接收消息的情况。打开串口终端,代码运行后,log信息如下:

5 结论

消息队列可以不同Task之间的通信,一个消息队列可接收多个Task发送的消息,对于数据长度大于消息队列数据长度的情况,可采用传送指针的方式实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/12499.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv9改进策略 | 低照度图像篇 | 2024最新改进CPA-Enhancer链式思考网络(适用低照度、图像去雾、雨天、雪天)

一、本文介绍 本文给大家带来的2024.3月份最新改进机制&#xff0c;由CPA-Enhancer: Chain-of-Thought Prompted Adaptive Enhancer for Object Detection under Unknown Degradations论文提出的CPA-Enhancer链式思考网络&#xff0c;CPA-Enhancer通过引入链式思考提示机制&am…

【提示学习论文】TCP:Textual-based Class-aware Prompt tuning for Visual-Language Model

TCP:Textual-based Class-aware Prompt tuning for Visual-Language Model&#xff08;CVPR2024&#xff09; 基于文本的类感知提示调优的VLMKgCoOp为baseline&#xff0c;进行改进&#xff0c;把 w c l i p w_{clip} wclip​进行投影&#xff0c;然后与Learnable prompts进行…

树莓派|角速度和加速度传感器

角速度传感器和加速度传感器是常见的惯性传感器&#xff0c;常用于测量物体的旋转和线性运动。 角速度传感器&#xff08;Gyroscope&#xff09;用于测量物体绕三个轴&#xff08;X、Y、Z&#xff09;的旋转速度或角速度。它可以提供关于物体在空间中的旋转方向和角度变化的信…

PXI/PXIe规格1553B总线测试模块

面向GJB5186测试专门开发的1553B总线适配卡&#xff0c;支持4Mbps和1Mbps总线速率。该产品提供2个双冗余1553B通道、1个测试专用通道、2个线缆测试通道。新一代的TM53x板卡除了支持耦合方式可编程、总线信号幅值可编程、共模电压注入、总线信号波形采集等功能外&#xff0c;又新…

Python专题:十三、日期和时间(2)

datetime 模块 today()函数 date类型 year month day

二分法的时间复杂度是logN

对数函数&#xff1a; &#xff08;a>0, a≠1&#xff0c; x>0&#xff09; 当αe时&#xff0c;记为yln x 当α10时&#xff0c;记为ylg x 当α2时&#xff0c;记为ylog x 其中x是自变量&#xff0c;函数的定义域是&#xff08;0&#xff0c;∞&#xff09;&#xff0c;…

【Flask框架】

6.Flask轻量型框架 6.1Flask简介 python提供的框架中已经写好了一个内置的服务器&#xff0c;服务器中的回应response行和头已经写好&#xff0c;我们只需要自己写显示在客户端&#xff0c;的主体body部分。 ---------------------------------------------------------- Fla…

Android Studio无法使用Google翻译问题记录

背景 其实关于Google翻译不能用的问题已经出现很久了&#xff0c;之前Google关掉了很多国内的一些Google服务&#xff0c;但是Google翻译还是能用的&#xff0c;直到不知什么时候起&#xff0c;Google翻译也不能用呢。 每次换电脑安装完AS后第一件事就是下载插件 Settings-Pl…

探索智慧生活:百度Comate引领人工智能助手新潮流

文章目录 百度Comate介绍1. 什么是百度Comate&#xff1f;主要特点 2. Comate的核心功能智能问答功能语音识别功能语音助手功能个性化服务 3. Comate 支持哪些语言&#xff1f; 使用教程(以vscode为例)1. 下载和安装Comate3. 常用操作快捷键(windows) 使用体验自然语言生成代码…

Gitlab、Redis、Nacos、Apache Shiro、Gitlab、weblogic相关漏洞

文章目录 一、Gitlab远程代码执行&#xff08;CVE-2021-22205&#xff09;二、Redis主从复制远程命令执行三、Nacos认证绕过漏洞&#xff08;CVE-2021-29441&#xff09;四、Apache Shiro认证绕过漏洞&#xff08;CVE-2020-1957&#xff09;五、Gitlab任意文件读取漏洞&#xf…

3.TCP的三次握手和四次挥手

一、前置知识 TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。在传输数据前通信双方必须建立连接&#xff08;所谓连接&#xff0c;是指客户端和服务端各自保存一份关于对方的信息&#xff0c;比如ip地址&#xff0c;端口号等&#xff09;。TCP通过三次握手建立一个…

从零开始:C++ String类的模拟实现

文章目录 引言1.类的基本结构2.构造函数和析构函数3.基本成员函数总结 引言 在C编程中&#xff0c;字符串操作是非常常见且重要的任务。标准库中的std::string类提供了丰富且强大的功能&#xff0c;使得字符串处理变得相对简单。然而&#xff0c;对于学习C的开发者来说&#x…

C语言----斐波那契数列(附源代码)

各位看官们好&#xff0c;当我写了上一篇博客杨辉三角后&#xff0c;有一些看官叫我讲一下斐波那契数列。对于这个大家应该是有了解的。最简单的规律就是f(n)f(n-2)f(n-1)。就是当前是前两项之和&#xff0c;然后下标1和0都是1.从第三项开始计算的。那么我们知道规律&#xff0…

位图(c++)

文章目录 1.位图概念2.位图的实现3.应用&#xff08;解决整形存在或次数问题&#xff09;3.1存在问题3.2次数问题 5.搜索的方法对比&#xff1a; 1.位图概念 和哈希一样&#xff0c;都是一个表来记录某个元素的个数或者存在与否&#xff1b;不同的是哈希使用的计算机定义的完整…

群辉部署小雅alist实现视听盛会

最近群辉搭建起来了&#xff0c;开始整蛊影视库&#xff0c;之前搞过nastool。这次折腾下小雅alist。 1.下载并安装 直接在群辉的docker里面下载映像 主要映射下端口和文件夹 #token mytoken.txt 获取地址&#xff1a;https://alist.nn.ci/zh/guide/drivers/aliyundriv…

Git使用(2):远程仓库

一、创建远程仓库 登录码云Gitee - 基于 Git 的代码托管和研发协作平台。 点击右上角&#xff0c;新建仓库。 创建完成&#xff0c;复制仓库地址接下来要使用。 二、将idea项目推送到码云 首先创建本地仓库VCS -> Create Git Repository。然后选择Manage Remotes&#xff0…

用友hr软件统一认证与致远OA单点登录身份周期管理怎么做

一、引言 随着企业信息化建设的深入&#xff0c;各类管理软件如用友HR、致远OA等已经成为事业单位日常运营不可或缺的工具。用友HR软件以其强大的人力资源管理功能&#xff0c;帮助企事业单位实现员工信息的集中管理&#xff1b;而致远OA则以其便捷的办公流程管理&#xff0c;…

springcloud简单了解及上手

springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…

C++ 并发编程指南(11)原子操作 | 11.6、计算机内存结构

文章目录 一、计算机内存结构1、内存的基本组成2、内存的类型3、内存的结构层次4、CPU架构5、局部性原理6、总结 前言 在探讨计算机的运行效率和数据处理能力时&#xff0c;内存结构无疑是一个至关重要的部分。内存&#xff0c;作为计算机系统中的关键组件&#xff0c;承担着存…

【保姆级介绍自动化的讲解】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…