FreeRTOS中,信号量(Semaphore)是一种用于任务间同步和互斥的机制。信号量可以分为二进制信号量(Binary Semaphore)、计数信号量(Counting Semaphore)和互斥信号量(Mutex)。下面详细介绍信号量的创建、使用和释放。

1. 创建信号量

二进制信号量:

SemaphoreHandle_t xBinarySemaphore;

void createBinarySemaphore() {
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore == NULL) {
        // 信号量创建失败
    } else {
        // 信号量创建成功
    }
}

计数信号量:

SemaphoreHandle_t xCountingSemaphore;

void createCountingSemaphore() {
    xCountingSemaphore = xSemaphoreCreateCounting(maxCount, initialCount);
    if (xCountingSemaphore == NULL) {
        // 信号量创建失败
    } else {
        // 信号量创建成功
    }
}

互斥信号量:

SemaphoreHandle_t xMutex;

void createMutex() {
    xMutex = xSemaphoreCreateMutex();
    if (xMutex == NULL) {
        // 互斥信号量创建失败
    } else {
        // 互斥信号量创建成功
    }
}

2. 使用信号量:

获取信号量:

if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
    // 成功获取信号量
} else {
    // 获取信号量失败
}
  1. xSemaphore:要获取的信号量句柄。
  2. portMAX_DELAY:等待时间,portMAX_DELAY 表示一直等待。

释放信号量:

if (xSemaphoreGive(xSemaphore) == pdTRUE) {
    // 成功释放信号量
} else {
    // 释放信号量失败
}

xSemaphore:要释放的信号量句柄。

三种信号量的区别:

1. 二进制信号量(Binary Semaphore)

特点

  • 只有两个状态:0(不可用)和1(可用)。
  • 主要用于任务之间的同步。

应用场景

  • 任务同步:一个任务等待另一个任务或中断完成某个事件。
  • 事件标志:用来表示某个事件是否发生。
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();

// 在一个任务或中断中释放信号量
xSemaphoreGive(xBinarySemaphore);

// 在另一个任务中获取信号量
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
    // 处理事件
}

2. 计数信号量(Counting Semaphore)

特点

  • 有一个计数值,可以递增和递减。
  • 初始值和最大值在创建时指定。
  • 主要用于管理资源访问。

应用场景

  • 资源管理:比如管理多个相同类型的资源(如多个相同的外设或缓冲区)。
  • 事件计数:记录多个事件的发生次数。

示例

SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(maxCount, initialCount);

// 在一个任务或中断中释放信号量(增加计数)
xSemaphoreGive(xCountingSemaphore);

// 在另一个任务中获取信号量(减少计数)
if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE) {
    // 使用一个资源
}

3. 互斥信号量(Mutex)

特点

  • 主要用于实现互斥访问,防止多个任务同时访问共享资源。
  • 优先级继承机制:如果一个高优先级任务等待一个被低优先级任务持有的互斥信号量,低优先级任务会临时提升其优先级,防止优先级反转。

应用场景

  • 互斥访问:保护临界区,防止并发访问造成数据不一致。

在FreeRTOS中,当一个互斥信号量(Mutex)被释放,并且有多个优先级相同的任务正在等待该信号量时,哪个任务会首先获得信号量取决于调度器的策略。FreeRTOS使用基于优先级的抢占式调度,并且在同优先级任务之间,调度器采用轮询调度(Round Robin Scheduling)。

示例

SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

// 在一个任务中获取互斥信号量
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
    // 访问共享资源
    // 释放互斥信号量
    xSemaphoreGive(xMutex);
}

示例,展示如何创建、使用和释放二进制信号量:

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

SemaphoreHandle_t xBinarySemaphore;

void vTask1(void *pvParameters) {
    for (;;) {
        // 尝试获取信号量
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
            // 成功获取信号量
            // 执行任务
            // 释放信号量
            xSemaphoreGive(xBinarySemaphore);
        }
    }
}

void vTask2(void *pvParameters) {
    for (;;) {
        // 尝试获取信号量
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
            // 成功获取信号量
            // 执行任务
            // 释放信号量
            xSemaphoreGive(xBinarySemaphore);
        }
    }
}

int main(void) {
    // 创建二进制信号量
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore != NULL) {
        // 创建任务
        xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
        xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

        // 释放初始信号量
        xSemaphoreGive(xBinarySemaphore);

        // 启动调度器
        vTaskStartScheduler();
    }

    for (;;);

    return 0;
}