FreeRTOS系列第20篇---FreeRTOS信号量API函数

      FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(之后简称互斥量)和递归互斥信号量(之后简称递归互斥量)。咱们能够把互斥量和递归互斥量当作特殊的信号量。
安全

      信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。若是使用信号量或者互斥量,须要包含semphr.h头文件。函数

      二进制信号量、计数信号量和互斥量信号量的建立API函数是独立的,可是获取和释放API函数都是相同的;递归互斥信号量的建立、获取和释放API函数都是独立的。spa

1建立二进制信号量

1.1函数描述

       SemaphoreHandle_t  xSemaphoreCreateBinary( void );操作系统

       这个函数用于建立一个二进制信号量。二进制信号量要么有效要么无效,这也是为何叫作二进制的缘由。.net

       新建立的信号量处于无效状态,这意味着使用API函数xSemaphoreTake()获取信号以前,须要先给出信号。code

       二进制信号量和互斥量很是类似,但也有细微的区别:互斥量具备优先级继承机制,二进制信号量没有这个机制。这使得二进制信号量更适合用于同步(任务之间或者任务和中断之间),互斥量更适合互锁。blog

       一旦得到二进制信号量后不须要恢复,一个任务或中断不断的产生信号,而另外一个任务不断的取走这个信号,经过这样的方式来实现同步。继承

       低优先级任务拥有互斥量的时候,若是另外一个高优先级任务也企图获取这个信号量,则低优先级任务的优先级会被临时提升,提升到和高优先级任务相同的优先级。这意味着互斥量必需要释放,不然高优先级任务将不能获取这个互斥量,而且那个拥有互斥量的低优先级任务也永远不会被剥夺,这就是操做系统中的优先级翻转。递归

       互斥量和二进制信号量都是SemaphoreHandle_t类型,而且能够用于任何具备这类参数的API函数中。队列

1.1.2返回值

  •  NULL:建立信号量失败,由于FreeRTOS堆栈不足。
  •  其它值:信号量建立成功。这个返回值存储着信号量句柄。

1.1.3用法举例

SemaphoreHandle_t xSemaphore;
 
void vATask( void * pvParameters )
{
    /* 建立信号量 */
   xSemaphore = xSemaphoreCreateBinary();
 
   if( xSemaphore == NULL )
   {
       /* 因堆栈不足,信号量建立失败,这里进行失败处理*/
   }
   else
   {
       /* 信号量可使用。信号量句柄存储在变量xSemahore中。
          若是在这里调用API函数xSemahoreTake()来获取信号量,
          则必然是失败的,由于建立的信号量初始是无效(空)的。*/
   }
}

2建立计数信号量

2.1函数描述

SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount,
                                 UBaseType_t uxInitialCount )

       建立计数信号量,计数信号量一般用于如下两种状况:

  1. 事件计数:在这种应用场合,每当事件发生,事件处理程序会“产生”一个信号量(信号量计数值会递增),每当处理任务处理事件,会取走一个信号量(信号量计数值会递减)。所以,事件发生或者事件被处理后,计数值是会变化的。
  2. 资源管理:在这种应用场合下,计数值表示有效资源的数目。为了得到资源,任务首先要得到一个信号量---递减信号量计数值。当计数值为0时,表示没有可用的资源。当占有资源的任务完成,它会释放这个资源,相应的信号量计数值会增一。计数值达到初始值(最大值)表示全部资源均可用。

2.2参数描述

  •  uxMaxCount:最大计数值,当信号到达这个值后,就再也不增加了。
  •  uxInitialCount:建立信号量时的初始值。

2.3返回值

       NULL表示信号量建立失败,不然返回信号量句柄。

2.4用法举例

void vATask( void * pvParameters )
 {
     xSemaphoreHandle xSemaphore;
 
     // 必须先建立信号量,才能使用它
     // 信号量能够计数的最大值为10,计数初始值为0.
     xSemaphore = xSemaphoreCreateCounting( 10, 0 );
 
     if( xSemaphore != NULL )
     {
         // 信号量建立成功
         // 如今可使用信号量了。
     }
 }

3建立互斥量

