13_FreeRTOS消息队列

news2024/11/29 12:39:21

目录

队列简介

FreeRTOS队列特点

队列操作基本过程 

队列结构体介绍

队列结构体整体示意图

队列相关API函数介绍

创建队列相关API函数介绍

往队列写入消息API函数

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

从队列读取消息API函数

实验源码


队列简介

队列是任务到任务任务到中断、中断到任务数据交流的一种机制(消息传递)

类似全局变量?假设有一个全局变量a = 0,现有两个任务都在写这个变量a

假设在任务1完成2步骤修改数据的时候,此时任务2优先级高打断任务1,a还是0因为任务a还没有写数据,任务2执行了加1,在回到任务1时,下一步是执行写数据此时r0是任务2执行完时候的1,任务1在吧R0赋值给0,相当于r0等于1在给a,并没有等于2。这样就照成了数据的受损。

全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损

使用队列的情况如下:

 读写队列做好了保护,防止多任务同时访问冲突;FreeRTOS基于队列,实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、递归互斥信号量,因此很有必要深入了解 FreeRTOS的队列。

在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目” ,队列能够存储“队列项目”的最大数量称为队列的长度

 队列长度为:5个

队列项目大小为:10字节

队列长度和队列项目不是固定的是创建的时候自己指定的。

FreeRTOS队列特点

