目录
何为队列?
在FreeRTOS中,队列是一种用于任务间通信的重要机制。队列可以用来在任务之间传递数据,实现数据的异步传输和共享。
队列的作用包括:
-
数据传输:任务可以通过将数据发送到队列中,供其他任务接收和处理。队列提供了一种可靠的机制,确保数据的有序传输和接收。
-
任务同步:队列可以用于任务之间的同步,一个任务可以等待另一个任务发送数据到队列中,从而实现任务的协调和同步。
-
缓冲区:队列可以作为一个缓冲区,用于存储和管理任务之间的数据。任务可以按照一定的顺序将数据放入队列中,其他任务可以按照相同的顺序从队列中取出数据进行处理。
-
通知机制:队列还可以用作任务之间的通知机制。一个任务可以通过向队列发送特定的数据来通知其他任务进行某种操作或者改变状态。
总之,队列在FreeRTOS中提供了一种高效、可靠的任务间通信机制,使得任务之间可以方便地进行数据传输、同步和协调。
示例代码
ESP32基于FreeRTOS的多任务通信,有三个任务,第一个任务是让LED1以0.5秒为间隔闪烁,每次点亮都会进行一次计数,第二个任务根据第一个任务的计数去做处理,比如计数到10以后,LED2灯亮,第三个任务是按键任务,可以把第一个任务中的计数清空。
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#define LED1_PIN 2
#define LED2_PIN 4
#define BUTTON_PIN 39
TaskHandle_t Task1Handle, Task2Handle, Task3Handle;
QueueHandle_t countQueue;
int count = 0;
void Task1(void* parameter) {
pinMode(LED1_PIN, OUTPUT);
while (1) {
digitalWrite(LED1_PIN, HIGH);
vTaskDelay(500 / portTICK_PERIOD_MS);
digitalWrite(LED1_PIN, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
count++;
Serial.print("Task1_count=");
Serial.println(count);
xQueueSend(countQueue, &count, portMAX_DELAY);
}
}
void Task2(void* parameter) {
pinMode(LED2_PIN, OUTPUT);
while (1) {
xQueueReceive(countQueue, &count, portMAX_DELAY);
Serial.println(count);
if (count >= 10) {
digitalWrite(LED2_PIN, HIGH);
} else {
digitalWrite(LED2_PIN, LOW);
}
}
}
void Task3(void *pvParameters) {
pinMode(BUTTON_PIN, INPUT);
while (1) {
if (digitalRead(BUTTON_PIN) == HIGH) {
count = 0;
xQueueSend(countQueue, &count, portMAX_DELAY);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
countQueue = xQueueCreate(100, sizeof(int));
xTaskCreate(Task1, "Task1", 1024, NULL, 1, &Task1Handle);
xTaskCreate(Task2, "Task2", 1024, NULL, 2, &Task2Handle);
xTaskCreate(Task3, "Task3", 1024, NULL, 3, &Task3Handle);
}
void loop()
{
}
函数讲解
xQueueCreate
xQueueCreate是一个FreeRTOS函数,用于创建一个队列。队列是一种用于在任务之间传递数据的数据结构,可以实现任务之间的通信和同步。
xQueueCreate函数的原型如下:
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
参数说明:
- uxQueueLength:队列的长度,表示队列可以容纳的数据项数量。
- uxItemSize:每个数据项的大小,以字节为单位。
xQueueCreate函数的返回值是一个QueueHandle_t类型的队列句柄,用于后续对队列的操作。
下面是一个示例代码,演示如何使用xQueueCreate函数创建一个队列:
// 创建一个队列,长度为10,每个数据项的大小为4字节(32位整数)
QueueHandle_t xQueue = xQueueCreate(10, sizeof(int));
if (xQueue != NULL) {
// 队列创建成功
// 执行其他操作
} else {
// 队列创建失败
// 执行错误处理
}
在上面的示例中,我们使用xQueueCreate函数创建了一个队列xQueue,它有容量为10的整型数据项。我们使用sizeof(int)来指定每个数据项的大小,这里假设整型数据占用4个字节。
请注意,创建队列时需要确保有足够的内存可用。如果内存不足,队列创建可能会失败,返回NULL。因此,在创建队列后,建议检查队列句柄是否为NULL,以确保队列创建成功。
创建队列后,您可以使用其他FreeRTOS队列函数(如xQueueSend、xQueueReceive等)来发送和接收数据。这些函数可以帮助实现任务之间的通信和同步。
xQueueSend
xQueueSend是一个FreeRTOS函数,用于向队列发送数据。它用于将数据项发送到先前创建的队列中,以供其他任务读取和处理。
xQueueSend函数的原型如下:
BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
参数说明:
- xQueue:队列句柄,表示要发送数据的队列。
- pvItemToQueue:指向要发送的数据项的指针。
- xTicksToWait:等待时间,如果队列已满,任务将在此处阻塞等待直到队列有空闲空间。
xQueueSend函数的返回值是一个BaseType_t类型的值,用于指示发送操作是否成功。如果返回pdPASS,则表示数据已成功发送到队列中;如果返回errQUEUE_FULL,则表示队列已满,数据发送失败。
下面是一个示例代码,演示如何使用xQueueSend函数向队列发送数据:
// 创建一个队列
QueueHandle_t xQueue = xQueueCreate(10, sizeof(int));
// 定义要发送的数据
int dataToSend = 42;
// 发送数据到队列
BaseType_t xStatus = xQueueSend(xQueue, &dataToSend, portMAX_DELAY);
if (xStatus == pdPASS) {
// 数据发送成功
// 执行其他操作
} else {
// 数据发送失败
// 执行错误处理
}
在上面的示例中,我们首先创建了一个队列xQueue,它有容量为10的整型数据项。然后,我们定义了一个要发送的数据dataToSend,并使用xQueueSend函数将其发送到队列中。我们使用portMAX_DELAY作为等待时间,这将导致任务在队列有空闲空间之前一直阻塞。
请注意,xQueueSend函数是阻塞函数,如果队列已满,任务将被阻塞,直到队列有空闲空间。如果您希望在队列已满时立即返回而不是阻塞任务,可以使用xQueueSendFromISR函数,它适用于从中断服务例程中发送数据。
xQueueReceive
xQueueReceive函数是FreeRTOS中用于从队列中接收数据的函数。它的原型如下:
BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
参数说明:
- xQueue:队列的句柄,即通过xQueueCreate函数创建的队列。
- pvBuffer:用于接收数据的缓冲区指针。接收到的数据将会被复制到这个缓冲区中。
- xTicksToWait:等待时间,即在队列中没有数据可接收时,任务将等待的时间。如果设置为0,则表示立即返回。
函数返回值:
- pdPASS:数据成功接收。
- errQUEUE_EMPTY:队列为空,没有数据可接收。
- errQUEUE_FULL:队列已满,无法接收数据。
下面是一个示例代码,演示如何使用xQueueReceive函数从队列中接收数据:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#define QUEUE_LENGTH 5
#define ITEM_SIZE sizeof(int)
void task1(void *pvParameters) {
QueueHandle_t xQueue = (QueueHandle_t)pvParameters;
int count = 0;
while (1) {
vTaskDelay(pdMS_TO_TICKS(500)); // 0.5秒延迟
// 发送计数到队列
xQueueSend(xQueue, &count, 0);
count++;
}
}
void task2(void *pvParameters) {
QueueHandle_t xQueue = (QueueHandle_t)pvParameters;
int receivedData;
while (1) {
if (xQueueReceive(xQueue, &receivedData, portMAX_DELAY) == pdPASS) {
// 接收到数据后的处理
printf("Received data: %d\n", receivedData);
}
}
}
void app_main() {
QueueHandle_t xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);
xTaskCreate(task1, "Task1", 1024, NULL, 1, NULL);
xTaskCreate(task2, "Task2", 1024, NULL, 2, NULL);
}
在这个示例代码中,有两个任务:task1和task2。task1每0.5秒向队列发送一个计数值,而task2则从队列中接收数据并进行处理。通过使用xQueueReceive函数,task2可以在队列中有数据可接收时获取数据并进行相应的处理。