FreeRTOS基础(十一):消息队列

     本文将详细全方位的讲解FreeRTOS的消息队列,其实在FreeRTOS中消息队列的重要性也不言而喻,与FreeRTOS任务调度同等重要,因为后面的各种信号量基本都是基于消息队列的。

目录

一、消息队列的简介

1.1 产生的原因

1.2 消息队列的解决办法

1.3 消息队列的基本概念

1.4 队列的特点

1.5 队列的入队和出队

1.6 队列操作基本过程

二、队列结构体介绍

三、队列相关API函数介绍

3.1 消息队列的使用流程

3.2 创建消息队列API函数

3.3 往队列写入消息API函数

3.4 从队列读取消息API函数

四、队列操作实验

五、队列相关API函数解析(了解)


一、消息队列的简介

1.1 产生的原因

       假设有一个全局变量a = 0,现有两个任务都在对变量a进行自增操作(写操作),如下图所示:

         对于自增操作它不是原子操作,会经过一系列的步骤,最后才将自增后的结果写入到寄存器,如果任务2的优先级高于任务1,在任务1将自增后的2即将写入到a之前,任务2打断任务1进行自增操作,任务2拿到的是a为1,然后再进行的自增,两个任务执行完后自增其实只进行了一次数据无保护,导致数据不安全,当多个任务同时对该全局变量操作时,数据易受损!!其实这就像是Linux中的由于线程的并发运行,导致线程对进程资源的竞争问题,不过在这里是由于任务的优先级影响的。

       因此,使用全局变量并不是安全的,于是产生了消息队列。消息队列可以理解成带中断保护的全局数组,它是用来存放数据的,我们是将他设计成一个队列结构体。

1.2 消息队列的解决办法

      使用队列的情况如下:

队列依旧是来存放数据的,可以看成是全局数组,

       我们对于队列的读写封装好了函数,并且在这个函数里面进行了保护(关闭中断带来的)关闭中断后,管理范围内的中断不会再响应,并且也不会进行任务的切换(PendSV任务切换中断也被关闭),也就是中断和其他任务都不能打断当前任务向队列中写入数据!,同理从队列读取数据也一样,这样保证同一时刻只有一个任务向队列写入数据或者从队列读取数据!因此,他可以做到:防止多任务同时访问冲突;我们只需要直接调用API函数(可以理解Linux中的可重入函数/线程安全函数)即可,简单易用!