1.数据入队出队方式:队列通常采用“先进先出”(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取,FreeRTOS中也可以配置为“后进先出”LIFO方式

2.数据传递方式:FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递,FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递

3.多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息

4.出队入队阻塞:当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队

        ①若阻塞时间为:0直接返回不会等待;

        ②若阻塞时间为0~port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队, 超时后直接返回不再等待;

        ③若阻塞时间为port_MAX_DELAY:死等,一直等到可以入队为止。出队阻塞与入队阻塞 类似;

入队阻塞:

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

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

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

出队阻塞:

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

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

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

当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务在等待同一 个队列的空间。那当队列中有空间时,优先级最高的任务会进入就绪态,如果优先级相同,那等待时间最久的任务回进入就绪态。

队列操作基本过程 

1.创建队列

2.往队列写入第一个消息

 3.往队列写入第二个消息

4.从队列读取第一个消息

队列结构体介绍

typedef struct QueueDefinition
{
int8_t* pcHead;		/* 存储区域的起始地址*/
int8_t*pcWriteTo;		/* 下一个写入的位置*/
Union
{
QueuePointers_t  xQueue;		   /*消息队列时使用*/
SemaphoreData_t  xSemaphore;  /*互斥信号量时使用*/	
}u;
List_t xTasksWaitingToSend;		/*等待发送列表*/
List_t xTasksWaitingToReceive		/* 等待接收列表*/
;volatile UBaseType_t uxMessagesWaiting; /*非空闲队列项目的数量*/
UBaseType_t  uxLength;		/*队列长度*/
UBaseType_t  uxltemSize;		/*队列项目的大小*/
volatile int8_t  cRxLock;			/* 读取上锁计数器*/
volatile int8_t cTxLock;			/*写入上锁计数器*/
/*其他的一些条件编译*/
}xQUEUE;

当用于队列使用时:

typedef struct QueuePointers
{
int8_t* pcTail;			/*存储区的结束地址*/
int8_t* pcReadFrom;	/*最后一个读取队列的地址*/
}QueuePointers_t;

当用于互斥信号量和递归互斥信号量时:

typedef struct SemaphoreData
{
TaskHandle_t  xMutexHolder;  /*互斥信号量持有者*/
UBaseType_t  uxRecursiveCallCount;	/*递归互斥信号量的获取计数器*/
}SemaphoreData_t;

队列结构体整体示意图

队列相关API函数介绍

创建队列相关API函数介绍

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

#define xQueueCreate ( uxQueueLength, uxltemSize)
xQueueGenericCreate( ( uxQueueLength ), ( uxltemSize ), (queueQUEUE_TYPE_BASE ))

此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配

 

 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:

#define queueQUEUE_TYPE_BASE    ((uint8_t) OU)/* 队列 */
#define queueQUEUE_TYPE_SET	  ((uint8_t) OU)/* 队列集*/
#define queueQUEUE_TYPE_MUTEX   ((uint8_t) 1U)/*互斥信号量*/
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE  ((uint8_t) 2U)/*计数型信号量*/
#define queueQUEUE_TYPE_BINARY_SEMAPHORE   ((uint8_t) 3U)/*二值信号量*/
#define queueQUEUE_TYPE_RECURSIVE_MUTEX   ((uint8_t) 4U)/* 递归互斥信号量*/

往队列写入消息API函数

 任务级往队列写入消息

/*往队列的尾部写入消息*/
#define xQueueSend( xQueue, pvltemToQueue, xTicksToWait)
xQueueGenericSend( (xQueue ), ( pvltemToQueue ), (xTicksToWait), queueSEND_TO_BACK)

/*往队列的尾部写入消息*/
#define xQueueSendToBack( xQueue, pvltemToQueue, xTicksToWait)
xQueueGenericSend((×Queue ), ( pvltemToQueue ), (xTicksToWait), queueSEND_TO_BACK)



/*往队列的头部写入消息*/
#define xQueueSendToFront( xQueue, pvltemToQueue, xTicksToWait)
xQueueGenericSend((xQueue), (pvltemToQueue), (xTicksToWait), queueSEND_TO_FRONT)

/*覆写队列消息(只用于队列长度为1的情况)*/
#define xQueueOverwrite( xQueue, pvitemToQueue )
xQueueGenericSend( (xQueue ), ( pvitemToQueue ), 0, queueOVERWRITE )

可以看到这几个写入函数调用的是同一个函数xQueueGenericSend(),只是指定了不同的写入位置!

队列一共有 3 种写入位置:

#define queueSEND_TO_BACK		((BaseType_t) 0)/*写入队列尾部*/
#define queueSEND_TO_FRONT 	((BaseType_t) 1)/*写入队列头部*/
#define queueOVERWRITE 			 ((BaseType_t) 2)/*覆写队列*/

注意:覆写方式写入队列,只有在队列的队列长度为1时,才能够使用

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

BaseType_t xQueueGenericSend(QueueHandle_t  xQueue, 
QueueHandle_t  xQueue,
TickType_t xTicksToWait,
const BaseType_t  xCopyPosition );

 

从队列读取消息API函数

/*从队列头部读取消息,并删除消息*/
BaseType_t xQueueReceive(
QueueHandle_t xQueue, 
void* const pvBuffer, 
TickType_t xTicksToWait )

此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。

 

/*从队列头部读取消息不会删除*/
BaseType_t xQueuePeek(
QueueHandle_t xQueue, 
void*const pvBuffer, 
TickType_t xTicksToWait)

此函数用于在任务中,从队列中读取消息,但与函数xQueueReceive()不同,此函数在成功读取消息后,并不会移除已读取的消息!

实验源码

start_task  用来创建task1和task2以及task3任务

task1 当按键key0或key1按下,将键值拷贝到队列key_queue(入队)当按键kev_up按  下,将传输大数据,这里拷贝大数据的地址到队列big_date_queue中

task2 读取队列key_queue中的消息(出队)打印出接收到的键值

task3 从队列big_date_queue读取大数据地址,通过地址访问大数据

/**
  ******************************************************************************
  * @file           : user_mian.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "user_key.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/ 

QueueHandle_t  key_queue;  		/*小数据句柄*/
QueueHandle_t  big_date_queue;  /*大数据句柄*/
char buff[100] = {" 大数组 12324164985465484684866"};
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);


//任务优先级
#define TASK1_PRIO			2
//任务堆栈大小	
#define TASK1_STK_SIZE 		100  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);


//任务优先级
#define TASK2_PRIO			2
//任务堆栈大小	
#define TASK2_STK_SIZE 		100  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);



