消息队列

FreeRTOS学习仓库地址:https://gitee.com/killerp/free-rtos_-study

消息队列是RTOS的基础数据结构,用于任务之间、任务与中断之间进行数据传递。

没有使用消息队列时,若想要在两个任务之间进行数据传递,那么必须通过全局变量来传递,而在多任务系统中,访问全局变量往往需要用户对资源进行保护,这样就使得编程变得麻烦。

消息队列封装了对共享数据的访问保护,同时还加入了阻塞等待机制。使用户编程时不用去考虑复杂的并发访问。

一、队列的结构

消息队列结构体的定义如下:

  • 消息队列可理解为一个环形的队列,通过pcHead和pcTail将队列的首位连接起来。

  • pcWriteTo和pcReadFrom分别是队列的首部和尾部(若采用默认的FIFO)

  • 队列中每个消息的大小是固定的,一个队列可容纳若干个消息,由消息大小和数量决定队列所占内存的大小

  • 队列有两个链表,分别用于存放因发送/接收消息而进入阻塞的任务。

  • 队列支持锁定,当队列锁定时,中断函数不能修改以上链表,实现对数据的保护。

/** 消息队列 入队使用的是内存复制*/
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;    //TODO 当结构体作为队列时  SemaphoreData_t xSemaphore; //当作为信号量时} u;List_t xTasksWaitingToSend;             //链表:保存那些因发送信号量而进入阻塞的任务 (按优先级排序)List_t xTasksWaitingToReceive;          //链表:保存那些因接收信号量而进入阻塞的任务 (按优先级排序)volatile UBaseType_t uxMessagesWaiting; //队列中的消息数量UBaseType_t uxLength;                   //队列的最大消息数量UBaseType_t uxItemSize;                 //一个消息的大小//队列上锁volatile int8_t cRxLock;                //队列上锁时,任务从队列接收消息的数量volatile int8_t cTxLock;                //队列上锁时,任务向队列发送的消息数量uint8_t ucStaticallyAllocated;      //标记队列的内存分配方式} xQUEUE;

消息队列可简单的抽象成如下图片:

在同一时间内,只能有一个任务 or 中断在修改队列的链表。是的,队列锁并不能阻止中断向队列复制数据。

在这里插入图片描述

二、创建队列

创建队列的过程与创建任务类似,需要为队列结构体和队列存储区分配内存,并初始化队列结构体的成员变量。

以动态内存分配为例:首先分配内存

    QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType ){Queue_t * pxNewQueue;   //指向新的队列结构体size_t xQueueSizeInBytes;   //队列总大小(字节)uint8_t * pucQueueStorage;  //指向队列存储区域起始地址//队列的长度至少为1configASSERT( uxQueueLength > ( UBaseType_t ) 0 );//计算队列使用的内存大小xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );/* 检查乘法溢出 */configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) );/* 检查溢出. */configASSERT( ( sizeof( Queue_t ) + xQueueSizeInBytes ) >  xQueueSizeInBytes );//todo 申请内存, pvPortMalloc字节对齐问题pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); if( pxNewQueue != NULL ){//队列结构体与队列实际内存区域在同一连续的内存中 所以pucQueueStorage跳过Queue_tpucQueueStorage = ( uint8_t * ) pxNewQueue;pucQueueStorage += sizeof( Queue_t ); #if ( configSUPPORT_STATIC_ALLOCATION == 1 ){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 )
{//移除编译器的警告( void ) ucQueueType;//设置队列结构体的成员//当队列作为信号量时,uxItemSize为0if( uxItemSize == ( UBaseType_t ) 0 ){//pcHead不能为null,因为pcHead为null表示互斥信号量 所以设为一个确定的值pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;}else{//作为队列,pcHead指向存储区pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;}//设置队列长度pxNewQueue->uxLength = uxQueueLength;pxNewQueue->uxItemSize = uxItemSize;//重置队列 ( void ) xQueueGenericReset( pxNewQueue, pdTRUE );}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;if( xNewQueue == pdFALSE ){//不是新的队列/* xTasksWaitingToSend的任务可以发送消息,而 xTasksWaitingToReceive则继续等待 */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;
}

三、发送消息到队列

RTOS中发送消息的函数五花八门,但万变不离其宗,只要掌握了以下这个函数,其他的发送函数都是这个函数的变体。

任务中发送消息

任务调用发送消息函数、在该函数中任务会进入死循环,若任务能成功发送消息,则退出循环。否则,任务就需要进入阻塞,等待队列中出现空闲位置。

同时通过进入临界区来保证当前任务对队列的独占性访问。具体代码逻辑如下:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition )
{//xEntryTimeSet标记任务是否已经设置过阻塞时间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//循环for( ; ; ){//进入临界区 要操作链表taskENTER_CRITICAL();{//队列未满或是覆盖写入时,消息可以写入队列if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );{//将队列项复制到队列指定位置xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );//如果有任务在等待消息if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){//将该任务从事件链表中移出if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){//移出的任务优先级更高,需要任务调度queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else if( xYieldRequired != pdFALSE )    //没有任务在等消息,但需要任务调度{queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();    //消息发送完成 退出临界区return pdPASS;}else    //队列满不能入队 需要延时 or 退出{//任务不要求延时 直接退出if( xTicksToWait == ( TickType_t ) 0 ){taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}else if( xEntryTimeSet == pdFALSE ) //未初始化阻塞{//初始化阻塞结构体,用于辅助计算阻塞时间vTaskInternalSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}}//到此、任务未能成功发送消息 需要阻塞等待//退出临界区后,其他任务或中断可能抢占当前任务(然后向队列获取消息,那么当前任务就有可能发送消息)taskEXIT_CRITICAL();//挂起调度器、防止其他任务访问队列的xTasksWaitingToSend链表vTaskSuspendAll();//锁住队列 防止中断修改xTasksWaitingToSend链表prvLockQueue( pxQueue );//检查当前任务的阻塞时间是否到达if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ){//阻塞时间未到,且队列中为满 则将当前任务插入队列的WaitingToSendif( prvIsQueueFull( pxQueue ) != pdFALSE ){traceBLOCKING_ON_QUEUE_SEND( pxQueue );vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );//解锁队列(允许中断修改链表)prvUnlockQueue( pxQueue );//恢复调度器,产生任务调度,当前任务进入阻塞,等到阻塞时间到达或有其他任务读取了消息时被唤醒,然后重新进入当前循环if( xTaskResumeAll() == pdFALSE ){portYIELD_WITHIN_API();}}else{//队列有位置,重新进入当前循环prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{//超时时间到且未能成功发送消息 退出prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}} /*lint -restore */
}