3.1函数描述

       SemaphoreHandle_t xSemaphoreCreateMutex( void )

       建立互斥量。可使用API函数xSemaphoreTake()和xSemaphoreGive()访问互斥量,可是毫不能够用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()访问。

       二进制信号量和互斥量很是类似,但也有细微的区别:互斥量具备优先级继承机制,二进制信号量没有这个机制。这使得二进制信号量更适合用于同步(任务之间或者任务和中断之间),互斥量更适合互锁。

       一旦得到二进制信号量后不须要恢复,一个任务或中断不断的产生信号,而另外一个任务不断的取走这个信号,经过这样的方式来实现同步。

       低优先级任务拥有互斥量的时候,若是另外一个高优先级任务也企图获取这个信号量,则低优先级任务的优先级会被临时提升,提升到和高优先级任务相同的优先级。这意味着互斥量必需要释放,不然高优先级任务将不能获取这个互斥量,而且那个拥有互斥量的低优先级任务也永远不会被剥夺,这就是操做系统中的优先级翻转。

       互斥量和二进制信号量都是SemaphoreHandle_t类型,而且能够用于任何具备这类参数的API函数中。

3.2返回值

       NULL表示信号量建立失败,不然返回信号量句柄。

3.3用法举例

xSemaphoreHandle xSemaphore;
 
voidvATask( void * pvParameters )
{
    // 互斥量在未建立以前是不可用的
    xSemaphore = xSemaphoreCreateMutex();
    if( xSemaphore != NULL )
    {
        // 建立成功
        // 在这里可使用这个互斥量了 
    }
}

4建立递归互斥量

4.1函数描述

       SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

       用于建立递归互斥量。被建立的互斥量能够被API函数xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不能够被API函数xSemaphoreTake()和xSemaphoreGive()使用。

       递归类型的互斥量能够被拥有者重复获取。拥有互斥量的任务必须调用API函数xSemaphoreGiveRecursive()将拥有的递归互斥量所有释放后,该信号量才真正被释放。好比,一个任务成功获取同一个互斥量5次,那么这个任务要将这个互斥量释放5次以后,其它任务才能获取到它。

       递归互斥量具备优先级继承机制,所以任务得到一次信号后必须在使用完后作一个释放操做。

       互斥量类型信号不能够用在中断服务例程中。

4.2返回值

       NULL表示互斥量建立失败,不然返回互斥量句柄。

4.3用法举例

xSemaphoreHandle xMutex;
 
void vATask( void * pvParameters )
{
    // 互斥量未建立前是不能被使用的
    xMutex = xSemaphoreCreateRecursiveMutex();
 
    if( xMutex != NULL )
    {
        // 建立成功
        // 在这里建立互斥量
    }
}

5删除信号量

5.1函数描述

       void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

       删除信号量。若是有任务阻塞在这个信号量上,则这个信号量不要删除。

5.2参数描述

       xSemaphore:信号量句柄

6获取信号量

6.1函数描述

       xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)

       获取信号量。信号量必须是经过API函数xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()预先建立过的。注意,递归互斥量类型信号量不能使用该函数、不用在中断服务程序中使用该函数。

6.2参数描述

  •  xSemaphore:信号量句柄
  •  xTickToWait:信号量无效时,任务最多等待的时间,单位是系统节拍周期个数。使用宏portTICK_PERIOD_MS能够辅助将系统节拍个数转化为实际时间(以毫秒为单位)。若是设置为0,表示不是设置等待时间。若是INCLUDE_vTaskSuspend设置为1,而且参数xTickToWaitportMAX_DELAY则能够无限等待。

6.3返回值

       成功获取到信号量返回pdTRUE,不然返回pdFALSE

6.4用法举例

SemaphoreHandle_t xSemaphore = NULL;
 
/*这个任务建立信号量 */
void vATask( void * pvParameters )
{
    /*建立互斥型信号量,用于保护共享资源。*/
    xSemaphore = xSemaphoreCreateMutex();
}
 
/* 这个任务使用信号量 */
void vAnotherTask( void * pvParameters )
{
    /* ... 作其它事情. */
 
    if( xSemaphore != NULL )
    {
        /*若是信号量无效,则最多等待10个系统节拍周期。*/
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
        {
            /*到这里咱们获取到信号量,如今能够访问共享资源了*/
 
            /* ... */
 
            /* 完成访问共享资源后,必须释放信号量*/
            xSemaphoreGive( xSemaphore );
        }
        else
        {
            /* 没有获取到信号量,这里处理异常状况。*/
        }
    }
}

7获取信号量(带中断保护)

7.1函数描述

xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
                         signedBaseType_t *pxHigherPriorityTaskWoken)

       API函数xSemaphoreTake()的另外一版本,用于中断服务程序。