//任务优先级
#define TASK3_PRIO			2
//任务堆栈大小	
#define TASK3_STK_SIZE 		100  
//任务句柄
TaskHandle_t Task3_Handler;
//任务函数
void task3(void *pvParameters);

 int main(void)
 {	

	/*配置系统中断分组为4位抢占*/
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	 /*延时函数初始化*/
	 delay_init();
	/*RCC配置*/
	 Rcc_config();
	/*GPIO初始化*/ 
	 Gpio_Init();
	/*USART1初始化*/
	 Uart1_Init(9600);
	 /*创建小数据队列*/ 
	 key_queue = xQueueCreate(2,sizeof(uint8_t));
	 if(key_queue != NULL)
	 {
		printf("key_queue队列创建!!\r\n\r\n");
	 }else 
	 {
		printf("key_queue队列创建失败!!\r\n\r\n");	 
	 }
	 /*创建大数据队列*/	 
	 big_date_queue = xQueueCreate(1,sizeof(char *));
	 if(key_queue != NULL)
	 {
		printf("big_date_queue队列创建!!\r\n\r\n");
	 }else 
	 {
		printf("big_date_queue队列创建失败!!\r\n\r\n");	 
	 }	 
	 
	 /*创建开始任务*/
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
		

}
 

/*!
	\brief		开始任务函数
	\param[in]	传递形参,创建任务时用户自己传入
	\param[out]	none
	\retval 	none
*/
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建任务1
    xTaskCreate((TaskFunction_t )task1,     	
                (const char*    )"task1",   	
                (uint16_t       )TASK1_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )TASK1_PRIO,	
                (TaskHandle_t*  )&Task1_Handler);   
    //创建任务2
    xTaskCreate((TaskFunction_t )task2,     
                (const char*    )"task2",   
                (uint16_t       )TASK2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2_Handler); 
	//创建任务3
    xTaskCreate((TaskFunction_t )task3,     
                (const char*    )"task3",   
                (uint16_t       )TASK3_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&Task3_Handler);

				
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


/*!
	\brief		task1实现入队
	\param[in]	传递形参,创建任务时用户自己传入
	\param[out]	none
	\retval 	none
*/
void task1(void *pvParameters)
{
	uint8_t key = 0;
	BaseType_t err = 0;
	char * buf;
	buf = &buff[0];
	
    while(1)
    {	
		/*获取按键值*/
		key = Key_Scan(0);
		/*写入小数据*/
		if(key == KEY0_PRES || key == KEY1_PRES)
		{
			/*写入按键值,队列满就死等*/
			err = xQueueSend(key_queue,&key,portMAX_DELAY);
			
			if(err != pdTRUE)
			{
				printf("key_queue队列小数据发送失败\r\n\r\n");
			}
		}else if(key == WKUP_PRES)
		{	
			/*写入大数据,队列满就死等*/
			err = xQueueSend(big_date_queue,&buf,portMAX_DELAY);
			
			if(err != pdTRUE)
			{
				printf("big_date_queue队列大数据发送失败\r\n\r\n");
			}
		}
		vTaskDelay(100);
    }
} 


/*!
	\brief		task2实现小数据出队
	\param[in]	传递形参,创建任务时用户自己传入
	\param[out]	none
	\retval 	none
*/
void task2(void *pvParameters)
{
	uint8_t key = 0;
	uint8_t err = 0;
    while(1)
    {
		/*接受小数据,队列空死等*/	
		err = xQueueReceive(key_queue,&key,portMAX_DELAY);
		
		if(err != pdTRUE)
		{
			printf("key_queue队列小数据读取失败\r\n\r\n");
		}else
		{
			printf("读取key_queue队列成功,数据:%d\r\n\r\n",key);
		}
    }
}

