首页 > 其他分享 >Freertos学习08-queue基本发送与接受

Freertos学习08-queue基本发送与接受

时间:2023-07-03 11:47:00浏览次数:37  
标签:QHandle Freertos 队列 08 queue printf xStatus void

一、前言

队列是任务间通信的主要形式。 它们可以用于在任务之间以及中断和任务之间发送消息。队列是一个先进先出(FIFO)的数据结构,类似于现实生活中的排队。任务可以将数据项放入队列的末尾,然后另一个任务可以从队列的开头取出这些数据项。这种方式可以实现任务之间的数据共享和通信。

  本节主要涉及以下内容:

  • 队列的特点
  • 队列的基本创建
  • 队列数据的发送与接收

二、队列特性

1.队列的长度与宽度:队列的项数被称为“长度”,每项占用内存大小称为“宽度”。
2.数据采用先进先出:写数据时放到尾部,读数据时从头部读,也可强制写数据与头部。
3.优先级:队列可以与任务的优先级相关联。当多个任务等待队列中的数据时,具有更高优先级的任务将首先获得数据。
  具体操作过程如下:
image

三、函数API

3.1创建队列

  函数原型如下:

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                            UBaseType_t uxItemSize );
参数 说明
uxQueueLength 队列长度,最多能存多少个数据(item)
uxItemSize 每个数据(item)的大小
返回值 非零,成功创建队列;NULL,堆内存不足,无法创建队列

3.2删除队列

  用于删除使用xQueueCreate()或xQueueCreateStatic()创建的队列,将会释放内存,函数原型如下:

void vQueueDelete( TaskHandle_t pxQueueToDelete );
参数 说明
pxQueueToDelete 队列句柄
返回值 无返回值

3.3写队列

  将数据写入到队列的后面,函数原型如下:

BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
                             const void * pvItemToQueue, 
                             TickType_t xTicksToWait );
参数 说明
xQueue 队列句柄
pvItemToQueue 指向要复制到队列中的数据的指针。
xTicksToWait 如果队列无法写入新数据,队列将进入阻塞态,xTicksToWait表示进入阻塞态的最大时间;如果xTicksToWait设置为0,无法写入数据时将立即返回;如果设置为portMAX_DELAY,将会一直等待直至有空间可写
返回值 非零,成功创建队列;NULL,堆内存不足,无法创建队列

3.4读队列

  使用xQueueReceive()函数读队列,读到一个数据后,队列中该数据会被移除。函数原型如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void *pvBuffer, 
                          TickType_t xTicksToWait );
参数 说明
xQueue 队列句柄
pvBuffer buff指针,将队列中的数据复制到该缓冲区
xTicksToWait 如果队列为空无法读取数据,队列将进入阻塞态,xTicksToWait表示进入阻塞态的最大时间;如果xTicksToWait设置为0,无法读出数据时将立即返回;如果设置为portMAX_DELAY,将会一直等待直至有数据可读
返回值 pdPASS,成功读取数据,或者并非第一时间读取了数据,但在xTicksToWait耗尽前读取到了数据,也返回成功;errQUEUE_EMPTY,无法读取数据因为队列为空,或者未能在xTicksToWait耗尽前成功读取数据

  如果队列数据需要传给多个函数,可使用xQueuePeek()读取,peek本意:一瞥,窥视,此函数读出数据后并不删除,方便多函数调用同一队列数据。函数原型

BaseType_t xQueuePeek( QueueHandle_t xQueue,
  		       void *pvBuffer, TickType_t 
                       xTicksToWait );

四、示例测试

1.传输结构体

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

typedef struct A_STRUCT
{
	char id;
	char data;
} xStruct; // 先构建结构体

void queue_send(void *pvParam)
{
	QueueHandle_t QHandle;
	QHandle = (QueueHandle_t)pvParam;

	BaseType_t xStatus;
	xStruct xUSB = {1, 78};

	while (1)
	{
		xStatus = xQueueSend(QHandle, &xUSB, 0);
		if (xStatus != pdPASS)
			printf("发送失败!\n");
		else
			printf("发送成功!\n");

		xUSB.id++;
		if (xUSB.id == 8)
			xUSB.id = 0;
		vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时;
	}
}

void queue_rec(void *pvParam)
{
	QueueHandle_t QHandle;
	QHandle = (QueueHandle_t)pvParam;

	BaseType_t xStatus;
	xStruct xUSB = {0, 0};

	while (1)
	{
		if (uxQueueMessagesWaiting(QHandle) != 0)
		{
			xStatus = xQueueReceive(QHandle, &xUSB, 0);
			if (xStatus != pdPASS)
				printf("接收失败!\n");
			else
				printf("rec id=%d data=%d!\n", xUSB.id, xUSB.data);

			vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时;
		}
		else
			printf("queue为空!\n");
	}
}

