Arduino 中的 FreeRTOS 队列
队列是一种数据结构,它有助于在不同的任务之间或在任务和中断之间交换数据。它保存有限数量的项目(在初始化时定义),并以 FIFO 模式运行。
我们将逐步讲解 FreeRTOS 库中的一个示例,以了解队列。
您可以在以下位置找到示例:**文件 → 示例 → FreeRTOS → StructQueue**。
在此代码中,两个任务从不同的模拟引脚读取模拟值,并将这些值传递到队列中。另一个任务从队列读取值并将它们打印到串口监视器上。还有一个第四个闪烁任务与队列无关,并行运行。
我们首先包含库:
// Include Arduino FreeRTOS library #include <Arduino_FreeRTOS.h> // Include queue support #include <queue.h>
接下来,我们定义一个结构体,其中包含两个整数,一个用于引脚编号,另一个用于引脚值。
// Define a struct struct pinRead { int pin; int value; };
然后,定义一个 **QueueHandle_t** 对象,稍后将在 Setup 中使用。
/* * Declaring a global variable of type QueueHandle_t * */ QueueHandle_t arrayQueue;
在 Setup 中,我们创建队列并将其大小设置为 **10 个 pinRead 结构体**(它一次可以存储 10 个这样的结构体)。如果队列创建正确,则创建 4 个任务。串口任务被赋予最高优先级 (2),**两个** **analogRead** 任务被赋予较低优先级 (1),闪烁任务被赋予最低优先级 (0)。循环中没有任何操作。
void setup() { /** * Create a queue. * https://www.freertos.org/a00116.html */ structQueue = xQueueCreate(10, // Queue length sizeof(struct pinRead) // Queue item size ); if (structQueue != NULL) { // Create task that consumes the queue if it was created. xTaskCreate(TaskSerial, // Task function "Serial", // A name just for humans 128, // This stack size can be checked & adjusted by reading the Stack Highwater NULL, 2, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest. NULL); // Create task that publish data in the queue if it was created. xTaskCreate(TaskAnalogReadPin0, // Task function "AnalogReadPin0", // Task name 128, // Stack size NULL, 1, // Priority NULL); // Create other task that publish data in the queue if it was created. xTaskCreate(TaskAnalogReadPin1, // Task function "AnalogReadPin1", // Task name 128, // Stack size NULL, 1, // Priority NULL); } xTaskCreate(TaskBlink, // Task function "Blink", // Task name 128, // Stack size NULL, 0, // Priority NULL ); } void loop() {}
接下来是两个 **analogRead** 任务的定义。两者非常相似。一个读取 A0 引脚,另一个读取 A1 引脚。每个任务填充一个 **pinRead** 结构体并将其添加到队列中。**xQueueSend** 的第三个参数是应该等待队列中有可用空间的最长时间。将其设置为 portMAX_DELAY,我们将使任务无限期地等待队列中有可用空间。参见 https://www.freertos.org/a00117.html
/** * Analog read task for Pin A0 * Reads an analog input on pin 0 and send the readed value through the queue. * See Blink_AnalogRead example. */ void TaskAnalogReadPin0(void *pvParameters) { (void) pvParameters; for (;;) { // Read the input on analog pin 0: struct pinRead currentPinRead; currentPinRead.pin = 0; currentPinRead.value = analogRead(A0); /** * Post an item on a queue. * https://www.freertos.org/a00117.html */ xQueueSend(structQueue, ¤tPinRead, portMAX_DELAY); // One tick delay (15ms) in between reads for stability vTaskDelay(1); } } /** * Analog read task for Pin A1 * Reads an analog input on pin 1 and send the readed value through the queue. * See Blink_AnalogRead example. */ void TaskAnalogReadPin1(void *pvParameters) { (void) pvParameters; for (;;) { // Read the input on analog pin 1: struct pinRead currentPinRead; currentPinRead.pin = 1; currentPinRead.value = analogRead(A1); /** * Post an item on a queue. * https://www.freertos.org/a00117.html */ xQueueSend(structQueue, ¤tPinRead, portMAX_DELAY); // One tick delay (15ms) in between reads for stability vTaskDelay(1); } }
串口任务初始化串口,然后只要队列中有项目,它就会将其读入局部结构体,然后在串口监视器上打印引脚编号和值。
void TaskSerial(void * pvParameters) { (void) pvParameters; // Init Arduino serial Serial.begin(9600); // Wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards. while (!Serial) { vTaskDelay(1); } for (;;) { struct pinRead currentPinRead; /** * Read an item from a queue. * https://www.freertos.org/a00118.html */ if (xQueueReceive(structQueue, ¤tPinRead, portMAX_DELAY) == pdPASS) { Serial.print("Pin: "); Serial.print(currentPinRead.pin); Serial.print(" Value: "); Serial.println(currentPinRead.value); } } }
闪烁任务并行运行,不使用队列。
/* * Blink task. * See Blink_AnalogRead example. */ void TaskBlink(void *pvParameters) { (void) pvParameters; pinMode(LED_BUILTIN, OUTPUT); for (;;) { digitalWrite(LED_BUILTIN, HIGH); vTaskDelay( 250 / portTICK_PERIOD_MS ); digitalWrite(LED_BUILTIN, LOW); vTaskDelay( 250 / portTICK_PERIOD_MS ); } }