Principe de file d'attente FreeRTOS

1. File d'attente

La file d'attente est préparée pour la communication entre les tâches et les tâches et les tâches et les interruptions . Les messages peuvent être transmis entre les tâches et les tâches et les tâches et les interruptions. Des éléments de données limités et de taille fixe peuvent être stockés dans la file d'attente. Lors de la création d'une file d'attente, vous devez spécifier la taille de l'élément de données et la longueur de la file d'attente.

1.1 Stockage des données

●La file d'attente utilise un mécanisme de mise en cache premier entré, premier sorti (FIFO).

Envoyer les données à la file d'attente (enqueue) : toujours envoyer à la fin de la file d'attente

Extraire les données de la file d'attente (dequeue) : toujours extraire depuis la tête de la file d'attente

FreeRTOS fournit également un mécanisme de mise en cache des données dernier entré, dernier sorti (LIFO).

●Queue copie les données à envoyer dans la file d'attente (passage de valeur)

Ce qui est stocké dans la file d'attente est la valeur originale des données, pas le pointeur (référence) des données. Bien entendu, les pointeurs peuvent également être utilisés pour passer.

1.2 Accès multitâche

La file d'attente n'appartient pas à une tâche spécifiquement désignée. N'importe quelle tâche peut envoyer des messages vers la file d'attente ou en récupérer.

1.3 Blocage de la suppression de la file d'attente

La suppression de la file d'attente fait référence à la tâche de lecture des messages de la file d'attente, et le blocage signifie que lorsqu'une tâche tente de lire les messages d'une file d'attente, un temps de blocage peut être spécifié. Par exemple, la tâche A lit les données de la file d'attente Q. Si la file d'attente Q est vide à ce moment-là, la tâche A dispose de trois options :

1. Retour direct : le temps de blocage est mis à 0

2. Attendez un moment : le temps d'attente est le temps de blocage défini

3. Attendez qu'il y ait des données avant de revenir : le temps de blocage est fixé à portMAX_DELAY

Il convient de mentionner que lorsque plusieurs tâches lisent une file d'attente vide, ces tâches entrent dans un état bloquant : plusieurs tâches attendent des données de la même file d'attente. Lorsqu'il y a des données dans la file d'attente, quelle tâche entrera à l'état prêt ?

●Tâches avec la plus haute priorité

●Si tout le monde a la même priorité, la tâche avec le temps d'attente le plus long passera à l'état prêt.

1.4 Blocage de file d'attente

Le blocage de la mise en file d'attente et le blocage de la sortie de la file d'attente sont fondamentalement les mêmes, c'est-à-dire que si la file d'attente est pleine lors de la mise en file d'attente, la méthode de traitement est la même que la sortie de la file d'attente.

2. Structure de la file d'attente

typedef struct QueuePointers
{
    int8_t * pcTail;     /*< 用作队列时指向最后一个出队的队列项的末尾地址*/
    int8_t * pcReadFrom; /*< 用作队列时指向最后一个出队的队列项首地址 */
} QueuePointers_t;

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;                 /*<  队列项的大小,int、char、short或其他*/

    volatile int8_t cRxLock;/*< 当队列上锁以后用来统计从队列中接收到的队列项数量,也就是出队的队列项数量 */
    volatile int8_t cTxLock;/*<当队列上锁以后用来统计发送到队列中队列项的数量,也就是入队的队列项数量  */

    #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;
typedef xQUEUE Queue_t;

3. Création de file d'attente

Il existe deux manières de créer une file d'attente : l'allocation de mémoire dynamique et l'allocation de mémoire statique.

●Allocation dynamique de mémoire : xQueueCreate, la mémoire de la file d'attente est allouée dynamiquement au sein de la fonction, l'appel réel : xQueueGenericCreate()

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
paramètre illustrer
uxQueueLength Longueur de la file d'attente, le nombre maximum de données (éléments) pouvant être stockées
uxItemSize Taille de chaque donnée (élément) : en octets
valeur de retour Non-0 : réussite, renvoi du handle, utilisation du handle pour faire fonctionner la file d'attente à l'avenir. NULL : échec dû à une mémoire insuffisante
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
                                       const UBaseType_t uxItemSize,
                                       const uint8_t ucQueueType )