void app_main(void)
{

	QueueHandle_t QHandle;

	QHandle = xQueueCreate(5, sizeof(xStruct)); // 创建队列

	if (QHandle != NULL)
	{
		printf("队列创建成功!\n");
		xTaskCreate(queue_send, "mytask1", 4096, (void *)QHandle, 0, NULL);
		xTaskCreate(queue_rec, "mytask2", 4096, (void *)QHandle, 0, NULL);
	}
	else
	{
		printf("创建队列失败!\n");
	}
}

2.队列数据的多进单出

  队列可由多个函数写入数据,并由单一函数读取,例如函数1负责汇报温度信息,函数2负责汇报湿度信息,由函数3统一读取并打印。测试代码中接受任务的优先级为3,而两个发送任务的优先级为1,这代表接受数据的优先级更高,队列中经常为空;亦或反过来,令发送任务优先级更高,接受优先级低,此时,队列出现常常为满的情况,发送任务进入阻塞态。

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

/* 定义2种数据来源(ID) */
typedef enum
{
   temperature,
   humidity
} ID_t;

/* 定义在队列中传输的数据的格式 */
typedef struct
{
   ID_t eDataID;
   int32_t lDataValue;
} Data_t;

void queue_send1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t xStruct_temp = {temperature, 37}; // 定义温度信息结构体
   BaseType_t xStatus;

   while (1)
   {
      if (xStruct_temp.lDataValue++ > 43) // 模拟温度变化
         xStruct_temp.lDataValue = 26;
      xStatus = xQueueSend(QHandle, &xStruct_temp, 0);
      if (xStatus != pdPASS)
         printf("温度发送失败!\n");
      else
         printf("温度发送成功!\n");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
   }
}

void queue_send2(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;
   Data_t xStruct_humi = {humidity, 37}; // 定义湿度信息结构体

   BaseType_t xStatus;

   while (1)
   {
      if (xStruct_humi.lDataValue++ > 99) // 模拟湿度变化
         xStruct_humi.lDataValue = 10;
      xStatus = xQueueSend(QHandle, &xStruct_humi, 0);
      if (xStatus != pdPASS)
         printf("湿度发送失败!\n");
      else
         printf("湿度发送成功!\n");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
   }
}

void queue_rec(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         if (Date_receive.eDataID == temperature)
            printf("当前温度啊啊啊:%ld'C\n", Date_receive.lDataValue);
         else
            printf("当前湿度:%ld%%\n", Date_receive.lDataValue);
      }
   }
}

void app_main(void)
{

   QueueHandle_t QHandle;

   QHandle = xQueueCreate(5, sizeof(Data_t)); // 创建队列,5长度,int宽度

   if (QHandle != NULL)
   {
      printf("队列创建成功!\n");
      xTaskCreate(queue_send1, "queue_send1", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_send2, "queue_send2", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec, "queue_rec", 4096, (void *)QHandle, 2, NULL);
   }
   else
   {
      printf("创建队列失败!\n");
   }
}

3.队列数据的单进多出,有时后我们会用到一些集成传感器,例如惯性传感器可反馈偏航角,角加速度,磁场方向等信息,并将这些信息分散至不同的任务进行处理。令发送任务优先级为2,接受任务优先级为1,因此发送任务优先,队列出现满的情况,因此发送任务进入阻塞态,接受任务得以运行,当读取完某个数据后,该数据将被立即删除,队列出现空位,发送任务从阻塞态进入就绪态运行准备发送下一次数据。注意,多个任务用xQueuePeek()读取,该函数读取后不删除,只需要在最后一个任务使用xQueueReceive(),即读取后立即删除。

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

/* 定义在队列中传输的数据的格式 */
typedef struct
{
   int32_t Angle;
   float Ang_acc;
   int32_t magnetism;

} Data_t;

void queue_send1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t xStruct = {28, 1.7, 5}; // 定义温度信息结构体
   BaseType_t xStatus;

   while (1)
   {
      xStatus = xQueueSend(QHandle, &xStruct, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("发送失败!\n");
      else
         printf("发送成功!\n");
   }
}

void queue_rec1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("111当前角度:%ld\n", Date_receive.Angle);
      }
   }
}

void queue_rec2(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("222当前角加速度%f\n", Date_receive.Ang_acc);
      }
   }
}

void queue_rec3(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("当前磁场方向:%ld\n", Date_receive.magnetism);
      }
   }
}

