tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第11章 信号量" author: "尚硅谷研究院" date: 2026-07-17
用生活理解:信号量就像动车上的卫生间指示灯。二值信号量 = 一个卫生间,有人/无人两种状态(0/1);计数信号量 = 一排卫生间,有几个空闲就给几个"可用"标志;互斥信号量 = 带"插队保护"的卫生间——如果一个大佬(高优先级任务)在等,正占用卫生间的人会临时获得"VIP卡"(优先级提升),赶紧用完让位。
信号量(Semaphore)是 FreeRTOS 中用于任务间同步和资源管理的机制,底层基于队列实现。
信号量操作只有两个:Give(释放)和 Take(获取)。
优先级翻转(Priority Inversion):
优先级继承(Mutex 解决翻转):
| Function | Params | Description |
|---|---|---|
xSemaphoreCreateBinary() |
无 | 动态创建二值信号量 |
xSemaphoreCreateCounting() |
uxMaxCount, uxInitialCount |
动态创建计数信号量 |
xSemaphoreCreateMutex() |
无 | 动态创建互斥信号量 |
xSemaphoreGive() |
xSemaphore |
释放信号量 |
xSemaphoreTake() |
xSemaphore, xTicksToWait |
获取信号量 |
uxSemaphoreGetCount() |
xSemaphore |
获取当前计数值 |
| Feature | Binary | Counting | Mutex |
|---|---|---|---|
| Initial Value | 0 | User specified | 1 |
| Max Value | 1 | User specified | 1 |
| Priority Inheritance | No | No | Yes |
| Recursive | No | No | Configurable |
| Use Case | Event notification | Resource counting | Mutual exclusion |
Requirements: KEY1 释放信号量 → task2 获取信号量并打印。
File: P10/10_二值信号量/Core/Src/freertos_demo.c
#include "semphr.h" /* 信号量操作头文件 */
QueueHandle_t semphore_handle; /* 二值信号量句柄 */
void FreeRTOS_Start(void)
{
/* 动态创建二值信号量,初始值为0 */
semphore_handle = xSemaphoreCreateBinary();
if (semphore_handle != NULL) {
printf("二值信号量创建成功\r\n");
}
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
/* task1:KEY1按下时释放信号量 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
BaseType_t err;
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS) {
if (semphore_handle != NULL) {
/* 释放(Give)二值信号量,计数值变为1 */
err = xSemaphoreGive(semphore_handle);
if (err == pdPASS) {
printf("信号量释放成功\r\n");
} else
printf("信号量释放失败\r\n");
}
}
vTaskDelay(10);
}
}
/* task2:获取信号量,永久等待 */
void Task2(void *pvParameters)
{
uint32_t i = 0;
BaseType_t err;
while (1) {
/* 获取(Take)信号量,无信号时永久阻塞 */
err = xSemaphoreTake(semphore_handle, portMAX_DELAY);
if (err == pdTRUE) {
printf("获取信号量成功\r\n");
} else {
printf("已超时%d\r\n", ++i);
}
}
}
Requirements: KEY1 释放计数信号量,task2 每秒获取一次并打印计数值。
File: P11/11_计数型信号量/Core/Src/freertos_demo.c
#include "semphr.h"
QueueHandle_t count_semphore_handle; /* 计数型信号量句柄 */
void FreeRTOS_Start(void)
{
/* 创建计数信号量:最大100,初始0 */
count_semphore_handle = xSemaphoreCreateCounting(100, 0);
if (count_semphore_handle != NULL) {
printf("计数型信号量创建成功\r\n");
}
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
/* task1:KEY1按下释放一次计数信号量 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS) {
if (count_semphore_handle != NULL) {
xSemaphoreGive(count_semphore_handle);
}
}
vTaskDelay(10);
}
}
/* task2:每秒获取一次计数信号量,打印剩余计数值 */
void Task2(void *pvParameters)
{
BaseType_t err = 0;
while (1) {
err = xSemaphoreTake(count_semphore_handle, portMAX_DELAY);
if (err == pdTRUE) {
/* uxSemaphoreGetCount() 获取当前计数值 */
printf("信号量的计数值=%d\r\n",
(int)uxSemaphoreGetCount(count_semphore_handle));
}
vTaskDelay(1000);
}
}
Requirements: 模拟优先级翻转场景,观察使用二值信号量时的"倒反天罡"现象。
File: P12/12_优先级翻转/Core/Src/freertos_demo.c
#include "semphr.h"
QueueHandle_t semphore_handle;
/* Task1 (prio 2): 低优先级 — 先获取信号量,然后HAL_Delay(3000)模拟长时间占用 */
/* Task2 (prio 3): 中优先级 — 只打印,证明它能抢占低优先级 */
/* Task3 (prio 4): 高优先级 — 等待信号量,但在低优先级释放前被中优先级"插队" */
void FreeRTOS_Start(void)
{
/* 创建二值信号量,并主动释放一次使其初始值为1 */
semphore_handle = xSemaphoreCreateBinary();
if (semphore_handle != NULL) {
printf("二值信号量创建成功\r\n");
}
xSemaphoreGive(semphore_handle); /* 初始释放一次 */
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
/* 低优先级任务 Task1 (prio=2):获取信号量后占用3秒 */
void Task1(void *pvParameters)
{
while (1) {
printf("低优先级Task1获取信号量\r\n");
xSemaphoreTake(semphore_handle, portMAX_DELAY); /* 获取信号量 */
printf("低优先级Task1正在运行\r\n");
HAL_Delay(3000); /* 模拟长时间占用资源 */
printf("低优先级Task1释放信号量\r\n");
xSemaphoreGive(semphore_handle); /* 释放信号量 */
vTaskDelay(1000);
}
}
/* 中优先级任务 Task2 (prio=3):纯打印,抢占低优先级 */
void Task2(void *pvParameters)
{
while (1) {
printf("中优先级的Task2正在执行\r\n");
HAL_Delay(1500);
printf("Task2 执行完成一次.....\r\n");
vTaskDelay(1000);
}
}
/* 高优先级任务 Task3 (prio=4):等待信号量,被中优先级"插队"阻塞 */
void Task3(void *pvParameters)
{
while (1) {
printf("高优先级Task3获取信号量\r\n");
xSemaphoreTake(semphore_handle, portMAX_DELAY); /* 等待信号量 */
printf("高优先级Task3正在运行\r\n");
HAL_Delay(1000);
printf("高优先级Task3释放信号量\r\n");
xSemaphoreGive(semphore_handle); /* 释放信号量 */
vTaskDelay(1000);
}
}
Requirements: 与 P12 相同实验场景,但使用互斥信号量 xSemaphoreCreateMutex() 观察优先级继承解决翻转。
File: P13/13_互斥信号量/Core/Src/freertos_demo.c
#include "semphr.h"
QueueHandle_t mutex_semphore_handle; /* 互斥信号量句柄 */
void FreeRTOS_Start(void)
{
/* 创建互斥信号量(已自动释放一次,初始值为1) */
mutex_semphore_handle = xSemaphoreCreateMutex();
if (mutex_semphore_handle != NULL) {
printf("互斥信号量创建成功\r\n");
}
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
/* 低优先级任务:使用互斥信号量,优先级被临时提升 */
void Task1(void *pvParameters)
{
while (1) {
printf("低优先级Task1获取信号量\r\n");
xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
printf("低优先级Task1正在运行\r\n");
HAL_Delay(3000); /* 此时若高优先级等待,Task1优先级被提升到与Task3相同 */
printf("低优先级Task1释放信号量\r\n");
xSemaphoreGive(mutex_semphore_handle);
vTaskDelay(1000);
}
}
/* 中优先级任务:由于低优先级已被提升,无法抢占 */
void Task2(void *pvParameters)
{
while (1) {
printf("中优先级的Task2正在执行\r\n");
HAL_Delay(1500);
printf("Task2 for循环完毕.....\r\n");
vTaskDelay(1000);
}
}
/* 高优先级任务:等待互斥信号量 */
void Task3(void *pvParameters)
{
while (1) {
printf("高优先级Task3获取信号量\r\n");
xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
printf("高优先级Task3正在运行\r\n");
HAL_Delay(1000);
printf("高优先级Task3释放信号量\r\n");
xSemaphoreGive(mutex_semphore_handle);
vTaskDelay(1000);
}
}
/* FreeRTOSConfig.h */
#define configUSE_COUNTING_SEMAPHORES 1 /* 使能计数信号量 */
#define configUSE_MUTEXES 1 /* 使能互斥信号量 */
#define configUSE_RECURSIVE_MUTEXES 1 /* 使能递归互斥信号量 */
| Function | Parameters | Return | Description |
|---|---|---|---|
SemaphoreHandle_t xSemaphoreCreateBinary() |
无 | NULL=失败 | 创建二值信号量(初始0) |
SemaphoreHandle_t xSemaphoreCreateCounting(uxMaxCount, uxInitialCount) |
最大计数值, 初始值 | NULL=失败 | 创建计数信号量 |
SemaphoreHandle_t xSemaphoreCreateMutex() |
无 | NULL=失败 | 创建互斥信号量(初始1,含优先级继承) |
BaseType_t xSemaphoreGive(xSemaphore) |
信号量句柄 | pdPASS/pdFALSE | 释放信号量(+1) |
BaseType_t xSemaphoreTake(xSemaphore, xTicksToWait) |
信号量句柄, 超时 | pdPASS/pdFALSE | 获取信号量(-1) |
BaseType_t xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken) |
同上+唤醒标记 | pdPASS/pdFALSE | 中断中释放 |
UBaseType_t uxSemaphoreGetCount(xSemaphore) |
信号量句柄 | 当前计数值 | 查询当前计数 |
BaseType_t xSemaphoreCreateRecursiveMutex() |
无 | 句柄/NULL | 创建递归互斥信号量 |
xSemaphoreGiveFromISR() 仅对二值/计数信号量有效。vTaskList() 观察任务状态。