【FreeRTOS】队列

本文深入探讨FreeRTOS中的队列机制,包括队列的基本概念、队列的使用方法及其实现原理。介绍如何通过队列实现任务间的通信,重点讲解队列的创建、数据的读写流程以及队列中的关中断、环形缓冲区和任务链表等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【FreeRTOS】之队列

队列是为任务与任务、任务与中断之间的通信而实现的,也称消息队列。队列中可以存储有限的、大小固定的数据项目。队列是FreeRTOS的重点,后面的信号量,互斥锁其本质都是队列。

QueueSend()
{
    // 关中断,任务的中断都是在定时器中断函数里完成切换的,关掉时钟中断就没有办法切换任务,这样就避免访问冲突的情况发生。而且关闭的是部分中断(某优先级以下的中断),近似于全部中断但不是关闭所有中断。注意:关闭中断不是关闭定时器
    // 写数据
    // 开中断
}

队列

  • 创建队列,队列的存储空间从heap中分配,API如下:
        /**
        * uxQueueLength:指定队列的长度,即最多可以存放的元素个数
        * uxItemSize:队列中存储的元素的大小(占用的字节数)
        */
        QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
    
  • 元素入队时,将数据插入队尾,使用API如下:
        /** 
        * xQueue:目标队列的句柄。
        * pvItemToQueue:入队元素的指针。队列将存储此指针指向的数据的备份。
        * xTicksToWait:指定等待队列有空间可以容纳新元素入队的最长等待(阻塞)时间,这种情况发生在队列已经满了的时候。如果需要等待,则任务会因为调用这个函数而进入阻塞状态,直到队列非满而能让这个任务写入数据或者指定的阻塞时间过期,才会转变为就绪态。如果此参数使用0,则当队列已经满了的时候,此函数立即返回而不阻塞。
        */
        #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
        xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
    
  • 元素出队时,从队首取出数据,使用API如下:
        /** xQueue:目标队列的句柄。
        * pvBuffer:用于存放读取到的队列元素的缓冲区,队列将把出队的元素拷贝到此缓冲区中。
        * xTicksToWait:参见xQueueSend()函数的解释。
        */
        BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
    

队列图片

使用队列的好处

  • 数据互斥
  • 休眠唤醒,提高CPU的使用率

队列的核心:关中断—环形缓冲区—任务链表

  • 关中断: 使用taskENTER_CRITICAL()关闭中断,防止写/读操作被打断,从而实现数据互斥
  • 环形缓冲区: 数据保存
  • 任务链表: 用两个任务链表实现休眠与唤醒
    队列阻塞任务链表
关调度与关中断
  • 关闭调度器一般是防止任务切换,但仍然可以响应中断,使用void vTaskSuspendAll( void );而关中断一般是将某优先级以下的中断关闭,防止中断对当前操作进行干扰,使用taskENTER_CRITICAL()
环形缓冲区
  • 实质上就是有读和写两个指针
  • 假设申请的ringbuf大小为LEN,那么w=w+1/LEN,r=r+1/LEN。这样能使指针指向的范围保持在[ringbuf头地址,头地址+LEN]之间
Queue list:两个任务链表
  • List_t xTaskWaitingToSend; // 存放写此队列不成功而挂起的任务
  • List_t xTaskWaitingToReceive; // 存放读此队列不成功而挂起的任务
队列如何知道唤醒哪个任务?
  • Queue.list[],此队列结构体中有需要唤醒的任务。
  • 以读队列为例,如果发现队列中没有数据,此时不仅需要将task自身从ReadyList[]移到pxDelayedTaskList[],还需要将自己插入Queue.list[]。
  • 经过上一步,发送任务在写数据之后就可以根据Queue.list唤醒接收任务。

队列结构体以及读写队列

队列结构体

