ESP32 FreeRTOS-信号量(8)

提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正

前言:

  参考资料:FreeRTOS API参考
  在freeRTOS中信号量也是非常重要的,有些资源我们不得不使用全局变量,这个时候多个任务都会访问这个变量,在这种情况下我们就可以使用互斥信号量,另外我们在任务与任务同步也可以使用信号量来实现。

一、二进制信号量

1.1、xSemaphoreCreateBinary()

API原型:

SemaphoreHandle_t xSemaphoreCreateBinary(void);

提示:在许多使用场景中,使用直达任务通知要比使用二次进入制号量的速度更快,内存效率更高。

  创建一个二进制信号量,并返回一个可以引用信号量的句柄。 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,或者保留为未定义(在这种情况下它将默认为 1),以便此 RTOS API 函数可用。
  每个二进制信号量都需要少量 RAM 用于保存信号量的状态。如果使用 xSemaphoreCreateBinary() 创建二进制信号量,则所需的 RAM 会自动从FreeRTOS 堆中分配。如果使用xSemaphoreCreateBinaryStatic()创建二进制信号量, 则 RAM 由应用程序编写器提供,这需要一个附加参数,但允许在编译时静态分配 RAM。有关详细信息,请参阅静态与动态分配页面。
  信号量是在“空”状态下创建的,这意味着必须首先使用xSemaphoreGive() API 函数提供信号量,然后才能使用xSemaphoreTake () 函数获取(获取)它。
  二进制信号量和互斥量非常相似,但有一些细微的差别:互斥量包括优先级继承机制,二进制信号量没有。这使得二进制信号量成为实现同步(任务之间或任务与中断之间)的更好选择,而互斥体成为实现简单互斥的更好选择。
  二进制信号量一旦获得就不需要返回,因此任务同步可以通过一个任务/中断连续“提供”信号量而另一个连续“获取”信号量来实现。xSemaphoreGiveFromISR()文档页面上的示例代码演示了这一点。请注意,通常可以使用直接任务通知以更有效的方式实现相同的功能。
  如果另一个更高优先级的任务试图获得相同的互斥锁,则可能会提高“获取”互斥锁的任务的优先级。拥有互斥锁的任务“继承”试图“获取”相同互斥锁的任务的优先级。这意味着必须始终“返还”互斥锁——否则优先级较高的任务将永远无法获得互斥锁,而优先级较低的任务也永远不会“剥夺”优先级。xSemaphoreTake()文档页面 上提供了一个用于实现互斥的互斥量示例。
  互斥量和二进制信号量都由 SemaphoreHandle_t 类型的变量引用,并且可以在采用该类型参数的任何任务级 API 函数中使用。与互斥锁不同,二进制信号量可用于中断服务程序。
返回值:
无效的 无法创建信号量,因为可用的FreeRTOS 堆不足 。
任何其他值 信号量创建成功。返回值是一个可以引用信号量的句柄。
用法示例:

SemaphoreHandle_t xSemaphore;
void vATask(void * pvParameters)
{
    
    
    /* 尝试创建一个信号量。*/
    xSemaphore = xSemaphoreCreateBinary();

    if(xSemaphore == NULL{
    
    
        /* 没有足够的 FreeRTOS 堆可供信号量使用
        创建成功。*/
    }
   	else
    {
    
    
        /* 现在可以使用信号量了。它的句柄存储在
        x信号量变量。在此处的信号量上调用 xSemaphoreTake()
        将失败,直到第一次给出信号量。*/
    }
}

1.2、xSemaphoreCreateBinaryStatic()

API原型:

SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);

  创建一个二进制信号量,并返回一个可以引用信号量的句柄。 必须在 FreeRTOSConfig.h 中将 configSUPPORT_STATIC_ALLOCATION设置为 1,此 RTOS API 函数才可用。
  每个二进制信号量都需要少量 RAM 用于保存信号量的状态。如果使用xSemaphoreCreateBinary()创建二进制信号量, 则所需的 RAM 会自动从FreeRTOS 堆中分配。如果使用 xSemaphoreCreateBinaryStatic() 创建二进制信号量,则 RAM 由应用程序编写器提供,这需要一个附加参数,但允许在编译时静态分配 RAM。有关详细信息,请参阅静态与动态分配页面。
  信号量是在“空”状态下创建的,这意味着必须首先使用xSemaphoreGive() API 函数提供信号量,然后才能使用xSemaphoreTake () 函数获取(获取)它。
  二进制信号量和互斥量非常相似,但有一些细微的差别:互斥量包括优先级继承机制,二进制信号量没有。这使得二进制信号量成为实现同步(任务之间或任务与中断之间)的更好选择,而互斥体成为实现简单互斥的更好选择。
  二进制信号量一旦获得就不需要返回,因此任务同步可以通过一个任务/中断连续“提供”信号量而另一个连续“获取”信号量来实现。xSemaphoreGiveFromISR()文档页面上的示例代码演示了这一点。请注意,通常可以使用直接任务通知以更有效的方式实现相同的功能。
  如果另一个更高优先级的任务试图获得相同的互斥锁,则可能会提高“获取”互斥锁的任务的优先级。拥有互斥锁的任务“继承”试图“获取”相同互斥锁的任务的优先级。这意味着必须始终“返还”互斥锁——否则优先级较高的任务将永远无法获得互斥锁,而优先级较低的任务也永远不会“剥夺”优先级。xSemaphoreTake()文档页面 上提供了一个用于实现互斥的互斥量示例。
  互斥量和二进制信号量都由 SemaphoreHandle_t 类型的变量引用,并且可以在采用该类型参数的任何任务级 API 函数中使用。与互斥锁不同,二进制信号量可用于中断服务程序。
参数:
  pxSemaphoreBuffer 必须指向一个 StaticSemaphore_t 类型的变量,它将用于保存信号量的状态。
返回值:
  NULL 因为 pxSemaphoreBuffer 为 NULL, 所以无法创建信号量。
  其他任何值 信号量已成功创建。 返回 值是一个句柄,通过该句柄可以引用信号量。
用法示例:

SemaphoreHandle_t xSemaphore = NULL;
StaticSemaphore_t xSemaphoreBuffer;

 void vATask(void * pvParameters)
 {
    
    
    /* 创建一个二进制信号量而不使用任何动态内存
    分配。信号量的数据结构将被保存到
    xSemaphoreBuffer 变量。*/
    xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer );

    /* pxSemaphoreBuffer 不是 NULL,所以预计
    句柄不会为 NULL。*/ 
    configASSERT ( xSemaphore );

    /* 其余的任务代码放在这里。*/
 }