/*!
	\brief		task3实现大数据出队
	\param[in]	传递形参,创建任务时用户自己传入
	\param[out]	none
	\retval 	none
*/
void task3(void *pvParameters)
{
	char * buf;
	uint8_t err = 0;
    while(1)
    {
		/*接受大数据,队列空死等*/	
		err = xQueueReceive(big_date_queue,&buf,portMAX_DELAY);
		
	 	if(err != pdTRUE)
		{
			printf("big_date_queue队列大数据读取失败\r\n\r\n");
		}else
		{
			printf("读取big_date_queue队列成功,数据:%s\r\n\r\n",buf);
		}		
    }
}

 /************************************************************** END OF FILE ****/

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

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

相关文章

开发微服务电商项目演示(五)

登录方式调整第1步&#xff1a;从zmall-common的pom.xml中移除spring-session-data-redis依赖注意&#xff1a;本章节中不采用spring-session方式&#xff0c;改用redis直接存储用户登录信息&#xff0c;主要是为了方便之后的jmeter压测&#xff1b;2&#xff09;这里只注释调用…

如何使用ArcGIS转换坐标

1.概述大家都知道ArcGIS提供了坐标转换功能&#xff0c;在我们手里的数据坐标系千差万别&#xff0c;经常会遇到转换坐标的时候&#xff0c;那么是否可以用ArcGIS进行转换&#xff1f;答案是肯定的&#xff0c;但是转换的过程比较复杂&#xff0c;这里为大家介绍一下转换的方法…

微软 new Bing 通过 ChatGPT 加持后搜索体验暴增,国内该如何申请使用那?

就在近期微软公布了自家的 Bing 将加持 ChatGPT 推出新版&#xff0c;消息一出 Bing 的下载量直接翻了 10 倍以上&#xff0c;48小时内就已经有 100 万名新用户申请加入了 还可以申请试用 new bing 吗&#xff1f; 目前用户还无法直接访问新版 bing &#xff0c;还是要前往申…

从一次有趣的漏洞分析到一个有趣的PHP后门

起因 事情的起因很有趣&#xff0c;前几天我正对着电脑发呆的时候&#xff0c;突然有个安全交流群的群友来找我交流一个问题 大概的意思就是&#xff0c;他在挖SRC的时候&#xff0c;发现一处资产存在目录遍历漏洞&#xff0c;它通过这个漏洞&#xff0c;找到目标资产使用了一…

基于图像的相机定位:概述

虚拟现实、增强现实、机器人和自动驾驶最近引起了学术界和工业界的广泛关注&#xff0c;其中基于图像的相机定位是一项关键任务。然而&#xff0c;还没有关于基于图像的相机定位的完整评论。迫切需要映射这个主题&#xff0c;使个人能够快速进入该领域。在本文中&#xff0c;概…

【Airplay_BCT】Bonjour conformance tests苹果IOT

从Airplay开始&#xff0c;接触到BCT&#xff0c;这是什么&#xff1f;被迫从安卓变成ios用户和开发。。。开始我的学习之旅&#xff0c;记录成长过程&#xff0c;不定时更新 Bonjour 下面是苹果官网关于bonjour的解释 Bonjour, also known as zero-configuration networking, …

pinia实战 购物车(自定义插件实现pinia持久化)

目录 一、实例 二、需求 三. 代码解析 shop.vue shop.ts 四、持久化插件 插件介绍 持久化实现思路 一、实例 二、需求 单选全选功能&#xff0c;并且可以互相联动 小计功能 总计功能 商品加减&#xff0c;数量为零时不能在减 三. 代码解析 shop.vue 1.获取shop模块实…

AttributeError: module ‘lib‘ has no attribute ‘OpenSSL_add_all_algorithms

pip安装crackmapexec后,运行crackmapexec 遇到报错 AttributeError: module lib has no attribute OpenSSL_add_all_algorithms 直接安装 pip3 install crackmapexec 解决 通过 python3 -m pip install --upgrade openssl 或者 python3 -m pip install openssl>22.1.…

YOLOv5/v7 Flask Web 车牌识别 | YOLOv7 + EasyOCR 实现车牌识别