paramètre illustrer
uxQueueLength Longueur de la file d'attente, le nombre maximum de données (éléments) pouvant être stockées
uxItemSize Taille de chaque donnée (élément) : en octets
ucQueueType Type de file d'attente. Étant donné que les sémaphores dans FreeRTOS sont également implémentés via des files d'attente, la fonction qui crée le sémaphore utilise finalement cette fonction. Par conséquent, vous devez spécifier le but de la file d'attente lors de sa création, c'est-à-dire le type de file d'attente. Il existe 6 types au total.
valeur de retour Non-0 : réussite, renvoi du handle, utilisation du handle pour faire fonctionner la file d'attente à l'avenir. NULL : échec dû à une mémoire insuffisante
paramètre illustrer
file d'attenteQUEUE_TYPE_BASE Messages de file d'attente ordinaires
file d'attenteQUEUE_TYPE_SET ensemble de files d'attente
fileQUEUE_TYPE_MUTEX Sémaphore mutuellement exclusif
file d'attenteQUEUE_TYPE_COUNTING_SEMAPHORE Sémaphore de comptage
queueQUEUE_TYPE_BINARY_SEMAPHORE sémaphore binaire
fileQUEUE_TYPE_RECURSIVE_MUTEX sémaphore récursif
  •  Allocation de mémoire statique : xQueueCreateStatic. La mémoire de la file d'attente doit être allouée à l'avance. Appel réel : xQueueGenericCreateStatic()
QueueHandle_t xQueueCreateStatic(
                           UBaseType_t uxQueueLength,
                           UBaseType_t uxItemSize,
                           uint8_t *pucQueueStorageBuffer,
                           StaticQueue_t *pxQueueBuffer
                       );
paramètre illustrer
uxQueueLength Longueur de la file d'attente, le nombre maximum de données (éléments) pouvant être stockées
uxItemSize Taille de chaque donnée (élément) : en octets
pucQueueStorageBuffer Si uxItemSize est différent de 0, pucQueueStorageBuffer doit pointer vers un tableau uint8_t dont la taille est au moins "uxQueueLength * uxItemSize"
pxQueueBuffer Une structure StaticQueue_t doit être implémentée pour enregistrer la structure de données de file d'attente
valeur de retour Non-0 : réussite, renvoie le handle, utilise le handle pour faire fonctionner la file d'attente à l'avenir. NULL : échec car pxQueueBuffer est NULL

QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
                                             const UBaseType_t uxItemSize,
                                             uint8_t * pucQueueStorage,
                                             StaticQueue_t * pxStaticQueue,
                                             const uint8_t ucQueueType )

  •  Analyse de la fonction de création de file d'attente (création dynamique)
//队列动态创建函数
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 ); 

        /* Check for multiplication overflow. */
        configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) );

        /* Check for addition overflow. */
        configASSERT( ( sizeof( Queue_t ) + xQueueSizeInBytes ) >  xQueueSizeInBytes );

        //申请内存,申请的是队列结构体和队列中消息存储区的总大小
        pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); 

        if( pxNewQueue != NULL )
        {
            //计算出消息存储区的首地址,申请到的内存是队列结构体和队列中消息存储区的总大小,队列结构体内存在前,紧跟在					//后面的就是消息存储区内存。
            pucQueueStorage = ( uint8_t * ) pxNewQueue;
            //指针偏移,计算消息存储区的首地址
            pucQueueStorage += sizeof( Queue_t );

            #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
                {
                    /* Queues can be created either statically or dynamically, so
                     * note this task was created dynamically in case it is later
                     * deleted. */
                    pxNewQueue->ucStaticallyAllocated = pdFALSE;
                }
            #endif /* configSUPPORT_STATIC_ALLOCATION */
			//初始化队列
            prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
        }
        else
        {
            traceQUEUE_CREATE_FAILED( ucQueueType );
            mtCOVERAGE_TEST_MARKER();
        }

        return pxNewQueue;
    }

