嵌入式开发必掌握:任务同步与通信机制实战(信号量+队列+事件组+任务通知)

嵌入式开发必掌握:任务同步与通信机制实战(信号量+队列+事件组+任务通知)

标签:嵌入式开发、RTOS、FreeRTOS、任务同步、信号量、队列、事件组、任务通知、互斥量、生产者消费者

前言

摘要:任务同步与通信是RTOS多任务编程的核心技术。本文从信号量机制、队列通信、事件组同步、任务通知到生产者-消费者模型实战,提供全套可直接量产的代码示例,同时总结二值信号量vs互斥量、队列vs任务通知、优先级翻转、死锁预防等踩坑经验,帮助开发者掌握高效可靠的任务同步编程技术。

多任务系统中,任务间的同步与通信是保证系统正确运行的关键。FreeRTOS提供了丰富的同步机制:信号量、互斥量、队列、事件组、任务通知等,每种机制都有其适用场景和使用技巧。

文章主要内容

  • 信号量机制详解
  • 互斥量与优先级继承
  • 队列通信机制
  • 事件组同步
  • 任务通知机制
  • 生产者-消费者模型
  • 死锁预防策略

一、信号量机制详解

1.1 信号量基本概念

信号量(Semaphore):用于任务同步和资源管理的计数器机制。

信号量类型

二值信号量
Binary Semaphore

计数信号量
Counting Semaphore

互斥量
Mutex

计数值: 0或1

用于任务同步

事件通知

计数值: 0~N

资源计数

多实例管理

特殊二值信号量

优先级继承

互斥访问

1.2 二值信号量

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

SemaphoreHandle_t binary_sem;

void Binary_Semaphore_Init(void)
{
    binary_sem = xSemaphoreCreateBinary();
    
    if(binary_sem == NULL)
    {
        printf("Binary Semaphore Create Failed\r\n");
    }
}

void ISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    xSemaphoreGiveFromISR(binary_sem, &xHigherPriorityTaskWoken);
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void Task_WaitEvent(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake(binary_sem, portMAX_DELAY) == pdTRUE)
        {
            printf("Event Received\r\n");
            
            ProcessEvent();
        }
    }
}

void Task_TriggerEvent(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        xSemaphoreGive(binary_sem);
        
        printf("Event Triggered\r\n");
    }
}

1.3 计数信号量

SemaphoreHandle_t counting_sem;

void Counting_Semaphore_Init(void)
{
    counting_sem = xSemaphoreCreateCounting(5, 0);
    
    if(counting_sem == NULL)
    {
        printf("Counting Semaphore Create Failed\r\n");
    }
}

void Resource_Producer(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(500));
        
        if(xSemaphoreGive(counting_sem) == pdTRUE)
        {
            UBaseType_t count = uxSemaphoreGetCount(counting_sem);
            printf("Resource Produced, Count: %d\r\n", count);
        }
        else
        {
            printf("Resource Pool Full\r\n");
        }
    }
}

void Resource_Consumer(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake(counting_sem, pdMS_TO_TICKS(2000)) == pdTRUE)
        {
            UBaseType_t count = uxSemaphoreGetCount(counting_sem);
            printf("Resource Consumed, Count: %d\r\n", count);
        }
        else
        {
            printf("Resource Timeout\r\n");
        }
    }
}

1.4 信号量应用场景

信号量应用

任务同步

资源计数

互斥访问

中断通知任务

任务间事件通知

资源池管理

限流控制

临界区保护

外设互斥访问


二、互斥量与优先级继承

2.1 互斥量基本概念

互斥量(Mutex):具有优先级继承机制的特殊二值信号量,用于互斥访问共享资源。

SemaphoreHandle_t mutex;

void Mutex_Init(void)
{
    mutex = xSemaphoreCreateMutex();
    
    if(mutex == NULL)
    {
        printf("Mutex Create Failed\r\n");
    }
}

void Task_AccessResource(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake(mutex, pdMS_TO_TICKS(100)) == pdTRUE)
        {
            printf("Resource Acquired\r\n");
            
            AccessSharedResource();
            
            xSemaphoreGive(mutex);
            
            printf("Resource Released\r\n");
        }
        else
        {
            printf("Resource Acquire Timeout\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(50));
    }
}

2.2 优先级翻转问题

互斥量 任务L 优先级1 任务M 优先级2 任务H 优先级3 互斥量 任务L 优先级1 任务M 优先级2 任务H 优先级3 高优先级任务被阻塞 优先级翻转! 获取互斥量 执行临界区 等待互斥量(阻塞) 就绪→抢占TaskL 长时间运行 运行完成 继续执行 释放互斥量 获取互斥量 执行临界区

