🧾 简介
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;
}