FreeRTOS 的信号量 ( Semaphore ) 和互斥量 ( Mutex ) 是用于在多任务平台下的同步、资源管理和保护资源免遭破坏的内核对象。

什么是信号量?

在 FreeRTOS 中的任务优先级中,高优先级的任务会抢占低优先级的任务,因此在执行高优先级的任务时,低优先级的任务可能会发生数据损坏的情况。因为该任务尚未执行,并且数据不断从传感器传到该任务,这会导致数据丢失和整个应用程序故障。因此,需要保护资源免受数据丢失的影响,而信号量在这里起着重要的作用。

信号量 ( Semaphore ) 是一种信令机制,其中处于等待状态的任务由另一个任务发出信号以执行。换句话说,当 task1 完成工作时,它将显示一个标志或将标志加 1,然后另一个任务 task2 接收到该标志,表明它现在可以执行其工作。当 task2 完成工作后,该标志将减少 1。

因此,从根本上讲,它是一种 ” 给予 ” 和 ” 接受 ” 机制,而信号量是一个整数变量,用于同步对资源的访问。

信号量类型

FreeRTOS 的信号量有两种类型,分别为:

⑴ 二进制信号量 ( Binary Semaphore )

它具有两个整数值 0 和 1。它与长度为 1 的队列有些相似。例如,我们有两个任务 task1 和 task2。 task1 将数据发送到 task2,因此 task2 继续检查队列项是否为 1,然后它可以读取数据,否则必须等待直到变为 1。在获取数据之后,task2 递减队列并将其设为 0,这意味着 task1 再次可以将数据发送到 task2。

⑵ 计数信号量 ( Counting Semaphore )

它的值大于 0,并且可以认为长度大于 1 的队列。该信号量用于计数事件。在这种使用情况下,事件处理程序将在事件发生时 ” 给出 ” 信号量 ( 增加信号量计数值 ),处理程序任务将在每次处理事件时 ” 接收 ” 信号量 ( 减少信号量计数值 )。

因此,计数值为发生的事件数与已处理的事件数之差。

如何在 FreeRTOS 中使用信号量

FreeRTOS 支持不同的 API,用于创建信号量、获取信号量和提供信号量。

现在,同一内核对象可以有两种类型的 API。如果必须从 ISR 提供信号量,则无法使用常规信号量 API。您应该使用受中断保护的 API。

使用二进制信号量,因为它易于理解和实现。由于此处使用了中断功能,因此您需要在 ISR 函数中使用受中断保护的 API。当我们说用中断同步任务时,这意味着在 ISR 之后立即将任务置于 ” 运行 ” 状态。

⑴ 创建信号量

要使用任何内核对象,我们必须首先创建。要创建二进制信号量,请使用 vSemaphoreCreateBinary()。该 API 不带任何参数,并返回 SemaphoreHandle_t 类型的变量。范例如下:

创建一个全局变量名称 sema_v 来存储信号量。

SemaphoreHandle_t  sema_v;
sema_v = xSemaphoreCreateBinary();

⑵ 提供信号量

为了提供信号量,有两个 API 函数,一种用于中断,另一种用于常规任务。

① xSemaphoreGive():此API在创建信号量时仅接受一个参数,即上述信号量的变量名,如 sema_v。可以从要同步的任何常规任务中调用它。

② xSemaphoreGiveFromISR():该函数是 xSemaphoreGive() 的受中断保护的 API 函数。当需要同步 ISR 和正常任务时,应从 ISR 函数中使用 xSemaphoreGiveFromISR()。

⑶ 获取信号量

要获取信号量,请使用 API​​ 函数 xSemaphoreTake()。该 API 带有两个参数。

① xSemaphore:信号量的名称。

② xTicksToWait:这是任务在 ” 阻塞 ” 状态下等待信号量变为可用的最长时间。

xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

 

☛ 什么是互斥量?

信号量是一种信号传递机制,与互斥量不同,互斥量 ( Mutex ) 是一种锁定机制,与具有独立的增量和递减功能的信号量不同,但在互斥量中,该功能本身具有并给出。这是一种避免共享资源损坏的技术。

为了保护共享资源,可以为资源分配一个令牌卡 ( 互斥体 )。拥有此卡的人都可以访问其他资源。其他人应该等到卡归还。这样,只有一种资源可以访问任务,而其他资源则等待机会。藉由下面的范例来了解 FreeRTOS 中的互斥量:

这里我们有三个任务,一个任务是在 LCD 上打印数据,第二个任务是将 LDR 数据发送到 LCD 任务,最后一个任务是在 LCD 上发送温度数据。因此,这里有两个任务共享同一个资源,即 LCD。如果 LDR 任务和温度任务同时发送数据,则数据之一可能已损坏或丢失。

因此,为了保护数据丢失,我们需要为 task1 锁定 LCD 资源,直到它完成显示任务为止。然后 LCD 任务将解锁,之后 task2 才可以执行其工作。下图中为互斥量和信号量的工作情况。

如何在 FreeRTOS 中使用互斥量

互斥量也可以与信号量相同地使用。首先,创建它,然后使用相应的 API 进行使用。

⑴ 创建互斥量

要创建一个互斥量,请使用 xSemaphoreCreateMutex()。顾名思义,互斥量是一种二进制信号量。它们用于不同的上下文和目的。二进制信号量用于同步任务,而互斥量用于保护共享资源。

该 API 没有任何参数,并返回 SemaphoreHandle_t 类型的变量。如果无法创建互斥量,则x SemaphoreCreateMutex() 返回 NULL。

SemaphoreHandle_t mutex_v;
mutex_v = xSemaphoreCreateMutex();

⑵ 获取互斥量

当任务想要访问资源时,它将通过使用 xSemaphoreTake() 来获取互斥对象。它与二进制信号量相同。它还需要两个参数。

① xSemaphore:将使用的互斥对象的名称。

② xTicksToWait:这是任务在 ” 阻止 ” 状态下等待 Mutex 可用的最长时间。

⑶ 提供互斥量

访问共享资源后,任务应返回互斥量,以便其他任务可以访问它。 xSemaphoreGive() 用于将互斥量返回。xSemaphoreGive() 函数仅采用一个参数,即在给出的互斥量。