FreeRTOS的消息是通过内存复制实现的。过程如图:先从write指针写入消息,再将write指针移动到下一个空闲位置。

static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue,const void * pvItemToQueue,const BaseType_t xPosition )
{BaseType_t xReturn = pdFALSE;UBaseType_t uxMessagesWaiting;/* 当前函数必须在临界区内调用 */uxMessagesWaiting = pxQueue->uxMessagesWaiting;//作为信号量的情况if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ){{//若是互斥信号量的话,说明信号量被释放、需要解除优先级反转if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder );pxQueue->u.xSemaphore.xMutexHolder = NULL;}else{mtCOVERAGE_TEST_MARKER();}}}//作为消息队列 发送到队列尾部(writeto)else if( xPosition == queueSEND_TO_BACK ){//内存复制( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); //pcWriteTo增加pxQueue->pcWriteTo += pxQueue->uxItemSize;                                                       //若pcWriteTo到达内存末尾,则回到内存首部if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail )                                            {pxQueue->pcWriteTo = pxQueue->pcHead;}else{mtCOVERAGE_TEST_MARKER();}}else  {//复制到队首(pcReadFrom)( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) {pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize );}else{mtCOVERAGE_TEST_MARKER();}//若是覆盖写入,则有一个消息被覆盖,uxMessagesWaiting-1if( xPosition == queueOVERWRITE ){if( uxMessagesWaiting > ( UBaseType_t ) 0 ){--uxMessagesWaiting;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}//入队成功pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1;return xReturn;
}

中断中发送消息

由于在中断中不能进入阻塞,所以需要有一个函数来另外实现在中断发送消息。与在任务中的区别在于,临界区保护以及当不满足发送条件时,直接退出函数,不会进入延时等待。

同时,在中断中若队列上锁,则不能修改链表。这是因为中断触发时,任务可能正在访问队列链表。

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;//检查队列configASSERT( pxQueue );configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );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 );/* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a*  semaphore or mutex.  That means prvCopyDataToQueue() cannot result*  in a task disinheriting a priority and prvCopyDataToQueue() can be*  called here even though the disinherit function does not check if*  the scheduler is suspended before accessing the ready lists. *///复制数据到队列( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );//若队列没有被锁if( cTxLock == queueUNLOCKED ){{//如果有任务正在等待数据if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){//将任务从等待链表中移除if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){//标记有更高优先级的任务解锁 退出中断后需要任务调度if( pxHigherPriorityTaskWoken != NULL ){*pxHigherPriorityTaskWoken = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}( void ) uxPreviousMessagesWaiting;}}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;
}

