FreeRTOS 二值信号量
一、二值信号量的基本概念
信号量是实现任务间同步和通信的一种机制,可以类比于裸机编程中的标志位。信号量分为多种类型,包括二值信号量、计数信号量、互斥信号量和递归互斥信号量。其中,二值信号量是最简单的一种,其取值只有0和1两种状态。当信号量为0时,表示没有资源可用,所有试图获取它的任务都将处于阻塞状态;当信号量为1时,表示有资源可用,任务可以成功获取信号量并继续执行。
二值信号量可以看作是一个深度为1的队列,这个队列要么为空(信号量为0),要么为满(信号量为1)。任务在获取信号量时,如果队列为空,则任务进入阻塞状态;如果队列为满,则任务成功获取信号量并继续执行。在释放信号量时,任务将信号量放回队列,使得队列从满变为空,从而唤醒等待在该信号量上的其他任务。
二、二值信号量的运作机制
在FreeRTOS中,二值信号量的运作机制主要涉及到信号的创建、获取和释放三个过程。
创建二值信号量:使用xSemaphoreCreateBinary()
函数创建一个二值信号量。该函数返回一个信号量句柄,用于后续的信号量操作。如果创建成功,信号量的初始值通常为0(表示没有资源可用)。
获取二值信号量:使用xSemaphoreTake()
函数获取二值信号量。如果信号量为1(表示有资源可用),则任务成功获取信号量并继续执行;如果信号量为0(表示没有资源可用),则任务进入阻塞状态,等待直到信号量变为1。
释放二值信号量:使用xSemaphoreGive()
函数释放二值信号量。释放信号量后,如果有任务在等待该信号量,则唤醒该任务并使其进入就绪状态。
此外,在中断服务程序中释放二值信号量时,应使用xSemaphoreGiveFromISR()
函数。该函数允许在中断上下文中释放信号量,并处理可能的任务优先级翻转问题。
三、二值信号量的常用API函数
FreeRTOS提供了丰富的API函数来操作二值信号量,以下是一些常用的API函数及其功能:
xSemaphoreCreateBinary():创建一个二值信号量并返回其句柄。
xSemaphoreTake():尝试获取二值信号量。如果信号量为0,则任务进入阻塞状态;如果信号量为1,则任务成功获取信号量。
xSemaphoreGive():释放二值信号量。如果有任务在等待该信号量,则唤醒该任务。
xSemaphoreGiveFromISR():在中断服务程序中释放二值信号量。该函数处理可能的任务优先级翻转问题。
vSemaphoreDelete():删除一个二值信号量。该函数释放信号量所占用的资源。
四、二值信号量实例
在这个例子中,我们创建了两个优先级相同的任务,这两个任务同时访问一个串口资源。为了避免竞争条件,我们使用二进制信号量进行同步。
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "stdio.h"
SemaphoreHandle_t xSem;
void Task1Function(void *param) {
while (1) {
if (xSemaphoreTake(xSem, portMAX_DELAY) == pdTRUE) {
printf("Task1\r\n");
xSemaphoreGive(xSem);
vTaskDelay(1);
} else {
printf("Task1 xSemaphoreTake is err\r\n");
}
}
}
void Task2Function(void *param) {
while (1) {
if (xSemaphoreTake(xSem, portMAX_DELAY) == pdTRUE) {
printf("Task2\r\n");
xSemaphoreGive(xSem);
vTaskDelay(1);
} else {
printf("Task2 xSemaphoreTake is err\r\n");
}
}
}
int main(void) {
xSem = xSemaphoreCreateBinary();
xSemaphoreGive(xSem); // 初始状态设为资源可用
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
vTaskStartScheduler();
while (1) {} // 通常不会执行到这里
return 0;
}
在这个例子中,xSemaphoreCreateBinary()
用于创建一个二进制信号量,xSemaphoreTake()
用于获取信号量,xSemaphoreGive()
用于释放信号量。通过信号量的同步,Task1和Task2可以交替访问串口资源,避免竞争条件。
五、二值信号量的实际应用场景
二值信号量在嵌入式系统开发中有着广泛的应用场景,以下是一些典型的例子:
任务同步:在多个任务之间实现同步操作。例如,一个任务负责数据采集,另一个任务负责数据处理。当数据采集任务完成数据采集后,通过释放二值信号量通知数据处理任务开始处理数据。
中断与任务同步:在中断服务程序与任务之间实现同步操作。例如,在串口接收中断中,当接收到数据时,通过释放二值信号量通知任务来处理接收到的数据。这样可以避免任务在轮询等待数据时浪费CPU资源。
互斥访问:虽然二值信号量主要用于同步操作,但在某些情况下也可以用于实现简单的互斥访问。然而,需要注意的是,二值信号量没有优先级继承机制,因此在处理优先级反转问题时需要谨慎使用。