信号量是什么
在FreeRTOS中,信号量(Semaphore)是一种用于任务间同步或保护共享资源的机制。
任务间同步:即确保一个任务在另一个任务执行到某个点后在执行。
资源保护:即控制对共享资源的访问,防止多个任务同时访问资源导致数据不一致。
信号量的类型
-
二进制信号量(Binary Semaphore):
二进制信号量在同一时间只允许一个任务访问资源。它们通常用于实现任务间的同步。例如,一个任务可以等待另一个任务发出信号,然后继续执行。(阻塞式)
-
计数信号量(Counting Semaphore):
计数信号量允许多个任务访问相同的资源。它们有一个初始计数值,每次获取信号量时计数减一,每次释放信号量时计数加一。当计数值为零时,表示没有可用资源,任务必须等待
使用信号量的基本步骤
-
创建信号量
xSemaphoreCreateBinary()
: 创建一个二进制信号量。
xSemaphoreCreateCounting()
: 创建一个计数信号量。
-
获取信号量
xSemaphoreTake()
: 获取信号量,如果信号量不可用,任务可以选择等待。
-
释放信号量
xSemaphoreGive()
: 释放信号量,使其他等待的任务能够继续执行。
FreeRTOS中信号量的API
xSemaphoreCreateBinary()
SemaphoreHandle_t xSemaphoreCreateBinary( void );
参数解析
无参数:xSemaphoreCreateBinary()函数没有任何参数。
返回值
成功:返回一个SemaphoreHandle_t类型的信号量句柄,该句柄可用于其他信号量操作函数。
失败:返回NULL,表示内存分配失败,无法创建信号量。
使用说明
在创建的初始状态下,二进制信号量是空的(即,初始值为0),因此在创建后需要先通过xSemaphoreGive()将其置为“满”状态(即,初始值为1),才能供任务使用。
xSemaphoreCreateCounting()
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
参数解析
uxMaxCount:指定信号量的最大计数值,即信号量可以达到的最大值。
uxInitialCount:指定信号量的初始计数值,即信号量在创建时的初始值。
返回值
成功:返回一个SemaphoreHandle_t类型的信号量句柄,该句柄可用于其他信号量操作函数。
失败:返回NULL,表示内存分配失败,无法创建信号量。
使用说明
计数信号量可以用来控制对有限资源的访问,或者用于多个任务之间的事件计数。
xSemaphoreTake()
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
参数解析
xSemaphore:指定要获取的信号量的句柄(handle)。
xTicksToWait:等待获取信号量的最长时间(以时钟节拍(tick)为单位)。可以是以下几种值:
portMAX_DELAY:永久等待,直到获取信号量。
0:立即返回,不等待信号量。
其他正整数值:等待指定时长,如果在指定时间内未能获取到信号量,则函数返回。
返回值
pdTRUE:成功获取到信号量。
pdFALSE:未能获取到信号量,在等待超时后返回。
使用说明
在调用xSemaphoreTake()之前,需要确保已经创建了相应的信号量,并且任务拥有该信号量的访问权限。
如果信号量当前不可用(即已经被其他任务获取),任务会根据xTicksToWait的设置进入阻塞状态,直到满足获取条件。
当任务成功获取信号量时,信号量的计数值会相应减少。
xSemaphoreGive()
BaseType_t xSemaphoreGive(
SemaphoreHandle_t xSemaphore
);
参数解析
xSemaphore:指定要释放的信号量的句柄(handle)。
返回值
pdTRUE:成功释放信号量。
pdFALSE:释放信号量失败。
使用说明
xSemaphoreGive()函数用于将信号量的计数值增加,表示释放一个资源或通知一个事件。
如果有任务在等待获取该信号量,释放后会有一个任务能够获取到信号量并继续执行。
一般情况下,使用xSemaphoreGive()的任务应当在使用完共享资源后释放信号量,以便其他任务可以使用它。
示例一:利用二进制信号量,当Send任务中Key1按下时释放一个信号量,Receive任务获取信号量。
下述代码中按键Key1相关配置不提供,仅对信号量的使用进行一个距离
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
// 定义信号量句柄
SemaphoreHandle_t BinarySemHandle;
// 发送任务
void vSenderTask(void *pvParameters)
{
BinarySemHandle = xSemaphoreCreateBinary(); // 创建一个二进制信号量
if (BinarySem != NULL)
{
// 成功创建信号量
// 立即释放一次信号量,初始化信号量为可用状态
xSemaphoreGive(BinarySemHandle);
}
for(;;)
{
// 检查按键1是否按下
if (HAL_GPIO_ReadPin(Key1_GPIO_Port, Key1_Pin) == GPIO_PIN_RESET)
{
// 按键1按下,发送信号量
xSemaphoreGive(BinarySem);
printf("发送任务:按键1按下,信号量已发送\n");
}
}
vTaskDelay(pdMS_TO_TICKS(200));
}
// 接收任务
void vReceiverTask(void *pvParameters)
{
for(;;)
{
// 尝试获取信号量
if (xSemaphoreTake(BinarySem, portMAX_DELAY) == pdTRUE)
{
// 成功获取信号量,可以继续执行
printf("接收任务:信号量已接收\n");
// 模拟处理接收到的信号量
vTaskDelay(pdMS_TO_TICKS(500));
}
else
{
printf("未接收到信号量\n");
}
}
}
// 主函数
int main(void)
{
// 创建发送任务
xTaskCreate(vSenderTask, "Sender Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// 创建接收任务
xTaskCreate(vReceiverTask, "Receiver Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// 启动调度器
vTaskStartScheduler();
// 如果程序能运行到这里,说明调度器启动失败
for(;;);
return 0;
}
示例2:利用计数信号量,按下Key1申请车位,按下Key2释放车位
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
SemaphoreHandle_t ParkingSem; // 定义信号量句柄
#define MAX_PARKING_SPACES 5 // 停车位的最大数量
// 申请车位任务
void vRequestParkingTask(void *pvParameters)
{
for(;;)
{
// 检查按键Key1是否按下
if (HAL_GPIO_ReadPin(Key1_GPIO_Port,Key1_Pin) == GPIO_PIN_RESET)
{
if (xSemaphoreTake(ParkingSem, 0) == pdTRUE) // 按键Key1按下,尝试申请车位
{
printf("申请车位任务:成功申请一个车位\n");
}
else
{
printf("申请车位任务:车位已满,无法申请车位\n");
}
// 防止按键抖动,延迟一段时间
vTaskDelay(pdMS_TO_TICKS(200));
}
// 延迟一段时间,模拟任务执行
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 释放车位任务
void vReleaseParkingTask(void *pvParameters)
{
for(;;)
{
// 检查按键Key2是否按下
if (HAL_GPIO_ReadPin(Key2_GPIO_Port, Key2_Pin) == GPIO_PIN_RESET)
{
if (uxSemaphoreGetCount(ParkingSem) < MAX_PARKING_SPACES) // 按键Key2按下,尝试释放车位
{
xSemaphoreGive(ParkingSem);
printf("释放车位任务:成功释放一个车位\n");
}
else
{
printf("释放车位任务:车位已满,无需释放车位\n");
}
// 防止按键抖动,延迟一段时间
vTaskDelay(pdMS_TO_TICKS(200));
}
// 延迟一段时间,模拟任务执行
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 主函数
int main(void)
{
// 创建一个计数信号量,初始值为MAX_PARKING_SPACES,最大值为MAX_PARKING_SPACES
ParkingSem = xSemaphoreCreateCounting(MAX_PARKING_SPACES, MAX_PARKING_SPACES);
if (ParkingSem != NULL) // 成功创建信号量
{
// 创建申请车位任务
xTaskCreate(vRequestParkingTask, "Request Parking Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// 创建释放车位任务
xTaskCreate(vReleaseParkingTask, "Release Parking Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// 启动调度器
vTaskStartScheduler();
} else
{
printf("无法创建计数信号量\n");
}
// 如果程序能运行到这里,说明调度器启动失败
for(;;);
return 0;
}