//队列初始化函数
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength,//队列长度
                                   const UBaseType_t uxItemSize,//队列项目长度
                                   uint8_t * pucQueueStorage,//队列项目存储区
                                   const uint8_t ucQueueType,//队列类型
                                   Queue_t * pxNewQueue )//队列结构体
{
    /* Remove compiler warnings about unused parameters should
     * configUSE_TRACE_FACILITY not be set to 1. */
    ( void ) ucQueueType;

    if( uxItemSize == ( UBaseType_t ) 0 )
    {
        /*  队列项长度为0,说明没有队列存储区,这里将pcHead指向队列开始地址*/
        pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
    }
    else
    {
        /* 队列项长度不为0,设置pcHead指向队列存储区的首地址*/
        pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
    }

    /*初始化队列结构体相关成员变量 */
    pxNewQueue->uxLength = uxQueueLength;
    pxNewQueue->uxItemSize = uxItemSize;
    ( void ) xQueueGenericReset( pxNewQueue, pdTRUE );

    #if ( configUSE_TRACE_FACILITY == 1 )
        {
            pxNewQueue->ucQueueType = ucQueueType;
        }
    #endif /* 跟踪调式相关字段初始化 */

    #if ( configUSE_QUEUE_SETS == 1 )
        {
            pxNewQueue->pxQueueSetContainer = NULL;
        }
    #endif /* 队列集相关初始化 */

    traceQUEUE_CREATE( pxNewQueue );
}