7.2参数描述

  •  xSemaphore:信号量句柄
  •  pxHigherPriorityTaskWoken:若是*pxHigherPriorityTaskWoken为pdTRUE,则须要在中断退出前手动进行一次上下文切换。从FreeRTOS V7.3.0开始,该参数为可选参数,并能够设置为NULL。

7.3返回值

       信号量成功获取返回pdTRUE,不然返回pdFALSE。

8获取递归互斥量

8.1函数描述

       xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );

       获取递归互斥信号量。互斥量必须是经过API函数xSemaphoreCreateRecursiveMutex()建立的类型。

       文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必须设置成1,此函数才有效。

      已经获取递归互斥量的任务能够重复获取该递归互斥量。使用xSemaphoreTakeRecursive() 函数成功获取几回递归互斥量,就要使用xSemaphoreGiveRecursive()函数返还几回,在此以前递归互斥量都处于无效状态。好比,某个任务成功获取5次递归互斥量,那么在它没有返还5次该递归互斥量以前,这个互斥量对别的任务无效。

8.2参数描述

  •  xMutex:互斥量句柄,必须是使用API函数xSemaphoreCreateRecursiveMutex()返回的。
  •  xTickToWait:互斥量无效时,任务最多等待的时间,单位是系统节拍周期个数。使用宏portTICK_PERIOD_MS能够辅助将系统节拍个数转化为实际时间(以毫秒为单位)。若是设置为0,表示不是设置等待时间。若是任务已经拥有信号量则xSemaphoreTakeRecursive()当即返回,无论xTickToWait是什么值。

8.3返回值

     成功获取递归互斥量返回pdTURE,不然返回pdFALSE

8.4用法举例

SemaphoreHandle_t xMutex = NULL;
 
// 这个任务建立互斥量
void vATask( void *pvParameters )
{
    // 这个互斥量用于保护共享资源
    xMutex =xSemaphoreCreateRecursiveMutex();
}
 
//这个任务使用互斥量
void vAnotherTask( void *pvParameters )
{
    // ... 作其它事情.
 
    if( xMutex != NULL )
    {
        // 若是互斥量无效,则最多等待10系统时钟节拍周期.   
        if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
            // 到这里咱们成功获取互斥量并能够访问共享资源了
 
            // ...
            // 因为某种缘由,某些代码须要在一个任务中屡次调用API函数
            // xSemaphoreTakeRecursive()。固然不会像本例中这样连续式
            //调用,实际代码会有更加复杂的结构
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 
            // 咱们获取一个互斥量三次,因此咱们要将这个互斥量释放三次
            //它才会变得有效。再一次说明,实际代码可能会更加复杂。
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
 
            // 到这里,这个共享资源能够被其它任务使用了.
        }
        else
        {
            // 处理异常状况
        }
    }
}

9释放信号量

9.1函数描述

       xSemaphoreGive(SemaphoreHandle_t xSemaphore )

       用于释放一个信号量。信号量必须是API函数xSemaphoreCreateBinary()xSemaphoreCreateCounting()xSemaphoreCreateMutex() 建立的。必须使用API函数xSemaphoreTake()获取这个信号量。

       这个函数毫不能够在中断服务例程中使用,可使用带中断保护版本的API函数xSemaphoreGiveFromISR()来实现相同功能。

       这个函数不能用于使用API函数xSemaphoreCreateRecursiveMutex()所建立的递归互斥量。

9.2参数描述

  •  xSemaphore:信号量句柄。

9.3返回值

       信号量释放成功返回pdTRUE,不然返回pdFALSE。

9.4用法举例

SemaphoreHandle_t xSemaphore = NULL;
 
voidvATask( void * pvParameters )
{
   // 建立一个互斥量,用来保护共享资源
   xSemaphore = xSemaphoreCreateMutex();
 
    if( xSemaphore != NULL )
    {
         if( xSemaphoreGive( xSemaphore ) != pdTRUE )
         {
              //咱们但愿这个函数调用失败,由于首先要获取互斥量
         }
         // 获取信号量,不等待
         if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
         {
              // 如今咱们拥有互斥量,能够安全的访问共享资源
              if( xSemaphoreGive( xSemaphore ) != pdTRUE )
              {
                   //咱们不但愿这个函数调用失败,由于咱们必须
                   //要释放已获取的互斥量
              }
         }
     }
}

