FreeRTOS的信号量包括二进制信号量、计数信号量、相互排斥信号量(以后简称相互排斥量)和递归相互排斥信号量(以后简称递归相互排斥量)。我们能够把相互排斥量和递归相互排斥量看成特殊的信号量。
信号量API函数实际上都是宏。它使用现有的队列机制。这些宏定义在semphr.h文件里。假设使用信号量或者相互排斥量。须要包括semphr.h头文件。
二进制信号量、计数信号量和相互排斥量信号量的创建API函数是独立的,可是获取和释放API函数都是同样的;递归相互排斥信号量的创建、获取和释放API函数都是独立的。
1创建二进制信号量
1.1函数描写叙述
SemaphoreHandle_t xSemaphoreCreateBinary( void );
这个函数用于创建一个二进制信号量。二进制信号量要么有效要么无效。这也是为什么叫做二进制的原因。
新创建的信号量处于无效状态。这意味着使用API函数xSemaphoreTake()获取信号之前,须要先给出信号。
二进制信号量和相互排斥量非常类似,但也有细微的差别:相互排斥量具有优先级继承机制,二进制信号量没有这个机制。这使得二进制信号量更适合用于同步(任务之间或者任务和中断之间),相互排斥量更适合互锁。
一旦获得二进制信号量后不须要恢复,一个任务或中断不断的产生信号。而还有一个任务不断的取走这个信号,通过这种方式来实现同步。
低优先级任务拥有相互排斥量的时候,假设还有一个高优先级任务也企图获取这个信号量,则低优先级任务的优先级会被暂时提高,提高到和高优先级任务同样的优先级。这意味着相互排斥量必须要释放,否则高优先级任务将不能获取这个相互排斥量,而且那个拥有相互排斥量的低优先级任务也永远不会被剥夺,这就是操作系统中的优先级翻转。
相互排斥量和二进制信号量都是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 )
创建计数信号量。计数信号量通经常使用于以下两种情况:
- 事件计数:在这种应用场合,每当事件发生,事件处理程序会“产生”一个信号量(信号量计数值会递增)。每当处理任务处理事件,会取走一个信号量(信号量计数值会递减)。因此,事件发生或者事件被处理后。计数值是会变化的。
- 资源管理:在这种应用场合下,计数值表示有效资源的数目。为了获得资源,任务首先要获得一个信号量---递减信号量计数值。当计数值为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。而且參数xTickToWait为portMAX_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高级篇已经開始写了。能够点击这里擦看最新的文章列表。