四、接收消息

任务中接收消息

接收消息的实现与发送消息类似,同样需要在循环中,成功获取队列的消息则退出,否则进入阻塞,等待消息到来。

代码注释如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait )
{BaseType_t xEntryTimeSet = pdFALSE;     //标记是否初始化的阻塞时间TimeOut_t xTimeOut;Queue_t * const pxQueue = xQueue;configASSERT( ( pxQueue ) );configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) );#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );}#endif//循环for( ; ; ){taskENTER_CRITICAL();{//获取队列中的消息数量const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;//若队列中有数据if( uxMessagesWaiting > ( UBaseType_t ) 0 ){//复制数据到buffer,消息数量-1prvCopyDataFromQueue( pxQueue, pvBuffer );traceQUEUE_RECEIVE( pxQueue );pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;//消息出队后,若有任务在等待发送消息,则恢复该任务去发送if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){//将等待发送的任务移出if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){//移出的任务优先级更高,发生抢占queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else    //没有任务在等待发送{mtCOVERAGE_TEST_MARKER();}taskEXIT_CRITICAL();return pdPASS;}else    //队列为空{//不作等待立刻退出if( xTicksToWait == ( TickType_t ) 0 ){taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else if( xEntryTimeSet == pdFALSE ) //若未初始化阻塞时间{//设置阻塞时间vTaskInternalSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}//到此、当前任务无法成功获取消息 需要进入阻塞等待taskEXIT_CRITICAL();//退出临界区后,其他任务或中断可能抢占当前任务(然后向队列发送消息,那么当前任务就能成功获取消息)//挂起调度器和锁住队列,防止任务、中断修改队列的链表vTaskSuspendAll();prvLockQueue( pxQueue );//任务阻塞时间未到if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ){//队列仍为空,需要继续等待if( prvIsQueueEmpty( pxQueue ) != pdFALSE ){traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );//将任务放到等待接收链表 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );prvUnlockQueue( pxQueue );  //解除锁定//恢复调度器,产生任务调度,当前任务进入阻塞,等到阻塞时间到达或有其他任务发送了消息时被唤醒,然后重新进入当前循环if( xTaskResumeAll() == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}else{//队列有消息了,重新进入当前循环prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else    //阻塞时间到达{prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();//再次查看队列是否有消息,若有则进入当前循环 无则退出等待if( prvIsQueueEmpty( pxQueue ) != pdFALSE ){traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else{mtCOVERAGE_TEST_MARKER();}}} /*lint -restore */
}

向队列复制消息。在这里需要注意当队列的消息大小为0时,这个队列是信号量,不需要内存的复制,这个部分在下一章中讲信号量说明。

读取消息时,是先将read指针移动,再读取。

static void prvCopyDataFromQueue( Queue_t * const pxQueue,void * const pvBuffer )
{if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ){//pcReadFrom向前移动一个单元pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize;          //是否溢出、需要回到队列头if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) {pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead;}else{mtCOVERAGE_TEST_MARKER();}//内存复制( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); }
}

中断中接收消息

同理,在中断中读取消息,当队列中没有消息时,不能进入等待,必须马上退出。

BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,void * const pvBuffer,BaseType_t * const pxHigherPriorityTaskWoken )
{BaseType_t xReturn;UBaseType_t uxSavedInterruptStatus;Queue_t * const pxQueue = xQueue;configASSERT( pxQueue );configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );portASSERT_IF_INTERRUPT_PRIORITY_INVALID();//进入临界区 保存中断状态uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();{//获取消息数量const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;//检查是否有消息if( uxMessagesWaiting > ( UBaseType_t ) 0 ){const int8_t cRxLock = pxQueue->cRxLock;    //获取接收锁traceQUEUE_RECEIVE_FROM_ISR( pxQueue );//数据有效,复制数据到bufferprvCopyDataFromQueue( pxQueue, pvBuffer );pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;//队列未锁,可以修改队列链表if( cRxLock == queueUNLOCKED ){//消息出队后,队列有空闲的位置,若有任务在等待发送消息,则需要恢复该任务if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){//将等待发送的任务移出if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){//移出的任务优先级更高,需要退出中断后启动任务调度if( pxHigherPriorityTaskWoken != NULL ){//设置返回参数*pxHigherPriorityTaskWoken = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else    {mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else    //队列上锁,不能操作链表{configASSERT( cRxLock != queueINT8_MAX );//设置cRxLock-1 使队列知道在其锁定的时候有消息被读取pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 );}//成功获取消息xReturn = pdPASS;}else    //队列为空 立刻返回{xReturn = pdFAIL;traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue );}}portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );return xReturn;
}