10释放信号量(带中断保护)

10.1函数描述

xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
                         signed BaseType_t *pxHigherPriorityTaskWoken )

       释放信号量。是API函数xSemaphoreGive()的另个版本,用于中断服务程序。信号量必须是经过API函数xSemaphoreCreateBinary()或xSemaphoreCreateCounting()建立的。这里没有互斥量,是由于互斥量不能够用在中断服务程序中。

10.2参数描述

       xSemaphore:信号量句柄

       pxHigherPriorityTaskWoken:若是*pxHigherPriorityTaskWoken为pdTRUE,则须要在中断退出前人为的经行一次上下文切换。从FreeRTOS V7.3.0开始,该参数为可选参数,并能够设置为NULL。

10.3返回值

       成功释放信号量返回pdTURE,不然返回errQUEUE_FULL。

10.4用法举例

#define LONG_TIME 0xffff
#define TICKS_TO_WAIT    10
 
SemaphoreHandle_t xSemaphore = NULL;
 
/* Repetitive task. */
void vATask( void * pvParameters )
{
    /* 咱们使用信号量同步,因此先建立一个二进制信号量.必须确保
       在建立这个二进制信号量以前,中断不会访问它。*/
    xSemaphore = xSemaphoreCreateBinary();
 
    for( ;; )
    {
        /* 咱们但愿每产生10次定时器中断,任务运行一次。*/
        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
        {
            /* 到这里成功获取到信号量*/
 
            ...
 
           /* 咱们成功执行完一次,因为这是个死循环,因此任务仍会
               阻塞在等待信号量上。信号量由ISR释放。*/
        }
    }
}
 
/* 定时器 ISR */
void vTimerISR( void * pvParameters )
{
    static unsigned char ucLocalTickCount = 0;
    static signed BaseType_txHigherPriorityTaskWoken;
 
    /*定时器中断发生 */
 
      ...执行其它代码
 
    /*须要vATask() 运行吗? */
    xHigherPriorityTaskWoken = pdFALSE;
    ucLocalTickCount++;
    if( ucLocalTickCount >= TICKS_TO_WAIT )
    {
        /* 释放信号量,解除vATask任务阻塞状态 */
        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
 
        /* 复位计数器 */
        ucLocalTickCount = 0;
    }
 
    /* 若是 xHigherPriorityTaskWoken 表达式为真,须要执行一次上下文切换*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

11释放递归互斥量

11.1函数描述

       xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )

       释放一个递归互斥量。互斥量必须是使用 API函数xSemaphoreCreateRecursiveMutex()建立的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必须设置成1本函数才有效。

11.2参数描述

  •  xMutex:互斥量句柄。必须是函数xSemaphoreCreateRecursiveMutex()返回的值。

11.3返回值

       若是递归互斥量释放成功,返回pdTRUE。

11.4用法举例

       见“8 获取递归互斥量”。

12获取互斥量持有任务的句柄

12.1函数描述

       TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );

       返回互斥量持有任务的句柄(若是有的话),互斥量由参数xMutex指定。

       若是调用此函数的任务持有互斥量,那么能够可靠的返回任务句柄,可是若是是别的任务持有互斥量,则不总可靠。

       文件FreeRTOSConfig.h中宏configUSE_MUTEXES必须设置成1本函数才有效。

12.2参数描述

  •  xMutex:互斥量句柄

12.3返回值

       返回互斥量持有任务的句柄。若是参数xMutex不是互斥类型信号量或者虽然互斥量有效但这个互斥量不被任何任务持有则返回NULL。


      这是FreeRTOS基础篇的最后一篇博文,到这里咱们已经能够移植、熟练使用FreeRTOS了。但若是想知道FreeRTOS背后的运行机制,这些是远远不够的,下面要走的路还会很长。要不要了解FreeRTOS背后运行机制,全凭各位的兴趣,毕竟咱们即便不清楚汽车的构造细节,但只要掌握驾驶技巧也能够很好的开车的。使用RTOS也与之类似,只要咱们掌握了基础篇的那些知识,咱们已经能够很好的使用FreeRTOS了。探索FreeRTOS背后运行的机制,是咱们对未知事件的好奇,也是咱们相信理解了FreeRTOS运行机制,可让咱们更优雅、更少犯错、更举重若轻的的使用RTOS。

      FreeRTOS高级篇已经开始写了,能够点击这里擦看最新的文章列表。