freeRTOS 线程间通信方式总结(干货总结)

目录

1. 队列(Queues)

2. 信号量(Semaphores)

(1)二进制信号量(Binary Semaphores)

(2)计数信号量(Counting Semaphores)

(3)互斥信号量(Mutex Semaphores)

3. 事件组(Event Groups)

4. 消息缓冲区(Message Buffers)

5. 流缓冲区(Stream Buffers)

总结


在 FreeRTOS 中,线程(任务)间通信是多任务系统中非常重要的部分,主要用于任务间数据传递、同步和事件通知。FreeRTOS 提供了多种线程间通信方式,以下是详细介绍及示例:

1. 队列(Queues)

特点
队列是 FreeRTOS 中最常用的通信方式,支持 FIFO(先进先出)或 LIFO(后进先出)模式,可在任务间、任务与中断间传递数据,具有异步通信特性。
队列中的每个元素大小固定,创建时需指定元素大小和队列长度。

适用场景

  • 任务间传递数据(如传感器数据、命令等)
  • 中断服务程序(ISR)向任务发送数据

示例代码

#include "FreeRTOS.h"
#include "queue.h"

// 定义队列句柄
QueueHandle_t xQueue;

// 发送任务
void vSenderTask(void *pvParameters) {
    int32_t lValueToSend = 0;
    BaseType_t xStatus;

    for (;;) {
        // 向队列发送数据,等待时间为0(不阻塞)
        xStatus = xQueueSend(xQueue, &lValueToSend, 0);
        if (xStatus != pdPASS) {
            // 队列已满,发送失败
            printf("Could not send to the queue.\r\n");
        }
        lValueToSend++;
        vTaskDelay(pdMS_TO_TICKS(100)); // 延迟100ms
    }
}

// 接收任务
void vReceiverTask(void *pvParameters) {
    int32_t lReceivedValue;
    BaseType_t xStatus;

    for (;;) {
        // 从队列接收数据,等待时间为portMAX_DELAY(无限等待)
        xStatus = xQueueReceive(xQueue, &lReceivedValue, portMAX_DELAY);
        if (xStatus == pdPASS) {
            // 接收成功,打印数据
            printf("Received = %d\r\n", lReceivedValue);
        }
    }
}

int main(void) {
    // 创建队列:可存储5个int32_t类型的元素
    xQueue = xQueueCreate(5, sizeof(int32_t));
    if (xQueue != NULL) {
        // 创建发送任务和接收任务
        xTaskCreate(vSenderTask, "Sender", 1000, NULL, 1, NULL);
        xTaskCreate(vReceiverTask, "Receiver", 1000, NULL, 2, NULL);
        
        // 启动调度器
        vTaskStartScheduler();
    }

    // 如果程序运行到这里,说明调度器启动失败
    for (;;);
    return 0;
}

2. 信号量(Semaphores)

特点
信号量是一种用于同步或互斥的机制,本质是一个计数器,支持 take(获取,计数器减 1)和 give(释放,计数器加 1)操作。
FreeRTOS 提供三种信号量:二进制信号量、计数信号量、互斥信号量。

(1)二进制信号量(Binary Semaphores)

特点:计数器只能为 0 或 1,适用于同步(如任务等待事件)或简单互斥。

示例(任务与中断同步)

#include "FreeRTOS.h"
#include "semphr.h"

SemaphoreHandle_t xBinarySemaphore;

// 中断服务程序(如定时器中断)
void vTimerISR(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    // 释放信号量,唤醒等待的任务
    xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
    // 如果有更高优先级任务被唤醒,进行上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 等待信号量的任务
void vHandlerTask(void *pvParameters) {
    for (;;) {
        // 等待信号量,无限等待
        xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);
        // 信号量被释放,执行处理逻辑
        printf("Handler task - Processing event.\r\n");
    }
}

int main(void) {
    // 创建二进制信号量(初始值为0)
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore != NULL) {
        xTaskCreate(vHandlerTask, "Handler", 1000, NULL, 1, NULL);
        // 初始化中断(此处省略具体硬件初始化代码)
        vTaskStartScheduler();
    }

    for (;;);
    return 0;
}
(2)计数信号量(Counting Semaphores)

特点:计数器可在 0 到最大值之间变化,适用于限制资源访问数量(如有限个外设)。

示例(限制资源访问)

// 创建计数信号量,最多允许3个任务同时访问资源
SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(3, 3);

// 访问资源的任务
void vResourceTask(void *pvParameters) {
    for (;;) {
        // 获取信号量(资源数减1)
        xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);
        // 访问共享资源
        printf("Task %d accessing resource.\r\n", (int)pvParameters);
        vTaskDelay(pdMS_TO_TICKS(500)); // 模拟资源使用时间
        // 释放信号量(资源数加1)
        xSemaphoreGive(xCountingSemaphore);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}
(3)互斥信号量(Mutex Semaphores)

特点:专为互斥设计,支持优先级继承机制(避免优先级反转),只能由获取它的任务释放。

示例(保护共享资源)

SemaphoreHandle_t xMutex;
int32_t lSharedVariable = 0; // 共享变量

