【花雕学编程】Arduino RTOS 之多生产者和多消费者

在这里插入图片描述
《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html

在这里插入图片描述
Arduino RTOS 之多生产者和多消费者

  1. 主要特点
    1.1 多任务并发

多生产者和多消费者模型允许多个任务并发运行,生产者可以同时生成数据,消费者可以同时消费数据,提高系统的并发处理能力。
1.2 解耦合设计

该模型实现了生产者和消费者之间的解耦,使得两者可以独立运行,生产者不需要知道消费者的存在,反之亦然,增强了系统的灵活性。
1.3 高效资源利用

通过合理的任务调度和资源管理,多生产者和多消费者能够实现对系统资源的高效利用,避免资源的闲置和浪费。
1.4 支持同步机制

RTOS支持信号量、消息队列等同步机制,确保生产者与消费者之间的数据一致性和安全性,防止数据丢失和竞争条件。

  1. 应用场景
    2.1 数据采集系统

在传感器网络中,多个传感器(生产者)采集数据,多个处理单元(消费者)实时处理数据,适用于环境监测、工业自动化等场景。
2.2 互联网应用

在物联网设备中,多个设备可以生成数据(如传感器、执行器),而云端或边缘计算设备则作为消费者,处理和分析数据。
2.3 机器人系统

机器人可以有多个传感器生成环境信息(生产者),而控制系统则可作为消费者,做出决策和执行动作。
2.4 多媒体处理

在音视频处理应用中,多个数据流(生产者)可以同时输入,而解码、渲染等处理模块(消费者)则并行处理这些数据流。

  1. 注意事项
    3.1 数据一致性

在多生产者和多消费者环境中,需确保对共享数据的访问是线程安全的,避免数据竞争和不一致性,通常需要使用互斥锁或信号量。
3.2 任务调度

设计合理的任务调度策略,确保生产者和消费者的执行顺序能够满足实时性要求,防止消费者因等待数据而阻塞。
3.3 资源管理

需要有效管理缓冲区等共享资源,避免缓冲区溢出或空闲,设计合适的生产者-消费者模型(如环形缓冲区)。
3.4 性能监测

监测系统的性能指标(如生产率和消费率),及时调整任务优先级和资源分配,以优化系统性能。
3.5 适应性设计

设计时应考虑系统的扩展性,能够灵活添加新的生产者或消费者,而无需对现有系统进行大幅修改。

在这里插入图片描述

1、基本的多生产者和多消费者示例

#include <Arduino.h>
#include <FreeRTOS.h>

#define BUFFER_SIZE 5

QueueHandle_t queue;

void producerTask(void *pvParameters);
void consumerTask(void *pvParameters);

void setup() {
    Serial.begin(9600);
    queue = xQueueCreate(BUFFER_SIZE, sizeof(int));
    xTaskCreate(producerTask, "Producer", 1000, NULL, 1, NULL);
    xTaskCreate(consumerTask, "Consumer", 1000, NULL, 1, NULL);
}

void loop() {
    // 主循环空着
}