1.3、vSemaphoreCreateBinary()

API原型:

vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )

  注意:vSemaphoreCreateBinary() 宏保留在源代码中, 以确保后向兼容,但不应在新设计中使用。 新设计中应使用 xSemaphoreCreateBinary() 函数。
  此外,在许多情况下,使用 直达任务通知 代替二进制信号量速度更快,更节省内存。
  使用现有队列机制创建信号量的宏。队列长度为 1 ,因为这是二进制信号量。数据大小为 0 ,因为实际上我们并不会存储任何数据,只想知道队列为空还是满。
  二进制信号量和互斥锁非常相似,但有一些小差异: 互斥锁包含优先继承机制, 而二进制信号量不包含。 因此,二进制信号量更适合(在任务间或任务与中断之间)实现同步, 而互斥锁更适合实现简单 互斥。
  一旦获得二进制信号量,则无需返回, 因此任务同步可以通过一个任务/中断连续“提供”信号量, 而另一个任务/中断连续“获取”信号量来实现。 可以通过 xSemaphoreGiveFromISR() 文档页面上的示例代码来演示。
  如果另一个优先级更高的任务尝试获取相同的互斥锁, 那么“获取”互斥锁的任务的优先级就有可能被提高。 拥有互斥锁的任务“继承”了 试图“获取”相同互斥锁任务的优先级, 这意味着必须始终“返回”互斥锁,否则 优先级较高的任务将永远无法获得互斥锁, 而优先级较低的任务将永远无法“取消继承”优先级。 用于实现互斥的互斥锁实例, 详见 xSemaphoreTake() 文档页面。
  互斥锁和二进制信号量都分配给了 SemaphoreHandle_t 类型的变量, 可在任何采用此类型参数的 API 函数中使用。
参数:
  xSemaphore 已创建信号量的句柄,应为 SemaphoreHandle_t 类型。
用法示例:

 SemaphoreHandle_t xSemaphore;
 void vATask( void * pvParameters )
 {
    
    
    // Semaphore不能在调用vSemaphoreCreateBinary()之前使用。
    // 这是一个宏,所以直接传入变量。
    vSemaphoreCreateBinary( xSemaphore );
    if( xSemaphore != NULL )
    {
    
    
        // 该semaphore被成功创建。
        // 现在可以使用该信号灯了。
    }
 }

二、计数型信号量

2.1、xSemaphoreCreateCounting()

API原型:

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
                                            UBaseType_t uxInitialCount);

提示: 在许多情况下, “任务通知”可以提供技术信号量的轻量级替代方案

  创建计数信号量 并返回句柄,通过该句柄可以引用新创建的信号量。 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,或保留未定义状态(此时,它默认 默认为1) ,才能使用此RTOS API 函数。

  每个计数信号量需要少量RAM ,用于保持 信号量的状态。 如果使用 xSemaphoreCreateCounting () 创建计数信号量 则会从 堆自动分配所需的。 RAM FreeRTOS 如果使用 xSemaphoreCreateCountingStatic() 创建计数信号量, 则 RAM会由应用程序编写器提供,这需要其他的 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面以了解更多信息。

  计数信号量通常用于两种情况:

  1. 盘点事件
    在此使用场景中, 每次发生事件(增加信号量计数值)时 ,事件处理程序都会“给出”信号量,处理程序任务 每次处理事件时,都会“获取”信号量 (递减信号量计数值)。 因此,计数值是 已发生的事件数量和 已处理的数量之间的差。 在这种情况下, 初始计数值最好为零。
    请注意, 使用直接到任务通知通常可以更有效地实现相同的功能。
  2. 资源管理
    在此使用方案中,计数值表示可用的资源数量 。 若要获取对资源的控制权,任务就必须首先获取 信号量-递减信号量计数值。 当计数值 达到零,则表示没有可用资源。 当任务结束使用 资源时,它会“返回”信号量-增加信号量计数 值。 在这种情况下,初始计数值最好 等于所述最大计数值,表明所有资源都是可用的。

参数:
  uxMaxCount 可以达到的最大计数值。 当信号量达到此值时,它不能再被“给定”。
  uxInitialCount 创建信号量时分配给信号量的计数值。
返回值:
  如果已成功创建信号量,则将返回该信号量的句柄 。 如果因为保留信号量所需的RAM 无法分配而无法创建信号量, 则会返回 NULL。
用法示例:

void vATask( void * pvParameters )
{
    
    
	SemaphoreHandle_t xSemaphore;
    /* 创建一个最大计数为10,初始计数为0的计数信号灯。
    初始计数为0。*/
    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
    if( xSemaphore != NULL )
    {
    
    
        /* 该semaphore被成功创建。*/
    }
}

2.2、xSemaphoreCreateCountingStatic()

API原型:

SemaphoreHandle_t xSemaphoreCreateCountingStatic(
                                 UBaseType_t uxMaxCount,
                                 UBaseType_t uxInitialCount
                                 StaticSemaphore_t *pxSemaphoreBuffer );

  创建计数信号量 并返回句柄,通过该句柄可以引用新创建的信号量。 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,或保留未定义状态(此时,它默认 默认为1) ,才能使用此RTOS API 函数。
  每个计数信号量需要少量RAM ,用于保持 信号量的状态。 如果使用 xSemaphoreCreateCounting () 创建计数信号量 则会从 堆自动分配所需的。 RAM FreeRTOS 如果使用 xSemaphoreCreateCountingStatic() 创建计数信号量, 则 RAM会由应用程序编写器提供,这需要其他的 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面以了解更多信息。
  计数信号量通常用于两种情况:

  1. 盘点事件
    在此使用场景中, 每次发生事件(增加信号量计数值)时 ,事件处理程序都会“给出”信号量,处理程序任务 每次处理事件时,都会“获取”信号量 (递减信号量计数值)。 因此,计数值是 已发生的事件数量和 已处理的数量之间的差。 在这种情况下, 初始计数值最好为零。
    请注意, 使用直接到任务通知通常可以更有效地实现相同的功能。
  2. 资源管理
    在此使用方案中,计数值表示可用的资源数量 。 若要获取对资源的控制权,任务就必须首先获取 信号量-递减信号量计数值。 当计数值 达到零,则表示没有可用资源。 当任务结束使用 资源时,它会“返回”信号量-增加信号量计数 值。 在这种情况下,初始计数值最好 等于所述最大计数值,表明所有资源都是可用的。

