FreeRTOS入门教程(队列详细使用示例)

简介: FreeRTOS入门教程(队列详细使用示例)

前言

上篇文章我们已经讲解了队列的概念和队列相关的API函数,那么本篇文章的话就开始带大家来学习使用队列。

一、队列基本使用

这个例子将会创建三个任务,其中两个任务用来发送数据到队列中,另一个任务用来从队列中读取数据

void Task1Function(void * param)
{
  int val;
  while (1)
  {
    val = 100;
    xQueueSend(xQueueCalcHandle, &val, 0);
    vTaskDelay(1000);
  }
}
void Task2Function(void * param)
{
  int val;
  while (1)
  {
    val = 200;
    xQueueSend(xQueueCalcHandle, &val, 0);
    vTaskDelay(1000);
  }
}
void Task3Function(void * param)
{
  int val;
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  BaseType_t xStatus;
  while (1)
  {
    xStatus = xQueueReceive(xQueueCalcHandle, &val, xTicksToWait);
    if( xStatus == pdPASS )
    {
      /* 读到了数据 */
      printf( "Received = %d\r\n", val );
    } 
    else
    {
      /* 没读到数据 */
      printf( "Could not receive from the queue.\r\n" );
    }   
  }
}
xQueueCalcHandle = xQueueCreate(5, sizeof(int));
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 2, NULL);

运行效果:

从运行效果中可以看出,当队列中有数据的时候就能够从队列中读取到数据,当队列中没有数据时,超时后会返回pdFALSE。

这里使用百问网的一张图片来描述这个过程:

二、如何分辨数据源

通过上面这个实验我们完成了队列数据的发送和队列数据的接收,但是我们无法得知数据是哪个队列所发送的,那么下面这个实验就带大家来完成分辨数据源的实验。

前面的实验中我们使用单独的一个int变量来代表数据,这样的话只能接收到对应的数据而无法分辨是谁发过来的数据,那么有什么办法来分辨是谁发来的数据呢?

这里的解决方法是使用结构体:

typedef enum
{
  Task1,
  Task2
}ID_t;
typedef struct data
{
  ID_t id;
  int data;
}Data_t;
static Data_t senddata[2] = {
  {Task1, 10},
  {Task2, 20}
};
void Task1Function(void * param)
{
  while (1)
  {
    xQueueSend(xQueueCalcHandle, &senddata[0], 0);
    vTaskDelay(1000);
  }
}
void Task2Function(void * param)
{
  while (1)
  {
    xQueueSend(xQueueCalcHandle, &senddata[1], 0);
    vTaskDelay(1000);
  }
}
void Task3Function(void * param)
{
  Data_t mydata;
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  BaseType_t xStatus;
  while (1)
  {
    xStatus = xQueueReceive(xQueueCalcHandle, &mydata, xTicksToWait);
    if( xStatus == pdPASS )
    {
      /* 读到了数据 */
      if(mydata.id == Task1)
      {
        printf("this is Task1 data :%d\r\n", mydata.data);
      }
      else
      {
        printf("this is Task2 data :%d\r\n", mydata.data);
      }
    } 
    else
    {
      /* 没读到数据 */
      printf( "Could not receive from the queue.\r\n" );
    }   
  }
}
xQueueCalcHandle = xQueueCreate(5, sizeof(Data_t));
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 2, NULL);

当接收到数据时会先判断结构体中的id,通过id来判断是哪个任务发送过来的数据。

运行效果:

三、传输大块数据

FreeRTOS 中的队列通常使用数据拷贝来传递数据。这意味着当你将数据发送到队列或从队列接收数据时,队列会在内部复制数据的副本,而不是传递指向原始数据的指针。

这种数据拷贝的方法确保了数据的安全性和一致性,因为多个任务可以独立访问它们自己的副本,而不会干扰其他任务。然而,需要注意的是,数据拷贝可能会引入一些性能开销,尤其是在处理大量数据时。

那么当使用队列来传输大量数据时该怎么做呢?

这里我们可以使用指针来解决这个问题,传递大块数据的时候我们可以使用指针来解决这个问题,在传输大块数据时,可以先得到数据的地址,将数据的地址作为数据传递过来,当接收到数据的地址时,就能够通过数据的地址来得到对应的数据了。

示例:

char pcbuffer[100] = "Hello World";
void Task1Function(void * param)
{
  char* buffer;
  while (1)
  {
    buffer = pcbuffer;
    xQueueSend(xQueueCalcHandle, &buffer, 0);
    vTaskDelay(1000);
  }
}
void Task2Function(void * param)
{
  char* buffer;
  while (1)
  {
    buffer = pcbuffer;
    xQueueSend(xQueueCalcHandle, &buffer, 0);
    vTaskDelay(1000);
  }
}
void Task3Function(void * param)
{
  char* rebuffer;
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  BaseType_t xStatus;
  while (1)
  {
    xStatus = xQueueReceive(xQueueCalcHandle, &rebuffer, xTicksToWait);
    if( xStatus == pdPASS )
    {
      /* 读到了数据 */
      printf("recv buffer : %s\r\n", rebuffer);
    } 
    else
    {
      /* 没读到数据 */
      printf( "Could not receive from the queue.\r\n" );
    }   
  }
}
xQueueCalcHandle = xQueueCreate(5, sizeof(char*));
if (xQueueCalcHandle == NULL)
{
  printf("can not create queue\r\n");
}
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 2, NULL);

运行结果:

这里有几个点需要注意:

1.

由于队列传递数据是复制数据的副本,所有传输数据时并不会影响到原来的数据,但是在这里使用到了地址,当改变这个地址空间的数据后,原来的数据也会受到影响。

2.

由于传递的是地址空间,那么这里的话就必须保证这个数据是全局数据,因为局部数据会被释放,释放后就无法进行使用了,所有需要保证数据是全局数据。

总结

本篇文章就讲解到这里,本篇文章主要给大家讲解了队列的具体代码和使用方法。


相关文章
|
7月前
|
存储 算法 安全
【Freertos基础入门】队列(queue)的使用
【Freertos基础入门】队列(queue)的使用
|
5月前
|
消息中间件 调度
FreeRTOS入门教程(任务状态)
FreeRTOS入门教程(任务状态)
78 0
|
7月前
|
算法 C++ 容器
C++初阶之一篇文章教会你queue和priority_queue(理解使用和模拟实现)(下)
优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器
|
5月前
|
存储 安全
FreeRTOS入门教程(队列的概念及相关函数介绍)
FreeRTOS入门教程(队列的概念及相关函数介绍)
48 0
|
5月前
|
消息中间件 存储 数据采集
FreeRTOS入门教程(任务通知)
FreeRTOS入门教程(任务通知)
48 0
|
5月前
|
存储 API 数据安全/隐私保护
FreeRTOS入门教程(信号量的概念及API函数使用)
FreeRTOS入门教程(信号量的概念及API函数使用)
125 0
|
5月前
|
存储 调度
FreeRTOS深入教程(队列内部机制和源码分析)
FreeRTOS深入教程(队列内部机制和源码分析)
67 0
|
5月前
|
存储
FreeRTOS入门教程(事件组概念和函数使用)
FreeRTOS入门教程(事件组概念和函数使用)
42 0
|
5月前
|
算法 调度
FreeRTOS入门教程(互斥锁的概念和函数使用)
FreeRTOS入门教程(互斥锁的概念和函数使用)
92 0
|
7月前
|
API 调度
FreeRTOS学习笔记—任务创建和删除
本文学习了如何创建和删除任务。最后,分析解决了遇到的问题。
49 0