FreeRTOS 中的计数信号量(Counting Semaphore)用于管理多个资源的访问或事件计数,允许信号量的值在 0 到设定的最大值之间变化。以下是计数信号量相关的 API 函数详解,包括动态和静态创建两种方式。
1. 动态创建计数信号量
函数原型
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount, // 最大计数值
UBaseType_t uxInitialCount // 初始计数值
);
- 功能:动态分配内存并初始化一个计数信号量。
- 参数:
uxMaxCount
:信号量支持的最大计数值(必须 ≥uxInitialCount
)。uxInitialCount
:信号量的初始计数值(0 ≤uxInitialCount
≤uxMaxCount
)。
- 返回值:
- 成功:返回信号量句柄(
SemaphoreHandle_t
)。 - 失败:返回
NULL
(内存不足或参数无效时)。
- 成功:返回信号量句柄(
- 特点:
- 需要确保 FreeRTOS 堆内存足够(通过
configTOTAL_HEAP_SIZE
配置)。 - 适用于动态分配资源的场景(如管理缓冲区池)。
- 需要确保 FreeRTOS 堆内存足够(通过
示例代码
SemaphoreHandle_t xCountingSemaphore = NULL;
void vTaskExample(void) {
// 动态创建计数信号量:最大5,初始3
xCountingSemaphore = xSemaphoreCreateCounting(5, 3);
if (xCountingSemaphore != NULL) {
// 创建成功
}
}
2. 静态创建计数信号量
函数原型
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
UBaseType_t uxMaxCount, // 最大计数值
UBaseType_t uxInitialCount, // 初始计数值
StaticSemaphore_t *pxSemaphoreBuffer // 静态内存缓冲区
);
- 功能:使用用户预先分配的静态内存初始化计数信号量。
- 参数:
uxMaxCount
:最大计数值。uxInitialCount
:初始计数值。pxSemaphoreBuffer
:指向StaticSemaphore_t
类型变量的指针(需预先分配内存)。
- 返回值:
- 成功:返回信号量句柄。
- 失败:返回
NULL
(参数无效时)。
- 特点:
- 需预先分配
StaticSemaphore_t
结构体内存(通常为全局或静态变量)。
- 需预先分配
示例代码
StaticSemaphore_t xSemaphoreBuffer;
SemaphoreHandle_t xCountingSemaphore = NULL;
void vTaskExample(void) {
// 静态创建计数信号量:最大5,初始3
xCountingSemaphore = xSemaphoreCreateCountingStatic(5, 3, &xSemaphoreBuffer);
if (xCountingSemaphore != NULL) {
// 创建成功
}
}
3. 释放(Give)信号量
函数原型
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
- 功能:释放信号量,计数值加 1(若未达到最大计数值)。
- 参数:
xSemaphore
:信号量句柄。
- 返回值:
pdPASS
:释放成功。pdFAIL
:释放失败(计数值已达最大值)。
- 使用场景:通常在任务中释放信号量(如资源归还)。
中断安全版本
BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
pxHigherPriorityTaskWoken
:标记是否有更高优先级任务被唤醒(需传递到portYIELD_FROM_ISR()
)。
4. 获取(Take)信号量
函数原型
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait // 阻塞时间
);
- 功能:尝试获取信号量,计数值减 1。若计数值为 0,任务可阻塞等待。
- 参数:
xSemaphore
:信号量句柄。xTicksToWait
:阻塞时间(单位:系统节拍),portMAX_DELAY
表示无限等待(需配置INCLUDE_vTaskSuspend=1
)。
- 返回值:
pdTRUE
:成功获取信号量。pdFALSE
:超时或参数无效。
中断安全版本
BaseType_t xSemaphoreTakeFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
5. 删除信号量
函数原型
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
- 功能:删除信号量并释放内存(仅对动态创建的信号量有效)。
- 参数:
xSemaphore
:信号量句柄。
使用场景示例
资源池管理
// 初始化一个管理5个缓冲区的信号量(初始全部可用)
SemaphoreHandle_t xBufferSemaphore = xSemaphoreCreateCounting(5, 5);
// 任务获取缓冲区
void vTaskConsumer(void *pvParameters) {
while (1) {
if (xSemaphoreTake(xBufferSemaphore, portMAX_DELAY) == pdTRUE) {
// 成功获取缓冲区,执行操作
// ...
// 使用完毕后释放
xSemaphoreGive(xBufferSemaphore);
}
}
}
事件计数
// 初始化一个最大10次事件的信号量(初始0次)
SemaphoreHandle_t xEventSemaphore = xSemaphoreCreateCounting(10, 0);
// 中断中触发事件
void vInterruptHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xEventSemaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// 任务处理累计事件
void vTaskEventHandler(void *pvParameters) {
while (1) {
if (xSemaphoreTake(xEventSemaphore, portMAX_DELAY) == pdTRUE) {
// 处理一个事件(计数值减1)
}
}
}
注意事项
- 初始值与最大值:
- 初始值应 ≤ 最大值,否则创建失败。
- 若初始值为 0,表示无可用资源;若初始值等于最大值,表示资源池已满。
- 阻塞行为:
- 当信号量计数值为 0 时,调用
xSemaphoreTake
的任务会阻塞。
- 当信号量计数值为 0 时,调用
- 优先级反转:
- 计数信号量不解决优先级反转问题,需资源互斥访问时改用互斥量(Mutex)。
- 溢出保护:
- 调用
xSemaphoreGive
时若计数值已达最大值,返回pdFAIL
。
- 调用
通过合理使用计数信号量,可以实现资源池管理、事件批量处理、流量控制等功能。