FreeRTOS中任务通信机制的配合使用

序号内容链接
1任务状态类型点我访问
2任务间通信方式点我访问
3任务通信机制的配合使用点我访问

在FreeRTOS中,任务通信机制之间的配合使用可以构建出非常强大和灵活的实时系统。不同的通信机制可以互相补充,以适应不同场景下的需求。下面是几种常见通信机制的配合使用方式:

1. 信号量与消息队列的配合

1.1 场景:

多个任务需要共享资源,同时这些任务也需要通过消息队列进行通信。

1.2 使用方式:

使用信号量保护对消息队列的访问,确保在高并发情况下消息队列的完整性和一致性。

1.3 例程场景描述:

创建两个任务:一个生产者任务(ProducerTask),用于生成数据并将其放入消息队列;一个消费者任务(ConsumerTask),用于从消息队列中读取数据并处理。

1.3.1 示例代码

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

// 定义消息队列
QueueHandle_t xQueue = NULL;

// 定义信号量
SemaphoreHandle_t xMutex = NULL;

// 模拟数据结构
typedef struct
{
    int value;
} Data_t;

// 生产者任务
void vProducerTask(void *pvParameters)
{
    Data_t data;
    while(1)
    {
        // 生成数据
        data.value = generateData(); // 假设generateData()是一个返回整数的函数
        
        // 获取信号量
        if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
        {
            // 发送数据到队列
            if(xQueueSend(xQueue, &data, portMAX_DELAY) == pdPASS)
            {
                printf("Data sent: %d\n", data.value);
            }
            
            // 释放信号量
            xSemaphoreGive(xMutex);
        }
        else
        {
            printf("Failed to take semaphore, skipping data send.\n");
        }
        
        // 延迟
        vTaskDelay(pdMS_TO_TICKS(500)); // 每隔500ms生成一次数据
    }
}

// 消费者任务
void vConsumerTask(void *pvParameters)
{
    Data_t data;
    while(1)
    {
        // 获取信号量
        if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
        {
            // 从队列接收数据
            if(xQueueReceive(xQueue, &data, portMAX_DELAY) == pdPASS)
            {
                printf("Data received: %d\n", data.value);
                processData(data); // 假设processData()是一个处理数据的函数
            }
            
            // 释放信号量
            xSemaphoreGive(xMutex);
        }
        else
        {
            printf("Failed to take semaphore, skipping data receive.\n");
        }
        
        // 延迟
        vTaskDelay(pdMS_TO_TICKS(1000)); // 每隔1s尝试接收数据
    }
}

