🧾 简介

xQueueGiveFromISR()是 FreeRTOS 中用于​​在中断服务程序(ISR)中释放信号量或轻量级队列项​​的核心函数。其核心功能是唤醒等待任务并管理中断上下文中的任务调度。


🔎 逻辑分析

1. 核心功能:中断环境下的信号量释放​

  • ​信号量/队列操作​
    该函数用于释放二值信号量、计数型信号量或长度为零的队列(此时不传递实际数据)。通过增加队列的 uxMessagesWaiting计数值(信号量的核心状态),标记资源可用性。
  • ​中断安全设计​
    专为中断上下文设计,不包含阻塞逻辑(普通任务函数如 xQueueSend()可能阻塞),确保中断快速退出。

​2. 关键参数解析​

  • xQueue
    目标队列/信号量的句柄。需确保其类型支持信号量操作(uxItemSize == 0)。
  • pxHigherPriorityTaskWoken
    输出参数,用于标记是否需要上下文切换:
    • pdTRUE​:释放操作唤醒了更高优先级的任务(需手动触发切换)。
    • NULL​:忽略切换请求(适用于不立即退出中断的场景)。

​3. 内部执行流程​

  • ​资源检查​
    验证队列是否未满(uxMessagesWaiting < uxLength),若已满则返回 errQUEUE_FULL
  • ​计数更新​
    增加 uxMessagesWaiting,表示新增一个可用资源(例如信号量计数+1)。
  • ​任务唤醒机制​
    • 若队列未锁定(cTxLock == queueUNLOCKED),检查等待接收的任务列表(xTasksWaitingToReceive)。
    • 唤醒优先级最高的任务,并标记 pxHigherPriorityTaskWoken(若其优先级高于当前任务)。
  • ​队列锁定处理​
    若队列被锁定(如其他任务正在操作),仅递增锁定计数 cTxLock,延迟唤醒操作到队列解锁时。

​4. 返回值与错误处理​

  • pdPASS
    信号量/队列释放成功。
  • errQUEUE_FULL
    队列已满(例如计数型信号量已达最大值)。
  • ​断言保护​
    通过 configASSERT()检查关键条件(如非互斥锁场景、中断优先级合规性),防止非法调用。

​5. 典型应用场景​

  • ​中断与任务同步​
    例如:串口接收中断中释放信号量,通知任务处理数据。
  • ​资源管理​
    释放计数型信号量表示资源可用(如释放内存块后增加资源计数)。
  • ​任务唤醒优化​
    结合 portYIELD_FROM_ISR()实现高效任务切换:
    BaseType_t highPriorityWoken = pdFALSE; xQueueGiveFromISR(xSemaphore, &highPriorityWoken); portYIELD_FROM_ISR(highPriorityWoken); // 必要时切换任务

⚠️ 注意事项​​

  • ​禁止在任务中调用​
    必须仅在中断中使用,任务环境应使用 xSemaphoreGive()或 xQueueSend()
  • ​互斥信号量限制​
    不可用于释放互斥锁(queueQUEUE_IS_MUTEX类型),因中断无法处理优先级继承逻辑。
  • ​零数据队列​
    仅适用于 uxItemSize == 0的队列(即信号量),否则需用 xQueueSendFromISR()

💎 总结​

xQueueGiveFromISR()是 FreeRTOS 中断环境下同步任务的核心工具,通过轻量级的队列计数操作实现任务唤醒与资源管理。其设计平衡了中断效率与任务响应实时性,是硬件事件驱动型系统的关键底层接口。


📁 附言

原型

BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	/* Similar to xQueueGenericSendFromISR() but used with semaphores where the
	item size is 0.  Don't directly wake a task that was blocked on a queue
	read, instead return a flag to say whether a context switch is required or
	not (i.e. has a task with a higher priority than us been woken by this
	post). */

	configASSERT( pxQueue );

	/* xQueueGenericSendFromISR() should be used instead of xQueueGiveFromISR()
	if the item size is not 0. */
	configASSERT( pxQueue->uxItemSize == 0 );

	/* Normally a mutex would not be given from an interrupt, especially if
	there is a mutex holder, as priority inheritance makes no sense for an
	interrupts, only tasks. */
	configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->pxMutexHolder != NULL ) ) );

	/* RTOS ports that support interrupt nesting have the concept of a maximum
	system call (or maximum API call) interrupt priority.  Interrupts that are
	above the maximum system call priority are kept permanently enabled, even
	when the RTOS kernel is in a critical section, but cannot make any calls to
	FreeRTOS API functions.  If configASSERT() is defined in FreeRTOSConfig.h
	then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
	failure if a FreeRTOS API function is called from an interrupt that has been
	assigned a priority above the configured maximum system call priority.
	Only FreeRTOS functions that end in FromISR can be called from interrupts
	that have been assigned a priority at or (logically) below the maximum
	system call	interrupt priority.  FreeRTOS maintains a separate interrupt
	safe API to ensure interrupt entry is as fast and as simple as possible.
	More information (albeit Cortex-M specific) is provided on the following
	link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */
	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

		/* When the queue is used to implement a semaphore no data is ever
		moved through the queue but it is still valid to see if the queue 'has
		space'. */
		if( uxMessagesWaiting < pxQueue->uxLength )
		{
			const int8_t cTxLock = pxQueue->cTxLock;

			traceQUEUE_SEND_FROM_ISR( pxQueue );

			/* A task can only have an inherited priority if it is a mutex
			holder - and if there is a mutex holder then the mutex cannot be
			given from an ISR.  As this is the ISR version of the function it
			can be assumed there is no mutex holder and no need to determine if
			priority disinheritance is needed.  Simply increase the count of
			messages (semaphores) available. */
			pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;

			/* The event list is not altered if the queue is locked.  This will
			be done when the queue is unlocked later. */
			if( cTxLock == queueUNLOCKED )
			{
				#if ( configUSE_QUEUE_SETS == 1 )
				{
					if( pxQueue->pxQueueSetContainer != NULL )
					{
						if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE )
						{
							/* The semaphore is a member of a queue set, and
							posting	to the queue set caused a higher priority
							task to	unblock.  A context switch is required. */
							if( pxHigherPriorityTaskWoken != NULL )
							{
								*pxHigherPriorityTaskWoken = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
						{
							if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
							{
								/* The task waiting has a higher priority so
								record that a context switch is required. */
								if( pxHigherPriorityTaskWoken != NULL )
								{
									*pxHigherPriorityTaskWoken = pdTRUE;
								}
								else
								{
									mtCOVERAGE_TEST_MARKER();
								}
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
				}
				#else /* configUSE_QUEUE_SETS */
				{
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* The task waiting has a higher priority so record that a
							context	switch is required. */
							if( pxHigherPriorityTaskWoken != NULL )
							{
								*pxHigherPriorityTaskWoken = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_QUEUE_SETS */
			}
			else
			{
				/* Increment the lock count so the task that unlocks the queue
				knows that data was posted while it was locked. */
				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;
}