FreeRTOS学习 消息队列相关推荐

  1. freeRTOS学习 — 消息邮箱

    1.freeRTOS中的消息邮箱 freeRTOS实现的消息邮箱是基于任务通知方式而实现的. 采用这种方式有什么优势呢? 从官方给出的测试报告中有说明到,唤醒由于信号量和事件标志组而处于阻塞态的任务, ...

  2. ESP32使用freeRTOS的消息队列

    零. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:ESP-IDF基本介绍,主要会涉及模组,芯片,开发板的介绍,环境搭建,程序编译下载,启动流程等一些基本的操作,让你对 ...

  3. FreeRTOS应用——消息队列

    13. 消息队列 消息队列是一种常用于任务键通信的数据结构,队列可以在任务与任务间.中断与任务间传递信息,实现了任务接收来自其他任务或者中断的不定长数据. 任务能从队列中读取信息,当队列中的消息为空时 ...

  4. [分布式学习]消息队列之rocketmq笔记

    文档地址 RocketMQ架构 哔哩哔哩上的视频 mq有很多,近期买了<分布式消息中间件实践>这本书,学习关于mq的相关知识.mq大致有有4个功能: 异步处理.比如业务端需要给用户发送邮件 ...

  5. 架构学习----消息队列

    http://www.cnblogs.com/beniao/archive/2008/06/26/1229934.html http://www.cnblogs.com/yangecnu/p/4227 ...

  6. FreeRTOS学习---“信号量”篇

    总目录 FreeRTOS学习-"任务"篇 FreeRTOS学习-"消息队列"篇 FreeRTOS学习-"信号量"篇 FreeRTOS学习-& ...

  7. FreeRTOS记录(六、FreeRTOS消息队列—Enocean模块串口通讯、RAM空间不足问题分析)

    本篇文章记录FreeRTOS消息队列的使用,我不从理论开始介绍,直接用起来,然后从发现的问题分析记录解决. ..补充RAM空间不足问题内容,增加FreeRTOS任务占用的RAM空间说明 2021/11 ...

  8. FreeRTOS — 消息队列

    以下内容转载自安富莱电子:http://forum.armfly.com/forum.php FreeRTOS 的一个重要的通信机制----消息队列,消息队列在实际项目中应用较多. 1.消息队列 1. ...

  9. FreeRTOS学习笔记

    FreeRTOS学习笔记 (这是我自己学习FreeRTOS整理的笔记,仅供参考) 第一部分:实现FreeRTOS内核 变量名: 定义变量时往往会把变量的类型当作前缀加在变量上 变量类型 前缀 char ...

最新文章

  1. python--复习之路的目录
  2. php route取值,route命令详解
  3. 【赠书】pandas创始人手把手教你利用Python进行数据分析
  4. MySQL千万数据量深分页优化
  5. JSONPath 解析 JSON 内容详解(翻译自 github)
  6. android判断点击次数_Android应用统计-使用时长及次数统计(一)
  7. Python Numpy 数组的初始化和基本操作
  8. qt2-无边框窗口创建、拖拽、阴影
  9. Spring Mvc Controller返回值、参数绑定、参数校验 (高级二)
  10. u盘 连接服务器系统软件,u盘服务器系统
  11. dump mysql database,Mysql Database 逻辑备份: mysqldump
  12. 3d激光雷达开发(法向量预测)
  13. 可拖动jquery插件
  14. ip xfrm命令是做什么的?
  15. 数字地球与计算机技术联系,数字地球与地球空间信息科学的关系
  16. NMEA的GPGGA数据包解析 字符转数字
  17. 七彩安卓影视APP源码独立解析接口
  18. 树莓派 智能小车 语音控制系统
  19. 应用统计学之——average和mea…
  20. 统计基础:3.3_假设检验之t检验(Student‘s t test)

热门文章

  1. 如何查询网站虚拟服务器的ip,正确姿势查看海外虚拟主机真实IP地址 cPanel面板IP信息...
  2. 内外网通过公网IP访问DMZ主机
  3. 社区团购热潮退却,谁是电商卖菜“接盘侠”?
  4. 读书笔记—水煮三国(纪念版)
  5. 基于Snort的入侵检测系统_相关论文
  6. win7安装node版本最高只支持13.14.0
  7. 中粮我买网为何能融资1亿美元?
  8. 凤凰刷机 教你S40刷机,原来刷机和破权不一样!!
  9. draco压缩引擎学习笔记(一)
  10. 安全多方计算与证券业数据生态