5. FreeRTOS系统队列及其创建

第一节 引言

Study 队列相关部分内容。
        对于FreeRTOS系统,队列被用在任务与任务间、中断和任务间进行一条或多条信息的传递,队列不属于某个特定的任务,系统中任何任务都可以向队列中发送消息,或者从队列中提取消息。往队列中发送数据叫做入队,从队列中提取数据叫做出队。FreeRTOS系统通常使用先进先出(FIFO)的方式进行信息传递,即,任务从队列中读取到的消息是最先进入到消息队列的消息,同时也支持后进先出方式(LIFO)。

        FreeRTOS 中数据发送到队列中会导致数据拷贝,将要发送的数据拷贝到队列中,这就意味着在队列中存储的是数据的原始值,使用的是值传递,而不是原数据的引用(即只传递数据的指针)。
        正常情况下,队列会有存储已满的情况或者队列是空的情况,对于这两种情况在入队与出队时就会有些不同。
        当队列已满时,就需要有入队阻塞的设计。在队列已满的情况下,一个任务向队列直接发送数据则会出现错误,所以该情况下就要有设计入队阻塞的设计,当任务向队列发送消息时设置阻塞时间,如果在阻塞时间内等待已满的队列有空间,则任务可以将发送的数据放入队列中。
        当队列已空时,就需要有出队阻塞的设计。在队列的为空情况下,一个任务从队列直接读取数据则会出现错误,所以该情况下就要有设计出队阻塞的设计,当任务从队列读取消息时设置阻塞时间,如果在阻塞时间内等待已空的队列有数据了,则任务可以将读取队列中的数据。

第二节 队列结构体

typedef struct QueueDefinition
{
    int8_t *pcHead;                    /*此为队列存储区域的开始地址*/
    int8_t *pcTail;                    /*队列存储区的最后一个字节*/
    int8_t *pcWriteTo;                /*存储区中下一个空闲区域*/
    union 
    {
        int8_t *pcReadFrom;            /*当用作队列时指向最后一个出队的队列项首地址*/
        UBaseType_t uxRecursiveCallCount; /*当用作递归互斥量时用来记录递归互斥量被调用的次数*/
    } 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 ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxQueueNumber;
        uint8_t ucQueueType;   /* 队列的类型 0: 队列或队列集 1: 互斥信号量 2: 计数型信号量 3: 二值信号量 4: 可递归信号量 */
    #endif
} xQUEUE;

第三节 队列创建函数 xQueueGenericCreate()

        创建队列有两个函数可以调用,使用动态内存分配的xQueueCreate()函数与使用静态内存分配 xQueueCreateStatic()函数。两个函数最后返回的都是可用于访问新创建的队列的队列句柄。
函数的调用顺序如下:
xQueueCreate()或xQueueCreateStatic() ->xQueueGenericCreate()
                    ->prvInitialiseNewQueue() ->xQueueGenericReset()
        下面是对函数的一些说明。

1.xQueueGenericCreate()

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )        
        xQueueGenericCreate()用于创建一个新的队列并返回可用于访问此队列的队列句柄。每创建一个新的队列都需要为其分配 RAM,用于存储队列的状态以及队列消息的存储区域。        
        uxQueueLength表示消息队列能够存储的最大单元数,即队列深度或队列长度。
        uxItemSize表示设置消息队列中单个消息单元的长度,以字节为单位。
        ucQueueType表示设置消息队列的类型,0: 队列或队列集 1: 互斥信号量 2: 计数型信号量 3: 二值信号量 4: 可递归信号量 。
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
        Queue_t *pxNewQueue;
        size_t xQueueSizeInBytes;
       uint8_t *pucQueueStorage;
       configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
           //计算队列环形存储空间需要的字节大小,并为队列申请内存空间,空间大小 = 队列结构体+队列环形存储区域
         xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); 
        pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
        if( pxNewQueue != NULL )
        {
                pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); //获取队列存储区的起始地址
                #if( configSUPPORT_STATIC_ALLOCATION == 1 )
               {
                        pxNewQueue->ucStaticallyAllocated = pdFALSE;
                }
                 #endif /* configSUPPORT_STATIC_ALLOCATION */
                 prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );  //对队列进行初始化
        }
        return pxNewQueue;
    }

2.prvInitialiseNewQueue()

        prvInitialiseNewQueue()被用来初始化队列结构体。
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
        (void) ucQueueType;
        if( uxItemSize == ( UBaseType_t ) 0 )
        {
                pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; //如果队列项目大小为 0(类型为信号量),则不需要队列存储区,pcHead指向队列存储区的起始地址
        }
        else
        {
                pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; //pcHead指向队列存储区域的起始地址
        }
        pxNewQueue->uxLength = uxQueueLength; //初始化队列长度
        pxNewQueue->uxItemSize = uxItemSize; //队列消息的大小
        (void) xQueueGenericReset( pxNewQueue, pdTRUE ); //

        pxNewQueue->ucQueueType = ucQueueType;  //队列的类型
        traceQUEUE_CREATE( pxNewQueue );
}

3.xQueueGenericReset()

xQueueGenericReset()函数来重置队列,继续初始化队列结构体中的成员。
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
        Queue_t * const pxQueue = ( Queue_t * ) xQueue;
        configASSERT( pxQueue );
        taskENTER_CRITICAL();
        {
                pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );  //队列存储区域的结束地址
                pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; //队列中现有消息数量初始化为0
                pxQueue->pcWriteTo = pxQueue->pcHead; //队列下一个写入的位置
                pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );  //最后一次读取的位置
                pxQueue->cRxLock = queueUNLOCKED;
                pxQueue->cTxLock = queueUNLOCKED;
        if( xNewQueue == pdFALSE )
        {
            //在初始化旧的队列之前,可能队列为空会而造成任务阻塞进入阻塞任务列表,要对阻塞任务列表中的任务进行阻塞状态解除
            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
            {
                if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                {
                    queueYIELD_IF_USING_PREEMPTION(); //如果解除阻塞的任务优先级为就绪态任务中的最高优先级,此时需要进行任务切换操作
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            //当队列为新创建时,则需要初始化队列结构体中的两个阻塞链表
       vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
            vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
        }
    }
    taskEXIT_CRITICAL();
    return pdPASS;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值