14.2.2 FreeRTOS信号量的函数解析
信号量相关的函数头文件定义在“semphr.h”文件中,原函数均定义在“queue.c”文件中,一些函数直接调用队列相关的函数。下面只分析信号量创建函数、信号量释放函数和信号量获取函数,其他函数请读者自己分析。
1.信号量创建函数
二值信号量创建函数:xSemaphoreCreateBinary()、xSemaphoreCreateBinaryStatic()。
计数信号量创建函数:xSemaphoreCreateCounting()、xSemaphoreCreateCountingStatic()。
互斥信号量创建函数:xSemaphoreCreateMutex()、xSemaphoreCreateMutexStatic()。
递归互斥信号量创建函数:xSemaphoreCreateRecursiveMute()。
其中,二值信号量、计数信号量、互斥信号量有动态和静态两种创建方法。这些函数均是调用队列的通用创建函数xQueueGenericCreate()和xQueueGenericCreateStatic()实现的,函数原型参见14.1.2小节。如果创建函数返回NULL,说明创建失败,反之则说明创建成功。下面以二值信号量创建函数为例进行分析。
二值信号量创建函数xSemaphoreCreateBinary()的代码如下。

由以上代码可知,二值信号量创建函数xSemaphoreCreateBinary()使用函数xQueueGenericCreate()来创建一个类型为queueQUEUE_TYPE_BINARY_SEMAPHORE、长度为1、队列项长度为0的队列。
要创建一个二值信号量,方法如下。

要创建一个计数信号量,方法如下。

要创建一个互斥信号量,方法如下。

要创建一个递归互斥信号量,方法如下。

2.信号量释放函数
信号量释放函数有两个:一个是任务级信号量释放函数xSemaphoreGive(),另一个是中断级信号量释放函数xSemaphoreGiveFromISR()。二值信号量、计数信号量和互斥信号量均使用这两个函数。
递归互斥信号量的释放函数是xSemaphoreGiveRecursive()。
(1)任务级信号量释放函数。
xSemaphoreGive()函数用于释放二值信号量、计数信号量和互斥信号量。此函数是一个宏,真正释放信号量的过程是由函数xQueueGenericSend()来完成的,函数原型如下。
![]()
①输入参数xSemaphore表示要释放的信号量句柄。
②返回参数:pdPASS表示成功,errQUEUE_FULL表示失败。
函数xSemaphoreGive()在文件“semphr.h”中有如下定义。

该函数调用队列中的xQueueGenericSend()函数,可以看出任务级释放信号量的过程就是向队列发送消息的过程,只是这里并没有发送具体的消息,阻塞时间semGIVE_BLOCK_TIME=0,入队方式采用后向入队。入队的时候,队列结构体成员变量uxMessagesWaiting会加1。对于二值信号量,通过判断uxMessagesWaiting就能知道信号量是否有效:uxMessagesWaiting=1,说明二值信号量有效,为0就无效。如果队列已满就返回错误值errQUEUE_FULL,提示入队失败。
(2)中断级信号量释放函数。
xSemaphoreGiveFromISR()函数用于在中断中释放信号量。此函数用来释放二值信号量和计数信号量,但不能在中断服务函数中释放互斥信号量。此函数是一个宏,真正执行的是函数xQueueGiveFromISR(),此函数原型如下。

①输入参数xSemaphore表示要释放的信号量句柄。
②输入参数pxHigherPriorityTaskWoken用于标记退出此函数以后是否进行任务切换。当此值为pdTRUE的时候,在退出中断服务函数之前一定要进行一次任务切换,否则应将此值设为NULL。(https://www.daowen.com)
③返回参数:pdPASS表示成功,errQUEUE_FULL表示失败。
(3)递归互斥信号量的释放函数。
xSemaphoreGiveRecursive()函数是一个宏,真正执行的是函数xQueueGiveMutexRecursive(),语句如下。


3.信号量获取函数
信号量获取函数和信号量释放函数一一对应,任务级信号量获取函数为xSemaphoreTake(),中断级信号量获取函数为xSemaphoreGiveFromISR(),二值信号量、计数信号量、互斥信号量均使用这两个函数。
递归互斥信号量的获取函数是xSemaphoreTakeRecursive()。
(1)任务级信号量获取函数。
xSemaphoreTake()函数用于获取二值信号量、计数信号量和互斥信号量。此函数是一个宏,真正获取信号量的过程是由通用队列接收函数xQueueGenericReceive()来完成的,函数原型如下。

①输入参数xSemaphore表示要获取的信号量句柄。
②输入参数xBlockTime表示阻塞时间。
③返回参数:pdPASS表示成功,errQUEUE_FULL表示失败。
该函数的宏定义如下。

获取信号量的过程即读取队列的过程,但这里并不是为了读取队列中的消息。在14.1.2小节介绍的函数xQueueGenericReceive()中,如果队列为空并且阻塞时间为0,就立即返回errQUEUE_EMPTY,表示队列满;如果队列为空并且阻塞时间不为0,就将任务添加到延时列表中。数据读取完成以后,还需要将队列结构体成员变量uxMessagesWaiting减1,然后解除某些因为入队而阻塞的任务,最后返回pdPASS表示出队成功。
(2)中断级信号量获取函数xSemaphoreTakeFromISR()。
在中断服务函数中获取信号量用函数xSemaphoreTakeFromISR()。该函数适用于获取二值信号量和计数信号量,不适用于获取互斥信号量。
xSemaphoreTakeFromISR()函数是通过xQueueReceiveFromISR()宏定义的,此函数原型如下。

①输入参数xSemaphore表示要获取的信号量句柄。
②输入参数pxHigherPriorityTaskWoken用于标记退出此函数以后是否进行任务切换。当此值为pdTRUE的时候,在退出中断服务函数之前一定要进行一次任务切换。
③返回参数:pdPASS表示获取信号量成功,pdFALSE表示获取信号量失败。
在中断中获取信号量真正使用的是函数xQueueReceiveFromISR(),这个函数就是中断级出队函数。当队列不为空的时候,就复制队列中的数据(用于信号量的时候不需要数据),同时将队列结构体中的成员变量uxMessagesWaiting减1。如果有任务因为入队而阻塞,就解除阻塞。如果解除阻塞的任务拥有更高优先级,就将参数pxHigherPriorityTaskWoken设置为pdTRUE,最后返回pdPASS表示出队成功,如果队列为空就直接返回pdFAIL表示出队失败。
(3)递归互斥信号量获取函数xSemaphoreTakeRecursive()。
递归互斥信号量的获取使用函数xSemaphoreTakeRecursive(),其定义如下。
![]()
①输入参数xMutex表示获取的递归互斥信号量句柄。
②输入参数xBlockTime表示阻塞时间。
③该函数无返回参数。
该函数采用宏定义,实现函数是xQueueTakeMutexRecursive(),宏定义如下。

