04 FreeRTOS 队列(queue)

1、队列的特性

        队列可以理解为一个传送带,一个流水线。

        队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)

        每个数据大小固定

        创建队列时就要指定长度、数据大小

        数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部读,把数据写在队列头部并不会覆盖原来头部的数据,因为队列中的数据使用环形缓冲区管理数据,把数据放到头部时,会先移动头部位置,并不会覆盖原来数据。

2、队列的函数

2.1 创建

//函数原型//队列长度,每一项的大小
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

2.2 写队列

        可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。

/* 等同于xQueueSendToBack
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSend(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);
/*
* 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToFront(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait
);
/*
* 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);
参数
说明
xQueue
队列句柄,要写哪个队列
pvItemToQueue
数据指针,这个数据的值会被复制进队列,
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列满则无法写入新数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间 (Tick Count)
如果被设为 0 ,无法写入数据时函数会立刻返回;
如果被设为 portMAX_DELAY ,则会一直阻塞直到有空间可写
返回值
pdPASS :数据成功写入了队列
errQUEUE_FULL :写入失败,因为队列满了。

 

2.3 读队列

        使用 xQueueReceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版 本:在任务中使用、在ISR中使用。

BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait 
);
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxTaskWoken
);
参数
说明
xQueue
队列句柄,要写哪个队列
pvBuffer
bufer 指针,队列的数据会被复制到这个 buffer
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列空则无法读出数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间 (Tick Count)
如果被设为 0 ,无法读出数据时函数会立刻返回;
如果被设为 portMAX_DELAY ,则会一直阻塞直到有数据可写
返回值
pdPASS :从队列读出数据入
errQUEUE_EMPTY :读取失败,因为队列空了。

2.4 删除

        删除队列的函数为 vQueueDelete() ,只能删除使用动态方法创建的队列,它会释放内存。

void vQueueDelete( QueueHandle_t xQueue );

2.5 查询

        可以查询队列中有多少个数据、有多少空余空间。

/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

2.6 复位

        队列刚被创建时,里面没有数据,使用过程中可以调用 xQueueReset() 把队列恢复为初始状态。

/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);

2.7 覆盖

        当队列长度为1时,可以使用 xQueueOverwrite() xQueueOverwriteFromISR() 来覆盖数据。 注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也意味着这些函数不会被阻塞。

/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);

2.8 偷看

        如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用"窥视",也就是 xQueuePeek() xQueuePeekFromISR() 。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么"偷看"时会导致阻塞;一旦队列中有数据,以后每次"偷看"都会成功。

/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer,
);

3、示例代码

3.1 同步

static int sum = 0;
static volatile int flagCalcEnd = 0;
static QueueHandle_t xQueueCalcHandle;void Task1Function( void * param)
{volatile int i = 0;	//使用volatile修饰,让系统不要去优化这个变量while(1){for(i = 0; i < 10000000; i++){sum++;}xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);sum = 1;}
}void Task2Function( void * param)
{int val;while(1){flagCalcEnd = 0;xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d\r\n", val);}
}//main函数中
printf("hello go\r\n");xQueueCalcHandle = xQueueCreate(2, sizeof(int));
if(xQueueCalcHandle == NULL){printf("can not create queue\r\n");
}xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

        使用队列的方法实现了同步,flagCalcEnd在被拉高的时候,用时2s

3.2 互斥

static QueueHandle_t xQueueUARTHandle;int InitUARTLock(void)
{int val;xQueueUARTHandle = xQueueCreate(1, sizeof(int));if(xQueueUARTHandle == NULL){printf("can not create queue\r\n");return -1;}xQueueSend(xQueueUARTHandle, &val, portMAX_DELAY);return 0;
}void GetUARTLock(void)
{int val;xQueueReceive(xQueueUARTHandle, &val, portMAX_DELAY);
}void PutUARTLock(void)
{int val;xQueueSend(xQueueUARTHandle, &val, portMAX_DELAY);
}void TaskGenericFunction( void * param)
{while(1){GetUARTLock();printf("%s\r\n", (char *)param);//在释放锁之前,任务三在等待PutUARTLock();	//在释放锁时,任务三进入就绪状态,但是此时任务四是运行状态,马上又要上锁,轮不到任务三去执行vTaskDelay(1);	//主动让一下/*除了通过vTaskDelay让出CPU资源,还有更合理的函数:使用taskYIELD(),主动发起—次任务切换vTaskDelay会让任务阻塞、暂停若干tick,taskYIELD()更合理可以设置不同的优先级来实现抢占*/}
}//main函数中
InitUARTLock();xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

         先初始化一个锁,这个锁用队列来实现,创建完队列,往里面随便写一个数据,队列里面有数据就表示别人可以来读取这个数据。假设某个任务要使用串口,先用GetUARTLock获得串口的锁,去读队列,得到这个队列的数据就表示得到这个串口的使用权,用完串口之后就往队列里随便写一个数据,表示使用完串口了,把这个使用权释放掉。

3.3 队列集

        创建两个队列Queue1和Queue2,Task1和Task2分别往这两个队列里写入数据,Task3使用Queue Set来监测这两个队列。队列集的长度是包含的队列长度之和。

