没志青年
发布于 2025-09-16 / 21 阅读
0

FreeRTOS 队列原理

队列必须掌握,这是其它的基石,不理解这个,其它的源码也看不懂。

queue.c、queue.h

队列结构体:

typedef struct QueueDefinition
{
    int8_t *pcHead;    // 队列存储区开始地址
    int8_t *pcWriteTo; // 指向存储区下一个空闲区域

    union
    {
        QueuePointers_t xQueue;     // 作为队列时的值
        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; // 队列上锁后,入队列的元素个数,没上锁则为queueUNLOCKED

#if ((configSUPPORT_STATIC_ALLOCATION == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1))
    uint8_t ucStaticallyAllocated;
    /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif

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

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

typedef xQUEUE Queue_t;

动态方式创建队列

静态方式创建队列

队列写消息

注意结构体中的读写阻塞列表

先不考虑队列集:

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;

    for (;;)
    {
        taskENTER_CRITICAL();
        {
            // 1.判断是否有剩余空间 2.如果队列采用覆写方式则不用管是否已满
            if ((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE))
            {
    
                
                    // 值拷贝到队列中
                    xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);

                    // 判断是否有任务在读等待队列消息而阻塞
                    if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
                    { // 有任务在等待
                        // 取消任务阻塞,添加任务到就绪列表
                       xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive));
   
                    }
                    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();
                    }
 
                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else // 如果队列满了
            {
                if (xTicksToWait == (TickType_t)0)
                {
                    // 阻塞超时为0则直接返回
                    taskEXIT_CRITICAL();
                    // traceQUEUE_SEND_FAILED(pxQueue);
                    return errQUEUE_FULL;
                }
                else if (xEntryTimeSet == pdFALSE) // 如果没有设置过期时间
                {
                    // 阻塞时间不为0则初始化超时时间结构体
                    vTaskInternalSetTimeOutState(&xTimeOut);
                    xEntryTimeSet = pdTRUE;
                }
            }
        }
        taskEXIT_CRITICAL();

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

        // 关闭任务调度
        vTaskSuspendAll();
        // 队列上锁
        prvLockQueue(pxQueue);

        // 更新时间并检查是否超时
        if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
        { // 未超时
            if (prvIsQueueFull(pxQueue) != pdFALSE)
            { // 队列满了
                // traceBLOCKING_ON_QUEUE_SEND(pxQueue);
                /* 将当前任务的事件列表项加入到队列的等待发送列表中 */
                /* 将当前任务的状态列表项从就绪列表中移除,将其加入到延时列表中 */
                vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);

                /* 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
            {
                // 解锁,再试一次
                prvUnlockQueue(pxQueue);
                (void)xTaskResumeAll();
            }
        }
        else
        {
            // 超时
            prvUnlockQueue(pxQueue);
            (void)xTaskResumeAll();

            return errQUEUE_FULL;
        }



    }  // For End
}   // Function End

值拷贝函数 prvCopyDataToQueue 的实现:

队列读消息