目录
编辑1、函数xSemaphoreCreateCounting()
2、函数xSemaphoreCreateCountingStatic()
1、计数型信号量简介
有些资料中也将计数型信号量叫做数值信号量,二值信号量相当于长度为 1 的队列,那么 计数型信号量就是长度大于 1 的队列。同二值信号量一样,用户不需要关心队列中存储了什么 数据,只需要关心队列是否为空即可。计数型信号量通常用于如下两个场合:
1、事件计数
2、资源管理
1、事件计数
在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值) ,其他任务会获取信号量( 信号量计数值减一,信号量值就是队列结构体成员变量 uxMessagesWaiting)来处理事件。在这种场合中创建的计数型信号量初始计数值为 0。
2、资源管理
在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。 一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减 一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量, 释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量, 比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为100。
2、创建计数型信号量
FreeRTOS 提供了两个计数型信号量创建函数,如下表 :
1、函数xSemaphoreCreateCounting()
此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateCountingSemaphore(),此函数原型如 下:
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount )
参数:
uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
uxInitialCount: 计数信号量初始值。
返回值:
NULL: 计数型信号量创建失败。
其他值: 计数型信号量创建成功,返回计数型信号量句柄。
下面是在实际操作中使用的方法
2、函数xSemaphoreCreateCountingStatic()
此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存 需要由用户分配。此函数也是一个宏,真正执行的是函数xQueueCreateCountingSemaphoreStatic(), 函数原型如下:
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount, StaticSemaphore_t * pxSemaphoreBuffer )
参数:
uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
uxInitialCount: 计数信号量初始值。
pxSemaphoreBuffer:指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL: 计数型信号量创建失败。
其他值: 计数型号量创建成功,返回计数型信号量句柄。
3 、计数型信号量创建过程分析
这里只分析动态创建计数型信号量函数 xSemaphoreCreateCounting(),此函数是个宏,定义如下:
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif
可以看出,真正干事的是函数 xQueueCreateCountingSemaphore(),此函数在文件 queue.c 中 有如下定义:
QueueHandle_txQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
QueueHandle_txHandle;
configASSERT( uxMaxCount != 0 );
configASSERT( uxInitialCount <= uxMaxCount );
xHandle = xQueueGenericCreate( uxMaxCount, \ (1) queueSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL ) {
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; (2)
traceCREATE_COUNTING_SEMAPHORE(); }
else {
traceCREATE_COUNTING_SEMAPHORE_FAILED(); }
return xHandle;
}
(1)计数型信号量也是在队列的基础上实现的,所以需要调用函数xQueuGnenricCreate()创建应该队列,队列长度为uxMaxCount,对列项长度为queueSEMAPHORE_QUEUE_ITEM_LENGTH(此宏为0,队列的类型为queueQUEUE_TYPE_COUNTING_SEMAPHORE,表示是个计数型信号量。
(2)队列结构体的成员变量 uxMessagesWaiting 用于计数型信号量的计数,根据计数型信 号量的初始值来设置 uxMessagesWaiting。
4、释放和获取计数信号量
计数型信号量的释放和获取与二值信号量相同
信号量的释放
释放信号量的函数有两个,如下表:
同队列一样,释放信号量也分为任务级和中断级。还有! 不管是二值信号量、计数型信号量还是互斥信号量,它们都使用上表中的函数释放信号量,递归互斥信号量有专用的释放函数。
1 、函数 xSemaphoreGive()
此函数用于释放二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正释放信 号量的过程是由函数 xQueueGenericSend()来完成的,函数原型如下:
BaseType_t xSemaphoreGive( xSemaphore )
参数:
xSemaphore:要释放的信号量句柄。
返回值:
pdPASS: 释放信号量成功。
errQUEUE_FULL: 释放信号量失败。
我们再来看一下函数 xSemaphoreGive()的具体内容,此函数在文件 semphr.h 中有如下定义:
#define xSemaphoreGive( xSemaphore )
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ),
NULL,
semGIVE_BLOCK_TIME,
queueSEND_TO_BACK )
可以看出任务级释放信号量就是向队列发送消息的过程,只是这里并没有发送具体的消息, 阻塞时间为 0(宏 semGIVE_BLOCK_TIME 为 0),入队方式采用的后向入队,入队的时候队列结构体成员变量 uxMessagesWaiting 会加一,对于二值信号量通过判断 uxMessagesWaiting 就可以知道信号量是否有效了,当 uxMessagesWaiting 为 1 的话说明二值信号量有效,为 0 就无效。如果队列满的话就返回错误值 errQUEUE_FULL,提示队列满,入队失败。
下面是相关计数型信号量的释放:
2、函数xSemaphoreGiveFromISR()
此函数用于在中断中释放信号量,此函数只能用来释放二值信号量和计数型信号量,绝对 不 能用来在中断服务函数中释放互斥信号量!此函数是一个宏,真正执行的是函数 xQueueGiveFromISR() ,此函数原型如下:
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t * pxHigherPriorityTaskWoken)
参数:
xSemaphore : 要释放的信号量句柄。
pxHigherPriorityTaskWoken : 标记退出此函数以后是否进行任务切换,这个变量的值由这
三个函数来设置的,用户不用进行设置,用户只需要提供一 个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退 出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS: 释放信号量成功。
errQUEUE_FULL: 释放信号量失败。
在中断中释放信号量真正使用的是函数 xQueueGiveFromISR(),此函数和中断级通用入队函数 xQueueGenericSendFromISR() 极其类似!
只是针对信号量做了微 小 的 改 动 。 函 数 xSemaphoreGiveFromISR()不能用于在中断中释放互斥信号量,因为互斥信号量涉及到优先级继 承的问题,而中断不属于任务,没法处理中断优先级继承。大家可以参考第十三章分析函数 xQueueGenericSendFromISR()的过程来分析 xQueueGiveFromISR()。
获取信号量
获取信号量也有两个函数,如下表:
同释放信号量的 API 函数一样,不管是二值信号量、计数型信号量还是互斥信号量,它们都使用表中的函数获取信号量
1、函数 xSemaphoreTake()
此函数用于获取二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正获取信 号量的过程是由函数 xQueueGenericReceive ()来完成的,函数原型如下:
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
TickType_t xBlockTime)
参数:
xSemaphore:要获取的信号量句柄。 xBlockTime: 阻塞时间。
返回值:
pdTRUE: 获取信号量成功。
pdFALSE: 超时,获取信号量失败。
再来看一下函数 xSemaphoreTake ()的具体内容,此函数在文件 semphr.h 中有如下定义:
#define xSemaphoreTake( xSemaphore, xBlockTime )
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ),
NULL,
( xBlockTime ),
pdFALSE )
取信号量的过程其实就是读取队列的过程,只是这里并不是为了读取队列中的消息。在讲解函数 xQueueGenericReceive()的时候说过如果队列为空并且阻塞时间为 0 的话就立即返回 errQUEUE_EMPTY,表示队列满。如果队列为空并且阻塞时间不为 0 的话就将任务 添加到延时列表中。如果队列不为空的话就从队列中读取数据(获取信号量不执行这一步),数 据读取完成以后还需要将队列结构体成员变量 uxMessagesWaiting 减一,然后解除某些因为入队而阻塞的任务,最后返回 pdPASS 表示出对成功。互斥信号量涉及到优先级继承,处理方式不同,后面讲解互斥信号量的时候在详细的讲解。
下面是该函数的操作案例:
2、函数xSemaphoreTakeFromISR ()
此函数用于在中断服务函数中获取信号量,此函数用于获取二值信号量和计数型信号量, 绝对不能使用此函数来获取互斥信号量 ! 此函数是一个宏 , 真正执行的是函数 xQueueReceiveFromISR (),此函数原型如下:
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t * pxHigherPriorityTaskWoken)
参数:
xSemaphore: 要获取的信号量句柄。
pxHigherPriorityTaskWoken : 标记退出此函数以后是否进行任务切换,这个变量的值由这
三个函数来设置的,用户不用进行设置,用户只需要提供一 个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退 出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS: 获取信号量成功。
pdFALSE: 获取信号量失败。
在中断中获取信号量真正使用的是函数 xQueueReceiveFromISR (),这个函数就是中断级出队函数!当队列不为空的时候就拷贝队列中的数据(用于信号量的时候不需要这一步),然后将队列结构体中的成员变量 uxMessagesWaiting 减一,如果有任务因为入队而阻塞的话就解除阻塞态,当解除阻塞的任务拥有更高优先级的话就将参数 pxHigherPriorityTaskWoken 设置为 pdTRUE,最后返回 pdPASS 表示出队成功。如果队列为空的话就直接返回 pdFAIL 表示出队失败!这个函数还是很简单的。
3.函数uxSemaphoreGetCount()
下面是用法:
该函数获取信号量 CountSemaphore 的信号量值,释放信号量的话信号量值就会加一。函数 uxSemaphoreGetCount()是用来获取信号量值的,这个函数是个宏,是对函数 uxQueueMessagesWaiting()的一个简单封装,其实就是返回队列结构体成员变量 uxMessagesWaiting 的值。
总结:
计数型信号量是在FreeRTOS中常用到的信号量,不可或缺,必须要掌握,加以练习。