static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueHandle1;
static QueueHandle_t xQueueHandle2;
static QueueSetHandle_t xQueueSet;void Task1Function(void * param)
{int i = 0;while (1){xQueueSend(xQueueHandle1, &i, portMAX_DELAY);i++;vTaskDelay(10);}
}void Task2Function(void * param)
{int i = -1;while (1){xQueueSend(xQueueHandle2, &i, portMAX_DELAY);i--;vTaskDelay(20);}
}void Task3Function(void * param)
{QueueSetMemberHandle_t handle;int i;while (1){/* 1. read queue set: which queue has data */handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);/* 2. read queue */xQueueReceive(handle, &i, 0);/* 3. print */printf("get data : %d\r\n", i);}
}//main函数中
TaskHandle_t xHandleTask1;/* 1. 创建2个queue */xQueueHandle1 = xQueueCreate(2, sizeof(int));
if (xQueueHandle1 == NULL)
{printf("can not create queue\r\n");
}xQueueHandle2 = xQueueCreate(2, sizeof(int));
if (xQueueHandle2 == NULL)
{printf("can not create queue\r\n");
}/* 2. 创建queue set */
xQueueSet = xQueueCreateSet(3);/* 3. 把2个queue添加进queue set */
xQueueAddToSet(xQueueHandle1, xQueueSet);
xQueueAddToSet(xQueueHandle2, xQueueSet);/* 4. 创建3个任务 */
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);

3.4 邮箱(mailboc)

        FreeRTOS的邮箱概念跟别的RTOS不一样,这里的邮箱称为"橱窗"也许更恰当:

                它是一个队列,队列长度只有1

                写邮箱:新数据覆盖旧数据,在任务中使用 xQueueOverwrite() ,在中断中使用 xQueueOverwriteFromISR() 。 既然是覆盖,那么无论邮箱中是否有数据,这些函数总能成功写入数据。

                读邮箱:读数据时,数据不会被移除;在任务中使用 xQueuePeek() ,在中断中使用 xQueuePeekFromISR() 。 这意味着,第一次调用时会因为无数据而阻塞,一旦曾经写入数据,以后读邮箱时总能成功。

        main函数中创建了队列(队列长度为1)、创建了发送任务、接收任务:

                发送任务的优先级为2,它先执行

                接收任务的优先级为1