参数:
  uxMaxCount 可以达到的最大计数值。 当信号量达到此值时,它不能再被“给定”。
  uxInitialCount 创建信号量时分配给信号量的计数值。
  pxSemaphoreBuffer 必须指向 StaticSemaphore_t 类型的变量, 该变量然后用于保存信号量的数据结构体。
返回值:
  如果已成功创建信号量,则将返回该信号量的句柄 。 如果因为保留信号量所需的RAM 无法分配而无法创建信号量, 则会返回 NULL。
用法示例:

static StaticSemaphore_t xSemaphoreBuffer;

void vATask( void * pvParameters )
{
    
    
SemaphoreHandle_t xSemaphore;

    /*创建一个计数信号,最大计数为10,初始计数为0。
    该信号的数据结构被存储在xSemaphoreBuffer变量中。
    xSemaphoreBuffer变量中 - 不进行动态内存分配。*/
    xSemaphore = xSemaphoreCreateCountingStatic( 10, 0, &xSemaphoreBuffer );

    /* pxSemaphoreBuffer不是NULL,所以预计该信号将被创建。将被创建。*/
    configASSERT( xSemaphore );
}

三、互斥信号量

3.1、xSemaphoreCreateMutex()

API原型:

SemaphoreHandle_t xSemaphoreCreateMutex( void )

  创建互斥锁 ,并返回 一个该互斥锁可以引用的句柄。 中断服务例程中, 不能使用互斥锁。
  configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_MUTEXES 必须同时在 FreeRTOSConfig.h 中设置为 1, 方能确保 xSemaphoreCreateMutex() 可用。(可不定义 configSUPPORT_DYNAMIC_ALOGRATION, 在这种情况下,它将默认为 1。)
  每个互斥锁需要少量 RAM , 以此来保持互斥锁的状态。 如果使用 xSemaphoreCreateMutex () 创建互斥锁 则会从 RAM 堆FreeRTOS中自动分配所需的 。 如果使用 xSemaphoreCreateMutexStatic() 创建互斥锁, 那么应由应用程序编写人员提供 RAM, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。
  使用 xSemaphoreTake () 获取互斥锁,并给出互斥锁 使用 xSemaphoreGive() 。 xSemaphoreTakeRecursive() 和 xSemaphoreGiveRecursive() 仅可用于 使用 xSemaphoreCreateRecursiveMutex () 创建的互斥锁。
  互斥锁和二进制信号量极为相似,但 仍有一些细微差异: 互斥锁具有优先级继承机制, 但二进制信号量没有。 因此,二进制信号量是 实现同步的更好选择(任务之间或任务与中断之间), 也是实施简单互斥方面的更好选择。
  如果另一个更高优先级的任务尝试获取相同的互斥锁, 则将暂时提高“获取”互斥锁的任务的优先级。 拥有互斥锁的任务 “继承”试图“获取”相同 互斥锁的任务的优先级。 这意味着必须始终“归还”互斥锁,否则 优先级较高的任务将始终无法获得互斥锁,而优先级较低 的始终无法“取消继承”优先级。
  用于实现互斥的互斥锁实例, 详见 xSemaphoreTake() 文档页面所示。
  一旦获得二进制信号量,则无需要返回 因此,任务同步可以通过一个任务/中断持续释放信号量 而另外一个持续获得信号量来实现。 相关演示请参阅 xSemaphoreGiveFromISR() 文档页面上的示例代码。 请注意,可以使用直接任务通知以更有效的方式 实现相同功能。
  对互斥锁和二进制信号量的句柄都分配给 SemaphoreHandle_t 类型的变量,并且可以在任何接受该类型参数的任务级别(与中断 安全相反)API 函数中使用。
返回值:
  如果已成功创建互斥锁类型信号量,则返回创建的 互斥锁的句柄。 如果 由于 创建递归互斥锁, 则返回 NULL。
用法示例:

SemaphoreHandle_t xSemaphore;
void vATask( void * pvParameters )
{
    
    
   /* 创建一个mutex类型的semaphore。*/
   xSemaphore = xSemaphoreCreateMutex();
   if( xSemaphore != NULL )
   {
    
    
       /* 该信号灯已成功创建,可以使用。*/
   }
}

3.2、xSemaphoreCreateMutexStatic()

API原型:

SemaphoreHandle_t xSemaphoreCreateMutexStatic(
                            StaticSemaphore_t *pxMutexBuffer );

  创建互斥锁 ,并返回 一个该互斥锁可以引用的句柄。 中断服务例程中, 不能使用互斥锁。
  configSUPPORT_STATIC_ALLOCATION 和 configUSE_mutexes 必须同时在 FreeRTOSConfig.h 中设置为 1, xSemaphoreCreateMutexStatic () 才可用。
  每个互斥锁需要少量 RAM , 以此来保持互斥锁的状态。 如果使用 xSemaphoreCreateMutex () 创建互斥锁 则会从 RAM 堆FreeRTOS中自动分配所需的 。 如果使用 xSemaphoreCreateMutexStatic () 创建互斥锁 则 RAM 由应用程序编写器提供,这需要用到一个附加参数, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。
  使用 xSemaphoreTake () 获取互斥锁,并给出互斥锁 使用 xSemaphoreGive() 。 xSemaphoreTakeRecursive() 和 xSemaphoreGiveRecursive() 仅可用于 使用 xSemaphoreCreateResursiveMutex () 创建的互斥体。
  互斥锁和二进制信号量极为相似,但 仍有一些细微差异: 互斥锁具有优先级继承机制, 但二进制信号量没有。 因此,二进制信号量是 实现同步的更好选择(任务之间或任务与中断之间), 也是实施简单互斥方面的更好选择。
  如果另一个更高优先级的任务尝试获取相同的互斥锁, 则将暂时提高“获取”互斥锁的任务的优先级。 拥有互斥锁的任务 “继承”试图“获取”相同 互斥锁的任务的优先级。 这意味着必须始终“归还”互斥锁,否则 优先级较高的任务将始终无法获得互斥锁,而优先级较低 的始终无法“取消继承”优先级。
  用于实现互斥的互斥锁实例, 详见 xSemaphoreTake() 文档页面所示。
  一旦获得二进制信号量,则无需要返回 因此,任务同步可以通过一个任务/中断持续释放信号量 而另外一个持续获得信号量来实现。 相关演示请参阅 xSemaphoreGiveFromISR() 文档页面上的示例代码。 请注意,可以使用直接任务通知以更有效的方式 实现相同功能。
  对互斥锁和二进制信号量的句柄都分配给 SemaphoreHandle_t 类型的变量,并且可以在任何接受该类型参数的任务级别(与中断 安全相反)API 函数中使用。
