信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥量,需要包含semphr.h头文件。
信号量包括二值信号量、计数信号量、互斥信号量和递归互斥信号量。和普通队列比起来,信号量虽然没有队列项实体,但是信号量值等同于队列项个数。
实际上就是创建一个队列项大小为0、队列长度为1的队列,FreeRTOS提供了两个API。
初始值为0
#define xSemaphoreCreateBinary() xQueueGenericCreate((UBaseType_t)1, semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE)
初始值为1
#define vSemaphoreCreateBinary(xSemaphore) \
{ \
(xSemaphore) = xQueueGenericCreate((UBaseType_t)1, semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE); \
if((xSemaphore) != NULL) \
{ \
(void) xSemaphoreGive((xSemaphore)); \
} \
}
实际上就是创建一个队列项大小为0的队列,创建的时候通过参数设定初始信号量
#define xSemaphoreCreateCounting(uxMaxCount, uxInitialCount) \
xQueueCreateCountingSemaphore((uxMaxCount), (uxInitialCount))
/* 创建计数信号量 */
QueueHandle_t xQueueCreateCountingSemaphore(const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount)
{
QueueHandle_t xHandle;
configASSERT(uxMaxCount != 0);
configASSERT(uxInitialCount <= uxMaxCount);
/* 创建队列 */
xHandle = xQueueGenericCreate(uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE);
if(xHandle != NULL)
{
/* 初始化信号量值 */
((Queue_t *)xHandle)->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else
{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
return xHandle;
}
1.如果信号量值不为0,则顺利获取,同时将等待释放信号量而阻塞的任务移除并重新挂接到就绪列表
2.如果信号量值为0,将队列挂接到等待获取信号量列表中,并挂接到延时列表中进行阻塞
#define xSemaphoreTake(xSemaphore, xBlockTime) \
xQueueSemaphoreTake((xSemaphore), (xBlockTime))
/* 获取信号量值 */
BaseType_t xQueueSemaphoreTake(QueueHandle_t xQueue, TickType_t xTicksToWait)
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t *const pxQueue = xQueue;
#if (configUSE_MUTEXES == 1)
BaseType_t xInheritanceOccurred = pdFALSE;
#endif
configASSERT((pxQueue));
configASSERT(pxQueue->uxItemSize == 0);
#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
{
configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
}
#endif
for(;;)
{
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 信号量计数 */
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
/* 信号量值大于0 */
if(uxSemaphoreCount > (UBaseType_t)0)
{
traceQUEUE_RECEIVE(pxQueue);
/* 信号量值减一 */
pxQueue->uxMessagesWaiting = uxSemaphoreCount - (UBaseType_t)1;
#if (configUSE_MUTEXES == 1)
{
if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
{
pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
/* 等待释放信号量而阻塞的任务列表不为空 */
if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE)
{
/* 将任务从释放信号量而阻塞的任务列表中移除,任务优先级大于当前任务优先级 */
if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE)
{
/* 请求切换任务 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 成功 */
return pdPASS;
}
/* 信号量值为0 */
else
{
/* 等待时间为0 */
if(xTicksToWait == (TickType_t)0)
{
#if (configUSE_MUTEXES == 1)
{
configASSERT(xInheritanceOccurred == pdFALSE);
}
#endif
/* 退出临界区 */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED(pxQueue);
/* 返回队列为空错误 */
return errQUEUE_EMPTY;
}
/* 没有记录过当前节拍状态 */
else if(xEntryTimeSet == pdFALSE)
{
/* 记录当前节拍状态 */
vTaskInternalSetTimeOutState(&xTimeOut);
/* 已经记录过当前节拍状态 */
xEntryTimeSet = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 挂起调度器 */
vTaskSuspendAll();
/* 锁定队列 */
prvLockQueue(pxQueue);
/* 检查是否超时,没有超时 */
if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
{
/* 检查信号量值是否为0,为0 */
if(prvIsQueueEmpty(pxQueue) != pdFALSE)
{
traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue);
#if (configUSE_MUTEXES == 1)
{
if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
{
taskENTER_CRITICAL();
{
xInheritanceOccurred = xTaskPriorityInherit(pxQueue->u.xSemaphore.xMutexHolder);
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
/* 将任务插入等待获取信号量而阻塞的任务列表中 */
vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait);
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
if(xTaskResumeAll() == pdFALSE)
{
/* 请求调度 */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 队列不为空,while下一个循环时取走队列项 */
else
{
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
(void)xTaskResumeAll();
}
}
/* 已经超时或者等待超时后 */
else
{
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
(void)xTaskResumeAll();
/* 检查队列是否为空,为空 */
if(prvIsQueueEmpty(pxQueue) != pdFALSE)
{
#if (configUSE_MUTEXES == 1)
{
if(xInheritanceOccurred != pdFALSE)
{
taskENTER_CRITICAL();
{
UBaseType_t uxHighestWaitingPriority;
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue);
vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority);
}
taskEXIT_CRITICAL();
}
}
#endif
traceQUEUE_RECEIVE_FAILED(pxQueue);
/* 返回队列为空错误 */
return errQUEUE_EMPTY;
}
/* 队列不为空,while下一个循环时取走队列项 */
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
直接利用xQueueGenericSend函数
#define xSemaphoreGive(xSemaphore) \
xQueueGenericSend((QueueHandle_t)(xSemaphore), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK)
获取信号量,直接利用xQueueReceiveFromISR函数,参考上一篇博客
#define xSemaphoreTakeFromISR(xSemaphore, pxHigherPriorityTaskWoken) \
xQueueReceiveFromISR((QueueHandle_t)(xSemaphore), NULL, (pxHigherPriorityTaskWoken))
释放信号量
在分析之前,先了解一个知识点:
队列锁定用于中断中,队列锁定并不表示不准入队/出队。队列锁定的情况下不允许操作因为等待而阻塞的任务列表,用cRxLock和cTxLock来做记录,并在解锁的时候补上该操作。
因此,释放信号量的步骤为:
1.如果信号量已经达到上限,直接返回错误。
2.如果信号量没有达到上限,则信号量加一。(释放信号量后,那些等着获取信号的任务就可以解除阻塞了)
3.如果没有上锁,将那些等待获取信号量的任务解除阻塞,重新加入就绪列表。
4.如果已经上锁,则将上锁期间释放次数加一,等解锁之后再做补偿,将等待获取信号量而阻塞的任务一个一个解除。
#define xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken) \
xQueueGiveFromISR((QueueHandle_t) (xSemaphore), (pxHigherPriorityTaskWoken))
/* 在中断中释放信号量 */
BaseType_t xQueueGiveFromISR(QueueHandle_t xQueue, BaseType_t *const pxHigherPriorityTaskWoken)
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t *const pxQueue = xQueue;
configASSERT(pxQueue);
configASSERT(pxQueue->uxItemSize == 0);
configASSERT(!((pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) && (pxQueue->u.xSemaphore.xMutexHolder != NULL)));
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
/* 屏蔽大于系统调用优先级的中断 */
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
/* 信号量值,小于最大信号量值 */
if(uxMessagesWaiting < pxQueue->uxLength)
{
const int8_t cTxLock = pxQueue->cTxLock;
traceQUEUE_SEND_FROM_ISR(pxQueue);
/* 信号量值加一 */
pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1;
/* 队列发送未上锁 */
if(cTxLock == queueUNLOCKED)
{
......
{
/* 判断等待获取信号量而阻塞的任务列表是否为空,不为空 */
if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
{
/* 将任务从等待获取信号量而阻塞的任务列表中移除 */
if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
{
/* 允许高优先级任务抢占 */
if(pxHigherPriorityTaskWoken != NULL)
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
}
/* 队列发送上锁 */
else
{
/* 锁定期间发送次数加一 */
pxQueue->cTxLock = (int8_t)(cTxLock + 1);
}
/* 成功 */
xReturn = pdPASS;
}
/* 信号量值,等于最大信号量值 */
else
{
traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue);
/* 返回队列已满错误 */
xReturn = errQUEUE_FULL;
}
}
/* 打开中断屏蔽 */
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
return xReturn;
}