目录
前言
一. FreeRTOS信号量
1.1 核心函数
1.1.1 创建信号量
1.1.2 释放信号量
1.1.3 获取信号量
1.2 STM32Cubenx初始化
1.2.1 创建3个任务
1.2.2 创建1个二进制信号量
1.3 使用演示
1.3.1 (二进制)信号量示例
1.3.2 (计数)信号量示例
二. 使用二进制信号量示例
2.1 task任务间交互使用二进制信号量
三. 使用计数信号量示例
3.1 task任务间交互使用计数信号量
前言
在 FreeRTOS 中,二进制信号量和计数信号量最核心的区别在于“计数范围”和“用途”。它们虽然都用于任务间同步或互斥,但设计目的不同。
特性 二进制信号量 计数信号量 计数值范围 仅0 或 1 0 到 最大值(创建时指定) 典型用途 同步(如中断通知任务)、互斥(尽量不用) 资源计数、事件计数 “Give”操作 值变为 1(若已为 1,则失败或覆盖) 值加 1(达到上限则失败) “Take”操作 值变为 0(若已为 0,则阻塞) 值减 1(若已为 0,则阻塞) 创建方式 xSemaphoreCreateBinary()xSemaphoreCreateCounting()是否支持优先级继承 否(互斥请用 Mutex) 否
详细说明
二进制信号量
本质是一个长度为 1 的队列,只有“有”和“无”两种状态。
常用于中断服务程序(ISR)通知任务:ISR 中 Give,任务中 Take 等待。
也常用于任务间同步:一个任务 Give,另一个任务 Take。
注意:它不适合做互斥锁,因为没有优先级继承机制,可能导致优先级翻转。互斥应使用 xSemaphoreCreateMutex()。计数信号量
本质是一个长度为 N 的队列,可以记录“事件发生了多少次”或“还有多少个资源可用”。
常用于资源管理:例如管理 5 个相同的缓冲区,任务取走一个计数减 1,归还一个计数加 1。
也用于事件计数:中断每发生一次 Give 一次,任务可以累计处理多个事件。选择建议
只需要“通知一次”或“二值状态” → 用二进制信号量。
需要记录数量或管理多个同类资源 → 用计数信号量。
需要互斥保护共享资源 → 两者都不要用,请用互斥量(Mutex)。
一. FreeRTOS信号量
1.1 核心函数
1.1.1 创建信号量
/* Definitions for myBinarySem01 */ osSemaphoreId_t myBinarySem01Handle; const osSemaphoreAttr_t myBinarySem01_attributes = { .name = "myBinarySem01" }; myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes);1.1.2 释放信号量
osSemaphoreRelease(释放信号量)
作用:往信号量里放回一个令牌(使其计数 +1),最多放回创建时的初始最大数量。
典型场景:
任务 A 用完后释放资源,通知其他任务"资源空出来了,可以用"
中断服务程序(ISR)里释放信号量,唤醒等待的任务(常用于中断→任务间的同步)注意:如果没有任务在等待、且计数已达最大值,释放会失败返回
osErrorResourcestatus = osSemaphoreRelease(myBinarySem01Handle);// 计数 +1,唤醒可能正在等待的任务1.1.3 获取信号量
osSemaphoreAcquire(获取信号量)
作用:尝试取走一个令牌(计数 -1)。如果篮子里没令牌了,就阻塞等待,直到:
有别的任务/中断释放了令牌(拿到后返回 osOK)
等待超时(返回 osErrorTimeout)
timeout参数:0表示不等待(拿不到立即返回),osWaitForever表示无限期等待。osStatus_t status = osSemaphoreAcquire(sem_id, 1000); // 最多等 1000ms if (status == osOK) { // 拿到了令牌,可以安全访问共享资源 use_shared_resource(); osSemaphoreRelease(sem_id); // 用完归还 }
1.2 STM32Cubenx初始化
1.2.1 创建3个任务
使用stm32cubemx快速生成3个任务:其中
任务1(默认任务):作用:往信号量里放回一个令牌
osThreadId_t defaultTaskHandle; const osThreadAttr_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityNormal, }; /* Definitions for myTask02 */ osThreadId_t myTask02Handle; const osThreadAttr_t myTask02_attributes = { .name = "myTask02", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; /* Definitions for myTask03 */ osThreadId_t myTask03Handle; const osThreadAttr_t myTask03_attributes = { .name = "myTask03", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); /* creation of myTask02 */ myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes); /* creation of myTask03 */ myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);1.2.2 创建1个二进制信号量
最大计数 1:令牌"篮子"里最多只能装 1 个令牌,
所以叫"二值"—令牌数要么是 0,要么是 1
/* Definitions for myBinarySem01 */ osSemaphoreId_t myBinarySem01Handle; const osSemaphoreAttr_t myBinarySem01_attributes = { .name = "myBinarySem01" }; myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes);
1.3 使用演示
1.3.1 (二进制)信号量示例
使用3个任务:1个二进制信号量,进行测试
myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes); // 最大计数=1, 初始计数=1
任务 优先级 对信号量的操作 Task01 (defaultTask) Normal 按KEY_A→ osSemaphoreRelease(放令牌 +1)Task02 (myTask02) Low 按KEY_B→ osSemaphoreAcquire(timeout=0)(取令牌 -1,不等待)Task03 (myTask03) Low 每 1 秒 osSemaphoreGetCount(只读令牌数)1.3.2 (计数)信号量示例
使用使用3个任务和1个计数信号量
最大计数上限 = 5,初始可用令牌 = 5。
上电:信号量初始令牌 = 5
1. 按一次 KEY_A → Release,令牌 + 1(已经是 5 → 失败,提示到达上限)
2. 按一次 KEY_B → Acquire,令牌变成 4
3. 任务 3 打印出当前 count=4
4. 再按 KEY_B 多次,令牌持续减少,直到 0,再按 KEY_B 就会提示 `Acquire fail: sem empty`
5. 此时按 KEY_A,令牌 + 1,又可以继续按 B 获取
注意:三个任务都是 500ms 延时,所以按键检测响应最慢 500ms,属于轮询检测,不是中断方式。
二. 使用二进制信号量示例
2.1 task任务间交互使用二进制信号量
具体现象为:
这份代码其实是信号量 API 的"功能演示板",它不是典型的资源保护用法(没有共享资源被保护),而是刻意展示了三个状态:
1. Acquire 成功:有令牌时拿走
2. Acquire 失败(非阻塞):timeout=0,拿不到立刻返回 osErrorResource,任务不会被挂起
3. Release 失败:二值信号量已满,再放返回 osErrorResource
4. 查询:osSemaphoreGetCount 随时读当前令牌数
串口打印:
/* USER CODE BEGIN Header */ /** ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "string.h" #include "queue.h" #include "semphr.h" #include "cmsis_os2.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ /* USER CODE END Variables */ /* Definitions for defaultTask */ osThreadId_t defaultTaskHandle; const osThreadAttr_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityNormal, }; /* Definitions for myTask02 */ osThreadId_t myTask02Handle; const osThreadAttr_t myTask02_attributes = { .name = "myTask02", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; /* Definitions for myTask03 */ osThreadId_t myTask03Handle; const osThreadAttr_t myTask03_attributes = { .name = "myTask03", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; /* Definitions for myBinarySem01 */ osSemaphoreId_t myBinarySem01Handle; const osSemaphoreAttr_t myBinarySem01_attributes = { .name = "myBinarySem01" }; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ /* USER CODE END FunctionPrototypes */ void StartDefaultTask(void *argument); void StartTask02(void *argument); void StartTask03(void *argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /** * @brief FreeRTOS initialization * @param None * @retval None */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* Create the semaphores(s) */ /* creation of myBinarySem01 */ myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes); /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* creation of defaultTask */ defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); /* creation of myTask02 */ myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes); /* creation of myTask03 */ myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ /* USER CODE BEGIN RTOS_EVENTS */ /* add events, ... */ /* USER CODE END RTOS_EVENTS */ } /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ // uint8_t key = 0; osStatus_t status; for(;;) { printf("Task01:\r\n"); if(!HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin)) // 按键A按下 { osDelay(5); // 消抖 if(!HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin)) { printf("KEY_A \r\n"); status = osSemaphoreRelease(myBinarySem01Handle); if(status == osOK) { // printf("release ok, key = %d\r\n", key); // key++; printf("Release ok status = %d\r\n",status); } else if(status == osErrorResource) { // ,再 Release 无效 printf("release fail: sem already full status = %d\r\n", status); } else { printf("release fail, status = %d\r\n", status); } } } osDelay(500); } /* USER CODE END StartDefaultTask */ } /* USER CODE BEGIN Header_StartTask02 */ /** * @brief Function implementing the myTask02 thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask02 */ void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t status; for(;;) { printf("Task02:\r\n"); if(!HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin)) // 按键B按下 { osDelay(5); if(!HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin)) { printf("KEY_B \r\n"); status = osSemaphoreAcquire(myBinarySem01Handle, 0); // 非阻塞 if(status == osOK) { printf("acquire ok status = %d\r\n",status); } else if(status == osErrorResource) { printf("acquire fail: sem empty status = %d\r\n",status); } else if(status == osErrorTimeout) { printf("acquire timeout status = %d\r\n",status); } else { printf("acquire fail, status = %d\r\n", status); } } } osDelay(500); } /* USER CODE END StartTask02 */ } /* USER CODE BEGIN Header_StartTask03 */ /** * @brief Function implementing the myTask03 thread. * @param argument: Not used * @retval None */ char task_info[500] = {0}; /* USER CODE END Header_StartTask03 */ void StartTask03(void *argument) { /* USER CODE BEGIN StartTask03 */ /* Infinite loop */ // uint8_t key = 0; uint32_t cnt; // const char *name; for(;;) { cnt = osSemaphoreGetCount(myBinarySem01Handle); printf("Task03 read tokens = %lu\r\n", cnt); // name = osSemaphoreGetName(myBinarySem01Handle); // if (name != NULL) // { // printf("sem name = %s\r\n", name); // } // else // { // printf("no name set\r\n"); // } osDelay(1000); } /* USER CODE END StartTask03 */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ /* USER CODE END Application */
三. 使用计数信号量示例
3.1 task任务间交互使用计数信号量
执行流程为:
代码为:
/* USER CODE BEGIN Header */ /** ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "string.h" #include "queue.h" #include "semphr.h" #include "cmsis_os2.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ /* USER CODE END Variables */ /* Definitions for defaultTask */ osThreadId_t defaultTaskHandle; const osThreadAttr_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityNormal, }; /* Definitions for myTask02 */ osThreadId_t myTask02Handle; const osThreadAttr_t myTask02_attributes = { .name = "myTask02", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; /* Definitions for myTask03 */ osThreadId_t myTask03Handle; const osThreadAttr_t myTask03_attributes = { .name = "myTask03", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityLow, }; /* Definitions for myBinarySem01 */ osSemaphoreId_t myBinarySem01Handle; const osSemaphoreAttr_t myBinarySem01_attributes = { .name = "myBinarySem01" }; /* Definitions for myCountingSem01 */ osSemaphoreId_t myCountingSem01Handle; const osSemaphoreAttr_t myCountingSem01_attributes = { .name = "myCountingSem01" }; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ /* USER CODE END FunctionPrototypes */ void StartDefaultTask(void *argument); void StartTask02(void *argument); void StartTask03(void *argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /** * @brief FreeRTOS initialization * @param None * @retval None */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* Create the semaphores(s) */ /* creation of myBinarySem01 */ myBinarySem01Handle = osSemaphoreNew(1, 1, &myBinarySem01_attributes); /* creation of myCountingSem01 */ myCountingSem01Handle = osSemaphoreNew(5, 5, &myCountingSem01_attributes); /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* creation of defaultTask */ defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); /* creation of myTask02 */ myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes); /* creation of myTask03 */ myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ /* USER CODE BEGIN RTOS_EVENTS */ /* add events, ... */ /* USER CODE END RTOS_EVENTS */ } /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t status; for(;;) { printf("task 1\r\n"); if(!HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin)) // 按键A按下 { osDelay(5); //消抖 if(!HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin)) { printf("KEY_A pressed: Release sem\r\n"); status = osSemaphoreRelease(myCountingSem01Handle); if(status == osOK) { printf("Release OK, token +1\r\n"); } else if(status == osErrorResource) { // 计数达到最大上限5,不能再加 printf("Release fail: sem reach max count(5)\r\n"); } else { printf("Release fail, status = %d\r\n", status); } } } osDelay(500); } /* USER CODE END StartDefaultTask */ } /* USER CODE BEGIN Header_StartTask02 */ /** * @brief Function implementing the myTask02 thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask02 */ void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t status; for(;;) { printf("task 2:\r\n"); if(!HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin)) // 按键B按下 { osDelay(5); if(!HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin)) { printf("KEY_B pressed: Acquire sem\r\n"); status = osSemaphoreAcquire(myCountingSem01Handle, 0); //非阻塞 if(status == osOK) { printf("Acquire OK, token -1\r\n"); } else if(status == osErrorResource) { printf("Acquire fail: sem empty, no token\r\n"); } else { printf("Acquire fail, status = %d\r\n", status); } } } osDelay(500); } /* USER CODE END StartTask02 */ } /* USER CODE BEGIN Header_StartTask03 */ /** * @brief Function implementing the myTask03 thread. * @param argument: Not used * @retval None */ char task_info[500] = {0}; /* USER CODE END Header_StartTask03 */ void StartTask03(void *argument) { /* USER CODE BEGIN StartTask03 */ uint32_t cnt; for(;;) { printf("task 3:\r\n"); cnt = osSemaphoreGetCount(myCountingSem01Handle); printf("===== Semaphore token count = %lu =====\r\n", cnt); osDelay(500); } /* USER CODE END StartTask03 */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ /* USER CODE END Application */