打印
[研电赛技术支持]

FreeRTOS 之二值信号量、计数信号量、互斥信号量、递归互斥信号量

[复制链接]
731|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-4-9 08:41 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
无论那种操作系统,信号量都是其中重要的一部分。信号量一般用来进行资源管理和任务同步,FreeRTOS 的信号量包括二值信号量、计数信号量、互斥信号量(简称互斥量)和递归互斥信号量(简称递归互斥量),其中互斥量和递归互斥量可以看成特殊的二值信号量。

简介
  信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或者临界资源的互斥访问。信号量可以简单的理解为一个计数器,这个计数器被合理使用,能够达到对临界资源预定的目的(互斥与同步)。

信号量概念是由荷兰计算机科学家 Edsger Dijkstra 于 1962 ~ 1963 年发明的

  FreeRTOS 中各种信号量的 API 函数实际上都是定义在 ./include/semphr.h 文件中宏,它们都是使用 FreeRTOS 的队列机制来实现的(所有信号量 API 都是对相应队列 API 的封装)。信号量各 API 用到的一些宏值如下所示:



  其中,二进制信号量、计数信号量和互斥信号量的创建 API 函数是独立的,但是获取和释放 API 函数都是相同的;递归互斥信号量的创建、获取和释放 API 函数都是独立的,下面详细学习一下!

无论哪一种信号量,都是使用 SemaphoreHandle_t 这个类型定义的句柄来指示。

二值信号量
  二值信号量通常用于各种同步操作。FreeRTOS 中的二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的。任务和中断使用这个特殊队列不需要在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。



  在代码实现上,二进制信号量(队列)实际上没有任何数据部分,只有一个 sizeof(Queue_t)。实际上,二值信号量的释放和获取都是通过操作队列结构体成员 uxMessageWaiting 来实现的。下面来详细学习一下!

创建
  信号量的创建就是分配信号量内存以及初始化信号量的值。FreeRTOS 提供了如下三个 API 用来创建二值信号量。无论哪个 API,其创建的出来的信号量其实都是一个长度为 1 的队列。

                函数                                                                   描述
vSemaphoreCreateBinary()                         动态创建二值信号量,老版 FreeRTOS 中使用的创建二值信号量的函数。
xSemaphoreCreateBinary()                          动态创建二值信号量,新版 FreeRTOS 中使用此函数创建二值信号量。
xSemaphoreCreateBinaryStatic()                  静态创建二值信号量。
vSemaphoreCreateBinary()
  此函数是老版本 FreeRTOS 中创建二值信号量函数,新版本已经不再使用了。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueGenericCreate() 函数创建了一个队列。其中,队列长度为 1,队列项长度为 semSEMAPHORE_QUEUE_ITEM_LENGTH(定义为 0),队列类型为 queueQUEUE_TYPE_BINARY_SEMAPHORE(定义为 3),如下所示:

#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    #define vSemaphoreCreateBinary( xSemaphore )                                                                                     \
    {                                                                                                                                \
        ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \
        if( ( xSemaphore ) != NULL )                                                                                                 \
        {                                                                                                                            \
            ( void ) xSemaphoreGive( ( xSemaphore ) );                                                                               \
        }                                                                                                                            \
    }
#endif


对应的函数原型: void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )。
参数:
xSemaphrore:用于保存创建成功的二值信号量的句柄。
返回值:

示例:
SemaphoreHandle_t xSemaphore = NULL;

void vATask( void * pvParameters )
{
   // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
   // This is a macro so pass the variable in directly.
   vSemaphoreCreateBinary( xSemaphore );

   if( xSemaphore != NULL )
   {
       // The semaphore was created successfully.
       // The semaphore can now be used.
   }
}




注意:
目前用于兼容哪些基于老版本 FreeRTOS 而做的应用层代码。
由于创建成功以后会立即调用函数 xSemaphoreGive() 释放二值信号量,此时新创建的二值信号量将变为有效状态(可以使用 xSemephoreTask 获取)。
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
内部使用 pvPortMalloc 动态申请 uxQueueLength * uxItemSize 长度的队列内存空间
使用 prvInitialiseNewQueue 初始化申请的队列内存空间,初始化后如本节第一张图所示。
入参 ucQueueType 只有在定义了 configUSE_TRACE_FACILITY 才会实际记录到队列中,否则根本不会使用
xSemaphoreCreateBinary()
  此函数是新版本 FreeRTOS 中用来创建二值信号量的。实际是定义于 semphr.h 中的一个宏,使用函数 xQueueGenericCreate() 来创建一个类型为 queueQUEUE_TYPE_BINARY_SEMAPHORE、长度为 1、队列项长度为 0 的队列。具体定义如下:

#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    #define xSemaphoreCreateBinary()    xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif


对应的函数原型: SemaphoreHandle_t xSemaphoreCreateBinary( void )
参数:

返回值:
NULL:二值信号量创建失败
其他值:创建成功的二值信号量的句柄
示例:
SemaphoreHandle_t xSemaphore = NULL;

void vATask( void * pvParameters )
{
   // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
   // This is a macro so pass the variable in directly.
   xSemaphore = xSemaphoreCreateBinary();

   if( xSemaphore != NULL )
   {
       // The semaphore was created successfully.
       // The semaphore can now be used.
   }
}


注意:
与旧版本中 API vSemaphoreCreateBinary 不同,这里创建好的二值信号量默认是空的,使用函数 xSemephoreTask() 是获取不到的。
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
内部使用 pvPortMalloc 动态申请 uxQueueLength * uxItemSize 长度的队列内存空间
使用 prvInitialiseNewQueue 初始化申请的队列内存空间,初始化后如本节第一张图所示。
入参 ucQueueType 只有在定义了 configUSE_TRACE_FACILITY 才会实际记录到队列中,否则根本不会使用
xSemephroeCreateBinaryStatic()
  此函数也是创建二值信号量的,只不过使用此函数创建二值信号量的话信号量所需要的 RAM 需要由用户来分配。此函数是定义于 semphr.h 中的一个宏,使用函数 xQueueGenericCreateStatic() 来创建一个类型为 queueQUEUE_TYPE_BINARY_SEMAPHORE、长度为 1、队列项长度为 0 的队列。具体定义如下:

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
    #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore )    xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, ( pxStaticSemaphore ), queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif /* configSUPPORT_STATIC_ALLOCATION */


对应的函数原型: SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
参数:
pxSemaphoreBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
由于是用户指定的内存,接口内部仅仅是填充数据,因此没有失败的情况。
示例:
SemaphoreHandle_t xSemaphore = NULL;
StaticSemaphore_t xSemaphoreBuffer;

void vATask( void * pvParameters )
{
   // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
   // The semaphore's data structures will be placed in the xSemaphoreBuffer
   // variable, the address of which is passed into the function.  The
   // function's parameter is not NULL, so the function will not attempt any
   // dynamic memory allocation, and therefore the function will not return
   // return NULL.
   xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer );

   // Rest of task code goes here.
}


注意:
创建二值信号量所需要的 RAM 需要由用户使用 StaticSemaphore_t xSemaphoreBuffer; 来创建
创建好的二值信号量默认是空的,使用函数 xSemephoreTask() 是获取不到的。
QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t * pucQueueStorage, StaticQueue_t * pxStaticQueue, const uint8_t ucQueueType )
调用 prvInitialiseNewQueue 将队列本身放到入参 pxStaticQueue 指向的内存中
pucQueueStorage 指向队列数据内存空间。对于二值信号量,没有数据部分,因此入参为 NULL
入参 ucQueueType 只有在定义了 configUSE_TRACE_FACILITY 才会实际记录到队列中,否则根本不会使用
释放
  释放信号量即表示将信号量置为有效状态。不管是二值信号量、计数信号量还是互斥信号量,它们都是使用下表中的函数来释放信号量(递归互斥信号量则有专用的释放函数)。

             函数                                                   描述
xSemaphoreGive()                           任务中使用的信号量释放函数
xSemaphoreGiveFromISR()                   中断中使用的信号量释放函数
xSemaphoreGive()
  此函数用于释放二值信号量、计数信号量或互斥信号量。此函数是定义于 semphr.h 中的一个宏,实际内部就是通过 xQueueGenericSend 向队列发送消息。具体定义如下:

#define xSemaphoreGive( xSemaphore )        \
                 xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \
                                   NULL, \
                                   semGIVE_BLOCK_TIME, \
                                   queueSEND_TO_BACK )