参数:
  pxMutexBuffer 必须指向 StaticSemaphore_t 类型的变量, 该变量将用于保存互斥锁型信号量的状态。
返回值:
  如果已成功创建互斥锁类型信号量,则返回创建的 互斥锁的句柄。 如果 因为 pxMutexBuffer 是 NULL 而未创建互斥锁,那么返回 NULL。
用法示例:

SemaphoreHandle_t xSemaphore = NULL;
StaticSemaphore_t xMutexBuffer;

void vATask( void * pvParameters )
{
    
    
	/* 创建一个mutex semaphore,不使用任何动态内存分配。
	分配。 mutex的数据结构将被保存到xMutexBuffer变量。*/
	xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
	
	/* pxMutexBuffer不是NULL,所以预计句柄不会是NULL。句柄不会是NULL。*/
	configASSERT( xSemaphore );
}

四、递归信号量

4.1、xSemaphoreCreateRecursiveMutex()

API原型:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

  创建一个递归互斥锁 , 并返回一个互斥锁 句柄。 不能在中断服务程序中使用递归互斥锁。 configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_mutexes 都必须在 FreeRTOSConfig.h 中定义为 1, 才能使用 xSemaphoreCreateRecursiveMutex() 函数(configSUPPORT_DYNAMIC_ALOUTION 也可以不定义,此时将默认定义为 1)。
  每个递归互斥锁都需要少量RAM用于保持 递归互斥锁的状态。 如果使用 xSemaphoreCreateRecursiveMutex() 创建互斥锁, 则会从 RAM堆FreeRTOS栈中自动分配所需的。 如果一个递归互斥锁是使用 xSemaphoreCreateRecursiveMutexStatic() 创建的, 那么RAM由应用程序编写者提供,这需要用到一个附加参数, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。
  分别使用 xSemaphoreTakeRecursive()来获取(持有), 和 xSemaphoreGiveRecursive() API函数释放。 不得使用 xSemaphoreTake() 和 xSemaphoreGive()。
  xSemaphoreCreateMutex()和 xSemaphoreCreateMutexStatic()用于创建非递归互斥锁。 非递归互斥锁只能被一个任务 获取一次, 如果同一个任务想再次获取则会失败, 因为当任务第一次释放互斥锁时,互斥锁就一直 处于释放状态。
  与非递归互斥锁相反,递归互斥锁可以被同一个任务获取很多次, 获取多少次就需要释放多少次, 此时才会返回递归互斥锁。
  与非递归互斥锁一样,递归互斥锁采用优先级继承 算法。 如果另一个优先级更高的任务试图获得相同的互斥锁, 则将暂时提高“获取”互斥锁的任务的优先级。 拥有互斥锁的任务 “继承”试图“获取”相同 互斥锁的任务的优先级。 这意味着必须始终“归还”互斥锁,否则 优先级较高的任务将始终无法获得互斥锁,而优先级较低 的任务将永远无法“取消继承”优先级。
返回值:
如果成功创建了递归互斥锁,那么将返回 创建的互斥锁的句柄。 如果因为保存互斥锁所需内存不能分配 从而没有 创建递归互斥锁, 则返回 NULL。
用法示例:

SemaphoreHandle_t xMutex;

void vATask( void * pvParameters )
{
    
    
	// 创建一个递归的mutex。
	xMutex = xSemaphoreCreateRecursiveMutex();
	
	if( xMutex != NULL )
	{
    
    
		/* 递归突发事件已成功创建,现在可以使用。
		现在可以使用了。*/
	}
}

4.2、xSemaphoreCreateRecursiveMutexStatic()

API原型:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic(
                              StaticSemaphore_t *pxMutexBuffer )

  创建一个递归互斥锁 , 并返回一个互斥锁 句柄。 不能在中断服务程序中使用递归互斥锁。 configUSE_RECURSIVE_MUTEXES 和 configSUPPORT_STATIC_ALLOCATION 都必须在 FreeRTOSConfig.h 中设置为 1, xSemaphoreCreateRecursiveMutexStatic() 才可用。
  每个递归互斥锁都需要少量 RAM 递归互斥锁的状态。 如果使用 xSemaphoreCreateRecursiveMutex() 创建互斥锁, 则会从 RAM堆FreeRTOS栈中自动分配所需的。 如果一个递归互斥锁是使用 xSemaphoreCreateRecursiveMutexStatic() 创建的, 那么RAM由应用程序编写者提供,这需要用到一个附加参数, 但允许在编译时静态分配 RAM 。 请参阅静态分配与 动态分配页面了解更多信息。
  分别使用 xSemaphoreTakeRecursive()来获取(持有), 和 xSemaphoreGiveRecursive() API函数释放。 不得使用 xSemaphoreTake() 和 xSemaphoreGive()。
  xSemaphoreCreateMutex()和 xSemaphoreCreateMutexStatic()用于创建非递归互斥锁。 非递归互斥锁只能被一个任务 获取一次, 如果同一个任务想再次获取则会失败, 因为当任务第一次释放互斥锁时,互斥锁就一直 处于释放状态。
  与非递归互斥锁相反,递归互斥锁可以被同一个任务获取很多次, 获取多少次就需要释放多少次, 此时才会返回递归互斥锁。
  与非递归互斥锁一样,递归互斥锁采用优先级继承 算法。 如果另一个优先级更高的任务试图获得相同的互斥锁, 则将暂时提高“获取”互斥锁的任务的优先级。 拥有互斥锁的任务 “继承”试图“获取”相同 互斥锁的任务的优先级。 这意味着必须始终“归还”互斥锁,否则 优先级较高的任务将始终无法获得互斥锁,而优先级较低 的始终无法“取消继承”优先级。
参数:
  pxMutexBuffer 必须指向 StaticSemaphore_t 类型的变量, 该变量将用于保存互斥锁型信号量的状态。
返回值:
  如果已成功创建递归互斥锁,则返回创建的 创建的互斥锁的句柄。 如果由于 pxMutexBuffer 为 NULL 而导致 递归互斥锁未创建,则返回 NULL。
用法示例:

SemaphoreHandle_t xSemaphore = NULL;
StaticSemaphore_t xMutexBuffer;

void vATask( void * pvParameters )
{
    
    
	/* 创建一个递归的mutex信号,不使用任何动态内存分配。
	内存分配。 该mutex的数据结构将被保存到
	xMutexBuffer变量。*/
	xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
	
	/* pxMutexBuffer不是NULL,所以预计该句柄不会是NULL。
	句柄不会是NULL。*/
	configASSERT( xSemaphore );
	
	/* 其余的任务代码都在这里。*/
}

