详细介绍FreeRTOS系统中信号量的使用方法

目录

一、二进制信号量(Binary Semaphores)

特点

常用 API

示例:任务与中断同步

二、计数信号量(Counting Semaphores)

特点

常用 API

示例:限制共享资源访问数量

三、互斥信号量(Mutex Semaphores)

特点

常用 API

示例:保护共享变量

四、信号量使用注意事项

总结


在 FreeRTOS 中,信号量(Semaphores)是一种用于任务同步、互斥访问共享资源或限制资源访问数量的核心机制。它通过一个整数计数器实现,支持两种基本操作:xSemaphoreTake()(获取信号量,计数器减 1)和 xSemaphoreGive()(释放信号量,计数器加 1)。

FreeRTOS 提供三种类型的信号量,每种适用于不同场景,下面详细介绍其使用方法:

一、二进制信号量(Binary Semaphores)

特点
  • 计数器只能为 0 或 1,适用于「事件同步」(如任务等待某个事件发生)。
  • 通常由一个任务 / 中断释放信号量,另一个任务等待信号量。
常用 API
  • 创建:xSemaphoreCreateBinary()
    初始值为 0(需先调用 xSemaphoreGive() 才能获取)。
  • 获取:xSemaphoreTake(xSemaphore, xTicksToWait)
    若计数器为 1,获取成功(计数器变为 0);若为 0,任务进入阻塞态等待 xTicksToWait 个时钟节拍。
  • 释放:xSemaphoreGive(xSemaphore)
    计数器从 0 变为 1,唤醒等待的任务(若有)。
  • 中断中释放:xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken)
    用于在中断服务程序(ISR)中释放信号量。
示例:任务与中断同步
#include "FreeRTOS.h"
#include "semphr.h"

// 定义二进制信号量句柄
SemaphoreHandle_t xBinarySemaphore;

// 中断服务程序(例如定时器中断)
void TIM_IRQHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    // 释放信号量,通知任务事件发生
    xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
    
    // 若有更高优先级任务被唤醒,请求上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    
    // 清除中断标志(硬件相关)
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}

// 处理事件的任务
void vHandlerTask(void *pvParameters) {
    for (;;) {
        // 等待信号量(无限等待,直到中断触发)
        xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);
        
        // 信号量被释放,执行事件处理逻辑
        printf("Event received! Processing...\r\n");
    }
}

int main(void) {
    // 初始化硬件(如定时器,此处省略)
    
    // 创建二进制信号量(初始值为0)
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore != NULL) {
        // 创建处理任务
        xTaskCreate(vHandlerTask, "Handler", 128, NULL, 1, NULL);
        
        // 启动调度器
        vTaskStartScheduler();
    }
    
    // 若调度器启动失败,进入死循环
    for (;;);
    return 0;
}

二、计数信号量(Counting Semaphores)

特点
  • 计数器可在 0 到用户指定的最大值之间变化,适用于「限制资源访问数量」(如有限个外设接口)。
  • 例如:3 个 UART 接口,最多允许 3 个任务同时使用。
常用 API
  • 创建:xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)
    • uxMaxCount:计数器最大值
    • uxInitialCount:初始值
  • 获取 / 释放:与二进制信号量相同(xSemaphoreTake()/xSemaphoreGive())。
示例:限制共享资源访问数量
#include "FreeRTOS.h"
#include "semphr.h"

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