1.3 消息队列的基本概念

       消息队列是任务到任务任务到中断中断到任务数据交流的一种机制(进行消息传递FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量因此很有必要深入了解 FreeRTOS 的队列 。在消息队列中可以存储数量有限、大小固定的数据,可以将队列理解为一个全局数组。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。如下图所示:

我们在进行创建队列时,就必须要指定队列长度以及队列项目的大小!,然后操作系统就会为我们分配相应的内存空间!

1.4 队列的特点

     当任务从队列中读取消息(出队)时,如果此时队列为空,则此任务将进入阻塞状态,等待消息队列不为空。 用户可以指定阻塞等待的时间,当等待的时间超过阻塞等待时间,任务将结束阻塞状态,转为就绪态。同理,入队也是如此。我们可以指定的阻塞时间如下:

①若阻塞时间为:   0 :代表直接返回不会等待;(不进行阻塞)
②若阻塞时间为:0~port_MAX_DELAY (最大值0xffffffff) :代表等待设定的阻塞时间(一段时间内阻塞),若在该时间内还无法入队,超时后直接返回不再等待;
③若阻塞时间为:   port_MAX_DELAY(最大值0xffffffff) :代表死等,(一直阻塞)一直等到可以入队为止。

1.5 队列的入队和出队

      注意:我们通过前面学习知道:每个任务对应一个任务控制块,它就是一个结构体,里面有状态列表项和事件列表项两个成员。

队列满了,此时写不进去数据:

 ①将该任务的状态列表项挂载在pxDelayedTaskList(阻塞状态)

 ②将该任务的事件列表项挂载在xTasksWaitingToSend(等待发送)

队列为空,此时读取不了数据:

 ①将该任务的状态列表项挂载在pxDelayedTaskList (阻塞状态)

 ②将该任务的事件列表项挂载在xTasksWaitingToReceive(等待接收);

思考一个问题:

         当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务    在等待同一个队列的空间。那当队列中有可以写入的空间时,哪个任务会进入就绪态? 

答:

  1、优先级最高的任务

  2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态

1.6 队列操作基本过程

二、队列结构体介绍

     队列结构体如下:

可以看到:里面有一个联合体,我们知道后面学的各种信号量都是基于队列实现的,那这个联合体就根据不同的结构,使用不同的成员。

队列结构体整体示意图:主要有两部分组成:队列结构体/队列控制块和队列项(存储数据的)构成。

三、队列相关API函数介绍

3.1 消息队列的使用流程

         在使用队列之前,我们一定要明确队列的使用流程,这样就会形成编码规范,不容易出错,使用队列的主要流程:创建队列 ——》写队列 ——》 读队列。

3.2 创建消息队列API函数

       创建消息队列函数主要有以下两个:

函数

描述

xQueueCreate()

动态方式创建队列

xQueueCreateStatic()

静态方式创建队列

动态和静态创建队列之间的区别:动态创建队列所需的内存空间由 FreeRTOS FreeRTOS 管理的堆中分配,而静态创建需要用户自行分配内存。我们通常用的是动态创建队列。

此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS FreeRTOS 管理的堆中分配 ,可以看到,真正起作用的函数是:xQueueGenericCreate()

形参

描述

uxQueueLength

队列长度

uxItemSize

队列项目的大小

返回值

描述

NULL

队列创建失败

其他值

队列创建成功,返回队列句柄

xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE )) 

底层调用的是上述函数,前面两个参数很容易理解,但是最后这个呢?又该如何理解?前面说 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:

 可以看到,使用哪个功能,第三个参数底层就会使用相应的宏定义。

       从上面我们可以知道,引入队列的头文件,创建队列,我们需要提前定义好消息队列句柄,接收其返回值用于后续访问该消息队列,同时应该传入队列长度参数和队列项目大小参数。

3.3 往队列写入消息API函数

     向队列中写入消息一共有以下函数:

往队列写入消息函数入口参数解析:

形参

描述

xQueue

待写入的队列(它的类型是一个队列句柄)

pvItemToQueue

待写入消息 (传的是一个地址)

xTicksToWait

阻塞超时时间

xCopyPosition

写入的位置

返回值

描述

pdTRUE

队列写入成功

errQUEUE_FULL

队列写入失败

 从上面我们可以知道,往队列中写入消息,传入提前定义好消息队列句柄,以及写入消息的地址,和传入如果队列已经满时,该任务的阻塞时间即可!

3.4 从队列读取消息API函数

      从队列读取消息API函数:

 从上面我们可以知道,读取队列中消息,传入提前定义好消息队列句柄,以及读取消息存放的地址,和传入如果队列当前为空时,该任务的阻塞时间即可!

四、队列操作实验

创建任务文件

#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "queue.h"


/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/

#define        START_TASK_STACK_SIZE  128   //定义堆栈大小为128字(1字等于4字节)
#define        START_TASK_PRIO         1    //定义任务优先级,0-31根据任务需求
TaskHandle_t   start_task_handler;    //定义任务句柄(结构体指针)
void start_task(void* args);




/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK1_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK1_PRIO         2             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task1_handler;           //定义任务句柄(结构体指针)
void task1(void* args);




/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK2_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK2_PRIO         3             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task2_handler;           //定义任务句柄(结构体指针)
void task2(void* args);



/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK3_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK3_PRIO         4             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task3_handler;           //定义任务句柄(结构体指针)
void task3(void* args);