对应的函数原型: BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )
参数:
xSemaphore:要释放的信号量句柄
返回值:
pdPASS:释放信号量成功
errQUEUE_FULL:释放信号量失败
示例:
SemaphoreHandle_t xSemaphore = NULL;

void vATask( void * pvParameters )
{
// Create the semaphore to guard a shared resource.
xSemaphore = vSemaphoreCreateBinary();

if( xSemaphore != NULL )
{
     if( xSemaphoreGive( xSemaphore ) != pdTRUE )
     {
         // We would expect this call to fail because we cannot give
         // a semaphore without first "taking" it!
     }

     // Obtain the semaphore - don't block if the semaphore is not
     // immediately available.
     if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
     {
         // We now have the semaphore and can access the shared resource.

         // ...

         // We have finished accessing the shared resource so can free the
         // semaphore.
         if( xSemaphoreGive( xSemaphore ) != pdTRUE )
         {
             // We would not expect this call to fail because we must have
             // obtained the semaphore to get here.
         }
     }
}
}




注意:
递归互斥信号量的释放不能使用该接口
没有发送具体的消息内容(xQueueGenericSend 第三个参数为 NULL)
阻塞时间为 0
入队方式采用后向入队
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
内部实际将 uxMessageWaiting 增加了 1
入队的时候队列结构体成员变量 uxMessageWaiting 会加一,对于二值信号量通过判断 uxMessageWaiting 就可以知道信号量是否有效了。uxMessageWaiting 为 1 的话,说明二值信号量有效,为 0 就无效。如果队列满的话就返回错误值 errQUEUE_FULL,提示队列满,入队失败。
xSemaphoreGiveFromISR()
  此函数用于在中断中释放信号量,但是,只能用来释放二值信号量和计数信号量!此函数是定义于 semphr.h 中的一个宏,实际是通过 xQueueGiveGromISR() 来向队列发送消息,具体定义如下:

#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )    xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )


对应的函数原型应该为 BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken):
参数:
xSemaphore:要释放的信号量句柄。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值是由这个函数来设置的,用户不能进行设置,用于只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候,在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS:释放信号量成功。
errQUEUE_FULL:释放信号量失败。
注意:
函数 xSemaphoreGiveFromISR() 不能用于在中断中释放互斥信号量,因为互斥信号量涉及到优先级继承的问题,而中断不属于任务,没法处理中断优先级继承。
BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken )
内部实际将 uxMessageWaiting 增加了 1
获取
  获取信号量将使信号量失效,同释放信号量的 API 一样,不管是二值信号量、计数信号量还是互斥信号量,它们都使用下表中的函数来获取信号量(递归互斥信号量则有专用的释放函数)。

               函数                                            描述
xSemaphoreTake()                     任务中使用的获取信号量函数
xSemaphoreTakeFromISR()            中断中使用的获取信号量函数
xSemaphoreTake()
  此函数用于获取二值信号量、计数信号量或互斥信号量。此函数是定义于 semphr.h 中的一个宏,实际内部是使用 xQueueSemaphoreTake() 从队列中读取消息,具体定义如下:

#define xSemaphoreTake( xSemaphore, xBlockTime )    xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )


对应的函数原型: BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime)
参数:
xSemaphore:要获取的信号量句柄。
xBlockTime:阻塞时间
返回值:
pdTRUE:获取信号量成功。
pdFALSE:超时,获取信号量失败。
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
内部实际上将 uxMessagesWaiting 减一
不考虑队列数据
xSemaphoreTakeFromISR()
  此函数用于在中断服务函数中获取信号量,此函数用于获取二值信号量和计数信号量,不能使用此函数来获取互斥信号量!此函数是一个宏,实际内部是使用 xQueueReceiveFromISR() 从队列中读取消息,具体定义如下:

#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )    xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )


对应的函数原型:BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken)
参数:
xSemaphore:要获取的信号量句柄。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值由这个函数来设置,用户不进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为判断TRUE的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS:获取信号量成功。
pdFALSE:获取信号量失败。
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken )
内部实际上将 uxMessagesWaiting 减一
不考虑队列数据
如果有任务因为入队而阻塞的话就解除阻塞态,当解除阻塞的任务拥有更高优先级的话就将参数 pxHigherPriorityTaskWoken 设置为 pdTRUE
计数信号量
  计数信号量常用于事件计数与资源管理。FreeRTOS 中的计数信号量其实就是一个有多个队列项的队列。与二值信号量一样,任务和中断使用这个特殊队列不需要在乎队列中存的是什么消息,只需要关心队列是否为空即可。