五、删除信号量 vSemaphoreDelete()

API原型:

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )

  删除semaphore,包括mutex类型的semaphores和递归semaphores。
  不要删除在其上有任务阻塞的信号(处于阻塞状态的任务,等待信号变得可用)。
参数:
xSemaphore 被删除的信号灯的句柄。

六、获取互斥锁持有者 xSemaphoreGetMutexHolder()

API原型:

TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex )

  必须在 FreeRTOSConfig.h 中将 INCLUDE_xSemaphoreGetMutexHolder 设置为 1, 此函数才可用。
  返回保存函数参数指定的 MUTEX 的任务的句柄(若有)。
  xSemaphoreGetMutexHolder () 可以可靠地用于确定调用任务是否 是互斥锁持有者,但如果由调用任务之外的任务持有互斥锁,则无法可靠地使用 xSemaphoreGetMutexHolder () 。 这是因为 MUTEX 支架可能会 在调用该函数的调用任务与测试该函数返回值之间 更改。
  FreeRTOSConfig.h中的configUSE_MUTEXES必须设置为1 ,才能使用xSemaphoreGetMutexHolder ()。
参数:
  xMutex 正在查询的互斥体的句柄。
返回值:
  保存 xMutex 参数指定的 MUTEX 的任务的句柄。 如果在 xMutex 参数中传递的信号量不是 MUTEX 类型的信号量,或者如果 MUTEX 可用,未被任何任务持有,则返回 NULL。

七、获取信号量计数 uxSemaphoreGetCount()

API原型:

UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );

  返回信号量计数。
参数:
  xSemaphore 正在查询的信号量的句柄。
返回值:
  如果信号量是计数信号量,则返回信号量的当前计数值 。 如果信号量是二进制信号量, 则当信号量可用时,返回 1,当信号量不可用时, 返回 0。

八、获取信号量 xSemaphoreTake()

API原型:

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );

  用于获取信号量的宏。之前必须通过调用xSemaphoreCreateBinary()、xSemaphoreCreateMutex()或xSemaphoreCreateCounting()来创建信号量。
  Isr调用调用调用宏使用使用使用使用使用使用使用使用使用使用使用使用使用使用使用使用使用使用
参数:
  xSemaphore 正在取得的信号数量的句柄——在创建信号数量时取得。
  xTicksToWait 等候信号量变为可用的时间(以滴答为单位)。宏端口TICK_PERIOD_MS 可用于将其转换为实时。
  如果INCLUDE_vTaskSuspend设置为“1”,则将延迟时间指定为portMAX_DELAY会导致任务无限期延迟延迟(没有超时)。
返回值:
  如果获得信号数量,则返回pdTRUE;如果xTicksToWait过期,信号数量不可使用,则返回pdFALSE。
用法示例:

SemaphoreHandle_t xSemaphore = NULL;
/* 一个创建semaphore的任务。*/
void vATask( void * pvParameters )
{
    
    
    /* 创建semaphore来保护一个共享资源。 由于我们在使用
    我们创建了一个互斥的信号灯,而不是一个二进制信号灯。
    而不是一个二进制信号。*/
    xSemaphore = xSemaphoreCreateMutex();
}
/* 一个使用semaphore的任务。*/
void vAnotherTask( void * pvParameters )
{
    
    
    /* ... 做其他事情。*/
    if( xSemaphore != NULL )
    {
    
    
        /* 看看我们是否能获得信号机。 如果semaphore不在
        可用,则等待10次,看它是否变得自由。*/
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
        {
    
    
            /* 我们能够获得信号链,现在可以访问共享资源。
            共享资源。*/

            /* ... */

            /* 我们已经完成了对共享资源的访问。 释放
            semaphore。*/
            xSemaphoreGive( xSemaphore );
        }
        else
        {
    
    
            /* 我们无法获得信号,因此无法安全访问
            共享资源的安全。*/
        }
    }
}

九、中断中获取信号量 xSemaphoreTakeFromISR()

API原型:

BaseType_t xSemaphoreTakeFromISR(
        SemaphoreHandle_t xSemaphore,
        signed BaseType_t *pxHigherPriorityTaskWoken)

  可从 ISR 调用的 xSemaphoreTake() 版本。 与 xSemaphoreTake() 不同,xSemaphoreTakeFromISR() 不允许 指定阻塞时间。
参数:
  xSemaphore 信号量被“获取”。 信号量由 SemaphoreHandle_t 类型的变量引用,必须在使用之前显式创建。
  pxHigherPriorityTaskWoken 信号量可能(尽管不太可能,并且取决于信号量类型)阻塞一个或多个任务,等待给出信号量。 调用 xSemaphoreTakeFromISR() 将使被阻塞的任务等待信号量离开已阻塞状态。 如果调用 API 函数导致任务离开已阻塞状态,并且未阻塞的任务的优先级等于或高于当前正在执行的任务(被中断的任务),则在内部,API 函数会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。
  如果 xSemaphoreTakeFromISR() 将 *pxHigherPriorityTaskWoken 设置为 pdTRUE,则应在退出中断之前执行上下文切换。 这将确保中断直接返回到最高优先级的就绪状态任务。 该机制与 xQueueReceiveFromISR() 函数中使用的机制相同,读者可以参考 xQueueReceiveFromISR() 文档以获得进一步解释。
  从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回值:
  如果信号量已成功获取,则返回 pdTRUE。 如果信号量因不可用而未成功获取,则返回 pdFALSE。

十、获取互斥锁型信号量 xSemaphoreTakeRecursive()

API原型:

xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,
                         TickType_t xTicksToWait );

  递归地获得或“获取”一个互斥锁型信号量的宏。 此互斥锁必须已经事先通过调用 xSemaphoreCreateRecursiveMutex() 完成创建;
  必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1, 此宏才可用。
  不得在使用 xSemaphoreCreateMutex() 创建的互斥锁上使用此宏。
  所有者可以反复“获取”递归使用的互斥锁。在所有者 为每个成功的“获取”请求调用 xSemaphoreGiveRecursive() 之前,该互斥锁不会再次变得可用。 例如, 如果一个任务成功地“获取”了同一个互斥锁 5 次, 那么任何其他任务将无法使用这个互斥锁, 直到任务也正好“给”回了这个互斥锁 5 次为止。
