目录
在 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;
}
四、信号量使用注意事项
-
中断安全:
- 二进制信号量和计数信号量可在中断中使用
xSemaphoreGiveFromISR()释放,但不能在中断中调用xSemaphoreTake()(会导致阻塞)。 - 互斥信号量禁止在中断中使用。
- 二进制信号量和计数信号量可在中断中使用
-
优先级继承:
仅互斥信号量支持优先级继承,二进制信号量不支持,因此互斥场景应优先使用互斥信号量。 -
资源释放:
- 若任务在持有信号量时被删除,需确保信号量被释放,否则会导致资源永久不可用。
- 可使用
vSemaphoreDelete()删除不再使用的信号量。
-
等待时间:
xSemaphoreTake()的xTicksToWait参数设为0表示不阻塞,立即返回;设为portMAX_DELAY表示无限等待。
总结
| 信号量类型 | 核心用途 | 计数器范围 | 优先级继承 | 中断中可用 |
|---|---|---|---|---|
| 二进制信号量 | 任务与事件同步 | 0 或 1 | 不支持 | 释放操作 |
| 计数信号量 | 限制资源访问数量 | 0~N | 不支持 | 释放操作 |
| 互斥信号量 | 共享资源互斥访问 | 0 或 1 | 支持 | 不可用 |
选择信号量时,需根据具体场景判断:同步用二进制信号量,限流用计数信号量,互斥用互斥信号量。
1212

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



