FreeRTOS信号量(Semaphores)简介
信号量是一种用于进程间通信和同步的机制,主要用于控制对共享资源的访问。在嵌入式系统开发中,尤其是在使用实时操作系统(RTOS)如FreeRTOS时,信号量是实现任务间协调的重要工具。
什么是信号量?
信号量本质上是一个计数器,用于跟踪可用资源的数量。当一个任务需要访问某个资源时,它必须先获取信号量。如果信号量的计数大于零,任务可以继续执行,并且信号量的计数会减一。如果信号量的计数为零,任务会被阻塞,直到有其他任务释放信号量。
信号量的类型
二进制信号量(Binary Semaphore)
二进制信号量只有两种状态:0或1。通常用于保护临界区,确保同一时间只有一个任务可以访问受保护的代码段。
创建二进制信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void);
获取二进制信号量
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
释放二进制信号量
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
计数信号量(Counting Semaphore)
计数信号量的计数可以大于1,用于管理多个相同类型的资源。例如,如果有多个任务需要访问一组资源(如多个UART通道),计数信号量可以帮助管理这些资源。
创建计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
获取计数信号量
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
释放计数信号量
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
信号量的应用场景
保护共享资源
在多任务环境中,多个任务可能需要访问相同的资源(如内存块、硬件设备等)。通过使用信号量,可以确保同一时间只有一个任务可以访问该资源,从而避免数据竞争和不一致性。
任务同步
信号量也可以用于任务间的同步。例如,一个任务生成数据并将其放入缓冲区,另一个任务从缓冲区中取出数据进行处理。通过信号量,可以确保生产者任务不会在消费者任务处理完数据之前覆盖缓冲区中的数据。
示例代码
以下是一个简单的示例,展示了如何在FreeRTOS中使用二进制信号量来保护共享资源。
#include "FreeRTOS.h"
#include "task.h"
#include "stdio.h"
#include "semphr.h"
SemaphoreHandle_t xSemaphore;
// 任务函数声明
void Send_Task(void *pvParameters);
void Receive_Task(void *pvParameters);
void LED_Task(void *pvParameters);
void InitializeTasks(void *pvParameters);
int main(void)
{
// 初始化系统
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
// 创建初始化任务
xTaskCreate(InitializeTasks, "init_Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// 启动调度器
vTaskStartScheduler();
while (1)
{
// 主循环
}
}
void InitializeTasks(void *pvParameters)
{
// 创建二进制信号量
xSemaphore = xSemaphoreCreateBinary();
if (xSemaphore == NULL)
{
printf("Semaphore creation failed\r\n");
}
else
{
printf("Semaphore created successfully\r\n");
}
// 创建任务
xTaskCreate(Send_Task, "Send_Task", configMINIMAL_STACK_SIZE, NULL, 3, NULL);
xTaskCreate(Receive_Task, "Receive_Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(LED_Task, "LED_Task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
// 删除初始化任务
vTaskDelete(NULL);
}
void Send_Task(void *pvParameters)
{
while (1)
{
// 检测按键状态
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1 || Key_Scan(GPIOB, GPIO_PIN_4) == 1)
{
// 获取信号量
if (xSemaphoreGive(xSemaphore) == pdTRUE)
{
printf("Semaphore Given\r\n");
}
else
{
printf("Semaphore Give Failed\r\n");
}
}
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
void Receive_Task(void *pvParameters)
{
while (1)
{
// 等待信号量
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE)
{
printf("Semaphore Taken\r\n");
// 处理接收到的数据
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 切换LED状态
}
else
{
printf("Semaphore Take Failed\r\n");
}
}
}
void LED_Task(void *pvParameters)
{
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED状态
vTaskDelay(pdMS_TO_TICKS(500)); // 延时500毫秒
}
}
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
uint8_t keyState = (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) ? 1 : 0;
if (keyState == 1)
{
vTaskDelay(pdMS_TO_TICKS(50));
uint8_t keyState_new = (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) ? 1 : 0;
printf("Key State New: %d\r\n", keyState_new);
if (1 == keyState_new)
{
return 1; // 按键按下
}
}
return 0; // 按键未按下
}
在这个示例中,Send_Task
和 Receive_Task
通过二进制信号量 xSemaphore
来同步对共享资源的访问。Send_Task
在检测到按键按下时释放信号量,而 Receive_Task
在获取到信号量后执行相应的操作。
总结
信号量是FreeRTOS中非常重要的同步机制,能够有效解决多任务环境下的资源竞争和任务同步问题。通过合理使用信号量,可以编写出更加健壮和高效的嵌入式应用程序。