QueueHandle_t  key_queue;                 //小数据队列句柄
QueueHandle_t  big_date_queue;            //大数据队列句柄
char buff[100]="我是大数组12345678911111111111";


/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{
	taskENTER_CRITICAL();        /*进入临界区*/
	
	xTaskCreate( (TaskFunction_t)         task1,
                             (char *)     "task1",  
              ( configSTACK_DEPTH_TYPE)   TASK1_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK1_PRIO ,
                        (TaskHandle_t *)  &task1_handler );
	

     xTaskCreate( (TaskFunction_t)         task2,
                             (char *)     "task2",  
              ( configSTACK_DEPTH_TYPE)   TASK2_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK2_PRIO ,
                        (TaskHandle_t *)  &task2_handler );							

							
	 xTaskCreate( (TaskFunction_t)         task3,
                             (char *)     "task3",  
              ( configSTACK_DEPTH_TYPE)   TASK3_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK3_PRIO ,
                        (TaskHandle_t *)  &task3_handler );	
							
							
	vTaskDelete(NULL);    //删除开始任务自身,传参NULL
							
	taskEXIT_CRITICAL();   /*退出临界区*/
		

    //临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式						
}




/********任务1的任务函数,无返回值且是死循环***********/

/*****任务1:实现入队*******/
void task1(void* args)
{
	uint8_t  key=0;
	char *buf=buff;
	BaseType_t   xReturn;
	
	while(1)
	{
		 key=KEY_Scan(0);            //按键扫描得到键值
		 if(key==KEY0_PRES ||key==KEY1_PRES )
		 {
			xReturn = xQueueSend( key_queue, &key, portMAX_DELAY );  //将键值写入队列
            if(xReturn !=pdTRUE )
			{
				printf("key_queue队列发送失败!\n");
			}
		 }
		 else if(key==WKUP_PRES)
		 {
			xReturn = xQueueSend( big_date_queue, &buf, portMAX_DELAY );//将键值写入队列
            if(xReturn !=pdTRUE )
			 if(xReturn !=pdTRUE )
			{
				printf("big_date_queue队列发送失败!\n");
			}
		 }
         vTaskDelay(10);       //FreeRTOS自带的延时函数,延时10毫秒
	
	}	
}




/********任务2的任务函数,无返回值且是死循环***********/

/***任务2:实现小数据出队*******/
void task2(void* args)
{
	uint8_t  key = 0;
	BaseType_t   xReturn;
	while(1)
	{
		 xReturn = xQueueReceive( key_queue,&key,portMAX_DELAY);
         if(xReturn !=pdTRUE )
	     {
			 printf("key_queue队列读取失败!\n");
		  } 
          else
		  {
			  printf("key_queue队列读取成功,数据:%d\n",key);
		  }			  
	
        //不需要延时,因为队列为空,该任务会直接会被阻塞,挂载到阻塞列表下面
	
	}	
}




/********任务3的任务函数,无返回值且是死循环***********/

/***任务3:实现大数据出队*******/
void task3(void* args)
{
	BaseType_t   xReturn;
	char * buf;
	while(1)
	{
	    xReturn = xQueueReceive( big_date_queue,&buf,portMAX_DELAY);
		if(xReturn !=pdTRUE )
	    {
			 printf("big_date_queue队列读取失败!\n");
		} 
        else
		{
			  printf("big_date_queue队列读取成功,数据:%s\n",buf);
		}	
		
	
	}	
}

/****如果按键未按下,消息队列未不会有数据,任务2和任务3都会进入阻塞态****/


//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
	    /***队列创建***/
	    key_queue = xQueueCreate( 2, sizeof(uint8_t) );
        if(key_queue !=NULL)
		{
			printf("key_queue队列创建成功!\n");
		}
	    
		big_date_queue = xQueueCreate( 1, sizeof(char *) );
        if(big_date_queue !=NULL)
		{
			printf("big_date_queue队列创建成功!\n");
		}
		
		
		
		/***开始任务的创建***/
	    xTaskCreate( (TaskFunction_t)     start_task,
                             (char *)     "start_task",  
              ( configSTACK_DEPTH_TYPE)   START_TASK_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) START_TASK_PRIO ,
                        (TaskHandle_t *)  &start_task_handler );
							
							
	vTaskStartScheduler();  //开启任务调度器
	
}