// 访问资源的任务
void vResourceTask(void *pvParameters) {
    int taskId = *(int *)pvParameters;
    for (;;) {
        // 尝试获取信号量(等待100ms,若超时则放弃)
        if (xSemaphoreTake(xCountingSemaphore, pdMS_TO_TICKS(100)) == pdPASS) {
            // 获取成功,访问共享资源
            printf("Task %d is using the resource.\r\n", taskId);
            vTaskDelay(pdMS_TO_TICKS(500)); // 模拟资源使用
            
            // 释放信号量,允许其他任务访问
            xSemaphoreGive(xCountingSemaphore);
            printf("Task %d released the resource.\r\n", taskId);
        } else {
            // 获取超时
            printf("Task %d failed to get resource.\r\n", taskId);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

int main(void) {
    int task1Id = 1, task2Id = 2, task3Id = 3;
    
    if (xCountingSemaphore != NULL) {
        // 创建3个任务竞争2个资源
        xTaskCreate(vResourceTask, "Task1", 128, &task1Id, 1, NULL);
        xTaskCreate(vResourceTask, "Task2", 128, &task2Id, 1, NULL);
        xTaskCreate(vResourceTask, "Task3", 128, &task3Id, 1, NULL);
        
        vTaskStartScheduler();
    }
    
    for (;;);
    return 0;
}

三、互斥信号量(Mutex Semaphores)

特点
  • 专为「互斥访问共享资源」设计,解决多任务竞争问题(如共享变量、硬件寄存器)。
  • 支持优先级继承机制:当低优先级任务持有互斥量时,若高优先级任务请求该互斥量,低优先级任务会临时提升优先级,避免优先级反转。
  • 限制:只能由获取它的任务释放,不能在中断中使用。
常用 API
  • 创建:xSemaphoreCreateMutex()
    初始值为 1(可用状态)。
  • 获取 / 释放:与其他信号量相同,但 xSemaphoreGive() 必须由获取信号量的任务调用。
示例:保护共享变量
#include "FreeRTOS.h"
#include "semphr.h"

// 互斥信号量句柄
SemaphoreHandle_t xMutex;
// 共享变量(需保护)
int32_t lSharedValue = 0;

// 任务1:递增共享变量
void vIncrementTask(void *pvParameters) {
    for (;;) {
        // 获取互斥量(无限等待)
        xSemaphoreTake(xMutex, portMAX_DELAY);
        
        // 临界区:安全修改共享变量
        lSharedValue++;
        printf("Increment: %d\r\n", lSharedValue);
        
        // 释放互斥量
        xSemaphoreGive(xMutex);
        
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// 任务2:读取共享变量
void vPrintTask(void *pvParameters) {
    for (;;) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);
        
        // 临界区:安全读取共享变量
        printf("Current value: %d\r\n", lSharedValue);
        
        // 释放互斥量
        xSemaphoreGive(xMutex);
        
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

int main(void) {
    // 创建互斥信号量
    xMutex = xSemaphoreCreateMutex();
    if (xMutex != NULL) {
        xTaskCreate(vIncrementTask, "Increment", 128, NULL, 1, NULL);
        xTaskCreate(vPrintTask, "Print", 128, NULL, 2, NULL);
        
        vTaskStartScheduler();
    }
    
    for (;;);
    return 0;
}

四、信号量使用注意事项

  1. 中断安全

    • 二进制信号量和计数信号量可在中断中使用 xSemaphoreGiveFromISR() 释放,但不能在中断中调用 xSemaphoreTake()(会导致阻塞)。
    • 互斥信号量禁止在中断中使用
  2. 优先级继承
    仅互斥信号量支持优先级继承,二进制信号量不支持,因此互斥场景应优先使用互斥信号量。

  3. 资源释放

    • 若任务在持有信号量时被删除,需确保信号量被释放,否则会导致资源永久不可用。
    • 可使用 vSemaphoreDelete() 删除不再使用的信号量。
  4. 等待时间

    • xSemaphoreTake() 的 xTicksToWait 参数设为 0 表示不阻塞,立即返回;设为 portMAX_DELAY 表示无限等待。

总结

信号量类型核心用途计数器范围优先级继承中断中可用
二进制信号量任务与事件同步0 或 1不支持释放操作
计数信号量限制资源访问数量0~N不支持释放操作
互斥信号量共享资源互斥访问0 或 1支持不可用

选择信号量时,需根据具体场景判断:同步用二进制信号量,限流用计数信号量,互斥用互斥信号量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

start_up_go

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

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

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

打赏作者

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

抵扣说明:

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

余额充值