一、信号量核心概念
信号量是 FreeRTOS 中用于任务间同步、互斥或资源计数的内核对象,主要分为 4 类:
- 二进制信号量:仅两种状态(可用 / 不可用),适用于单次资源占用或任务同步;
- 计数信号量:支持 N 个资源计数,适用于多实例资源分配;
- 互斥信号量:用于任务间互斥访问共享资源,支持优先级继承;
- 递归互斥信号量:允许同一任务多次获取互斥量,避免自我死锁。
二、通用信号量 API 函数(基础操作)
1. 信号量句柄定义
c
运行
SemaphoreHandle_t xSemaphore; // 通用信号量句柄(适用于所有信号量类型)
- 功能:定义信号量句柄,用于后续操作信号量(创建、获取、释放等);
- 说明:句柄本质是指向信号量控制块的指针,创建成功后由 API 函数返回。
2. 信号量删除函数
c
运行
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
- 功能:删除已创建的信号量,释放其占用的内存(动态创建的信号量需手动删除);
- 参数:
xSemaphore:待删除的信号量句柄; - 注意:删除正在被任务等待的信号量会导致等待任务永久阻塞,需确保信号量不再被使用时删除。
三、二进制信号量 API 函数
1. 动态创建二进制信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateBinary(void);
- 功能:动态分配内存,创建一个二进制信号量(初始状态为 "不可用");
- 返回值:
- 非 NULL:创建成功,返回信号量句柄;
- NULL:内存不足,创建失败;
- 说明:创建后需通过
xSemaphoreGive()激活信号量,使其变为 "可用" 状态。
2. 静态创建二进制信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxStaticSemaphore);
- 功能:使用静态内存创建二进制信号量,避免动态内存分配;
- 参数:
pxStaticSemaphore:指向静态信号量控制块的指针(需用户提前定义); - 返回值:
- 非 NULL:创建成功,返回信号量句柄;
- NULL:参数无效(如指针为 NULL);
- 适用场景:对内存分配有严格控制的场景(如裸机移植后的资源受限环境)。
3. 获取二进制信号量(阻塞方式)
c
运行
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
- 功能:尝试获取信号量,若信号量不可用则任务阻塞指定时间;
- 参数:
xSemaphore:信号量句柄;xTicksToWait:阻塞超时时间(单位:tick,portMAX_DELAY表示永久阻塞);
- 返回值:
pdPASS:成功获取信号量;pdFALSE:超时未获取到信号量;
- 说明:获取成功后,信号量状态变为 "不可用"。
4. 释放二进制信号量
c
运行
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
- 功能:释放二进制信号量,使其变为 "可用" 状态,唤醒等待该信号量的最高优先级任务;
- 参数:
xSemaphore:信号量句柄; - 返回值:
pdPASS:释放成功;errQUEUE_FULL:信号量已处于可用状态,释放失败;
- 注意:仅能在任务中调用,不能在中断服务函数中使用(中断中需用
xSemaphoreGiveFromISR())。
5. 中断中释放二进制信号量
c
运行
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken);
- 功能:在中断服务函数中释放二进制信号量;
- 参数:
xSemaphore:信号量句柄;pxHigherPriorityTaskWoken:输出参数,标记是否唤醒了更高优先级任务(pdTRUE表示需要进行任务切换);
- 返回值:
pdPASS:释放成功;errQUEUE_FULL:信号量已可用,释放失败;
- 说明:调用后需根据
pxHigherPriorityTaskWoken的值决定是否触发任务切换(如portYIELD_FROM_ISR())。
四、计数信号量 API 函数
1. 动态创建计数信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
- 功能:创建计数信号量,支持指定最大计数和初始计数;
- 参数:
uxMaxCount:信号量最大计数(超过则无法释放);uxInitialCount:信号量初始计数;
- 返回值:
- 非 NULL:创建成功,返回句柄;
- NULL:内存不足,创建失败;
- 示例:创建最大计数为 5、初始计数为 0 的信号量:
xSemaphoreCreateCounting(5, 0)。
2. 静态创建计数信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxStaticSemaphore);
- 功能:使用静态内存创建计数信号量;
- 参数:
uxMaxCount:最大计数;uxInitialCount:初始计数;pxStaticSemaphore:静态信号量控制块指针;
- 返回值:
- 非 NULL:创建成功;
- NULL:参数无效;
- 适用场景:与静态二进制信号量一致,适用于内存受限环境。
3. 获取计数信号量(阻塞方式)
c
运行
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
- 功能:与二进制信号量的
xSemaphoreTake()完全一致; - 说明:获取成功后,信号量计数减 1;若计数为 0,任务阻塞。
4. 释放计数信号量
c
运行
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
- 功能:释放计数信号量,计数加 1(不超过最大计数);
- 说明:与二进制信号量的
xSemaphoreGive()用法一致,仅计数逻辑不同。
5. 中断中释放计数信号量
c
运行
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken);
- 功能:在中断中释放计数信号量;
- 用法:与二进制信号量的
xSemaphoreGiveFromISR()完全一致。
五、互斥信号量 API 函数
1. 动态创建互斥信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateMutex(void);
- 功能:创建互斥信号量(初始状态为 "可用"),支持优先级继承(避免优先级翻转);
- 返回值:
- 非 NULL:创建成功;
- NULL:内存不足;
- 说明:互斥信号量与二进制信号量的核心区别是支持优先级继承,适用于共享资源互斥访问(如串口、SPI 总线)。
2. 静态创建互斥信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxStaticSemaphore);
- 功能:使用静态内存创建互斥信号量;
- 参数:
pxStaticSemaphore:静态互斥信号量控制块指针; - 返回值:
- 非 NULL:创建成功;
- NULL:参数无效;
3. 获取互斥信号量
c
运行
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
- 功能:与二进制信号量的
xSemaphoreTake()用法一致; - 说明:获取成功后,互斥信号量状态变为 "不可用";若被低优先级任务占用,高优先级任务会触发优先级继承(低优先级任务临时提升至高优先级)。
4. 释放互斥信号量
c
运行
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
- 功能:释放互斥信号量,恢复被继承的优先级(若有);
- 注意:
- 仅能由获取互斥信号量的任务释放,其他任务释放会失败;
- 不能在中断中调用(互斥信号量不支持中断中操作);
- 返回值:
pdPASS:释放成功;errQUEUE_FULL:未获取过信号量,释放失败。
六、递归互斥信号量 API 函数
1. 动态创建递归互斥信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
- 功能:创建递归互斥信号量,允许同一任务多次获取(最多
configMAX_RECURSIVE_MUTEX_CALLS次,默认 255); - 返回值:
- 非 NULL:创建成功;
- NULL:内存不足;
- 适用场景:任务中存在嵌套访问共享资源的场景(如函数 A 调用函数 B,两者均需访问同一资源)。
2. 静态创建递归互斥信号量
c
运行
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic(StaticSemaphore_t *pxStaticSemaphore);
- 功能:使用静态内存创建递归互斥信号量;
- 参数:
pxStaticSemaphore:静态递归互斥信号量控制块指针; - 返回值:
- 非 NULL:创建成功;
- NULL:参数无效。
3. 获取递归互斥信号量
c
运行
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
- 功能:获取递归互斥信号量,同一任务多次获取时计数递增;
- 参数:与
xSemaphoreTake()一致; - 返回值:
pdPASS:获取成功;pdFALSE:超时失败;
- 说明:首次获取时信号量变为 "不可用",后续同一任务获取仅递增计数。
4. 释放递归互斥信号量
c
运行
BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xSemaphore);
- 功能:释放递归互斥信号量,计数递减;当计数减为 0 时,信号量变为 "可用";
- 注意:
- 仅能由获取信号量的任务释放;
- 释放次数需与获取次数一致,否则信号量无法完全释放;
- 返回值:
pdPASS:释放成功;errQUEUE_FULL:未获取过信号量或释放次数过多。
七、关键使用注意事项
- 信号量创建后需检查句柄是否为 NULL,避免空指针操作;
- 中断中仅能使用
xSemaphoreGiveFromISR()释放信号量,不能使用xSemaphoreTake()或xSemaphoreGive(); - 互斥信号量和递归互斥信号量不支持中断中操作;
- 计数信号量的最大计数需根据资源数量合理设置,避免溢出;
- 信号量使用完毕后需手动删除(动态创建),避免内存泄漏。