//队列复位函数
BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
                               BaseType_t xNewQueue )
{
    Queue_t * const pxQueue = xQueue;

    configASSERT( pxQueue );

    taskENTER_CRITICAL();
    {
        pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
        pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
        pxQueue->pcWriteTo = pxQueue->pcHead;
        pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); 
        pxQueue->cRxLock = queueUNLOCKED;
        pxQueue->cTxLock = queueUNLOCKED;
		//根据xNewQueue字段确定该队列是否是新创建的队列,如果不是需要进行下面的处理
        if( xNewQueue == pdFALSE )
        {
            
            /* 由于复位队列以后队列依旧是空的,所以对于那些由于出队(从队列中读取消息)
             * 而阻塞的任务就依旧保持阻塞状态。但是对于那些由于入队(向队列中发送消息)
             * 而阻塞的任务就不同了,这些任务要解除阻塞状态,从队列的相应列表中移除*/
            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
            {
                //如果将阻塞任务从xTasksWaitingToSend列表中移除之后触发了任务切换,则进行任务切换
                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();

    /* A value is returned for calling semantic consistency with previous
     * versions. */
    return pdPASS;
}

 La structure une fois l'initialisation de la file d'attente terminée est la suivante :

4. Envoyez un message à la file d'attente

 Les données peuvent être écrites en tête de file d'attente ou en queue. Il existe deux versions de ces fonctions : utilisées dans les tâches et utilisées dans ISR. Le prototype de la fonction est le suivant :

/* 等同于xQueueSendToBack
 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSend(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );

/* 
 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSendToBack(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );


/* 
 * 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
 */
BaseType_t xQueueSendToBackFromISR(
                                      QueueHandle_t xQueue,
                                      const void *pvItemToQueue,
                                      BaseType_t *pxHigherPriorityTaskWoken
                                   );

/* 
 * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
 */
BaseType_t xQueueSendToFront(
                                QueueHandle_t    xQueue,
                                const void       *pvItemToQueue,
                                TickType_t       xTicksToWait
                            );

/* 
 * 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
 */
BaseType_t xQueueSendToFrontFromISR(
                                      QueueHandle_t xQueue,
                                      const void *pvItemToQueue,
                                      BaseType_t *pxHigherPriorityTaskWoken
                                   );
/* 覆盖队列,队列长度必须为1
 * xQueue: 写哪个队列
 * pvItemToQueue: 数据地址
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */
BaseType_t xQueueOverwrite(
                           QueueHandle_t xQueue,
                           const void * pvItemToQueue
                      );

BaseType_t xQueueOverwriteFromISR(
                           QueueHandle_t xQueue,
                           const void * pvItemToQueue,
                           BaseType_t *pxHigherPriorityTaskWoken
                      );

 Les paramètres de ces fonctions sont similaires, et ils sont expliqués ici :

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=wAAACH5BAEKAAAAAAAAAAABAAEAAAICRAEAOw==

Expliquons la fonction de mise en file d'attente au niveau des tâches xQueueGenericSend() (fonction de mise en file d'attente générale, la fonction de mise en file d'attente sans interruption ci-dessus est finalement appelée ainsi) : 

//入队函数
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,//队列句柄
                              const void * const pvItemToQueue,//指向要发送的消息,发送过程中会将消息复制到队列中
                              TickType_t xTicksToWait,//阻塞时间
                              const BaseType_t xCopyPosition )//入队方式:1.后向入队 2.前向入队 3.覆写入队
{
    BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
    TimeOut_t xTimeOut;
    Queue_t * const pxQueue = xQueue;

    for( ; ; )
    {
        taskENTER_CRITICAL();//进入临界区
        {
            /* 查询队列是否还有剩余的空间,如果采用覆写的方式入队则不用在乎队列是不是满的*/
            if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
            {
                traceQUEUE_SEND( pxQueue );

                #if ( configUSE_QUEUE_SETS == 1 )//队列集相关
                    ........省略相关代码.......
                    }
                #else /* configUSE_QUEUE_SETS */
                    {
                        //将消息复制到队列中,重点说明一下
                        //如果选择后向入队:将消息复制到队列结构成员pcWriteTo指向的队列项;复制成功后pcWriteTo增加 							//uxItemSize个字节,指向下一个队列项目
                        //如果选择前向入队或者是覆写时:将消息复制到u.pcReadFrom所指向的队列项目;同样复制成功后其向前
                        //移动uxItemSize个字节
                        //最后当消息写入成功之后,会将uxMessagesWaiting加一
                        xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

                        /* 检查是否有任务由于等待消息而进入阻塞状态 */
                        if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                        {
                            //因为前面已经向队列发送了一条消息,所以接下来将阻塞任务从xTasksWaitingToReceive上移除并
                            //将其添加到就绪列表中;如果调度器已经暂停,则这些任务就会挂到列表xPendingReadyList上
                            //如果取消阻塞的任务优先级比当前正在运行的任务的优先级高,则标记需要任务切换,即返回值为pdTRUE
                           if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
                            {//解除阻塞态任务优先级最高,因此要进行一次任务切换
                                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 )
                {//阻塞时间为0,队列已满,之间返回
                   
                    taskEXIT_CRITICAL();
                    traceQUEUE_SEND_FAILED( pxQueue );
                    return errQUEUE_FULL;
                }
                else if( xEntryTimeSet == pdFALSE )
                {//阻塞时间不为0,初始化时间结构体,记录当前系统时钟节拍计数器的值xTickCount和溢出次数xNumOfOverflows
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* Entry time was already set. */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* 任务执行到这里说明当前的状况是队列已满,而且设置了不为0的超时时间 */
		//调度器暂停,先处理当前任务,队列上锁
        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 last 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 a 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 */
}

Fonction de mise en file d'attente générale au niveau de l'interruption : xQueueGenericSendFromISR().

BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
                                     const void * const pvItemToQueue,
                                     BaseType_t * const pxHigherPriorityTaskWoken,//标记退出此函数后是否进行任务切换
                                     const BaseType_t xCopyPosition )
{
    BaseType_t xReturn;
    UBaseType_t uxSavedInterruptStatus;
    Queue_t * const pxQueue = xQueue;

    
    portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

    uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
        {//队列未满或者是采用覆写入队的方式
            const int8_t cTxLock = pxQueue->cTxLock;//判断队列是否上锁
            const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;

            traceQUEUE_SEND_FROM_ISR( pxQueue );

            
            ( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

            /* 队列上锁的时候不能操作时间列表,队列解锁的时候会补上这些操作 */
            if( cTxLock == queueUNLOCKED )//队列未上锁
            {
                #if ( configUSE_QUEUE_SETS == 1 )
                   
                ......省略队列集..............
                #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();
                        }

                        /* Not used in this path. */
                        ( void ) uxPreviousMessagesWaiting;
                    }
                #endif /* configUSE_QUEUE_SETS */
            }
            else
            {
                /* cTxLock加一,这样就知道在队列上锁期间向队列发送了数据 */
                configASSERT( cTxLock != queueINT8_MAX );

                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;
}

5. Verrouillage et déverrouillage de la file d'attente

  • Fonction de verrouillage de file d'attente : prvLockQueue().
#define prvLockQueue( pxQueue )                            \
    taskENTER_CRITICAL();                                  \
    {                                                      \
        if( ( pxQueue )->cRxLock == queueUNLOCKED )        \
        {                                                  \
            ( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \
        }                                                  \
        if( ( pxQueue )->cTxLock == queueUNLOCKED )        \
        {                                                  \
            ( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \
        }                                                  \
    }                                                      \
    taskEXIT_CRITICAL()
//上锁函数只是一个宏,本质上就是将cRxLock,cTxLock初始化为queueLOCKED_UNMODIFIED
  •  Fonction de déverrouillage de la file d'attente :
static void prvUnlockQueue( Queue_t * const pxQueue )
{
      //上锁计数器(cTxLock 和 cRxLock)记录了在队列上锁期间入队或出队的数量,当队列
        //上锁以后,队列项是可以加入或者移除队列的,但是相应的列表不会更新*/
    taskENTER_CRITICAL();
    {
        int8_t cTxLock = pxQueue->cTxLock;

        /* See if data was added to the queue while it was locked. */
        while( cTxLock > queueLOCKED_UNMODIFIED )
        {
            /* Data was posted while the queue was locked.  Are any tasks
             * blocked waiting for data to become available? */
            #if ( configUSE_QUEUE_SETS == 1 )

            #else /* configUSE_QUEUE_SETS */
                {
                    /* Tasks that are removed from the event list will get added to
                     * the pending ready list as the scheduler is still suspended. */
                    if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                    {
                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
                        {
                            /*如果刚刚从列表 xTasksWaitingToReceive 移除的任务优先级比当前任务的优 先 级 高,那 么 就 要 标 记需 要 进行任 务 切换。这 里 调 用 函 数vTaskMissedYield()来完成此任务,函数 vTaskMissedYield()只是简单地将全局变量xYieldPending 设置为 pdTRUE,那么真正的任务切换是在哪里完成的呢? 在时钟节拍处理函数 xTaskIncrementTick()中,此函数会判断 xYieldPending 的值,从而决定是否进行任务切换
 */
                            vTaskMissedYield();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            #endif /* configUSE_QUEUE_SETS */
            //处理完一条就减一,直到处理完所有
            --cTxLock;
        }

        pxQueue->cTxLock = queueUNLOCKED;
    }
    taskEXIT_CRITICAL();

    /* Do the same for the Rx lock. */
    taskENTER_CRITICAL();
    {//处理cRxLock
        int8_t cRxLock = pxQueue->cRxLock;

        while( cRxLock > queueLOCKED_UNMODIFIED )
        {
            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
            {
                if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                {
                    vTaskMissedYield();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                --cRxLock;
            }
            else
            {
                break;
            }
        }

        pxQueue->cRxLock = queueUNLOCKED;
    }
    taskEXIT_CRITICAL();
}

 6. Lire les messages de la file d'attente

/* 从队列中读取队列项,完成以后删除掉队列项
 * xQueue: 读取哪个队列
 * pvItemToQueue: 数据地址, 用来保存复制出来的数据
 * xTicksToWait: 没有数据的话阻塞一会
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */
BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait );
/* 从队列中读取队列项,完成以后删除掉队列项,中断中使用
 * xQueue: 读取哪个队列
 * pvItemToQueue: 数据地址, 用来保存复制出来的数据
 * xTicksToWait: 没有数据的话阻塞一会
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */
BaseType_t xQueueReceiveFromISR(
                                    QueueHandle_t    xQueue,
                                    void             *pvBuffer,
                                    BaseType_t       *pxTaskWoken
                                );
/* 偷看队列,完成以后不会删除掉队列项
 * xQueue: 偷看哪个队列
 * pvItemToQueue: 数据地址, 用来保存复制出来的数据
 * xTicksToWait: 没有数据的话阻塞一会
 * 返回值: pdTRUE表示成功, pdFALSE表示失败
 */
BaseType_t xQueuePeek(
                          QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait
                      );

BaseType_t xQueuePeekFromISR(
                                 QueueHandle_t xQueue,
                                 void *pvBuffer,
                             );

 

Je suppose que tu aimes

Origine blog.csdn.net/qq_40648827/article/details/127656119
conseillé
Classement