2.3 优先级继承机制

#define configUSE_MUTEXES                    1

SemaphoreHandle_t mutex_with_inheritance;

void Mutex_PriorityInheritance_Init(void)
{
    mutex_with_inheritance = xSemaphoreCreateMutex();
    
    if(mutex_with_inheritance == NULL)
    {
        printf("Mutex Create Failed\r\n");
    }
}

void Task_LowPriority(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake(mutex_with_inheritance, portMAX_DELAY) == pdTRUE)
        {
            printf("Low Priority Task: Acquired Mutex\r\n");
            printf("Priority Inherited to: %d\r\n", 
                   uxTaskPriorityGet(NULL));
            
            vTaskDelay(pdMS_TO_TICKS(100));
            
            xSemaphoreGive(mutex_with_inheritance);
            
            printf("Low Priority Task: Released Mutex\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

void Task_HighPriority(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(10));
        
        if(xSemaphoreTake(mutex_with_inheritance, portMAX_DELAY) == pdTRUE)
        {
            printf("High Priority Task: Acquired Mutex\r\n");
            
            vTaskDelay(pdMS_TO_TICKS(50));
            
            xSemaphoreGive(mutex_with_inheritance);
            
            printf("High Priority Task: Released Mutex\r\n");
        }
    }
}

2.4 递归互斥量

SemaphoreHandle_t recursive_mutex;

void Recursive_Mutex_Init(void)
{
    recursive_mutex = xSemaphoreCreateRecursiveMutex();
    
    if(recursive_mutex == NULL)
    {
        printf("Recursive Mutex Create Failed\r\n");
    }
}

void Function_Level2(void)
{
    if(xSemaphoreTakeRecursive(recursive_mutex, portMAX_DELAY) == pdTRUE)
    {
        printf("Level 2: Acquired\r\n");
        
        xSemaphoreGiveRecursive(recursive_mutex);
        
        printf("Level 2: Released\r\n");
    }
}

void Function_Level1(void)
{
    if(xSemaphoreTakeRecursive(recursive_mutex, portMAX_DELAY) == pdTRUE)
    {
        printf("Level 1: Acquired\r\n");
        
        Function_Level2();
        
        xSemaphoreGiveRecursive(recursive_mutex);
        
        printf("Level 1: Released\r\n");
    }
}

三、队列通信机制

3.1 队列基本概念

队列(Queue):用于任务间传递数据的FIFO缓冲区。

队列特性

FIFO顺序

拷贝传递

阻塞机制

先进先出

保证数据顺序

数据拷贝到队列

数据从队列拷贝

安全传递

发送阻塞: 队列满

接收阻塞: 队列空

3.2 队列创建与使用

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

QueueHandle_t data_queue;

typedef struct
{
    uint8_t id;
    uint32_t timestamp;
    float value;
} SensorData_t;

void Queue_Init(void)
{
    data_queue = xQueueCreate(10, sizeof(SensorData_t));
    
    if(data_queue == NULL)
    {
        printf("Queue Create Failed\r\n");
    }
}