参数:
  xMutex 正在获得的互斥锁的句柄。 这是由 xSemaphoreCreateRecursiveMutex() 返回的句柄。
  xTicksToWait 等待信号量变为可用的时间(以滴答为单位)。 宏 portTICK_PERIOD_MS 可用于将其转换为实时。 可以用一个为零的阻塞时间来轮询信号量。 如果任务已有信号量,则无论 xTicksToWait 的值是多少,xSemaphoreTakeRecursive() 都将立即返回。
返回值:
  如果获得信号量,则返回 pdTRUE。 如果 xTicksToWait 过期,信号量不可用,则返回 pdFALSE。
用法示例:

 SemaphoreHandle_t xMutex = NULL;

 // 一个创建mutex的任务。
 void vATask( void * pvParameters )
 {
    
    
    // 创建mutex来保护一个共享资源。
    xMutex = xSemaphoreCreateRecursiveMutex();
 }

 // 一个使用该mutex的任务。
 void vAnotherTask( void * pvParameters )
 {
    
    
    // ... 做其他事情。

    if( xMutex != NULL )
    {
    
    
        // 看看我们是否能获得这个mutex。 如果mutex不可用
        // 等待10次,看它是否变得自由。   
        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
    
    
            // 我们能够获得mutex,现在可以访问
            // 共享资源。

            // ...
            // 由于某些原因,代码的性质决定了进一步调用 
            // xSemaphoreTakeRecursive()是在同一个mutex上进行的。 在真正的
            // 代码中,这些调用不会是连续的,因为这将使
            //没有意义。 相反,这些调用可能会被埋在
            // 一个更复杂的调用结构。
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );

            // 该mutex现在已经被 "拿 "了三次,所以不会被 
            // 所以直到它被送回给另一个任务时,才可以使用。
            // 三次。 同样的,真正的代码也不可能有
            // 这些调用的顺序,而是埋在一个更复杂的
            // 呼叫结构中。 这只是为了说明问题。
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );

            // 现在这个mutex可以被其他任务占用了。
        }
        else
        {
    
    
            // 我们无法获得mutex,因此无法安全访问
            // 共享资源的安全。
        }
    }
 }

十一、释放信号量 xSemaphoreGive()

API原型:

xSemaphoreGive( SemaphoreHandle_t xSemaphore );

  用于释放信号量的宏。该信号量此前必须 通过调用 xSemaphoreCreateBinary()、xSemaphoreCreateMutex() 或 xSemaphoreCreateCounting() 创建。
  不得在 ISR 中使用此宏。请参阅 xSemaphoreGiveFromISR (),了解可以从 ISR 中使用的替代方案。
  此宏不得用于由 xSemaphoreCreateRecursiveMutex() 创建的信号量。
参数:
  xSemaphore 要释放的信号量的句柄。这是信号量创建时返回的句柄。
返回值:
  如果信号量释放成功,则返回 pdTRUE;如果发生错误,则返回 pdFALSE。信号量的实现基于队列。发布消息时,如果队列上没有空间,那么可能会发生错误,这表明最初未能正确获取信号量。
用法示例:

 SemaphoreHandle_t xSemaphore = NULL;
 void vATask( void * pvParameters )
 {
    
    
    // 创建semaphore来保护一个共享资源。 由于我们在使用
    // 用于互斥的信号,我们创建一个互斥信号。
    // 而不是一个二进制信号。
    xSemaphore = xSemaphoreCreateMutex();

    if( xSemaphore != NULL )
    {
    
    
        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
        {
    
    
            // 我们希望这个调用会失败,因为我们不能在没有先 "给 "的情况下给
            // 一个semaphore而不先 "接受 "它!
        
        // 获取信号灯--如果信号灯不是立即可用的,则不要阻塞。
        // 立即可用。
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
        {
    
    
            // 我们现在有了semaphore,可以访问共享资源了。
            // ...
            // 我们已经完成了对共享资源的访问,所以可以释放这个
            // semaphore。
            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
            {
    
    
                // 我们不会指望这个调用会失败,因为我们必须已经
                //获得了semaphore才会到这里。
            }
        }
    }
 }

十二、释放互斥锁型信号量 xSemaphoreGiveRecursive()

API原型:

xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )

  递归地释放或“给出”一个互斥锁型信号量的宏。 此互斥锁必须已经事先通过调用 xSemaphoreCreateRecursiveMutex() 完成创建;
  必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1, 此宏才可用。
  不得在使用 xSemaphoreCreateMutex() 创建的互斥锁上使用此宏。
  所有者可以反复“获取”递归使用的互斥锁。在所有者 为每个成功的“获取”请求调用 xSemaphoreGiveRecursive() 之前,该互斥锁不会再次变得可用。 例如, 如果一个任务成功地“获取”了同一个互斥锁 5 次, 那么任何其他任务将无法使用这个互斥锁, 直到任务也正好“给”回了这个互斥锁 5 次为止。
参数:
  xMutex 正在释放或“给出”的互斥锁的句柄。 这是由 xSemaphoreCreateRecursiveMutex() 返回的句柄。
返回值:
  如果成功给出信号量,则返回 pdTRUE。

十三、中断中释放信号量 xSemaphoreGiveFromISR()

API原型:

xSemaphoreGiveFromISR(
				        SemaphoreHandle_t xSemaphore,
				        signed BaseType_t *pxHigherPriorityTaskWoken)

  用于释放信号量的宏。释放前信号量必须已经 通过调用 xSemaphoreCreateBinary() 或 xSemaphoreCreateCounting() 创建。
  互斥锁型信号量(那些调用 xSemaphoreCreateMutex() 创建的信号量) 不得与此宏一起使用。
  此宏可在 ISR 中使用。
参数:
  xSemaphore 要释放的信号量的句柄。这是创建信号量时返回的句柄。
  pxHigherPriorityTaskWoken 如果释放信号量导致任务解除阻塞,并且解除阻塞的任务的优先级高于当前运行的任务,则 xSemaphoreGiveFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xSemaphoreGiveFromISR() 将此值设置为 pdTRUE ,则应在退出中断之前请求上下文切换。
  从 FreeRTOSV7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL。
返回值:
  如果成功给出信号量,则返回 pdTRUE,否则 errQUEUE_FULL。
用法示例:
  请注意, 使用直达任务通知代替信号量, 通常可以更有效地实现如下所示的功能。

#define LONG_TIME 0xffff
#define TICKS_TO_WAIT 10

SemaphoreHandle_t xSemaphore = NULL;

