跳到主要内容

STM32 FreeRTOS信号量

在嵌入式系统中,任务之间的同步和资源共享是一个常见的问题。FreeRTOS 提供了多种机制来解决这些问题,其中之一就是信号量(Semaphore)。信号量是一种用于任务间通信和资源管理的同步机制。本文将详细介绍如何在 STM32 中使用 FreeRTOS 信号量,并通过代码示例和实际案例帮助你理解其工作原理。

什么是信号量?

信号量是一种用于控制多个任务对共享资源访问的机制。它可以用来实现任务之间的同步,或者限制对某些资源的并发访问。信号量有两种主要类型:

  1. 二进制信号量(Binary Semaphore):只能取 0 或 1 的值,通常用于任务同步。
  2. 计数信号量(Counting Semaphore):可以取多个值,通常用于资源管理。

在 FreeRTOS 中,信号量是通过 xSemaphoreCreateBinary()xSemaphoreCreateCounting() 函数创建的。

创建和使用信号量

创建二进制信号量

以下是一个创建二进制信号量的示例:

c
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

SemaphoreHandle_t xBinarySemaphore;

void vTask1(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
// 信号量获取成功,执行任务
// ...
// 释放信号量
xSemaphoreGive(xBinarySemaphore);
}
}
}

void vTask2(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
// 信号量获取成功,执行任务
// ...
// 释放信号量
xSemaphoreGive(xBinarySemaphore);
}
}
}

int main(void) {
// 创建二进制信号量
xBinarySemaphore = xSemaphoreCreateBinary();

// 创建任务
xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

// 启动调度器
vTaskStartScheduler();

// 永远不会到达这里
for (;;);
}

在这个示例中,vTask1vTask2 两个任务通过二进制信号量进行同步。xSemaphoreTake() 用于获取信号量,xSemaphoreGive() 用于释放信号量。

创建计数信号量

计数信号量可以用于管理多个资源。以下是一个创建计数信号量的示例:

c
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

SemaphoreHandle_t xCountingSemaphore;

void vTask1(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE) {
// 信号量获取成功,执行任务
// ...
// 释放信号量
xSemaphoreGive(xCountingSemaphore);
}
}
}

void vTask2(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE) {
// 信号量获取成功,执行任务
// ...
// 释放信号量
xSemaphoreGive(xCountingSemaphore);
}
}
}

int main(void) {
// 创建计数信号量,初始值为 5
xCountingSemaphore = xSemaphoreCreateCounting(5, 5);

// 创建任务
xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

// 启动调度器
vTaskStartScheduler();

// 永远不会到达这里
for (;;);
}

在这个示例中,计数信号量的初始值为 5,表示有 5 个资源可供任务使用。每次任务获取信号量时,信号量的值减 1;每次任务释放信号量时,信号量的值加 1。

实际应用场景

任务同步

假设你有两个任务,一个任务负责采集传感器数据,另一个任务负责处理数据。你可以使用二进制信号量来确保数据采集任务在数据处理任务完成之前不会再次运行。

c
SemaphoreHandle_t xDataReadySemaphore;

void vSensorTask(void *pvParameters) {
while (1) {
// 采集传感器数据
// ...
// 数据采集完成,释放信号量
xSemaphoreGive(xDataReadySemaphore);
}
}

void vDataProcessingTask(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xDataReadySemaphore, portMAX_DELAY) == pdTRUE) {
// 处理数据
// ...
}
}
}

资源管理

假设你有多个任务需要访问一个共享资源(如 SPI 总线),你可以使用计数信号量来限制同时访问该资源的任务数量。

c
SemaphoreHandle_t xSPISemaphore;

void vSPITask1(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xSPISemaphore, portMAX_DELAY) == pdTRUE) {
// 访问 SPI 总线
// ...
// 释放信号量
xSemaphoreGive(xSPISemaphore);
}
}
}

void vSPITask2(void *pvParameters) {
while (1) {
// 等待信号量
if (xSemaphoreTake(xSPISemaphore, portMAX_DELAY) == pdTRUE) {
// 访问 SPI 总线
// ...
// 释放信号量
xSemaphoreGive(xSPISemaphore);
}
}
}

总结

信号量是 FreeRTOS 中用于任务同步和资源管理的重要机制。通过二进制信号量和计数信号量,你可以有效地控制任务之间的同步和共享资源的访问。本文介绍了如何创建和使用信号量,并通过实际应用场景展示了其用途。

提示

在实际项目中,信号量的使用需要谨慎,避免死锁和优先级反转等问题。建议在使用信号量时,仔细设计任务之间的同步逻辑。

附加资源

练习

  1. 修改本文中的代码示例,使用计数信号量来管理 3 个共享资源。
  2. 设计一个任务同步的场景,使用二进制信号量确保任务 A 在任务 B 完成之前不会运行。

通过本文的学习,你应该能够在 STM32 项目中熟练使用 FreeRTOS 信号量。继续实践和探索,你将掌握更多嵌入式系统的开发技巧。