FreeRTOS信号量

信号量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;
}

 

你可能感兴趣的:(FreeRTOS)