int main(void)
{
    // 创建信号量
    xMutex = xSemaphoreCreateMutex();
    
    // 创建消息队列
    xQueue = xQueueCreate(10, sizeof(Data_t));
    
    // 创建任务
    xTaskCreate(vProducerTask, "ProducerTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vConsumerTask, "ConsumerTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    
    // 启动FreeRTOS
    vTaskStartScheduler();
    
    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

1.3.2 代码解析

信号量:xMutex用于保护对消息队列xQueue的访问。在发送或接收数据之前,任务必须首先获取信号量。这样可以确保在任何时刻只有一个任务能够访问队列,防止数据竞争和不一致性。
消息队列:xQueue用于在生产者任务和消费者任务之间传递数据。数据被封装在Data_t结构中,这样可以传递更复杂的数据类型,而不仅仅是原始值。
任务:vProducerTask和vConsumerTask分别作为生产者和消费者角色运行。生产者任务生成数据并将其发送到队列中,而消费者任务从队列中读取数据并进行处理。
这种信号量与消息队列的配合使用模式在实时系统中非常常见,尤其是在需要保证数据完整性且多个任务需要访问同一资源的情况下。

2. 信号量与事件标志的配合

2.1 应用场景:

一个任务需要控制对共享资源的访问,同时需要通知其他任务特定事件的发生。

2.2 使用方式:

使用信号量控制对共享资源的访问,使用事件标志通知其他任务事件的发生。

2.3 例程场景描述

创建一个任务,它将模拟某种硬件中断,每当有新的数据到达时,会设置一个事件标志。另一个任务将检查这个事件标志,一旦检测到数据到达的事件,它就会处理这些数据。

2.3.1 示例代码

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

// 定义事件标志组
EventGroupHandle_t xEventGroup;

// 定义事件标志位
#define DATA_RECEIVED_BIT (1 << 0)

// 事件生成任务
void vDataReceivedTask(void *pvParameters)
{
    while(1)
    {
        // 假设这里检测到了数据到达
        // 在实际应用中,这可能是一个硬件中断服务程序的一部分
        vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟数据每秒到达一次

        // 设置事件标志
        xEventGroupSetBits(xEventGroup, DATA_RECEIVED_BIT);

        // 延迟是为了避免快速连续触发事件
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// 数据处理任务
void vDataProcessingTask(void *pvParameters)
{
    while(1)
    {
        // 等待数据接收事件
        EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, DATA_RECEIVED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

        if(uxBits & DATA_RECEIVED_BIT)
        {
            // 处理数据
            processData(); // 假设processData()是一个处理数据的函数
        }
    }
}

int main(void)
{
    // 创建事件标志组
    xEventGroup = xEventGroupCreate();

    // 创建任务
    xTaskCreate(vDataReceivedTask, "DataReceivedTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vDataProcessingTask, "DataProcessingTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

2.3.2 代码解析

事件标志组:xEventGroup是事件标志的集合,可以设置或清除单个或多个位来表示不同的事件状态。
事件标志位定义:DATA_RECEIVED_BIT定义了一个事件位,表示数据已经到达。这是一个二进制位,通过左移操作符设置为1。
事件生成任务:vDataReceivedTask模拟数据到达的情况,每过一秒就设置DATA_RECEIVED_BIT事件标志。
数据处理任务:vDataProcessingTask等待DATA_RECEIVED_BIT事件标志被设置。一旦事件发生,它将调用processData()函数来处理数据。
事件等待:xEventGroupWaitBits()函数用于等待一个或多个事件位被设置。在这个例子中,它等待DATA_RECEIVED_BIT被设置,并且不会清除该位,因为第二个参数pdFALSE指示不要自动清除事件位。
通过这种方式,事件标志可以作为一个轻量级的通知机制,允许一个任务在另一个任务或中断服务程序中产生的事件发生时做出反应。这比使用信号量更为灵活,因为你可以通过组合不同的事件位来响应多种情况。

3. 消息队列与事件标志的配合

3.1 场景:

一个任务需要发送数据给另一个任务,并且希望接收方在处理完数据后能通知发送方。

3.2 使用方式:

发送方通过消息队列发送数据,接收方处理完数据后,通过事件标志通知发送方数据已被处理。

3.3 例程场景描述

创建两个任务,其中有一个任务负责收集传感器数据并将其放入消息队列中,同时设置事件标志来通知另一个任务数据已经可用。第二个任务将等待事件标志,并在接收到事件后从队列中读取并处理数据。

3.3.1 示例代码

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"

// 定义消息类型
typedef struct
{
    uint32_t sensorValue;
} SensorData_t;

// 定义消息队列
QueueHandle_t xQueue;

// 定义事件标志组
EventGroupHandle_t xEventGroup;

// 定义事件标志位
#define SENSOR_DATA_AVAILABLE_BIT (1 << 0)

// 传感器数据收集任务
void vSensorDataTask(void *pvParameters)
{
    SensorData_t xSensorData;
    static int count = 0;

    while(1)
    {
        // 假设这里是读取传感器数据的地方
        xSensorData.sensorValue = count++; // 模拟传感器值递增

        // 发送数据到队列
        if (xQueueSend(xQueue, &xSensorData, portMAX_DELAY) == pdPASS)
        {
            // 设置事件标志
            xEventGroupSetBits(xEventGroup, SENSOR_DATA_AVAILABLE_BIT);
        }

        // 模拟传感器读取间隔
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 数据处理任务
void vDataProcessingTask(void *pvParameters)
{
    SensorData_t xSensorData;
    EventBits_t uxBits;

    while(1)
    {
        // 等待事件标志
        uxBits = xEventGroupWaitBits(xEventGroup, SENSOR_DATA_AVAILABLE_BIT, pdTRUE, pdFALSE, portMAX_DELAY);

        if(uxBits & SENSOR_DATA_AVAILABLE_BIT)
        {
            // 从队列中读取数据
            if (xQueueReceive(xQueue, &xSensorData, portMAX_DELAY) == pdTRUE)
            {
                // 处理数据
                processSensorData(xSensorData.sensorValue); // 假设processSensorData()是一个处理数据的函数
            }
        }
    }
}

int main(void)
{
    // 创建消息队列
    xQueue = xQueueCreate(10, sizeof(SensorData_t));
    configASSERT(xQueue != NULL);

    // 创建事件标志组
    xEventGroup = xEventGroupCreate();
    configASSERT(xEventGroup != NULL);

    // 创建任务
    xTaskCreate(vSensorDataTask, "SensorDataTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vDataProcessingTask, "DataProcessingTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

3.3.2 代码解析

消息队列:xQueue是消息队列句柄,用于存储SensorData_t类型的结构体。
事件标志组:xEventGroup用于管理事件标志。
事件标志位:SENSOR_DATA_AVAILABLE_BIT定义了数据可用的事件位。
传感器数据收集任务:vSensorDataTask模拟传感器数据的收集过程,将数据发送到队列,并设置事件标志。
数据处理任务:vDataProcessingTask等待事件标志被设置,然后从队列中读取数据并处理。
事件等待和数据接收:xEventGroupWaitBits()用于等待事件标志,而xQueueReceive()用于从队列中读取数据。
通过结合使用消息队列和事件标志,可以确保任务之间的同步和通信更加健壮和高效。事件标志提供了轻量级的事件通知,而消息队列则提供了数据传输的手段。

4. 信号量与互斥锁的配合

4.1 场景:

多个任务需要互斥访问共享资源,同时还需要控制任务的执行顺序。

4.2 使用方式:

使用互斥锁保护共享资源,使用信号量控制任务的执行顺序。

4.3 例程场景描述

假设有两个任务,一个任务负责收集数据并将其存储在一个共享缓冲区中,另一个任务负责从缓冲区读取数据并处理。为了确保数据的完整性和一致性,我们需要在写入和读取操作期间锁定缓冲区,防止两个任务同时访问。

4.3.1 示例代码

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

// 定义共享缓冲区
#define BUFFER_SIZE 10
char sharedBuffer[BUFFER_SIZE];
volatile size_t bufferWriteIndex = 0;
volatile size_t bufferReadIndex = 0;

// 定义互斥锁
SemaphoreHandle_t mutex;

// 定义信号量
SemaphoreHandle_t semaphore;

// 数据收集任务
void vDataCollectionTask(void *pvParameters)
{
    char data;
    while(1)
    {
        // 模拟数据收集
        data = collectData(); // 假设collectData()是一个返回数据的函数

        // 获取互斥锁
        if(xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
        {
            // 写入数据到共享缓冲区
            sharedBuffer[bufferWriteIndex] = data;
            bufferWriteIndex = (bufferWriteIndex + 1) % BUFFER_SIZE;
            
            // 释放互斥锁
            xSemaphoreGive(mutex);
            
            // 发送信号量,通知数据处理任务有新数据
            xSemaphoreGive(semaphore);
        }
        
        // 模拟延时
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

// 数据处理任务
void vDataProcessingTask(void *pvParameters)
{
    char data;
    while(1)
    {
        // 等待信号量
        xSemaphoreTake(semaphore, portMAX_DELAY);
        
        // 获取互斥锁
        if(xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
        {
            // 从共享缓冲区读取数据
            data = sharedBuffer[bufferReadIndex];
            bufferReadIndex = (bufferReadIndex + 1) % BUFFER_SIZE;
            
            // 释放互斥锁
            xSemaphoreGive(mutex);
            
            // 处理数据
            processData(data); // 假设processData()是一个处理数据的函数
        }
    }
}

int main(void)
{
    // 创建互斥锁
    mutex = xSemaphoreCreateMutex();
    configASSERT(mutex != NULL);

    // 创建信号量
    semaphore = xSemaphoreCreateBinary();
    configASSERT(semaphore != NULL);

    // 创建任务
    xTaskCreate(vDataCollectionTask, "DataCollectionTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vDataProcessingTask, "DataProcessingTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

4.3.2 代码解析

互斥锁:mutex用于保护对共享缓冲区sharedBuffer的访问,确保任何时候只有一个任务可以读写缓冲区。
信号量:semaphore用于通知vDataProcessingTask任务有新数据可用。vDataCollectionTask在向缓冲区写入数据后会发送信号量,而vDataProcessingTask则会等待信号量。
数据收集任务:vDataCollectionTask收集数据并将其写入共享缓冲区,之后释放互斥锁并通过信号量通知数据处理任务。
数据处理任务:vDataProcessingTask等待信号量,然后在互斥锁的保护下从缓冲区读取数据并处理。
通过这种组合使用,我们可以确保即使在多任务环境下,对共享资源的访问也是安全的,而且数据处理任务能够及时响应新数据的产生。

5. 事件标志与消息缓冲区的配合

5.1 场景:

一个任务需要向多个任务发送数据,并且需要知道数据是否已经被所有目标任务接收。

5.2 使用方式:

使用消息缓冲区发送数据,使用事件标志集合来跟踪哪些任务已经接收并处理了数据。

5.3 例程场景描述

创建两个任务,一个任务负责接收来自外部的事件(例如硬件中断),并将事件信息存储到消息缓冲区中。另一个任务将等待特定的事件标志,当事件标志被设置时,它会从消息缓冲区读取事件信息并处理。

5.3.1 示例代码

#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "message_buffer.h"

// 定义消息类型
typedef struct
{
    uint32_t eventId;
    uint32_t eventInfo;
} Event_t;

// 定义事件标志组
EventGroupHandle_t xEventGroup;

// 定义事件标志位
#define EVENT_OCCURRED_BIT (1 << 0)

// 定义消息缓冲区
MessageBufferHandle_t xMessageBuffer;

// 事件生成任务
void vEventGeneratorTask(void *pvParameters)
{
    Event_t xEvent;

    while(1)
    {
        // 模拟事件发生
        xEvent.eventId = 1; // 假设这是某种事件ID
        xEvent.eventInfo = 123; // 假设这是事件的附加信息

        // 将事件写入消息缓冲区
        if (xMessageBufferSend(xMessageBuffer, &xEvent, portMAX_DELAY) == pdPASS)
        {
            // 设置事件标志
            xEventGroupSetBits(xEventGroup, EVENT_OCCURRED_BIT);
        }

        // 模拟延时
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 事件处理任务
void vEventHandlerTask(void *pvParameters)
{
    Event_t xEvent;
    EventBits_t uxBits;

    while(1)
    {
        // 等待事件标志
        uxBits = xEventGroupWaitBits(xEventGroup, EVENT_OCCURRED_BIT, pdTRUE, pdFALSE, portMAX_DELAY);

        if(uxBits & EVENT_OCCURRED_BIT)
        {
            // 从消息缓冲区读取事件
            if (xMessageBufferReceive(xMessageBuffer, &xEvent, portMAX_DELAY) == pdPASS)
            {
                // 处理事件
                handleEvent(xEvent.eventId, xEvent.eventInfo);
            }
        }
    }
}

int main(void)
{
    // 创建事件标志组
    xEventGroup = xEventGroupCreate();

    // 创建消息缓冲区
    xMessageBuffer = xMessageBufferCreate(sizeof(Event_t));

    // 创建任务
    xTaskCreate(vEventGeneratorTask, "EventGeneratorTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vEventHandlerTask, "EventHandlerTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

5.3.2 代码解析

事件标志组:xEventGroup用于标记事件的发生。当事件发生时,事件标志EVENT_OCCURRED_BIT会被设置。
消息缓冲区:xMessageBuffer用于存储事件信息。当事件发生时,事件信息会被写入缓冲区。
事件生成任务:vEventGeneratorTask模拟事件发生,将事件信息写入消息缓冲区,并设置事件标志。
事件处理任务:vEventHandlerTask等待事件标志被设置,然后从消息缓冲区读取事件信息并处理。
事件等待和数据接收:xEventGroupWaitBits()用于等待事件标志,而xMessageBufferReceive()用于从消息缓冲区读取数据。
通过这种机制,事件处理任务可以保持休眠状态,直到有事件发生。这种方法减少了CPU的占用率,提高了系统的响应速度和整体效率。同时,消息缓冲区确保了事件信息的安全传输,避免了数据丢失或覆盖的风险。

6. 任务通知与事件标志的配合

6.1 场景:

一个任务需要通知多个任务发生了某个事件,但不需要发送复杂的数据。

6.2 使用方式:

使用任务通知来简单地告知任务事件的发生,使用事件标志来同步多个任务对事件的响应。

6.3 例程场景描述

创建两个任务,一个任务负责产生事件并通知另一个任务,而被通知的任务将等待通知并检查事件标志,一旦收到通知,就执行相应的动作。

6.3.1 示例代码

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

// 定义事件标志组
EventGroupHandle_t xEventGroup;

// 定义事件标志位
#define EVENT_OCCURRED_BIT (1 << 0)

// 事件生成任务
void vEventGeneratorTask(void *pvParameters)
{
    uint32_t ulNotificationValue;

    while(1)
    {
        // 模拟事件发生
        ulNotificationValue = pdTRUE;

        // 通知事件处理任务
        if (xTaskNotifyGive(pdGET_TASK_HANDLE("EventHandlerTask")) != pdPASS)
        {
            // 如果通知失败,这里可以添加错误处理代码
        }

        // 设置事件标志
        xEventGroupSetBits(xEventGroup, EVENT_OCCURRED_BIT);

        // 模拟延时
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 事件处理任务
void vEventHandlerTask(void *pvParameters)
{
    uint32_t ulNotificationValue;
    EventBits_t uxBits;

    while(1)
    {
        // 等待通知或事件标志
        ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        uxBits = xEventGroupWaitBits(xEventGroup, EVENT_OCCURRED_BIT, pdTRUE, pdFALSE, portMAX_DELAY);

        if(ulNotificationValue > 0 || (uxBits & EVENT_OCCURRED_BIT))
        {
            // 执行事件处理逻辑
            processEvent();
        }
    }
}

int main(void)
{
    // 创建事件标志组
    xEventGroup = xEventGroupCreate();

    // 创建任务
    xTaskCreate(vEventGeneratorTask, "EventGeneratorTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vEventHandlerTask, "EventHandlerTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

注意事项
在上述示例中,事件生成任务通过xTaskNotifyGive函数向事件处理任务发送通知。这将增加事件处理任务的通知值,事件处理任务可以使用ulTaskNotifyTake函数等待通知。
xTaskNotifyGive函数要求传入要通知的任务句柄,这里使用pdGET_TASK_HANDLE宏获取事件处理任务的句柄。在实际应用中,可能需要更复杂的方法来传递任务句柄或者在创建任务时保存任务句柄。
在事件处理任务中,ulTaskNotifyTake函数会阻塞直到接收到通知,同时xEventGroupWaitBits函数也会阻塞直到事件标志被设置。
事件处理任务通过检查ulNotificationValue和uxBits来判断是否收到了通知或事件标志已被设置。

6.3.2 改进方案

在实际应用中,通常建议将任务通知和事件标志分开处理,避免在同一个条件语句中混合使用。这是因为它们的工作方式不同,任务通知可以携带额外的数据,而事件标志则用于简单的同步目的。因此,一个更清晰的实现方式是分别等待任务通知和事件标志,而不是将它们混合在一个if语句中。

while(1)
{
    ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    uxBits = xEventGroupWaitBits(xEventGroup, EVENT_OCCURRED_BIT, pdFALSE, pdFALSE, 0);

    if(ulNotificationValue > 0)
    {
        // 执行因通知触发的动作
        handleNotification();
    }

    if(uxBits & EVENT_OCCURRED_BIT)
    {
        // 执行因事件标志触发的动作
        processEvent();
    }
}

这样,你可以更清楚地分离两种不同的同步机制的处理逻辑。

7. 信号量与任务通知的配合

7.1 场景:

一个任务需要控制对资源的访问,同时也需要简单地通知其他任务某些事件的发生。

7.2 使用方式:

使用信号量来控制对资源的访问,使用任务通知来告知事件的发生。

7.3 例程场景描述:

这里有两个任务,一个“工作”任务和一个“监控”任务。工作任务会在完成某项工作后通过信号量通知监控任务,同时通过任务通知传递一些额外的信息,如工作的类型或状态。监控任务则会等待信号量,并读取任务通知中的信息。

7.3.1 示例代码

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

// 定义信号量
SemaphoreHandle_t xSemaphore;

// 工作任务
void vWorkTask(void *pvParameters)
{
    int workType = 0;
    while(1)
    {
        // 模拟工作完成
        workType++;
        vTaskDelay(pdMS_TO_TICKS(500)); // 模拟工作耗时

        // 通过信号量通知监控任务
        xSemaphoreGive(xSemaphore);

        // 通过任务通知传递工作类型
        xTaskNotifyGiveFromISR(NULL, (uint32_t)workType, eNoAction);
    }
}

// 监控任务
void vMonitorTask(void *pvParameters)
{
    uint32_t ulNotificationValue = 0;
    while(1)
    {
        // 等待信号量
        xSemaphoreTake(xSemaphore, portMAX_DELAY);

        // 读取任务通知中的工作类型
        ulNotificationValue = ulTaskNotifyTake(pdTRUE, 0);

        // 处理工作类型
        if(ulNotificationValue > 0)
        {
            printf("Received work of type: %d\n", ulNotificationValue);
            processWork((int)ulNotificationValue);
        }
    }
}

int main(void)
{
    // 创建信号量
    xSemaphore = xSemaphoreCreateBinary();

    // 创建工作任务和监控任务
    xTaskCreate(vWorkTask, "WorkTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    xTaskCreate(vMonitorTask, "MonitorTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);

    // 启动FreeRTOS
    vTaskStartScheduler();

    // 如果FreeRTOS没有启动,进入无限循环
    for(;;);
}

注意事项
任务通知的使用:在上面的代码中,xTaskNotifyGiveFromISR函数用于从任务上下文中发送任务通知,如果是在中断服务程序中发送通知,则应使用xTaskNotifyGiveFromISR。然而,由于我们的示例中是在任务中发送通知,所以直接使用了xTaskNotifyGiveFromISR的无参数版本xTaskNotifyGive。实际中你应该根据发送通知的上下文选择正确的函数。
任务通知的读取:ulTaskNotifyTake函数用于读取任务通知的值。参数pdTRUE表示应该清除通知值,0表示没有超时限制。
信号量的使用:信号量xSemaphore用于同步两个任务。xSemaphoreGive函数用于发送信号,xSemaphoreTake函数用于等待信号。
任务优先级:在xTaskCreate函数中,tskIDLE_PRIORITY + 1和tskIDLE_PRIORITY + 2用于设置任务的优先级。在FreeRTOS中,优先级较高的任务将优先得到执行。
通过这种配合使用,我们可以构建一个健壮的实时系统,其中任务可以根据工作完成的情况进行有效的通信和同步。

  • 25
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值