/* 重复性任务。*/
void vATask( void * pvParameters )
{
    
    
    /* 我们使用semaphore进行同步,所以我们创建一个二进制的
    semaphore,而不是一个mutex。 我们必须确保中断
    在它被创建之前不要试图使用该信号! */
    xSemaphore = xSemaphoreCreateBinary();

    for( ; ; )
    {
    
    
        /* 我们希望这个任务每隔10个定时器的时间就运行一次。 这个semaphore
        是在这个任务开始之前创建的。

        阻塞等待信号灯的可用。*/
        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
        {
    
    
            /* 是时候执行了。*/

            ...

            /* 我们已经完成了我们的任务。 返回到循环的顶部,在那里
            返回到循环的顶部,在那里我们将封锁信号,直到再次执行的时间
            再次执行。 注意当使用信号灯与一个
            ISR时,没有必要将信号灯 "送 "回去。
            归还。*/
        }
    }
}

/* 定时器ISR */
void vTimerISR( void * pvParameters )
{
    
    
	static unsigned char ucLocalTickCount = 0;
	static signed BaseType_t xHigherPriorityTaskWoken;

    /* 发生了一个定时器刻度。*/

    ... 做其他的时间功能。

    /* 现在是vATask()运行的时间吗?*/
    xHigherPriorityTaskWoken = pdFALSE;
    ucLocalTickCount++;
    if( ucLocalTickCount >= TICKS_TO_WAIT )
    {
    
    
        /* 通过释放信号来解除对任务的封锁。*/
        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );

        /* 重置计数,以便我们在10个刻度的时间内再次释放semaphore。
        时间。*/
        ucLocalTickCount = 0;
    }

    /* 如果xHigherPriorityTaskWoken被设置为 "true",我们就应该屈服。
    我们应该屈服。 这里实际使用的宏是
    端口特定的。*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

十四、使用示例

二值信号量

/**
 * @file 12_BinarySemaphore.c
 * @author WSP
 * @brief 二值信号量一般用作任务与任务的同步或者任务与中断的同步
 * @version 0.1
 * @date 2022-10-18
 * @note 
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_BinarySemaphore";

// #define USER_TASK_SEMAPHORE_SYNCHRONIZATION
/**
 * @brief   Task_One 使用任务信号量同步
 * @param   arg
 * @return  NULL
*/
#ifdef USER_TASK_SEMAPHORE_SYNCHRONIZATION
void Task_One(void *arg)
{
    
    
    SemaphoreHandle_t SemaphoreHandle = (SemaphoreHandle_t)arg;
    while (1){
    
    
        vTaskDelay(5000/portTICK_PERIOD_MS);
        ESP_LOGI(TAG,"Task_One Semaphore Give");
        xSemaphoreGive(SemaphoreHandle);        // 给信号量 释放信号量
    }
}
#else
static void timer_Transfer_Semaphore_callback(void* arg)
{
    
    
    SemaphoreHandle_t SemaphoreHandle = (SemaphoreHandle_t)arg;
    xSemaphoreGiveFromISR(SemaphoreHandle,NULL);
    ESP_LOGI(TAG,"Timer Transfer Semaphore Give");
}
#endif
/**
 * @brief   Task_Two
 * @param   arg
 * @return  NULL
*/
void Task_Two(void *arg)
{
    
    
    SemaphoreHandle_t SemaphoreHandle = (SemaphoreHandle_t)arg;
    // 延时等待这个时候任务二创建就被挂起,任务一先进行,任务一一旦获取信号量以后,
    // 就一直占用信号量直到释放信号量,导致优先级反转,二值信号量就会出现优先级反转这种情况。
    vTaskDelay(2000/portTICK_PERIOD_MS);   
    bool LED_State = 0; 
    BaseType_t SemaphoreState;   
    while (1){
    
    
        SemaphoreState = xSemaphoreTake(SemaphoreHandle,portMAX_DELAY);   // 拿到信号量 获取信号量
        if(SemaphoreState == pdTRUE){
    
    
            ESP_LOGI(TAG,"Task_Two Take %s",LED_State == false ? "LED OFF" :  "LED ON");
            LED_State = !LED_State;
        }else{
    
    
            ESP_LOGI(TAG,"Task_Two Take Semaphore fail");
        }
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void BinarySemaphore_Init(void)
{
    
    
    SemaphoreHandle_t SemaphoreHandle = NULL;
    SemaphoreHandle = xSemaphoreCreateBinary(); // 创建信号量
    xSemaphoreGive(SemaphoreHandle);            // 创建以后是空的状态,在获取之前要释放信号量
#ifdef USER_TASK_SEMAPHORE_SYNCHRONIZATION
    // 创建任务一
    xTaskCreate(Task_One,                       // 任务函数
                "Task_One",                     // 任务名
                2048,                           // 任务堆载
                (void *)SemaphoreHandle,        // 任务参数
                1,                              // 任务优先级
                NULL);                          // 任务句柄
#else
    const esp_timer_create_args_t Semaphore_timer_args = {
    
    
            .callback = &timer_Transfer_Semaphore_callback, // 定时器回调函数
            .name = "Transfer Timer",                       // 定时器名称
            .arg = (void *)SemaphoreHandle,                 // 定时器传入参数
    };
    esp_timer_handle_t Semaphore_timer;                     // 创建一个定时器变量
    //创建一个定时器
    ESP_ERROR_CHECK(esp_timer_create(&Semaphore_timer_args, &Semaphore_timer)); // 创建定时器
    esp_timer_start_periodic(Semaphore_timer,5000000);      // 开始定时器
#endif
    // 创建任务二
    xTaskCreate(Task_Two,                       // 任务函数       
                "Task_Two",                     // 任务名       
                2048,                           // 任务堆载       
                (void *)SemaphoreHandle,        // 任务参数       
                2,                              // 任务优先级
                NULL);                          // 任务句柄
}

计数型信号量

/**
 * @file 13_CountSemaphore.c
 * @author WSP
 * @brief 计数型信号量,计数型信号量一般使用汽车位来描述会更好理解些
 * @version 0.1
 * @date 2022-10-18
 * @copyright Copyright (c) 2022
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_CountSemaphore";
int PublicData_Count = 0;
SemaphoreHandle_t SemaphoreHandle;
/**
 * @brief   Task_Count_Car_Value
 * @param   arg
 * @return  NULL
*/
void Task_Count_Car_Value(void *arg)
{
    
    
    int SemaphoreCountValue = 0;
    BaseType_t Result;
    while (1){
    
    
        SemaphoreCountValue = uxSemaphoreGetCount(SemaphoreHandle);     // 获取计数型信号量
        Result = xSemaphoreTake(SemaphoreHandle,0);                     // 获取信号量 来了一辆汽车(占用汽车位)
        if (Result == pdPASS)
            ESP_LOGI(TAG,"Current parking value: %d",SemaphoreCountValue);  // 当前停车位数量,6s离开一辆汽车,3秒来一辆汽车
        vTaskDelay(3000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_Count_Car_Left
 * @param   arg
 * @return  NULL
*/
void Task_Count_Car_Left(void *arg)
{
    
    
    while (1){
    
    
        vTaskDelay(6000/portTICK_PERIOD_MS);
        xSemaphoreGive(SemaphoreHandle);                                // 释放信号量(让出停车位)  汽车离开停车场
        ESP_LOGI(TAG,"Car left the parking");
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void CountSemaphore_Init(void)
{
    
    
    SemaphoreHandle = xSemaphoreCreateCounting(100,99);  // 创建信号量 计数值最大100 默认值99(停车场目前最大容纳100辆车目前已经停了99辆了)
    // 创建任务一
    xTaskCreate(Task_Count_Car_Value,   // 任务函数
                "CountCarValue",        // 任务名
                2048,                   // 任务堆载
                NULL,                   // 任务参数
                1,                      // 任务优先级
                NULL);                  // 任务句柄
    // 创建任务二
    xTaskCreate(Task_Count_Car_Left,    // 任务函数
                "CarLeft",              // 任务名
                2048,                   // 任务堆载
                NULL,                   // 任务参数
                2,                      // 任务优先级
                NULL);                  // 任务句柄
}

互斥信号量

/**
 * @file 14_MutexesSemaphore.c
 * @author WSP
 * @brief 互斥信号量 使用互斥信号量对共同访问资源保护 
 * @version 0.1
 * @date 2022-10-19
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_MutexesSemaphore";
int PublicData_Count = 0;
QueueHandle_t SemaphoreMutexHandle = NULL;
/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_One(void *arg)
{
    
    
    BaseType_t QueueResult;
    while (1){
    
    
        QueueResult = xSemaphoreTake(SemaphoreMutexHandle,portMAX_DELAY);
        if(QueueResult == pdPASS){
    
    
            for (int i = 0; i < 5; i++)
            {
    
    
                vTaskDelay(1000/portTICK_PERIOD_MS);
                ESP_LOGI(TAG," Task_One xSemaphoreTake PublicData_Count value:%d",PublicData_Count);
                PublicData_Count = i;
            }
            PublicData_Count = 0;
        }
        xSemaphoreGive(SemaphoreMutexHandle);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_Two(void *arg)
{
    
    
    BaseType_t QueueResult;
    while (1){
    
    
        QueueResult = xSemaphoreTake(SemaphoreMutexHandle,portMAX_DELAY);
        if(QueueResult == pdPASS){
    
    
            for (int i = 0; i < 5; i++)
            {
    
    
                vTaskDelay(1000/portTICK_PERIOD_MS);
                ESP_LOGI(TAG," Task_Two xSemaphoreTake PublicData_Count value:%d",PublicData_Count);
                PublicData_Count = i;
            }
            PublicData_Count = 0;
        }
        xSemaphoreGive(SemaphoreMutexHandle);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void MutexesSemaphore_Init(void)
{
    
    
    SemaphoreMutexHandle = xSemaphoreCreateMutex();  // 创建信号量
    if(SemaphoreMutexHandle != NULL){
    
    
        // 创建任务一
        xTaskCreate(Task_One,
                    "Task_One",
                    2048,
                    NULL,
                    1,
                    NULL);
        // 创建任务二
        xTaskCreate(Task_Two,
                    "Task_Two",
                    2048,
                    NULL,
                    2,
                    NULL);
    }
}

斥递归信号量

/**
 * @file 15_RecursiveMutexesSemaphore.c
 * @author WSP
 * @brief 互斥递归信号量 两把钥匙锁一个箱子
 * @version 0.1
 * @date 2022-10-20
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_RecursiveMutexesSemaphore";
int PublicData_Count = 0;
/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_One(void *arg)
{
    
    
    BaseType_t QueueResult;
    QueueHandle_t SemaphoreRecursiveMutexHandle = (QueueHandle_t)arg;
    while (1){
    
    
        QueueResult = xSemaphoreTakeRecursive(SemaphoreRecursiveMutexHandle,portMAX_DELAY);
        if(QueueResult == pdPASS){
    
    
            vTaskDelay(1000/portTICK_PERIOD_MS);
            PublicData_Count ++;
            ESP_LOGI(TAG," Task_One xSemaphoreTake First:%d",PublicData_Count);
            QueueResult = xSemaphoreTakeRecursive(SemaphoreRecursiveMutexHandle,portMAX_DELAY);
            if(QueueResult == pdPASS){
    
    
                vTaskDelay(1000/portTICK_PERIOD_MS);
                PublicData_Count ++;
                ESP_LOGI(TAG," Task_One xSemaphoreTake Three:%d",PublicData_Count);
            }
            xSemaphoreGiveRecursive(SemaphoreRecursiveMutexHandle);
        }
        xSemaphoreGiveRecursive(SemaphoreRecursiveMutexHandle);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_Two(void *arg)
{
    
    
    BaseType_t QueueResult; 
    QueueHandle_t SemaphoreRecursiveMutexHandle = (QueueHandle_t)arg;
    while (1){
    
    
        QueueResult = xSemaphoreTakeRecursive(SemaphoreRecursiveMutexHandle,portMAX_DELAY);
        if(QueueResult == pdPASS){
    
    
            vTaskDelay(1000/portTICK_PERIOD_MS);
            PublicData_Count ++;
            ESP_LOGI(TAG," Task_Two xSemaphoreTake PublicData_Count value:%d",PublicData_Count);
            PublicData_Count  = 0;
        }
        xSemaphoreGiveRecursive(SemaphoreRecursiveMutexHandle);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void RecursiveMutexesSemaphore_Init(void)
{
    
    
    QueueHandle_t SemaphoreRecursiveMutexHandle = NULL;
    SemaphoreRecursiveMutexHandle = xSemaphoreCreateRecursiveMutex();  // 创建信号量
    if(SemaphoreRecursiveMutexHandle != NULL){
    
    
        // 创建任务一
        xTaskCreate(Task_One,
                    "Task_One",
                    2048,
                    (void *)SemaphoreRecursiveMutexHandle,
                    1,
                    NULL);
        // 创建任务二
        xTaskCreate(Task_Two,
                    "Task_Two",
                    2048,
                    (void *)SemaphoreRecursiveMutexHandle,
                    2,
                    NULL);
    }
}

猜你喜欢

转载自blog.csdn.net/believe666/article/details/127205600