摘要(From AI):
这篇博客详细介绍了 FreeRTOS 中二值信号量和计数信号量的基本概念、API 使用方法及实际应用场景,辅以完整的示例代码,适合初学者学习
前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。
文章目录
- Binary Semaphore
- xSemaphoreCreateBinary()
- 返回值
- xSemaphoreGive()
- xSemaphoreTake()
- Example Code: Binary Semaphore for Task Synchronization
- Count Semaphore
- xSemaphoreCreateCounting()
- uxSemaphoreGetCount()
- Example Code: Counting Semaphore for Parking Lot Management
参考资料
Michael_ee视频教程
freeRTOS官网
espressif 在线文档
Binary Semaphore
Semaphore:信号量,用于控制 Task 进行同步,Binary 表示这个信号量只有两个值 0/1
xSemaphoreCreateBinary()
创建一个二值信号量,并返回一个句柄供引用。
#include “FreeRTOS.h”
#include “semphr.h”SemaphoreHandle_t xSemaphoreCreateBinary( void );
关键点
内存分配
- 自动从 FreeRTOS 堆中分配所需内存。
- 如果需要静态分配内存,可使用
xSemaphoreCreateBinaryStatic()
初始状态: - 信号量以空状态创建。
- 必须先通过
xSemaphoreGive()
给予信号量,才能通过xSemaphoreTake()
获取
参数
无
返回值
-
NULL
信号量创建失败(堆内存不足) -
非空句柄:信号量创建成功,返回的句柄可用于引用该信号量
过时 API
- 旧的
vSemaphoreCreateBinary()
宏已被废弃,且不应在新应用中使用。 xSemaphoreCreateBinary()
创建的信号量默认为“空状态”,而vSemaphoreCreateBinary()
默认为“非空状态”
使用前提
- 配置要求:
- 确保在
FreeRTOSConfig.h
中启用configSUPPORT\_DYNAMIC\_ALLOCATION
- 确保在
xSemaphoreGive()
释放(或“给予”)一个先前创建并已成功“获取”的信号量
#include “FreeRTOS.h”
#include “semphr.h”BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
参数
xSemaphore
:要释放的信号量,类型为SemaphoreHandle\_t
,必须在使用前显式创建(如通过vSemaphoreCreateBinary()
、xSemaphoreCreateCounting()
或xSemaphoreCreateMutex()
)
返回值
-
pdPASS
:操作成功,信号量被成功释放 -
pdFAIL
:操作失败,因为调用任务不是信号量的持有者。任务必须先成功“获取”信号量后才能释放
xSemaphoreTake()
获取(或“占用”)一个先前通过 vSemaphoreCreateBinary()
、 xSemaphoreCreateCounting()
或 xSemaphoreCreateMutex()
创建的信号量
#include “FreeRTOS.h”
#include “semphr.h”BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
参数
xSemaphore
:要获取的信号量,类型为SemaphoreHandle_t
,必须在使用前显式创建xTicksToWait
:任务等待信号量可用的最大时间(以 FreeRTOS 系统时钟节拍为单位)0
:若信号量不可用,立即返回portMAX_DELAY
:任务将无限期等待信号量可用(需在FreeRTOSConfig.h
中启用INCLUDE_vTaskSuspend
)- 使用
pdMS_TO_TICKS()
宏可将毫秒时间转换为节拍时间
返回值
pdPASS
:信号量获取成功- 如果指定了等待时间(
xTicksToWait
非零),任务可能被阻塞,直到信号量可用
- 如果指定了等待时间(
pdFAIL
:信号量获取失败- 如果指定了等待时间,任务会阻塞等待,但超时后仍未获取到信号量
注意事项:
-
必须从运行中的任务中调用,不能在调度器启动前或初始化阶段调用
-
不应在临界区或调度器暂停时调用
Example Code: Binary Semaphore for Task Synchronization
- 由于任务的优先级相同,FreeRTOS 调度器会以时间片轮转方式调度两个任务
信号量保证了两个任务不会同时操作共享变量iCount
,从而避免数据竞争
Task1 和 Task2 都会试图对共享变量 iCount
进行递增操作:
- 每次操作前,任务会调用
xSemaphoreTake()
获取信号量,以独占访问资源 - 操作完成后,调用
xSemaphoreGive()
释放信号量,允许其他任务访问
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h"#include "freertos/semphr.h" // 信号量头文件int iCount = 0;
SemaphoreHandle_t semaphoreHandle = NULL;void myTask1(void *pvParameters)
{while(1){xSemaphoreTake(semaphoreHandle, portMAX_DELAY);// 锁住信号量for(int i = 0; i < 10; i++){iCount++;ESP_LOGI("Task1", "iCount = %d\n", iCount);vTaskDelay(pdMS_TO_TICKS(1000));}xSemaphoreGive(semaphoreHandle);// 释放信号量vTaskDelay(pdMS_TO_TICKS(2000));}
}void myTask2(void *pvParameters)
{while(1){xSemaphoreTake(semaphoreHandle, portMAX_DELAY);// 锁住信号量for(int i = 0; i < 10; i++){iCount++;ESP_LOGI("Task2", "iCount = %d\n", iCount);vTaskDelay(pdMS_TO_TICKS(1000));}xSemaphoreGive(semaphoreHandle);// 释放信号量vTaskDelay(pdMS_TO_TICKS(2000));}
}void app_main(void)
{semaphoreHandle = xSemaphoreCreateBinary();xSemaphoreGive(semaphoreHandle);xTaskCreate(myTask1, "Task1", 2048, NULL, 5, NULL);xTaskCreate(myTask2, "Task2", 2048, NULL, 5, NULL);
}
Count Semaphore
计数型信号量(Counting Semaphore) 用于在任务或中断之间实现资源的共享与同步。它与二值信号量的主要区别在于其内部维护了一个计数器,允许多个任务或中断“获取”信号量,而无需严格的互斥限制
xSemaphoreCreateCounting()
创建一个计数型信号量,返回一个句柄用于引用该信号量。信号量可用于事件计数或资源管理
用途
- 事件计数
- 用于记录事件发生的次数
- 每次事件发生时调用
xSemaphoreGive()
增加计数,任务通过xSemaphoreTake()
处理事件并减少计数 - 初始计数值应为 0
- 资源管理
- 用于管理一定数量的共享资源
- 每次任务获取资源时调用
xSemaphoreTake()
,计数值减少;释放资源时调用xSemaphoreGive()
,计数值增加 - 初始计数值应为可用资源的数
#include “FreeRTOS.h”
#include “semphr.h”SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
内存分配
-
如果使用
xSemaphoreCreateCounting()
,所需内存会自动从 FreeRTOS 堆中分配 -
如果使用
xSemaphoreCreateCountingStatic()
,则需要用户提供内存,实现静态分配
参数
uxMaxCount
:信号量的最大计数值。当信号量达到此值时,不能再“给予”信号量uxInitialCount
:信号量的初始计数值
返回值
NULL
:创建失败,通常由于堆内存不足- 非空句柄:信号量创建成功,可通过返回的句柄引用信号量
注意事项:
- 必须在
FreeRTOSConfig.h
中启用configSUPPORT_DYNAMIC_ALLOCATION
uxSemaphoreGetCount()
返回信号量的当前计数值
#include “FreeRTOS.h”
#include “semphr.h”UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
参数
xSemaphore
:信号量的句柄,用于指定需要查询的信号量
返回值
返回指定信号量的当前计数值
此功能可用于实时监控信号量的状态或判断可用资源的数量
Example Code: Counting Semaphore for Parking Lot Management
资源管理
信号量的计数值表示资源的当前可用数量,通过动态增减信号量,实现对有限资源(如停车位)的精确管理。
任务协调
通过信号量,确保多个任务对共享资源的访问井然有序,避免资源竞争和冲突。
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h"#include "freertos/semphr.h" // 信号量头文件int iCount = 0;
SemaphoreHandle_t semaphoreHandle = NULL;void carInTask(void *pvParameters)
{int empty = 0;while (1){empty = uxSemaphoreGetCount(semaphoreHandle); // 获取信号量剩余数量ESP_LOGI("carInTask", "empty = %d\n", empty);vTaskDelay(pdMS_TO_TICKS(1000));if (xSemaphoreTake(semaphoreHandle, 0) == pdPASS){ESP_LOGI("carInTask", "carIn\n");}else{ESP_LOGI("carInTask", "carIn failed\n");}}
}void carOutTask(void *pvParameters)
{while (1){vTaskDelay(pdMS_TO_TICKS(6000));xSemaphoreGive(semaphoreHandle);ESP_LOGI("carOutTask", "carOut\n");}
}void app_main(void)
{semaphoreHandle = xSemaphoreCreateCounting(5, 5);// xSemaphoreGive(semaphoreHandle);// 在使用计数信号量时,无需手动释放信号量xTaskCreate(carInTask, "carInTask", 2048, NULL, 5, NULL);xTaskCreate(carOutTask, "carOutTask", 2048, NULL, 5, NULL);
}