/*
 * Definition of the queue used by the scheduler.
 * Items are queued by copy, not reference.  See the following link for the
 * rationale: https://www.FreeRTOS.org/Embedded-RTOS-Queues.html
 */
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    int8_t * pcHead;           /*< 指向队列存储区的开头. */
    int8_t * pcWriteTo;        /*< 指向存储区域中空闲的下一个位置. */

    union
    {
        QueuePointers_t xQueue;     /*< 将此结构用作队列时专门需要的数据,这里就是ringbuf了. */
        SemaphoreData_t xSemaphore; /*< 仅当此结构用作信号量时才需要的数据,作为信号量时用此值,信号量与队列共用结构体. */
    } u;

    List_t xTasksWaitingToSend;             /*< 队列满时,待发送任务列表。 按优先级顺序存储. */
    List_t xTasksWaitingToReceive;          /*< 被阻塞等待从此队列读取的任务列表。 按优先级顺序存储. */

    volatile UBaseType_t uxMessagesWaiting; /*< 当前队列中的项目数. */
    UBaseType_t uxLength;                   /*< 队列的长度定义为它将容纳的项目数,而不是字节数. */
    UBaseType_t uxItemSize;                 /*< 队列中每个项目的大小. */

    volatile int8_t cRxLock;                /*< 存储队列被锁定时从队列中接收(从队列中删除)的项目数。 当队列未锁定时设置为 queueUNLOCKED。 */
    volatile int8_t cTxLock;                /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */

    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated; /*< 如果队列使用的内存是静态分配的,则设置为 pdTRUE,以确保不会尝试释放内存。 */
    #endif

    #if ( configUSE_QUEUE_SETS == 1 )
        struct QueueDefinition * pxQueueSetContainer;
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxQueueNumber;
        uint8_t ucQueueType;
    #endif
} xQUEUE;

Queue创建好之后的总大小是 Queue结构体大小 + Ringbuf大小(Queue uxLength * uxItemSize)。ringbuf是紧跟在Queue之后的。
xQueueGenericCreate()中:

pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

读队列

流程
  1. 关中断
  2. 判断有无数据
    • 有数据:直接进行第三步
    • 无数据:等有数据被唤醒后进行第三步
      • 返回ERR
      • 休眠:
        (a). 在Queue.xTasksWaitingToReceive记录自己
        (b). 将自己所在任务从ReadyList[]放入DelayedList[]
  3. 终于有数据
    (a). Copy数据
    (b). 唤醒Queue.xTaskWaitingToSend[]中的第一个任务,并从此列表中移除;并将前面移除的任务从pxDelayedTaskList[]移到ReadyList[]