事件计数:每当某个事件发生时,任务或者中断将释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。
资源管理:信号量的计数值表示系统中可用的资源数目,任务必先获取到信号量才能获得资源访问权,当信号量的计数值为零时表示系统没有可用的资源。所以的注意使用完资源的时候必须归还信号量。
创建
FreeRTOS 提供了如下两个 API 用来创建计数信号量。

                     函数                                               描述
xSemaphoreCreateCounting()                使用动态方法创建计数信号量
xSemaphoreCreateCountingStatic()        使用静态方法创建计数信号量
xSemaphoreCreateCounting()
  此函数用于创建一个计数信号量,所需要的内存通过动态内存管理方法分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateCountingSemaphore() 函数创建了一个队列。具体定义如下:

#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )    xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif


对应的函数原型:SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
参数:
uxMaxCount:计数信号量最大计数值,当信号量值等于此值得时候释放信号量就会失败。
uxInitialCount:计数信号量初始值。
返回值:
NULL:计数信号量创建失败。
其他值:计数信号量创建成功,返回计数信号量句柄。
示例:
SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
SemaphoreHandle_t xSemaphore = NULL;

  // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
  // The max value to which the semaphore can count should be 10, and the
  // initial value assigned to the count should be 0.
  xSemaphore = xSemaphoreCreateCounting( 10, 0 );

  if( xSemaphore != NULL )
  {
      // The semaphore was created successfully.
      // The semaphore can now be used.
  }
}



QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
内部调用 xQueueGenericCreate 创建一个队列的长度为uxMaxCount,队列项长度为 queueSEMAPHORE_QUEUE_ITEM_LENGTH(此宏为 0),队列的类型为 queueQUEUE_TYPE_COUNTING_SEMAPHORE 的队列。
uxMessagesWaiting = uxInitialCount
xSemaphoreCreateCountingStatic()
  此函数也是用来创建计数信号量的,使用此函数创建计数信号量的时候所需要的内存由用户分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateCountingSemaphoreStatic() 函数创建了一个队列。具体定义如下:

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
    #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer )    xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) )
#endif /* configSUPPORT_STATIC_ALLOCATION */


对应的函数原型:SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer );
参数:
uxMaxCount:计数信号量最大计数值,当信号量值等于此值得时候释放信号量就会失败。
uxInitialCount:计数信号量初始值。
pxSemaphoreBuffer:指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL:计数信号量创建失败。
其他值:计数信号量创建成功,返回计数信号量句柄。
QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t * pxStaticQueue )
内部调用 xQueueGenericCreateStatic 创建一个队列的长度为uxMaxCount,队列项长度为 queueSEMAPHORE_QUEUE_ITEM_LENGTH(此宏为 0),队列的类型为 queueQUEUE_TYPE_COUNTING_SEMAPHORE 的队列。
uxMessagesWaiting = uxInitialCount
释放
计数信号量的释放与二值信号量相同。

获取
计数信号量的获取与二值信号量相同。

互斥信号量
  互斥信号量的主要作用是对资源实现互斥访问。互斥信号量除了具备传统二值信号量的基本特性外,还专门为了解决优先级反转问题。FreeRTOS 中的互斥信号量其实就是一个拥有优先级继承的二值信号量。



优先级继承
  当一个互斥信号量正在别一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级任务会将低优先级的任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。

  优先级继承尽可能地降低了高优先级任务处于阻塞态的时间,并且将已出现的“优先级翻转”的影响降到最低。优先级继承并不能完全消除优先级翻转,它只是尽可能地降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转发生。

  互斥信号量其实就是一个拥有优先级继承的二值信号量。优先级继承机制要求任务 “获取” 信号量使用完成后,必须再次 “释放”信号量。与二值信号量不同,互斥信号量不能用于中断服务函数中,原因如下:

互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
中断服务函数中不能因为要等待互斥信号量而设置阻塞时间而进入阻塞态
创建
FreeRTOS 提供了如下两个互斥信号量创建函数:

                       函数                                           描述