YOLOv7 Flask Web 车牌识别图片效果展示 本篇博文只包含源码以及使用方式,目前不同提供详细开发教程。 YOLOv7 Flask Web 车牌识别视频效果展示 YOLOv7 + EasyOCR 实现车牌识别 什么是Flask? 简介 Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更…

【Opencv实战】几十年前的Vlog火了:黑白老照片如何上色?这黑科技操作一定要知道,复原度超高,竟美的出奇~(图像修复神级代码)

导语 哈喽大家好呀&#xff01;我是每天疯狂赶代码的木木子吖&#xff5e;情人节快乐呀&#xff01; 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 我们都知道&#xff0c;有很多经典的老照片…

云原生时代顶流消息中间件Apache Pulsar部署实操-上

文章目录安装运行时Java版本推荐Locally Standalone集群启动验证部署分布式集群部署说明初始化集群元数据部署BookKeeper部署BrokerAdmin客户端和验证Tiered Storage(层级存储)概述支持分级存储何时使用工作原理安装 运行时Java版本推荐 Locally Standalone集群 启动 # 下载…

Eureka集群搭建教程

前言&#xff1a; 为了提升注册中心稳定性&#xff0c;防止注册中心宕机后&#xff0c;服务不可用的情况&#xff0c;我们可以通过将Eureka注册中心搭建成集群模式&#xff0c;当一台注册中心微服务宕机后&#xff0c;另一台依然可以支持服务的注册与发现。本文将讲解下如何搭…

在Spring-boot中操作MongoDB

MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的。在使用spring-boot操作MongoDB数据…

项目介绍 + 定长内存池设计及实现

你好&#xff0c;我是安然无虞。 文章目录项目介绍当前项目做的是什么?技术栈内存池是什么?池化技术内存池内存池主要解决的问题malloc定长内存池学习目的定长内存池设计项目介绍 当前项目做的是什么? 这个项目是实现一个高并发的内存池, 它的原型是 Google 的一个开源项…

C++——哈希3|位图

目录 常见哈希函数 位图 位图扩展题 位图的应用 常见哈希函数 1. 直接定址法--(常用) 这种方法不存在哈希冲突 取关键字的某个线性函数为散列地址&#xff1a;Hash&#xff08;Key&#xff09; A*Key B 优点&#xff1a;简单、均匀 缺点&#xff1a;需要事先知道关键字的…

C语言学习笔记(八): 自定义数据类型

结构体变量 什么是结构体 C语言允许用户自己建立由不同类型数据组成的组合型的数据结构&#xff0c;它称为结构体 结构体的成员可以是任何类型的变量&#xff0c;如整数&#xff0c;字符串&#xff0c;浮点数&#xff0c;其他结构体&#xff0c;指针等 struct Student //s…

streamlit自定义组件教程和组件开发环境配置

About create your own component&#xff1a; you can follow this tutorial streamlit tutorial 重要&#xff01;以下步骤都是在教程的基础上更改的。这个教程做的很棒。 Component development environment configuration&#xff1a; 根据文章 https://streamlit-com…

【iOS】APP IM聊天框架的设计(基于第三方SDK)

【iOS】APP IM聊天框架的设计&#xff08;基于第三方SDK&#xff09; 前言 在开发社交聊天类型的APP的时候&#xff0c;IM是必不可少的功能&#xff0c;而且很多公司的IM服务都是接的第三方的&#xff0c;很少用自研的&#xff0c;国内的IM厂商也都很成熟&#xff0c;本文所有…

基于文心大模型套件ERNIEKit实现文本匹配算法,模块化方便应用落地

文心大模型,产业级知识增强大模型介绍 官网:https://wenxin.baidu.com/ 文心大模型开发套件ERNIEKit,面向NLP工程师,提供全流程大模型开发与部署工具集,端到端、全方位发挥大模型效能。 提供业界效果领先的ERNIE 3.0系列开源模型和基于ERNIE的前沿任务模型,满足企业和开…

暴力破解(new)

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 01 暴力破解介绍及应用场景 》暴力破解介绍 》暴力破解字典 GitHub - k8gege/Passwor…