代码:xQueueReceive
点击展开代码

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait )
{
    BaseType_t xEntryTimeSet = pdFALSE;
    TimeOut_t xTimeOut;
    Queue_t * const pxQueue = xQueue;

    /* Check the pointer is not NULL. */
    configASSERT( ( pxQueue ) );

    /* The buffer into which data is received can only be NULL if the data size
     * is zero (so no data is copied into the buffer). */
    configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) );

    /* Cannot block if the scheduler is suspended. */
    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
    {
        configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
    }
    #endif

    /*lint -save -e904  This function relaxes the coding standard somewhat to
     * allow return statements within the function itself.  This is done in the
     * interest of execution time efficiency. */
    for( ; ; )
    {
        taskENTER_CRITICAL();      // 关中断
        {
            const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

            /* Is there data in the queue now?  To be running the calling task
             * must be the highest priority task wanting to access the queue. */
            if( uxMessagesWaiting > ( UBaseType_t ) 0 )     // 判断队列中有无数据
            {
                /* Data available, remove one item. */
                prvCopyDataFromQueue( pxQueue, pvBuffer );    // 从队列复制数据
                traceQUEUE_RECEIVE( pxQueue );
                pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;

                /* There is now space in the queue, were any tasks waiting to
                 * post to the queue?  If so, unblock the highest priority waiting
                 * task. */
                if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
                {
                    if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )   // 从xTasksWaitingToSend移除第一个等待任务
                    {
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else     // 没有数据
            {
                if( xTicksToWait == ( TickType_t ) 0 )
                {
                    /* The queue was empty and no block time is specified (or
                     * the block time has expired) so leave now. */
                    taskEXIT_CRITICAL();
                    traceQUEUE_RECEIVE_FAILED( pxQueue );
                    return errQUEUE_EMPTY;
                }
                else if( xEntryTimeSet == pdFALSE )     
                {
                    /* The queue was empty and a block time was specified so
                     * configure the timeout structure. */
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* Entry time was already set. */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* Interrupts and other tasks can send to and receive from the queue
         * now the critical section has been exited. */

        vTaskSuspendAll();
        prvLockQueue( pxQueue );

        /* Update the timeout state to see if it has expired yet. */
        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )     // 没有数据愿意等待
        {
            /* The timeout has not expired.  If the queue is still empty place
             * the task on the list of tasks waiting to receive from the queue. */
            if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
            {
                traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );     // 把当前任务放入xTasksWaitingToReceive,并将任务从ReadyList[]移到pxDelayedTaskList[]
                prvUnlockQueue( pxQueue );

                if( xTaskResumeAll() == pdFALSE )
                {
                    portYIELD_WITHIN_API();      // 让步休眠
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /* The queue contains data again.  Loop back to try and read the
                 * data. */
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        else
        {
            /* Timed out.  If there is no data in the queue exit, otherwise loop
             * back and attempt to read the data. */
            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();

            if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
            {
                traceQUEUE_RECEIVE_FAILED( pxQueue );
                return errQUEUE_EMPTY;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    } /*lint -restore */
}
读队列阻塞
  • 如果队列为空时读取队列,并且参数xTicksToWait不为0,则将当前任务移至移至DelayedList中阻塞。直到队列中有消息到来或者超时,才会唤醒DelayedList中优先级最高的或者最先超时的任务。

写队列

流程
  1. 一言不合关中断
  2. 判断有无空间,若有空间跳过此步骤
    • 有空间:直接进行第三步
    • 无空间:
      • 返回ERR
      • 休眠:
        (a): 将当前任务放入Queue.xTasksWaitingToSend
        (b): 将当前任务从ReadyList[]移至DelayedList[]
  3. 终于有空间了:
    (a): write Data
    (b): 唤醒接收任务:从Queue.xTasksWaitingToReceive中移除第一个任务, 并将此任务从DelayedList[]移至ReadyList[]
被阻塞的发送任务如何被唤醒?

有两种情况:

  • 超时(此情况在tick中断中处理)
    • 任务写队列不成功时,它会被挂起:从ready list移到delayed list中
    • 在delayed list中,按照"超时时间"排序
    • 系统Tick中断不断发生,在Tick中断里判断delayed list中的任务时间到没?时间到后就唤醒它,具体代码参照task.c中的BaseType_t xTaskIncrementTick( void )
  • 其他任务读取队列导致队列有空间(此情况在其他任务的xQueueGenericSend中处理)
    • prvCopyDataFromQueue(pxQueue, pvBuffer)有队列在读完数据之后会留出空间
    • 这时会检测xTaskWaitingToSend有无任务在等待发送数据,若有则唤醒。具体代码请参考下方代码(queue.c中的xQueueGenericSend)
pxDelayedTaskList[]
  • 按照被唤醒的时间存放阻塞任务的链表。
Tick中断都做了什么?(xTaskIncrementTick)
  • 增加计数值, const TickType_t xConstTickCount = xTickCount + ( TickType_t)1
  • 判断是否有阻塞中的任务超时,若有则把DelayedList中超时的任务移动到ReadyList[]
  • BaseType_t xTaskIncrementTick( void ):
        BaseType_t xTaskIncrementTick( void ) {
            TCB_t * pxTCB;
            TickType_t xItemValue;
            BaseType_t xSwitchRequired = pdFALSE;
    
            traceTASK_INCREMENT_TICK( xTickCount );
        
            if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) {
                const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;   // 计数
                xTickCount = xConstTickCount;
        
                if( xConstTickCount == ( TickType_t ) 0U ) {
                    taskSWITCH_DELAYED_LISTS();
                } else {
                    mtCOVERAGE_TEST_MARKER();
                }
        
                if( xConstTickCount >= xNextTaskUnblockTime ) {
                    for( ; ; ) {
                        if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) {
                            xNextTaskUnblockTime = portMAX_DELAY;
                            break;
                        } else {    // 延时链表不为空说明有任务在等待
                            pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); 
                            xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );    // 获取第一个延时任务超时时间
        
                            if( xConstTickCount < xItemValue ) { // 判断是否超时,没超时则退出循环
                                xNextTaskUnblockTime = xItemValue; 
                                break;
                            } else {
                                mtCOVERAGE_TEST_MARKER();
                            }
    
                            listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
        
                            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) {
                                listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
                            } else {
                                mtCOVERAGE_TEST_MARKER();
                            }
    
                            prvAddTaskToReadyList( pxTCB );    // 将超时任务移至ReadyList
        
                            #if ( configUSE_PREEMPTION == 1 ) {
                                if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) {
                                    xSwitchRequired = pdTRUE;
                                } else {
                                    mtCOVERAGE_TEST_MARKER();
                                }
                            }
                            #endif /* configUSE_PREEMPTION */
                        }
                    }
                }
                ······    // 此处省略若干代码,具体参考https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/356fff8028734dc50a46972b2e315e2871b0a12e/tasks.c#L2734
            } else {
                ++xPendedTicks;
                #if ( configUSE_TICK_HOOK == 1 ) {
                    vApplicationTickHook();
                }
                #endif
            }
            return xSwitchRequired;
        }   
    