void Producer_Task(void *pvParameters)
{
    SensorData_t data;
    uint32_t count = 0;
    
    while(1)
    {
        data.id = 1;
        data.timestamp = xTaskGetTickCount();
        data.value = (float)count * 0.1f;
        
        if(xQueueSend(data_queue, &data, pdMS_TO_TICKS(100)) == pdPASS)
        {
            printf("Sent: ID=%d, Value=%.2f\r\n", data.id, data.value);
            count++;
        }
        else
        {
            printf("Queue Full\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void Consumer_Task(void *pvParameters)
{
    SensorData_t data;
    
    while(1)
    {
        if(xQueueReceive(data_queue, &data, pdMS_TO_TICKS(1000)) == pdPASS)
        {
            printf("Received: ID=%d, Time=%lu, Value=%.2f\r\n",
                   data.id, data.timestamp, data.value);
        }
        else
        {
            printf("Queue Empty\r\n");
        }
    }
}

3.3 队列集

#include "queue.h"

QueueHandle_t queue1;
QueueHandle_t queue2;
QueueSetHandle_t queue_set;

void QueueSet_Init(void)
{
    queue1 = xQueueCreate(5, sizeof(uint32_t));
    queue2 = xQueueCreate(5, sizeof(uint32_t));
    
    queue_set = xQueueCreateSet(5 + 5);
    
    xQueueAddToSet(queue1, queue_set);
    xQueueAddToSet(queue2, queue_set);
}

void MultiQueue_Consumer(void *pvParameters)
{
    QueueSetMemberHandle_t active_member;
    uint32_t data;
    
    while(1)
    {
        active_member = xQueueSelectFromSet(queue_set, portMAX_DELAY);
        
        if(active_member == queue1)
        {
            xQueueReceive(queue1, &data, 0);
            printf("Queue1: %lu\r\n", data);
        }
        else if(active_member == queue2)
        {
            xQueueReceive(queue2, &data, 0);
            printf("Queue2: %lu\r\n", data);
        }
    }
}

3.4 队列应用场景

typedef struct
{
    uint8_t cmd;
    uint8_t param[16];
    uint8_t param_len;
} Command_t;

QueueHandle_t cmd_queue;

void Command_Processor_Task(void *pvParameters)
{
    Command_t cmd;
    
    while(1)
    {
        if(xQueueReceive(cmd_queue, &cmd, portMAX_DELAY) == pdPASS)
        {
            switch(cmd.cmd)
            {
                case 0x01:
                    ProcessCommand1(cmd.param, cmd.param_len);
                    break;
                    
                case 0x02:
                    ProcessCommand2(cmd.param, cmd.param_len);
                    break;
                    
                default:
                    printf("Unknown Command: 0x%02X\r\n", cmd.cmd);
                    break;
            }
        }
    }
}

void UART_RX_Task(void *pvParameters)
{
    Command_t cmd;
    
    while(1)
    {
        if(ReceiveCommand(&cmd))
        {
            xQueueSend(cmd_queue, &cmd, portMAX_DELAY);
        }
    }
}

四、事件组同步

4.1 事件组基本概念

事件组(Event Group):用于多事件同步的位标志集合。

事件组特性

位标志

多事件同步

逻辑操作

每个位代表一个事件

24位可用
0-23

等待任意事件
OR逻辑

等待所有事件
AND逻辑

设置事件位

清除事件位

等待事件位

4.2 事件组创建与使用

#include "event_groups.h"

EventGroupHandle_t event_group;

#define EVENT_BIT_0    (1 << 0)
#define EVENT_BIT_1    (1 << 1)
#define EVENT_BIT_2    (1 << 2)
#define EVENT_BIT_3    (1 << 3)

void EventGroup_Init(void)
{
    event_group = xEventGroupCreate();
    
    if(event_group == NULL)
    {
        printf("Event Group Create Failed\r\n");
    }
}

void EventProducer_Task(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        xEventGroupSetBits(event_group, EVENT_BIT_0);
        printf("Set Event Bit 0\r\n");
        
        vTaskDelay(pdMS_TO_TICKS(500));
        
        xEventGroupSetBits(event_group, EVENT_BIT_1);
        printf("Set Event Bit 1\r\n");
    }
}

void EventConsumer_Task(void *pvParameters)
{
    EventBits_t event_bits;
    
    while(1)
    {
        event_bits = xEventGroupWaitBits(
            event_group,
            EVENT_BIT_0 | EVENT_BIT_1,
            pdTRUE,
            pdTRUE,
            portMAX_DELAY);
        
        if((event_bits & EVENT_BIT_0) && (event_bits & EVENT_BIT_1))
        {
            printf("Both Events Received\r\n");
        }
    }
}

4.3 多事件同步场景

#define EVENT_SENSOR_READY    (1 << 0)
#define EVENT_NETWORK_READY   (1 << 1)
#define EVENT_USER_READY      (1 << 2)

void Sensor_Task(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(100));
        
        ReadSensorData();
        
        xEventGroupSetBits(event_group, EVENT_SENSOR_READY);
    }
}

void Network_Task(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(200));
        
        if(NetworkConnect())
        {
            xEventGroupSetBits(event_group, EVENT_NETWORK_READY);
        }
    }
}

void Main_Task(void *pvParameters)
{
    EventBits_t event_bits;
    
    while(1)
    {
        event_bits = xEventGroupWaitBits(
            event_group,
            EVENT_SENSOR_READY | EVENT_NETWORK_READY,
            pdTRUE,
            pdTRUE,
            portMAX_DELAY);
        
        printf("All Systems Ready\r\n");
        
        ProcessData();
    }
}

4.4 中断中设置事件