// 任务1:修改共享变量
void vTask1(void *pvParameters) {
    for (;;) {
        xSemaphoreTake(xMutex, portMAX_DELAY);
        lSharedVariable++; // 临界区操作
        xSemaphoreGive(xMutex);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// 任务2:读取共享变量
void vTask2(void *pvParameters) {
    for (;;) {
        xSemaphoreTake(xMutex, portMAX_DELAY);
        printf("Shared variable = %d\r\n", lSharedVariable); // 临界区操作
        xSemaphoreGive(xMutex);
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

int main(void) {
    xMutex = xSemaphoreCreateMutex(); // 创建互斥信号量
    if (xMutex != NULL) {
        xTaskCreate(vTask1, "Task1", 1000, NULL, 1, NULL);
        xTaskCreate(vTask2, "Task2", 1000, NULL, 2, NULL);
        vTaskStartScheduler();
    }
    for (;;);
    return 0;
}

3. 事件组(Event Groups)

特点:事件组是一组二进制标志(bit),每个 bit 代表一个事件,任务可等待一个或多个事件的组合(逻辑与 / 或)。
适用于任务需要等待多个事件中的一个或全部发生的场景。

示例

#include "FreeRTOS.h"
#include "event_groups.h"

// 定义事件标志(使用不同的bit位)
#define BIT_0 (1 << 0)
#define BIT_1 (1 << 1)

EventGroupHandle_t xEventGroup;

// 发送事件的任务1
void vTask1(void *pvParameters) {
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(500));
        // 设置BIT_0事件
        xEventGroupSetBits(xEventGroup, BIT_0);
    }
}

// 发送事件的任务2
void vTask2(void *pvParameters) {
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(1000));
        // 设置BIT_1事件
        xEventGroupSetBits(xEventGroup, BIT_1);
    }
}

// 等待事件的任务
void vHandlerTask(void *pvParameters) {
    EventBits_t xEventBits;
    for (;;) {
        // 等待BIT_0或BIT_1事件(逻辑或),无限等待
        xEventBits = xEventGroupWaitBits(
            xEventGroup,          // 事件组句柄
            BIT_0 | BIT_1,        // 等待的事件
            pdTRUE,               // 获取后清除事件标志
            pdFALSE,              // 满足任一事件即可
            portMAX_DELAY         // 等待时间
        );

        if ((xEventBits & BIT_0) != 0) {
            printf("Received BIT_0 event.\r\n");
        }
        if ((xEventBits & BIT_1) != 0) {
            printf("Received BIT_1 event.\r\n");
        }
    }
}

int main(void) {
    xEventGroup = xEventGroupCreate(); // 创建事件组
    if (xEventGroup != NULL) {
        xTaskCreate(vTask1, "Task1", 1000, NULL, 1, NULL);
        xTaskCreate(vTask2, "Task2", 1000, NULL, 1, NULL);
        xTaskCreate(vHandlerTask, "Handler", 1000, NULL, 2, NULL);
        vTaskStartScheduler();
    }
    for (;;);
    return 0;
}

4. 消息缓冲区(Message Buffers)

特点:专为变长数据设计(如字符串、字节流),比队列更高效,适用于传递不定长消息(如串口数据、网络包)。
消息缓冲区基于 DMA 友好的环形缓冲区实现,支持从 ISR 发送消息。

示例

#include "FreeRTOS.h"
#include "message_buffer.h"

#define BUFFER_SIZE 100 // 消息缓冲区大小(字节)
MessageBufferHandle_t xMessageBuffer;

// 发送消息的任务
void vSenderTask(void *pvParameters) {
    char *pcMessage = "Hello, Message Buffer!";
    size_t xMessageLength = strlen(pcMessage) + 1; // 包含终止符

    for (;;) {
        // 发送消息,等待时间0
        xMessageBufferSend(xMessageBuffer, pcMessage, xMessageLength, 0);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

// 接收消息的任务
void vReceiverTask(void *pvParameters) {
    char cBuffer[BUFFER_SIZE];
    size_t xReceivedLength;

    for (;;) {
        // 接收消息,无限等待
        xReceivedLength = xMessageBufferReceive(
            xMessageBuffer,
            cBuffer,
            BUFFER_SIZE,
            portMAX_DELAY
        );
        if (xReceivedLength > 0) {
            printf("Received: %s\r\n", cBuffer);
        }
    }
}

int main(void) {
    xMessageBuffer = xMessageBufferCreate(BUFFER_SIZE);
    if (xMessageBuffer != NULL) {
        xTaskCreate(vSenderTask, "Sender", 1000, NULL, 1, NULL);
        xTaskCreate(vReceiverTask, "Receiver", 1000, NULL, 2, NULL);
        vTaskStartScheduler();
    }
    for (;;);
    return 0;
}

5. 流缓冲区(Stream Buffers)

特点:用于连续字节流的传递(如音频、视频数据),与消息缓冲区类似,但不自动添加消息边界,适用于无需消息边界的场景。

总结

通信方式核心用途特点
队列定长数据传递FIFO/LIFO,支持任务 / 中断间通信
信号量同步、互斥、资源计数二进制 / 计数 / 互斥三种类型
事件组多事件等待(与 / 或逻辑)基于 bit 标志,支持批量事件处理
消息缓冲区变长消息传递高效处理字符串、不定长数据
流缓冲区字节流传递无消息边界,适合连续数据流

选择通信方式时,需根据实际场景(数据长度、同步需求、资源限制等)灵活选用。例如,传递传感器数据可用队列,任务同步可用二进制信号量,多事件等待可用事件组。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

start_up_go

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

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

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

打赏作者

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

抵扣说明:

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

余额充值