xSemaphoreCreateMutex()                    使用动态方法创建互斥信号量
xSemaphoreCreateMutexStatic()            使用静态方法创建互斥信号量
xSemaphoreCreateMutex()
  此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateMutex() 函数创建了一个队列。具体定义如下:

#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) )
    #define xSemaphoreCreateMutex()    xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif


对应的函数原型如下:SemaphoreHandle_t xSemaphoreCreateMutex( void )
参数:
无。
返回值:
NULL:互斥信号量创建失败。
其他值:创建成功的互斥信号量的句柄
示例:
SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
  // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
  // This is a macro so pass the variable in directly.
  xSemaphore = xSemaphoreCreateMutex();

  if( xSemaphore != NULL )
  {
      // The semaphore was created successfully.
      // The semaphore can now be used.
  }
}


QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
内部调用函数 xQueueGenericCreate() 创建一个队列,队列长度为 1,队列项长度为 0,队列类型为 queueQUEUE_TYPE_MUTEX 的队列。
信号量使用的内存空间由 pvPortMalloc 动态申请
内部使用 prvInitialiseMutex 初始化互斥信号量的各种变量
信号量创建后,会立刻调用 xQueueGenericSend 使信号量有效
xSemaphoreCreateMutexStatic()
  此函数也是创建互斥信号量的,只是创建的信号量所需要的 RAM 需要由用户来分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateMutexStatic() 函数创建了一个队列。具体定义如下:

#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) )
    #define xSemaphoreCreateMutexStatic( pxMutexBuffer )    xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) )
#endif


对应的函数原型:SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
参数:
pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL:互斥信号量创建失败。
其他值:创建成功的互斥信号量的句柄。
示例:
SemaphoreHandle_t xSemaphore;
StaticSemaphore_t xMutexBuffer;

void vATask( void * pvParameters )
{
// A mutex cannot be used before it has been created.  xMutexBuffer is
// into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
// attempted.
xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );

// As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
// so there is no need to check it.
}


QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t * pxStaticQueue )
内部调用函数 xQueueGenericCreateStatic() 创建一个队列,队列长度为 1,队列项长度为 0,队列类型为 queueQUEUE_TYPE_MUTEX 的队列。
信号量使用的内存是由用户定义 pxStaticQueue 内存
内部使用 prvInitialiseMutex 初始化互斥信号量的各种变量
信号量创建后,会立刻调用 xQueueGenericSend 使信号量有效
释放
  互斥信号量的释放与二值信号量相同。只不过,由于互斥信号量不能在中断中使用,因此,xSemaphoreGiveFromISR 不能用于释放互斥信号量!

获取
  互斥信号量的获取与二值信号量相同。只不过,由于互斥信号量不能在中断中使用,因此,xSemaphoreTakeFromISR 不能用于获取互斥信号量!

递归互斥信号量
  递归互斥信号量是一种特殊的互斥信号量。已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量(即可以嵌套使用),且次数不限。一个任务获取了多少次递归互斥信号量就必须释放多少次,释放之前递归互斥量都处于无效状态,其他任务无法获取,只有持有递归信号量的任务才能获取和释放。

同互斥信号量一样,递归互斥信号量不能用在中断服务函数中

创建
FreeRTOS 提供了如下两个递归互斥信号量创建函数:

                              函数                                                   描述
xSemaphoreCreateRecursiveMutex()                  使用动态方法创建递归互斥信号量
xSemaphoreCreateRecursiveMutexStatic()         使用静态方法创建递归互斥信号量
xSemaphoreCreateRecursiveMutex()
  此函数用于创建一个递归互斥信号量,所需要的内存通过动态内存管理方法分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateMutex() 函数创建了一个 queueQUEUE_TYPE_RECURSIVE_MUTEX 类型的队列。具体定义如下:

#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
    #define xSemaphoreCreateRecursiveMutex()    xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif


对应的函数原型如下:SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
参数:
无。
返回值:
NULL:互斥信号量创建失败。
其他值:创建成功的互斥信号量的句柄
示例:
SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
  // Semaphore cannot be used before a call to xSemaphoreCreateRecursiveMutex().
  // This is a macro so pass the variable in directly.
  xSemaphore = xSemaphoreCreateRecursiveMutex();

  if( xSemaphore != NULL )
  {
      // The semaphore was created successfully.
      // The semaphore can now be used.
  }
}


QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
内部调用函数 xQueueGenericCreate() 创建一个队列长度为 1,队列项长度为 0,队列类型为 queueQUEUE_TYPE_RECURSIVE_MUTEX 的队列。
信号量使用的内存空间由 pvPortMalloc 动态申请
信号量创建后,会立刻调用 xQueueGenericSend 使信号量有效
xSemaphoreCreateRecursiveMutexStatic()
  此函数也是创建互斥信号量的,只是创建的信号量所需要的 RAM 需要由用户来分配。此函数是定义于 semphr.h 中的一个宏,具体定义就是使用 xQueueCreateMutexStatic() 函数创建了一个队列。具体定义如下:

#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
    #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore )    xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, ( pxStaticSemaphore ) )
#endif /* configSUPPORT_STATIC_ALLOCATION */


对应的函数原型:SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer );
参数:
pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL:互斥信号量创建失败。
其他值:创建成功的互斥信号量的句柄。
示例:
SemaphoreHandle_t xSemaphore;
StaticSemaphore_t xMutexBuffer;

void vATask( void * pvParameters )
{
// A recursive semaphore cannot be used before it is created.  Here a
// recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
// The address of xMutexBuffer is passed into the function, and will hold
// the mutexes data structures - so no dynamic memory allocation will be
// attempted.
xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );

// As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
// so there is no need to check it.
}



QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t * pxStaticQueue )
内部调用函数 xQueueGenericCreateStatic() 创建一个队列长度为 1,队列项长度为 0,类型为 queueQUEUE_TYPE_RECURSIVE_MUTEX 的队列。
释放
  释放信号量即表示将信号量置为有效状态。与二值信号量不同,递归互斥信号量的释放必须使用 xSemaphoreGiveRecursive 这个专用的 API 来进行释放。

对应的函数原型:BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex );
参数:
xMutex:指向一个递归互斥信号的句柄
返回值:
pdPASS:释放信号量成功。
pdFAIL:释放信号量失败。
示例:
SemaphoreHandle_t xMutex = NULL;

// A task that creates a mutex.
void vATask( void * pvParameters )
{
  // Create the mutex to guard a shared resource.
  xMutex = xSemaphoreCreateRecursiveMutex();
}

// A task that uses the mutex.
void vAnotherTask( void * pvParameters )
{
  // ... Do other things.

  if( xMutex != NULL )
  {
      // See if we can obtain the mutex.  If the mutex is not available
      // wait 10 ticks to see if it becomes free.
      if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
      {
          // We were able to obtain the mutex and can now access the
          // shared resource.

          // ...
          // For some reason due to the nature of the code further calls to
          // xSemaphoreTakeRecursive() are made on the same mutex.  In real
          // code these would not be just sequential calls as this would make
          // no sense.  Instead the calls are likely to be buried inside
          // a more complex call structure.
          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );

          // The mutex has now been 'taken' three times, so will not be
          // available to another task until it has also been given back
          // three times.  Again it is unlikely that real code would have
          // these calls sequentially, it would be more likely that the calls
          // to xSemaphoreGiveRecursive() would be called as a call stack
          // unwound.  This is just for demonstrative purposes.
          xSemaphoreGiveRecursive( xMutex );
          xSemaphoreGiveRecursive( xMutex );
          xSemaphoreGiveRecursive( xMutex );

          // Now the mutex can be taken by other tasks.
      }
      else
      {
          // We could not obtain the mutex and can therefore not access
          // the shared resource safely.
      }
  }
}



BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
内部调用函数 xQueueGenericSend() 向队列发送消息。
获取
  获取信号量即表示将信号量置为无效状态。与二值信号量不同,递归互斥信号量得获取必须使用 xSemaphoreTakeRecursive 这个专用的 API 来进行获取。

对应的函数原型:BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex, TickType_t xBlockTime );
参数:
xMutex:要获取的信号量句柄
xBlockTime:阻塞时间
返回值:
pdPASS:信号量获取成功
其他值:信号量获取失败
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
内部调用函数 xQueueSemaphoreTake()
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/ZCShouCSDN/article/details/130984296

使用特权

评论回复
沙发
shenxiaolin| | 2024-4-30 16:09 | 只看该作者
信号量是曹组系统的基础,可以做很多的事情

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1364

主题

13994

帖子

8

粉丝