void EXTI0_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        xEventGroupSetBitsFromISR(event_group, 
                                  EVENT_BIT_0,
                                  &xHigherPriorityTaskWoken);
        
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

五、任务通知机制

5.1 任务通知基本概念

任务通知(Task Notification):直接向任务发送32位通知值,比队列更轻量高效。

任务通知优势

速度快

内存省

使用简单

无需创建对象

直接发送通知

每个任务自带

无需额外内存

API简单

无需管理对象

适用场景

事件通知

简单数据传递

替代二值信号量

5.2 任务通知使用

TaskHandle_t task_handle;

void Task_WaitNotification(void *pvParameters)
{
    uint32_t notification_value;
    
    while(1)
    {
        notification_value = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        printf("Notification Received: %lu\r\n", notification_value);
    }
}

void Task_SendNotification(void *pvParameters)
{
    uint32_t count = 0;
    
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        
        xTaskNotify(task_handle, count, eIncrement);
        
        count++;
    }
}

void ISR_SendNotification(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

5.3 通知值操作

typedef enum
{
    eNoAction = 0,
    eSetBits,
    eIncrement,
    eSetValueWithOverwrite,
    eSetValueWithoutOverwrite
} eNotifyAction;

void Task_NotificationExample(void *pvParameters)
{
    uint32_t notification_value;
    BaseType_t ret;
    
    while(1)
    {
        ret = xTaskNotifyWait(0x00, 0xFFFFFFFF, &notification_value, portMAX_DELAY);
        
        if(ret == pdPASS)
        {
            if(notification_value & 0x01)
            {
                printf("Event 1\r\n");
            }
            
            if(notification_value & 0x02)
            {
                printf("Event 2\r\n");
            }
            
            if(notification_value & 0x04)
            {
                printf("Event 3\r\n");
            }
        }
    }
}

void SetNotificationBits(void)
{
    xTaskNotify(task_handle, 0x01, eSetBits);
}

void SetNotificationValue(void)
{
    xTaskNotify(task_handle, 100, eSetValueWithOverwrite);
}

void IncrementNotification(void)
{
    xTaskNotify(task_handle, 0, eIncrement);
}

5.4 任务通知vs队列

特性任务通知队列
创建无需创建需要创建
内存每个任务自带需要分配内存
速度快(直接操作)较慢(拷贝数据)
数据量32位值任意大小
接收者单个任务多个任务
适用场景简单通知复杂数据传递

六、生产者-消费者模型

6.1 使用队列实现

#define BUFFER_SIZE    10

typedef struct
{
    uint32_t data[BUFFER_SIZE];
    uint8_t count;
} DataBuffer_t;

QueueHandle_t data_queue;
SemaphoreHandle_t mutex;

void ProducerConsumer_Init(void)
{
    data_queue = xQueueCreate(BUFFER_SIZE, sizeof(uint32_t));
    mutex = xSemaphoreCreateMutex();
}

void Producer_Task(void *pvParameters)
{
    uint32_t data = 0;
    
    while(1)
    {
        data++;
        
        if(xQueueSend(data_queue, &data, pdMS_TO_TICKS(100)) == pdPASS)
        {
            printf("Produced: %lu\r\n", data);
        }
        else
        {
            printf("Buffer Full\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Consumer_Task(void *pvParameters)
{
    uint32_t data;
    
    while(1)
    {
        if(xQueueReceive(data_queue, &data, pdMS_TO_TICKS(1000)) == pdPASS)
        {
            printf("Consumed: %lu\r\n", data);
            
            ProcessData(data);
        }
        else
        {
            printf("Buffer Empty\r\n");
        }
    }
}

6.2 使用计数信号量实现

#define BUFFER_SIZE    10

uint32_t shared_buffer[BUFFER_SIZE];
uint32_t write_index = 0;
uint32_t read_index = 0;

SemaphoreHandle_t empty_sem;
SemaphoreHandle_t full_sem;
SemaphoreHandle_t mutex;

void BoundedBuffer_Init(void)
{
    empty_sem = xSemaphoreCreateCounting(BUFFER_SIZE, BUFFER_SIZE);
    full_sem = xSemaphoreCreateCounting(BUFFER_SIZE, 0);
    mutex = xSemaphoreCreateMutex();
}

void Producer_BoundedBuffer(void *pvParameters)
{
    uint32_t data = 0;
    
    while(1)
    {
        xSemaphoreTake(empty_sem, portMAX_DELAY);
        xSemaphoreTake(mutex, portMAX_DELAY);
        
        shared_buffer[write_index] = data++;
        write_index = (write_index + 1) % BUFFER_SIZE;
        
        printf("Produced: %lu\r\n", data);
        
        xSemaphoreGive(mutex);
        xSemaphoreGive(full_sem);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Consumer_BoundedBuffer(void *pvParameters)
{
    uint32_t data;
    
    while(1)
    {
        xSemaphoreTake(full_sem, portMAX_DELAY);
        xSemaphoreTake(mutex, portMAX_DELAY);
        
        data = shared_buffer[read_index];
        read_index = (read_index + 1) % BUFFER_SIZE;
        
        printf("Consumed: %lu\r\n", data);
        
        xSemaphoreGive(mutex);
        xSemaphoreGive(empty_sem);
        
        ProcessData(data);
    }
}

七、死锁预防策略

7.1 死锁产生条件

死锁四个必要条件

互斥条件

占有并等待

不可抢占

循环等待

资源互斥访问

占有资源同时请求其他资源

已分配资源不能强制剥夺

存在循环等待链

破坏死锁

破坏循环等待

破坏占有并等待

7.2 死锁示例

SemaphoreHandle_t mutex_a;
SemaphoreHandle_t mutex_b;

void Task1_Deadlock(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(mutex_a, portMAX_DELAY);
        printf("Task1: Acquired Mutex A\r\n");
        
        vTaskDelay(pdMS_TO_TICKS(100));
        
        xSemaphoreTake(mutex_b, portMAX_DELAY);  // 等待Mutex B
        printf("Task1: Acquired Mutex B\r\n");
        
        xSemaphoreGive(mutex_b);
        xSemaphoreGive(mutex_a);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Task2_Deadlock(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(mutex_b, portMAX_DELAY);
        printf("Task2: Acquired Mutex B\r\n");
        
        vTaskDelay(pdMS_TO_TICKS(100));
        
        xSemaphoreTake(mutex_a, portMAX_DELAY);  // 等待Mutex A
        printf("Task2: Acquired Mutex A\r\n");
        
        xSemaphoreGive(mutex_a);
        xSemaphoreGive(mutex_b);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

7.3 死锁预防方法

方法1:按顺序获取锁

void Task1_NoDeadlock(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(mutex_a, portMAX_DELAY);
        printf("Task1: Acquired Mutex A\r\n");
        
        vTaskDelay(pdMS_TO_TICKS(100));
        
        xSemaphoreTake(mutex_b, portMAX_DELAY);
        printf("Task1: Acquired Mutex B\r\n");
        
        xSemaphoreGive(mutex_b);
        xSemaphoreGive(mutex_a);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Task2_NoDeadlock(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(mutex_a, portMAX_DELAY);  // 先获取Mutex A
        printf("Task2: Acquired Mutex A\r\n");
        
        vTaskDelay(pdMS_TO_TICKS(100));
        
        xSemaphoreTake(mutex_b, portMAX_DELAY);  // 再获取Mutex B
        printf("Task2: Acquired Mutex B\r\n");
        
        xSemaphoreGive(mutex_b);
        xSemaphoreGive(mutex_a);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

方法2:使用超时机制

void Task_TimeoutLock(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake(mutex_a, pdMS_TO_TICKS(1000)) == pdTRUE)
        {
            printf("Acquired Mutex A\r\n");
            
            if(xSemaphoreTake(mutex_b, pdMS_TO_TICKS(1000)) == pdTRUE)
            {
                printf("Acquired Mutex B\r\n");
                
                xSemaphoreGive(mutex_b);
            }
            else
            {
                printf("Mutex B Timeout\r\n");
            }
            
            xSemaphoreGive(mutex_a);
        }
        else
        {
            printf("Mutex A Timeout\r\n");
        }
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

方法3:避免嵌套锁

void Task_NoNestedLock(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(mutex_a, portMAX_DELAY);
        ProcessResourceA();
        xSemaphoreGive(mutex_a);
        
        xSemaphoreTake(mutex_b, portMAX_DELAY);
        ProcessResourceB();
        xSemaphoreGive(mutex_b);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

八、任务同步踩坑总结

8.1 常见问题与解决方案

任务同步常见问题

优先级翻转

死锁

信号量误用

队列溢出

使用互斥量

优先级继承

按顺序获取锁

使用超时

理解信号量类型

正确选择同步机制

增大队列大小

使用超时机制

8.2 踩坑经验汇总

坑点1:二值信号量vs互斥量混淆

// ❌ 错误:用二值信号量做互斥访问
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
xSemaphoreGive(sem);  // 初始值设为1

// 问题:没有优先级继承,可能优先级翻转

// ✅ 正确:使用互斥量做互斥访问
SemaphoreHandle_t mutex = xSemaphoreCreateMutex();

// 互斥量特点:
// 1. 具有优先级继承机制
// 2. 只能由获取者释放
// 3. 适用于互斥访问

坑点2:中断中调用阻塞函数

// ❌ 错误:在中断中调用阻塞函数
void ISR_Handler(void)
{
    xSemaphoreTake(sem, portMAX_DELAY);  // 错误!
    xQueueSend(queue, &data, portMAX_DELAY);  // 错误!
}

// ✅ 正确:使用FromISR函数
void ISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken);
    xQueueSendFromISR(queue, &data, &xHigherPriorityTaskWoken);
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

坑点3:队列数据传递错误

// ❌ 错误:传递指针,数据可能被修改
uint32_t data = 100;
xQueueSend(queue, &data, portMAX_DELAY);  // 发送指针
data = 200;  // 数据被修改,队列中的数据也变了!

// ✅ 正确:队列会拷贝数据,传递值即可
uint32_t data = 100;
xQueueSend(queue, &data, portMAX_DELAY);  // 队列内部拷贝
data = 200;  // 不影响队列中的数据

// 注意:如果传递大结构体,考虑传递指针,但要确保数据生命周期

坑点4:事件位超过24位

// ❌ 错误:使用超过24位的事件位
#define EVENT_BIT_25    (1 << 25)  // 错误!超过24位

// ✅ 正确:只使用0-23位
#define EVENT_BIT_0     (1 << 0)
#define EVENT_BIT_23    (1 << 23)

// FreeRTOS事件组只有24位可用

坑点5:任务通知覆盖

// ❌ 错误:使用eSetValueWithOverwrite,可能丢失通知
xTaskNotify(task_handle, value1, eSetValueWithOverwrite);
xTaskNotify(task_handle, value2, eSetValueWithOverwrite);  // value1被覆盖

// ✅ 正确:根据需求选择合适的操作
xTaskNotify(task_handle, value, eIncrement);  // 累加
xTaskNotify(task_handle, bits, eSetBits);     // 设置位

8.3 同步机制选择指南

场景推荐机制原因
中断通知任务二值信号量/任务通知简单高效
互斥访问资源互斥量优先级继承
资源计数计数信号量计数功能
任务间数据传递队列安全可靠
多事件同步事件组灵活组合
简单事件通知任务通知轻量高效

九、总结与互动

9.1 核心要点总结

  1. 信号量:理解二值、计数、互斥量的区别和应用场景
  2. 队列:掌握队列创建、发送、接收,理解数据拷贝机制
  3. 事件组:使用事件位实现多事件同步
  4. 任务通知:轻量高效的同步机制,替代简单信号量
  5. 死锁预防:按顺序获取锁,使用超时机制

9.2 实战经验总结

  • 二值信号量用于同步,互斥量用于互斥访问
  • 中断中必须使用FromISR函数
  • 队列传递数据会拷贝,注意大结构体的效率
  • 任务通知比队列快,但只能传递32位值
  • 避免死锁:按顺序获取锁,使用超时

投票组件

你对任务同步机制的最大困惑是什么?

  1. 不知道何时使用信号量、队列、事件组
  2. 优先级翻转问题不理解,不知道如何解决
  3. 死锁问题经常遇到,不知道如何预防
  4. 中断与任务同步配合不好,系统不稳定
  5. 其他问题(请评论区说明)

欢迎在评论区分享你的任务同步经验和遇到的问题!


互动引导

思考题

  1. 如何设计一个多任务系统,实现数据采集、处理、显示的流水线?
  2. 如何使用信号量解决优先级翻转问题?
  3. 如何设计一个线程安全的环形缓冲区?

实践建议

  1. 先从简单的二值信号量开始,理解任务同步
  2. 逐步学习队列、事件组等复杂机制
  3. 实现生产者-消费者模型,理解实际应用
  4. 尝试解决优先级翻转、死锁等实际问题

文章标签:嵌入式开发、RTOS、FreeRTOS、任务同步、信号量、队列、事件组、任务通知、互斥量、生产者消费者

版权声明:本文为原创文章,转载请注明出处。文章内容基于FreeRTOS平台,适用于各类RTOS开发场景。

技术交流:欢迎评论区留言讨论,分享你的任务同步经验和踩坑故事!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值