主函数任务调度文件

#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"


#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"

extern TaskHandle_t Start_Handle;

int main(void)
{
    //硬件初始化
     My_UsartInit();
    
	
	
	
	 //调用入口函数
     freertos_demo();
	 
}

五、队列相关API函数解析(了解)

至此,已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见! 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Django 部署指南

部署 Django 应用程序涉及将我们的应用程序从开发环境部署到生产环境,并确保它可以在生产服务器上安全运行和扩展。其实了解几种部署方案,相信你对将来的项目更得心应手。 1、问题背景 Django 是一款流行的 Python Web 框架,但对于新手来说&…

618网购节,电商能挡住恶意网络爬虫的攻击吗?

目录 爬虫盗取电商数据的步骤 电商平台如何发现网络爬虫? 如何拦截违法网络爬虫 2023年,杭州中院审结了两起涉及“搬店软件”的不正当竞争案件。本案的原告是国内某大型知名电子商务平台的运营主体,而被告则是开发了一款名为“某搬家快速商品…

【数据结构】——线性表(顺序表)——内有代码详解

目录 一、引言 二、线性表 2.1 定义 2.2 特点 三、顺序表 3.1 顺序表的概念 3.2 顺序表的特点 3.3 顺序表的定义 3.3.1 静态定义 3.3.2 动态定义 3.4 顺序表的初始化 3.4.1 静态初始化 3.4.2 动态初始化 3.5 顺序表的销毁 3.6 顺序表元素的打印 3.7 顺序表的插入…

百度AI大底座

“百度AI大底座”是源自百度多年产业深度实践积累、结合AI全栈技术科研成果打造的国内首个全栈自研的AI基础设施, 面向企业和产业AI开发与应用提供端到端自主可控、自我进化的解决方案,能够快捷、低成本地实现“AI能力的随用随 取”。AI大底座可助力企业…

Python 学习flask创建项目

1、使用pycharm创建flask项目 2、运行访问地址 3、可以看到访问地址内容 4、可以增加路由,尝试访问获取参数

树莓派4B_OpenCv学习笔记4:测试摄像头_imread加载显示图像_imwrite保存图片

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1: 今日对之前的测试CSI摄像头函数进行一些理解说明&#x…

Shell脚本文本处理三剑客(grep、awk、sed)和正则表达式

一、正则表达式 1.正则表达式基础 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串,将匹配的子串替换或者从某个串中取出符号某个条件的子串等&…

【微信小程序】页面事件

下拉刷新 上拉触底 上拉触底距离指的是触发上拉触底事件时,滚动条距离页面底部的距离。 可以在全局或页面的json配置文件中,通过onReachBottomDistance属性来配置上拉触底的距离。 小程序默认的触底距离是50x,在实际开发中,可以根据自己的需…

【C++】─篇文章带你熟练掌握 map 与 set 的使用

