ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

二进制信号量和计数信号量区别(FreeRTOS)

二进制信号量和计数信号量区别(FreeRTOS) 目录前言一. 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 或 10 到 最大值创建时指定典型用途同步如中断通知任务、互斥(尽量不用)资源计数、事件计数“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等待超时返回 osErrorTimeouttimeout参数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放令牌 1Task02 (myTask02)Low按KEY_B→osSemaphoreAcquire(timeout0)取令牌 -1不等待Task03 (myTask03)Low每 1 秒osSemaphoreGetCount只读令牌数1.3.2 (计数)信号量示例使用使用3个任务和1个计数信号量最大计数上限 5初始可用令牌 5。上电信号量初始令牌 51. 按一次 KEY_A → Release令牌 1已经是 5 → 失败提示到达上限2. 按一次 KEY_B → Acquire令牌变成 43. 任务 3 打印出当前 count44. 再按 KEY_B 多次令牌持续减少直到 0再按 KEY_B 就会提示 Acquire fail: sem empty5. 此时按 KEY_A令牌 1又可以继续按 B 获取注意三个任务都是 500ms 延时所以按键检测响应最慢 500ms属于轮询检测不是中断方式。二. 使用二进制信号量示例2.1 task任务间交互使用二进制信号量具体现象为这份代码其实是信号量 API 的功能演示板它不是典型的资源保护用法没有共享资源被保护而是刻意展示了三个状态1. Acquire 成功有令牌时拿走2. Acquire 失败非阻塞timeout0拿不到立刻返回 osErrorResource任务不会被挂起3. Release 失败二值信号量已满再放返回 osErrorResource4. 查询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 */
返回列表