void producerTask(void *pvParameters) {
    int item;
    while (1) {
        item = random(100); // 生成随机数
        if (xQueueSend(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Produced: ");
            Serial.println(item);
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *pvParameters) {
    int item;
    while (1) {
        if (xQueueReceive(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Consumed: ");
            Serial.println(item);
        }
    }
}

2、多生产者和多个消费者

#include <Arduino.h>
#include <FreeRTOS.h>

#define BUFFER_SIZE 5

QueueHandle_t queue;

void producerTask(void *pvParameters);
void consumerTask(void *pvParameters);

void setup() {
    Serial.begin(9600);
    queue = xQueueCreate(BUFFER_SIZE, sizeof(int));
    for (int i = 0; i < 2; i++) {
        xTaskCreate(producerTask, "Producer", 1000, NULL, 1, NULL);
    }
    for (int i = 0; i < 3; i++) {
        xTaskCreate(consumerTask, "Consumer", 1000, NULL, 1, NULL);
    }
}

void loop() {
    // 主循环空着
}

void producerTask(void *pvParameters) {
    int item;
    while (1) {
        item = random(100);
        if (xQueueSend(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Produced: ");
            Serial.println(item);
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *pvParameters) {
    int item;
    while (1) {
        if (xQueueReceive(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Consumed: ");
            Serial.println(item);
        }
    }
}

3、生产者和消费者的优先级示例

#include <Arduino.h>
#include <FreeRTOS.h>

#define BUFFER_SIZE 5

QueueHandle_t queue;

void producerTask(void *pvParameters);
void consumerTask(void *pvParameters);

void setup() {
    Serial.begin(9600);
    queue = xQueueCreate(BUFFER_SIZE, sizeof(int));
    xTaskCreate(producerTask, "Producer", 1000, NULL, 2, NULL);
    xTaskCreate(consumerTask, "Consumer", 1000, NULL, 1, NULL);
}

void loop() {
    // 主循环空着
}

void producerTask(void *pvParameters) {
    int item;
    while (1) {
        item = random(100);
        if (xQueueSend(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Produced: ");
            Serial.println(item);
        }
        vTaskDelay(50 / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *pvParameters) {
    int item;
    while (1) {
        if (xQueueReceive(queue, &item, portMAX_DELAY) == pdPASS) {
            Serial.print("Consumed: ");
            Serial.println(item);
        }
        vTaskDelay(150 / portTICK_PERIOD_MS);
    }
}

要点解读
生产者-消费者模型:

该模型允许多个任务并行工作。生产者生成数据并将其放入队列,消费者从队列中读取数据进行处理。此设计模式适用于需要高效数据处理的场景。
使用队列:

FreeRTOS 提供的队列用于在任务之间传递数据。在示例中,使用 xQueueCreate 创建一个队列,生产者通过 xQueueSend 将数据放入队列,消费者使用 xQueueReceive 从队列中获取数据。
优先级设置:

在第三个示例中,生产者任务的优先级设置高于消费者,以确保数据能够快速生成并及时处理。这种优先级设置可以根据具体需求调整,以平衡生产和消费的速率。
任务延迟:

通过 vTaskDelay 控制任务的执行频率,避免 CPU 占用过高,同时确保系统的响应性。不同的延迟时间可以影响系统的性能和响应。
扩展性和灵活性:

该模型易于扩展,可以根据需要增加更多的生产者和消费者任务,适应不同的负载和数据处理需求。通过调整队列大小和任务优先级,可以灵活应对不同的应用场景。

在这里插入图片描述
4、工业传感器数据采集与处理系统
场景描述
在工业自动化生产线中,多个传感器(如温度、压力、流量传感器)作为生产者实时采集数据,多个数据处理任务(如滤波、异常检测、存储)作为消费者并行处理数据。系统需满足实时性要求,确保关键数据优先处理。

代码实现

#include <Arduino_FreeRTOS.h>
#include <queue.h>
 
#define QUEUE_LENGTH 10
#define DATA_SIZE sizeof(float)
 
QueueHandle_t sensorQueue;
 
// 生产者任务:温度传感器
void TaskTempSensor(void *pvParameters) {
  float temp;
  while (1) {
    temp = random(20, 100); // 模拟温度数据
    xQueueSend(sensorQueue, &temp, portMAX_DELAY); // 发送到队列
    vTaskDelay(500 / portTICK_PERIOD_MS); // 500ms采集一次
  }
}
 
// 生产者任务:压力传感器
void TaskPressureSensor(void *pvParameters) {
  float pressure;
  while (1) {
    pressure = random(1, 10); // 模拟压力数据
    xQueueSend(sensorQueue, &pressure, portMAX_DELAY);
    vTaskDelay(300 / portTICK_PERIOD_MS); // 300ms采集一次
  }
}
 
// 消费者任务:数据处理与存储
void TaskDataProcessor(void *pvParameters) {
  float data;
  while (1) {
    if (xQueueReceive(sensorQueue, &data, portMAX_DELAY) == pdPASS) {
      // 模拟数据处理:温度数据优先存储
      if ((int)pvParameters == 1) { // 任务优先级标识
        Serial.print("Temp: "); Serial.println(data);
      } else {
        Serial.print("Pressure: "); Serial.println(data);
      }
    }
  }
}
 
void setup() {
  Serial.begin(115200);
  sensorQueue = xQueueCreate(QUEUE_LENGTH, DATA_SIZE); // 创建队列
 
  // 创建生产者任务
  xTaskCreate(TaskTempSensor, "TempSensor", 128, NULL, 2, NULL); // 优先级2
  xTaskCreate(TaskPressureSensor, "PressureSensor", 128, NULL, 1, NULL); // 优先级1
 
  // 创建消费者任务(带优先级)
  xTaskCreate(TaskDataProcessor, "DataProcessor1", 128, (void*)1, 3, NULL); // 优先级3
  xTaskCreate(TaskDataProcessor, "DataProcessor2", 128, (void*)2, 2, NULL); // 优先级2
 
  vTaskStartScheduler();
}
 
void loop() {}

要点解读

优先级调度:温度传感器任务优先级高于压力传感器,确保关键数据优先处理。
队列同步:使用FreeRTOS队列实现生产者-消费者解耦,避免数据竞争。
资源管理:队列长度限制防止内存耗尽,任务堆栈大小根据需求分配。
实时性保障:高优先级任务可抢占低优先级任务CPU资源,满足实时性要求。
可扩展性:新增传感器或处理任务只需修改任务创建代码,无需改动核心逻辑。

5、智能家居多设备控制与状态监控
场景描述
智能家居系统中,多个设备(如灯光、空调、窗帘)作为生产者发送状态数据,多个控制任务(如手机APP指令、定时任务)作为消费者协调设备动作。系统需处理异步事件并避免任务饥饿。

代码实现

#include <Arduino_FreeRTOS.h>
#include <semphr.h>
 
SemaphoreHandle_t mutex; // 互斥锁保护共享资源
 
// 生产者任务:灯光状态上报
void TaskLightStatus(void *pvParameters) {
  bool status;
  while (1) {
    status = random(0, 2); // 模拟灯光开关状态
    xSemaphoreTake(mutex, portMAX_DELAY); // 获取互斥锁
    Serial.print("Light: "); Serial.println(status ? "ON" : "OFF");
    xSemaphoreGive(mutex); // 释放互斥锁
    vTaskDelay(1000 / portTICK_PERIOD_MS); // 1秒上报一次
  }
}
 
// 消费者任务:手机APP控制
void TaskAppControl(void *pvParameters) {
  while (1) {
    xSemaphoreTake(mutex, portMAX_DELAY);
    Serial.println("APP Command: Toggle Light"); // 模拟APP指令
    xSemaphoreGive(mutex);
    vTaskDelay(2000 / portTICK_PERIOD_MS); // 2秒发送一次指令
  }
}
 
// 消费者任务:定时控制
void TaskTimerControl(void *pvParameters) {
  while (1) {
    xSemaphoreTake(mutex, portMAX_DELAY);
    Serial.println("Timer: Turn Off Light at 22:00"); // 模拟定时任务
    xSemaphoreGive(mutex);
    vTaskDelay(5000 / portTICK_PERIOD_MS); // 5秒执行一次
  }
}
 
void setup() {
  Serial.begin(115200);
  mutex = xSemaphoreCreateMutex(); // 创建互斥锁
 
  // 创建生产者任务
  xTaskCreate(TaskLightStatus, "LightStatus", 128, NULL, 2, NULL);
 
  // 创建消费者任务
  xTaskCreate(TaskAppControl, "AppControl", 128, NULL, 1, NULL);
  xTaskCreate(TaskTimerControl, "TimerControl", 128, NULL, 1, NULL);
 
  vTaskStartScheduler();
}
 
void loop() {}

要点解读

互斥锁保护:使用xSemaphoreCreateMutex防止共享资源(如串口)被多个任务同时访问。
任务优先级平衡:APP控制与定时任务优先级相同,通过时间片轮转避免任务饥饿。
异步事件处理:生产者任务独立运行,消费者任务按需响应,提高系统并发性。
低功耗优化:可通过vTaskSuspend/vTaskResume动态调整任务状态,减少空闲时功耗。
调试友好性:串口输出任务状态,便于监控系统运行情况。

6、机器人多传感器数据融合与决策
场景描述
移动机器人需同时处理视觉、激光雷达、IMU等多传感器数据,生产者任务负责采集数据,消费者任务负责融合数据并决策路径。系统需满足高实时性和低延迟要求。

代码实现

#include <Arduino_FreeRTOS.h>
#include <queue.h>
 
typedef struct {
  float x, y; // 坐标数据
} SensorData;
 
QueueHandle_t visionQueue, lidarQueue;
 
// 生产者任务:视觉传感器
void TaskVisionSensor(void *pvParameters) {
  SensorData data;
  while (1) {
    data.x = random(-100, 100); // 模拟视觉检测坐标
    data.y = random(-100, 100);
    xQueueSend(visionQueue, &data, portMAX_DELAY);
    vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms采集一次
  }
}
 
// 生产者任务:激光雷达
void TaskLidarSensor(void *pvParameters) {
  SensorData data;
  while (1) {
    data.x = random(-50, 50); // 模拟激光雷达距离数据
    data.y = random(-50, 50);
    xQueueSend(lidarQueue, &data, portMAX_DELAY);
    vTaskDelay(50 / portTICK_PERIOD_MS); // 50ms采集一次
  }
}
 
// 消费者任务:数据融合与决策
void TaskDataFusion(void *pvParameters) {
  SensorData visionData, lidarData;
  while (1) {
    if (xQueueReceive(visionQueue, &visionData, 0) == pdPASS &&
        xQueueReceive(lidarQueue, &lidarData, 0) == pdPASS) {
      // 模拟数据融合:加权平均
      float fusedX = (visionData.x * 0.6 + lidarData.x * 0.4);
      float fusedY = (visionData.y * 0.6 + lidarData.y * 0.4);
      Serial.print("Fused Position: (");
      Serial.print(fusedX); Serial.print(", "); Serial.print(fusedY); Serial.println(")");
    }
    vTaskDelay(20 / portTICK_PERIOD_MS); // 20ms处理一次
  }
}
 
void setup() {
  Serial.begin(115200);
  visionQueue = xQueueCreate(5, sizeof(SensorData));
  lidarQueue = xQueueCreate(5, sizeof(SensorData));
 
  // 创建生产者任务
  xTaskCreate(TaskVisionSensor, "VisionSensor", 128, NULL, 2, NULL);
  xTaskCreate(TaskLidarSensor, "LidarSensor", 128, NULL, 3, NULL); // 更高优先级
 
  // 创建消费者任务
  xTaskCreate(TaskDataFusion, "DataFusion", 256, NULL, 1, NULL);
 
  vTaskStartScheduler();
}
 
void loop() {}

要点解读

多队列同步:为不同传感器创建独立队列,避免数据混淆。
优先级反转避免:激光雷达任务优先级高于视觉传感器,确保高频率数据优先处理。
低延迟设计:数据融合任务以20ms周期运行,满足机器人实时控制需求。
任务堆栈优化:数据融合任务堆栈大小增加至256字节,以容纳复杂计算。
硬件加速潜力:可结合ESP32的双核特性,将传感器采集与数据处理分配到不同核心。

注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值