目录
在 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 标志,支持批量事件处理 |
| 消息缓冲区 | 变长消息传递 | 高效处理字符串、不定长数据 |
| 流缓冲区 | 字节流传递 | 无消息边界,适合连续数据流 |
选择通信方式时,需根据实际场景(数据长度、同步需求、资源限制等)灵活选用。例如,传递传感器数据可用队列,任务同步可用二进制信号量,多事件等待可用事件组。
2094

被折叠的 条评论
为什么被折叠?