代码:xQueueGenericSend
点击展开代码

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait,
                              const BaseType_t xCopyPosition )
{
    BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
    TimeOut_t xTimeOut;
    Queue_t * const pxQueue = xQueue;

    configASSERT( pxQueue );
    configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
    configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
    {
        configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
    }
    #endif

    /*lint -save -e904 This function relaxes the coding standard somewhat to
     * allow return statements within the function itself.  This is done in the
     * interest of execution time efficiency. */
    for( ; ; )
    {
        taskENTER_CRITICAL();    // 一言不合关中断
        {
            /* Is there room on the queue now?  The running task must be the
             * highest priority task wanting to access the queue.  If the head item
             * in the queue is to be overwritten then it does not matter if the
             * queue is full. */
            if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
            {     // 发现此队列没满
                traceQUEUE_SEND( pxQueue );

                #if ( configUSE_QUEUE_SETS == 1 )
                {
                    const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;

                    xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

                    if( pxQueue->pxQueueSetContainer != NULL )
                    {
                        if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) )
                        {
                            /* Do not notify the queue set as an existing item
                             * was overwritten in the queue so the number of items
                             * in the queue has not changed. */
                            mtCOVERAGE_TEST_MARKER();
                        }
                        else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE )
                        {
                            /* The queue 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. */
                            queueYIELD_IF_USING_PREEMPTION();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        /* If there was a task waiting for data to arrive on the
                         * queue then unblock it now. */
                        if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                        {
                            if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
                            {
                                /* The unblocked task has a priority higher than
                                 * our own so yield immediately.  Yes it is ok to
                                 * do this from within the critical section - the
                                 * kernel takes care of that. */
                                queueYIELD_IF_USING_PREEMPTION();
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }
                        }
                        else if( xYieldRequired != pdFALSE )
                        {
                            /* This path is a special case that will only get
                             * executed if the task was holding multiple mutexes
                             * and the mutexes were given back in an order that is
                             * different to that in which they were taken. */
                            queueYIELD_IF_USING_PREEMPTION();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                }
                #else /* configUSE_QUEUE_SETS */
                {
                    xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );  //把数据拷贝到队列中

                    /* If there was a task waiting for data to arrive on the
                     * queue then unblock it now. */
                    if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                    {
                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )   // 有任务等待读数据将将其唤醒
                        {
                            /* The unblocked task has a priority higher than
                             * our own so yield immediately.  Yes it is ok to do
                             * this from within the critical section - the kernel
                             * takes care of that. */
                            queueYIELD_IF_USING_PREEMPTION();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else if( xYieldRequired != pdFALSE )
                    {
                        /* This path is a special case that will only get
                         * executed if the task was holding multiple mutexes and
                         * the mutexes were given back in an order that is
                         * different to that in which they were taken. */
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                #endif /* configUSE_QUEUE_SETS */

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else     // 队列没有空间
            {
                if( xTicksToWait == ( TickType_t ) 0 )    // 队列已满,不想等待或等待超时就直接返回错误
                {
                    /* The queue was full and no block time is specified (or
                     * the block time has expired) so leave now. */
                    taskEXIT_CRITICAL();

                    /* Return to the original privilege level before exiting
                     * the function. */
                    traceQUEUE_SEND_FAILED( pxQueue );
                    return errQUEUE_FULL;
                }
                else if( xEntryTimeSet == pdFALSE )
                {
                    /* The queue was full and a block time was specified so
                     * configure the timeout structure. */
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* Entry time was already set. */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* Interrupts and other tasks can send to and receive from the queue
         * now the critical section has been exited. */

        vTaskSuspendAll();
        prvLockQueue( pxQueue );

        /* Update the timeout state to see if it has expired yet. */
        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
        {
            if( prvIsQueueFull( pxQueue ) != pdFALSE )
            {
                traceBLOCKING_ON_QUEUE_SEND( pxQueue );
                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );    // 愿意等待就将当前任务放到 xTasksWaitingToSend 链表上, 并在vTaskPlaceOnEventList内部会将函数从ReadyList[]放到pxDelayedTaskList[]

                /* Unlocking the queue means queue events can effect the
                 * event list. It is possible that interrupts occurring now
                 * remove this task from the event list again - but as the
                 * scheduler is suspended the task will go onto the pending
                 * ready list instead of the actual ready list. */
                prvUnlockQueue( pxQueue );

                /* Resuming the scheduler will move tasks from the pending
                 * ready list into the ready list - so it is feasible that this
                 * task is already in the ready list before it yields - in which
                 * case the yield will not cause a context switch unless there
                 * is also a higher priority task in the pending ready list. */
                if( xTaskResumeAll() == pdFALSE )
                {
                    portYIELD_WITHIN_API();
                }
            }
            else
            {
                /* Try again. */
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        else
        {
            /* The timeout has expired. */
            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();

            traceQUEUE_SEND_FAILED( pxQueue );
            return errQUEUE_FULL;
        }
    } /*lint -restore */
}
写队列阻塞
  • 如果在队列满时写队列,并且xTicksToWait不为0,这时会将当前发送数据到队列的任务移入DelayedList中阻塞,直到队列有空间或发送超时才被唤醒。
### FreeRTOS 队列的使用方法 在 FreeRTOS 中,队列是一种重要的通信机制,允许任务之间或者中断与任务之间的据传递。以下是关于 FreeRTOS 队列使用的详细介绍。 #### 创建队列 要创建一个队列,可以调用 `xQueueCreate` 函[^2]。该函需要两个参队列长度 (`uxQueueLength`) 和每个项目的据大小 (`uxItemSize`)。如果内存不足,则会返回 NULL。因此,在实际开发中,建议检查返回值以确认队列是否成功创建。 ```c QueueHandle_t xQueue = xQueueCreate(10, sizeof(uint32_t)); if (xQueue == NULL) { // 处理错误情况 } ``` #### 向队列发送据 向队列发送据可以通过阻塞模式或非阻塞模式完成。常用的 API 是 `xQueueSend` 或其变体(如 `xQueueOverwrite`)。这些函通常用于将消息放入队列供其他任务读取。 - **阻塞模式**: 如果队列已满,任务会被挂起直到有空间可用。 - **非阻塞模式**: 若无空间立即返回失败状态。 示例如下: ```c uint32_t ulValueToSend = 1; BaseType_t xStatus; // 尝试向队列发送据并等待最多10个ticks的时间 xStatus = xQueueSend(xQueue, &ulValueToSend, pdMS_TO_TICKS(10)); if (xStatus != pdPASS) { // 发送失败处理逻辑 } ``` #### 从队列接收据 同样地,可以从队列中获取据,支持阻塞和非阻塞两种方式。API 如 `xQueueReceive` 可用来提取最新的条目。 ```c uint32_t ulReceivedValue; BaseType_t xStatus; // 接收来自队列的消息,并愿意最长等待20个tick周期 xStatus = xQueueReceive(xQueue, &ulReceivedValue, pdMS_TO_TICKS(20)); if (xStatus == pdTRUE) { // 据有效,执行后续操作 } else { // 超时或其他异常情形下的应对措施 } ``` #### 解决常见问题 当遇到与队列有关的问题时,可能涉及以下几个方面: 1. **队列未被正确初始化**:确保每次调用 `xQueueCreate` 返回的有效句柄都被妥善存储并加以利用[^2]。 2. **资源竞争条件**:多个生产者/消费者访问同一队列可能导致同步问题;务必遵循互斥原则设计程序结构[^3]。 3. **超时设置不合理**:过短可能会频繁触发时间到期事件而影响效率;反之则增加响应延迟风险。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值