/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{prvSetupHardware();/* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */xQueue = xQueueCreate( 1, sizeof(uint32_t) );if( xQueue != NULL ){/* 创建1个任务用于写队列* 任务函数会连续执行,构造buffer数据,把buffer地址写入队列* 优先级为2*/xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL );/* 创建1个任务用于读队列* 优先级为1*/xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建队列 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

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

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

相关文章

干货分享 —— 如何开展web项目自动化测试!

前言 自动化(Automation)是指机器设备、系统或过程(生产、管理过程)在没有人或较少人直接参与的情况下&#xff0c;根据人的要求&#xff0c;通过自动检测、信息处理、分析判断、操纵和控制&#xff0c;达到预期目标的过程。自动化测试是指测试过程是在没有人为或较少人为干预…

从零构建vue3+ts+vite项目打包及项目依赖配置

❗️❗️❗️❗️ 写在最前: 本文是根据B站作者 月光分层 视频vuets 工程化配置以及作者笔记稍作整理 &#x1f496;&#x1f496;作者B站地址https://space.bilibili.com/14110850 &#x1f496;&#x1f496;视频教程地址vuets 工程化配置 &#x1f496;&#x1f496;作者微信…

【计算机毕业设计】安卓054基于Android校园助手

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

LAMP源码编译安装——CentOS7

文章目录 LAMP是什么LAMP软件组件LinuxApacheMySQLPHP 源码安装Apache一、准备工作二、安装环境依赖包三、配置软件模块四、编译及安装五、优化配置文件路径六、添加httpd系统服务&#xff08;有两种方法&#xff09;方法一&#xff1a;方法二&#xff1a; 七、修改httpd 服务配…

公告:关于博主的重要通知

大家好&#xff0c;我是博主夏目。 本期不分享知识&#xff0c;博主想说明一下博主的一些重要提示。 分享的内容&#xff0c;从不收费&#xff0c;也未向任何人进行收费。 意在分享知识&#xff0c;传播文化&#xff0c;结交更多志同道合的朋友。 截至目前&#xff0c;从未…

《C++ Primer Plus》第十二章复习题和编程练习

目录 一、复习题二、编程练习 一、复习题 1. 假设String类有如下私有成员&#xff1a; // String 类声明 class String { private: char* str;int len;// ... };a. 下述默认构造函数有什么问题&#xff1f; String::String() { } // 默认构造函数b. 下述构造函数有什么问题…

VirtualBox+Ubuntu22.10+Docker+ROS2

Docker 拉取ros2镜像 docker pull osrf/ros:foxy-desktop 运行 docker run -it --nameros2 -p 50022:22 osrf/ros:foxy-desktop 进入容器安装组件 apt-get update apt-get install vim apt-get install git apt-get install net-tools # 安装ssh apt-get install openssh…

centos下给es7.12.1设置密码

安装可参考&#xff1a; centos7下安装elasticsearch7.8.1并配置远程连接_在一台服务器centos7上安装和配置elasticsearch。-CSDN博客 1、先停掉es进程 2、设置输入密码后访问配置 cd /home/soft/elasticsearch-7.12.1/config vim elasticsearch.yml 3、启动es服务 cd /home/…

香橙派 AIpro开发板初上手

一、香橙派 AIpro开箱 最近拿到了香橙派 AIpro&#xff08;OrangePi AIpro&#xff09;&#xff0c;下面就是里面的板子和相关的配件。包含主板、散热组件、电源适配器、双C口电源线、32GB SD卡。我手上的这个是8G LPDDR4X运存的版本。 OrangePi AIpro开发板是一款由香橙派与华…

在Python中实现限定抽奖次数的机制

目录 一、引言 二、需求分析 三、设计思路 四、代码实现 4.1 使用字典存储用户抽奖次数 4.2 使用数据库存储用户抽奖次数 五、扩展与优化 六、总结 一、引言 在当今互联网应用中&#xff0c;抽奖系统作为吸引用户、提高用户参与度和活跃度的重要手段&#xff0c;已经被…

为什么配置了安全组还是有攻击进来?

面对DDoS攻击&#xff0c;即使配置了安全组规则来限制入站流量&#xff0c;攻击者仍可能找到绕过这些基本防护措施的方法&#xff0c;尤其是当攻击流量巨大时。这是因为安全组主要工作在网络层和传输层&#xff0c;它们依据IP地址、协议和端口号来过滤流量&#xff0c;对于应用…

Android14 WMS-窗口添加流程(一)-Client端

窗口布局在onCreate方法中通过setContentView(R.layout.xxx)加载&#xff0c;但窗口的显示并不是在wm_on_create_called中, 而是在wm_on_resume_called后&#xff0c;也就是说应用onResume时此窗口是不可见的&#xff0c;真正可见是当此window窗口的mDrawState变化状态从NO_SUR…

Raven2掠夺者2渡鸦2游戏预约注册教程 账号注册教程

《渡鸦2》是一款源自韩国的创新力作&#xff0c;作为《Raven》系列的最新续篇&#xff0c;这款游戏在MMORPG手游领域内再度扩展了其标志性的暗黑奇幻宇宙&#xff0c;融入了大量革新的游戏设计与丰富内容。定档于2024年5月29日开启公测的《渡鸦2》&#xff0c;正处在紧张刺激的…

blender复制uv贴图

1、新建两个猴头 2、点击其中一个进入uv编辑模式 3、在uv编辑中打开一个图像 4、新建一个材质球&#xff0c;将图像渲染到模型上 打开图像纹理 选择刚才打开的图像 切换到材质预览模式后&#xff0c;就可以看到贴图了 5、选择一个孤岛 6、然后选择拼排孤岛 可以看到该模型展开…

【全开源】JAVA人力资源招聘社会校招类型招聘系统校园招聘PC端

塑造企业高效招聘新体验 一、源码简介 招聘PC端源码&#xff0c;一款面向企业的招聘管理系统解决方案。它拥有完整的招聘流程管理功能&#xff0c;从职位发布到候选人管理&#xff0c;再到面试安排与结果反馈&#xff0c;所有环节都通过直观易用的界面进行展现&#xff0c;大…

Vivado打开之前项目仿真过的波形文件

第一步&#xff1a;顶部菜单 点击&#xff1a;Open Static Simulation 然后在弹出的窗口找到.sim结尾的文件夹&#xff0c;在里面找到wdb结尾的文件&#xff0c;点击ok 第二步&#xff1a;依次点击下方红圈 找到wcfg结尾的文件&#xff0c;点击ok即可

第十三届蓝桥杯国赛大学B组填空题(c++)

A.2022 动态规划 AC; #include<iostream> #define int long long using namespace std; int dp[2050][15]; //dp[i][j]:把数字i分解为j个不同的数的方法数 signed main(){dp[0][0]1;for(int i1;i<2022;i){for(int j1;j<10;j){//一种是已经分成j个数,这时只需每一个…

坦克飞机大战项目详解:从包结构到测试发布

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、项目初始化与包结构构建 代码案例&#xff1a; 二、资源文件与配置文件管理 代码案例…

MySQL简单测试和安装

MySQL 的特点 1、MySQL 性能卓越、服务稳定&#xff0c;很少出现异常宕机。 2、MySQL开放源代码且无版权制约&#xff0c;自主性及使用成本低。 3、MySQL历史悠久(版本众多)&#xff0c;用户使用活跃&#xff0c;遇到问题可以寻求帮助。 4、MySQL体积小(相对大型关系型数据库)…

linux安装mysql后,配置mysql,并连接navicate软件

Xshell连接登陆服务器 输入全局命令 mysql -u root -p 回车后&#xff0c;输入密码&#xff0c;不显示输入的密码 注意mysql服务状态&#xff0c;是否运行等 修改配置文件my.cnf&#xff0c;这里没找到就找my.ini&#xff0c;指定有一个是对的 find / -name my.cnf 接下…