void app_main(void)
{

   QueueHandle_t QHandle;

   QHandle = xQueueCreate(5, sizeof(Data_t)); // 创建队列,5长度,int宽度

   if (QHandle != NULL)
   {
      printf("队列创建成功!\n");
      xTaskCreate(queue_send1, "queue_send1", 4096, (void *)QHandle, 2, NULL);
      xTaskCreate(queue_rec1, "queue_rec1", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec2, "queue_rec2", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec3, "queue_rec3", 4096, (void *)QHandle, 1, NULL);
   }
   else
   {
      printf("创建队列失败!\n");
   }
}

THE END!

标签:QHandle,Freertos,队列,08,queue,printf,xStatus,void
From: https://www.cnblogs.com/seekwhale13/p/17521286.html

相关文章

  • 韦东山freeRTOS系列教程之【第四章】同步互斥与通信
    文章目录系列教程总目录概述4.1同步与互斥的概念4.2同步与互斥并不简单4.3各类方法的对比系列教程总目录本教程连载中,篇章会比较多,为方便同学们阅读,点击这里可以查看文章的目录列表,目录列表页面地址:javascript:void(0)概述本章是概述性的内容。可以把多任务系统当做一个团队,......
  • 08_调试与使用虚拟的GPIO控制器
    目录资料下载视频观看调试与使用虚拟的GPIO控制器1.硬件功能2.编写设备树文件3.上机实验3.2编译、替换设备树3.3编译、安装驱动程序4.STM32MP157上的bug资料下载coding无法使用浏览器打开,必须用git工具下载:gitclonehttps://e.coding.net/weidongshan/linux/doc_and_sourc......
  • 韦东山freeRTOS系列教程之【第十二章】资源管理(Resource Management)
    文章目录系列教程总目录概述12.1屏蔽中断12.1.1在任务中屏蔽中断12.1.2在ISR中屏蔽中断12.2暂停调度器系列教程总目录本教程连载中,篇章会比较多,为方便同学们阅读,点击这里可以查看文章的目录列表概述在前面讲解互斥量时,引入过临界资源的概念。在前面课程里,已经实现了临界资源......
  • AtCoder Beginner Contest 308 G Minimum Xor Pair Query
    洛谷传送门AtCoder传送门考虑没有删除操作怎么做。可以动态维护\(ans\),新加进来一个\(x\),我们计算\(\min\limits_{y\inS}x\oplusy\)。对\(S\)建01Trie,然后从高位往低位按位贪心,在01Trie上优先往与\(x\)这一位相同的方向走,但是前提是它的子树内有数,对于01Trie......
  • 洛谷 P1081 题解
    P1081[NOIP2012提高组]开车旅行题解Link洛谷题目链接Solution首先考虑这道题的暴力做法,对于第一问,枚举每个起始点,暴力计算每个点之后最近和第二近的位置,计算答案,最后取最大值。对于第二问,对每个询问独立模拟即可。复杂度较高,无法通过此题。第一个优化:考虑到对于固定的当......
  • AtCoder Beginner Contest 308 A~F
    AtCoderBeginnerContest308手速有点慢A-NewScheme判断给定数字是否满足条件voidsolve(){ boolok=true; inta[10]; for(inti=1;i<=8;i++) cin>>a[i]; for(inti=1;i<=8;i++) { if(i>=2&&a[i]<a[i-1]) ok=......
  • AcWing,第108场周赛T3 拼接数组
    AcWing,第108场周赛T3前置知识:P1115最大子段和的dp和线段树作法分析:对于一个数组,可以直接求出最大字段和,但由于多个数组拼接在一起,没有办法直接求得拼接数组的最大字段和。求最大字段和我已知有两种方法:dp线段树先对每一个数组用线段树求出最大前缀和,最大字段和,最大后缀......
  • 2024备考408Week16
    一、本周总结:使用时间:(离每周目标40h还差10h,差距还很大!)总计30h,数学12h50min,专业课12h9min,英语3h40min,政治1h21min。二、存在问题:1.数学、专业课(DS+OS+CO+CN)做题训练不够,思考不够深入,计算不够熟练和准确,后期一定要开始加强了;2.碎片化时间和整块时间没有合理安排,碎片化时间应该安排......
  • 光脚丫学ASP.NET MVC(0008):非操作方法
    视频演示:http://u.115.com/file/e6r505ey控制器中的方法并不一定都是操作方法。要使控制器中的方法称为操作方法,则必须使其为公共方法,并且没有为其添加NonActionAttribute特性。私有方法不是操作方法,静态方法也不是操作方法。如下面的两个方法,均不是控制器的操作方法。privateA......
  • AtCoder Beginner Contest 308
    A:1#include<cstdio>2#include<cstring>3#include<algorithm>4#include<iostream>5#include<string>6#include<vector>7#include<stack>8#include<bitset>9#include<cstdlib>10#include......