目录 一、关联式容器二、键值对三、pair3.1 pair的常用接口说明3.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.2 [有参构造函数 / 拷贝构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.3 [有参构造函数](htt…

vue3 基于el-tree增加、删除节点(非TypeScript 写法)

话不多说&#xff0c;直接贴代码 <template><div class"custom-tree-container"><!-- <p>Using render-content</p><el-tree style"max-width: 600px" :data"dataSource" show-checkbox node-key"id" …

智能网联汽车信息安全风险识别与应对策略研究综述

摘要&#xff1a;随着智能网联汽车技术的飞速发展&#xff0c;其信息安全问题逐渐成为公众关注的焦点。本文概述了智能网联汽车技术的发展背景和信息安全风险的来源&#xff0c;采用STRIDE威胁分析方法对智能网联汽车的四层模型进行风险识别&#xff0c;进一步探讨了抗女巫攻击…

Renesas MCU之FreeRTOS的应用

目录 概述 1 FSP配置FreeRTOS 1.1 软件版本信息 1.2 配置FreeRTOS 2 FreeRTOS的Task 2.1 FSP下的项目结构 2.2 Task代码 2.2.1 Task测试案例配置 2.2.2 测试代码实现 3 自定义Task 3.1 编写代码 3.2 测试函数 4 测试 4.1 Task断点测试 4.2 板卡运行测试 概述 …

spring boot sso

代码&#xff1a;https://gitee.com/forgot940629/ssov2 授权服务 登录成功后&#xff0c;session中会存储UsernamePasswordAuthenticationToken&#xff0c;之后每次请求code时都会用UsernamePasswordAuthenticationToken生成OAuth2Authentication&#xff0c;并将OAuth2Aut…

动态规划(多重背包问题+二进制优化)

引言 多重背包&#xff0c;相对于01背包来说&#xff0c;多重背包是每个物品会有相应的个数&#xff0c;最多可以选那么多个&#xff0c;因而对于朴素多重背包&#xff0c;需要在01背包的基础上&#xff0c;再加一层物品的循环 朴素多重背包例题 P2347 [NOIP1996 提高组] 砝…

【FAS】《Liveness Detection on Face Anti-spoofing》

文章目录 原文总结与评价CNN-RNN vs 三维卷积作者的方法 原文 [1]欧阳文汉.反人脸图像欺诈的活体识别方法研究[D].浙江大学,2020.DOI:10.27461/d.cnki.gzjdx.2020.002675. 总结与评价 时序运动信息与传统的空间纹理信息相结合 基于相位平移的运动放大算法不错 视觉大小细胞…

【Python报错】已解决Attributeerror: ‘list‘ object has no attribute ‘join‘( Solved)

解决Python报错&#xff1a;AttributeError: ‘list’ object has no attribute ‘join’ (Solved) 在Python中&#xff0c;字符串&#xff08;str&#xff09;对象有一个非常有用的join()方法&#xff0c;它允许你将序列中的元素连接&#xff08;join&#xff09;成一个字符串…

深入理解C++三五零法则

三五零法则就是三法则&#xff08;The Rule of Three&#xff09;、五法则&#xff08;The Rule of Five&#xff09;、零法则&#xff08;The Rule of Zero&#xff09;。三五零法则是和C的特殊成员函数有关&#xff0c;特别是那些涉及对象如何被创建、复制、移动和销毁的函数…

苹果不会在WWDC 2024中推出任何搭载M4芯片的Mac电脑

虽然苹果公司已在上月推出了首搭 M4 芯片的 iPad Pro&#xff0c;不过彭博社的马克・古尔曼在最近的实时通讯中透露苹果公司不会在即将进行的 WWDC 2024 开发者大会中推出任何搭载 M4 芯片的 Mac 电脑&#xff08;不会推出任何硬件产品&#xff09;。 此前报道&#xff0c;苹果…

如何自动生成数据库的样本数据(以MySQL和SQLynx为例)

目录 1 功能概述 2 主要特点 3 使用场景 4 使用示例 5 结论 SQLynx 是一款领先的 SQL 集成开发环境&#xff08;IDE&#xff09;&#xff0c;其强大的功能得到了全球用户的广泛认可。SQLynx 不仅在数据库管理和 SQL 查询方面表现出色&#xff0c;还提供了一项特别实用的功能…

【Python报错】已解决AttributeError: ‘method‘ object has no attribute ‘xxx‘

解决Python报错&#xff1a;AttributeError: ‘method’ object has no attribute ‘xxx’ 在Python中&#xff0c;AttributeError通常表明你试图访问的对象没有你请求的属性或方法。如果你遇到了AttributeError: method object has no attribute xxx的错误&#xff0c;这通常意…