概念
队列又称消息队列,是一种常用于任务间通信的数据结构
消息队列是一种异步的通信方式。
作用
队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息。
当队列中的消息是空时,读取消息的任务将被阻塞,
用户还可以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。
当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;
当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态。
创建消息队列时 FreeRTOS 会先给消息队列分配一块内存空间,这块内存的大小等于消息队列控制块大小加上(单个消息空间大小与消息队列长度的乘积),接着再初始化消
息队列,此时消息队列为空。
FreeRTOS 的消息队列控制块由多个元素组成,当消息队列被 创建时,系统会为控制块分配对应的内存空间,
用于保存消息队列的一些信息如消息的存储位置,头指针pcHead、尾指针 pcTail、消息大小 uxItemSize 以及队列长度 uxLength等。
同时每个消息队列都与消息空间在同一段连续的内存空间中,在创建成功的时候,这些内存就被占用了,只有删除了消息队列的时候,这段内存才会被释放掉,创建成功的时候就
已经分配好每个消息空间与消息队列的容量,无法更改,每个消息空间可以存放不大于消息大小 uxItemSize 的任意类型的数据,所有消息队列中的消息空间总数即是消息队列的长
度,这个长度可在消息队列创建时指定
特点
- 消息支持先进先出方式排队,支持异步读写工作方式。
- 读写队列均支持超时机制。
- 消息支持后进先出方式排队,往队首发送消息(LIFO)。
- 可以允许不同长度(不超过队列节点最大值)的任意类型消息。
- 一个任务能够从任意一个消息队列接收和发送消息。
- 多个任务能够从同一个消息队列接收和发送消息。
- 当队列使用结束后,可以通过删除队列函数进行删除。
队列是 FreeRTOS 主要的任务间通讯方式,可以在任务与任务间、中断和任务间传送信息,发送到队列的消息是通过拷贝方式实现的,这意味着队列存储的数据是原数据,而不是原数据的
引用。
运作机制
消息队列空间
消息队列的空间大小:消息队列控制块大小+单个消息空间大小 * 消息队列的长度
消息队列的长度 = 所有消息队列中的消息空间的总数
消息队列的运行
任务或者中断服务程序都可以给消息队列发送消息
发送消息时
**
- 如果队列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾
- 如果满了,会根据用户指定的阻塞超时时间进行阻塞
- 队列不允许入队
- 任务保持阻塞状态等待队列允许入队
- 当其它任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转移为就绪态。
- 当等待的时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,
- 此时发送消息的任务或者中断程序会收到一 个错误码 errQUEUE_FULL
读取消息时
**
- 当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。
- 在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。
- 当其它任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。
- 当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。
发送紧急消息
当发送紧急消息时,发送的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。
消息队列的阻塞机制
接收数据
每个任务在进行读取消息队列的数据时,进入阻塞,保证当前任务能够正常完成读写操作,不受任务干扰。
每一个任务读写消息队列的函数都有该机制。
任务A对某个队列进行读写操作时,检测消息队列中没有数据,有3个选择:
- 跳过当前读写数据的操作,继续执行其他操作
- 等待队列中的数据,这里可以设置等待的时间
- 例如等待1000个系统时钟,那么A在1000系统时钟内处于阻塞状态。
- 当消息队列中有数据后,A从阻塞状态转为就绪态,执行代码
- 当超过1000个系统时钟后,依然没有数据,则A转为就绪状态,得到没有等到消息的错误代码,继续执行代码
- 例如等待1000个系统时钟,那么A在1000系统时钟内处于阻塞状态。
- 一直等待队列中的数据
队列中无可用消息空间时,说明消息队列已满,此时,系统会根据用户指定的阻塞超时时间将任务阻塞,在指定的超时时间内如果还不能完成入队操作,发送消
息的任务或者中断服务程序会收到一个错误码 errQUEUE_FULL,然后解除阻塞状态;
当然,只有在任务中发送消息才允许进行阻塞状态,而在中断中发送消息不允许带有阻塞机制的,需要调用在中断中发送消息的 API函数接口,因为发送消息的上下文环境是在中断
中,不允许有阻塞的情况。
消息控制块
作用:保存以下内容:
- 头指针 pcHead
- 尾指针 pcTail
- 消息大小 uxItemSize
- 队列长度 uxLength
typedef struct QueueDefinition {int8_t *pcHead; (1)int8_t *pcTail; (2)int8_t *pcWriteTo; (3)union {int8_t *pcReadFrom; (4)UBaseType_t uxRecursiveCallCount; (5)} u;List_t xTasksWaitingToSend; (6)List_t xTasksWaitingToReceive; (7)volatile UBaseType_t uxMessagesWaiting; (8)UBaseType_t uxLength; (9)UBaseType_t uxItemSize; (10)volatile int8_t cRxLock; (11)volatile int8_t cTxLock; (12)#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated;#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;
代码清单 17-1(1):pcHead 指向队列消息存储区起始位置,即第一个消息空间。
代码清单 17-1(2):pcTail 指向队列消息存储区结束位置地址。
代码清单 17-1(3):pcWriteTo 指向队列消息存储区下一个可用消息空间。
代码清单 17-1(4):pcReadFrom 与 uxRecursiveCallCount 是一对互斥变量,使用联合体用来确保两个互斥的结构体成员不会同时出现。当结构体用于队列时,pcReadFrom 指向出
队消息空间的最后一个,见文知义,就是读取消息时候是从 pcReadFrom 指向的空间读取消息内容。
代码清单 17-1(5):当结构体用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数。
代码清单 17-1(6):xTasksWaitingToSend 是一个发送消息阻塞列表,用于保存阻塞在此队列的任务,任务按照优先级进行排序,由于队列已满,想要发送消息的任务无法发送 消息。
代码清单 17-1(7):xTasksWaitingToReceive 是一个获取消息阻塞列表,用于保存阻塞 在此队列的任务,任务按照优先级进行排序,由于队列是空的,想要获取消息的任务无法 获取到消息。
代码清单 17-1(8):uxMessagesWaiting 用于记录当前消息队列的消息个数,如果消息队列被用于信号量的时候,这个值就表示有效信号量个数。
代码清单 17-1(9):uxLength 表示队列的长度,也就是能存放多少消息。
代码清单 17-1(10):uxItemSize 表示单个消息的大小。
代码清单 17-1(11):队列上锁后,储存从队列收到的列表项数目,也就是出队的数量,如果队列没有上锁,设置为 queueUNLOCKED。
消息队列的函数
消息队列创建函数 xQueueCreate()
xQueueCreate()用于创建一个新的队列并返回可用于访问这个队列的队列句柄。
| 函 数 原 型 | QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,UBaseType_t uxItemSize ); |
|
|---|---|---|
| 功 能 | 用于创建一个新的队列。 | |
| 参 数 | uxQueueLength | 队列能够存储的最大消息单元数目,即队列长度。 |
| uxItemSize | 队列中消息单元的大小,以字节为单位。 | |
| 返回值 | 如果创建成功则返回一个队列句柄,用于访问创建的队列。 如果创建不成功则返回 |
NULL,可能原因是创建队列需要的 RAM 无法分配成功。
| |
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )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 );if ( uxItemSize == ( UBaseType_t ) 0 ) {/* 消息空间大小为 0*/xQueueSizeInBytes = ( size_t ) 0; (1)}else {/* 分配足够消息存储空间,空间的大小为队列长度*单个消息大小 */xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); (2)}/* 向系统申请内存,内存大小为消息队列控制块大小+消息存储空间大小 */pxNewQueue=(Queue_t*)pvPortMalloc(sizeof(Queue_t)+xQueueSizeInBytes); (3)if ( pxNewQueue != NULL ) {/* 计算出消息存储空间的起始地址 */pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); (4)#if( configSUPPORT_STATIC_ALLOCATION == 1 ){pxNewQueue->ucStaticallyAllocated = pdFALSE;}#endifprvInitialiseNewQueue( uxQueueLength, (5)uxItemSize,pucQueueStorage,ucQueueType,pxNewQueue );}return pxNewQueue;}#endif/*-----------------------------------------------------------*/
消息队列静态创建函数 xQueueCreateStatic()
xQueueCreateStatic()用于创建一个新的队列并返回可用于访问这个队列的队列句柄。
队列句柄其实就是一个指向队列数据结构类型的指针。
| 函
数
原
型 | QueueHandlet xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t pucQueueStorageBuffer,
StaticQueue_t pxQueueBuffer );
| |
| —- | —- | —- |
| 功
能 | 用于创建一个新的队列。 | |
| 参
数 | uxQueueLength | 队列能够存储的最大单元数目,即队列深度。 |
| | uxItemSize | 队列中数据单元的长度,以字节为单位。 |
| | pucQueueStorageBuffer | 指针,指向一个uint8 t类型的数组,数组的大小至少有
uxQueueLength* uxItemSize 个字节。当uxItemSize 为0时,
pucQueueStorageBuffer 可以为NULL。 |
| | pxQueueBuffer | 指针,指向StaticQueue_ t 类型的变量,该变量用于存储队列
的数据结构。 |
| 返
回
值 | 如果创建成功则返回一个队列句柄,用于访问创建的队列。如果创建不成功则返回
NULL,可能原因是创建队列需要的RAM无法分配成功。 | |
/* 创建一个可以最多可以存储 10 个 64 位变量的队列 */#define QUEUE_LENGTH 10#define ITEM_SIZE sizeof( uint64_t )/* 该变量用于存储队列的数据结构 */static StaticQueue_t xStaticQueue;/* 该数组作为队列的存储区域,大小至少有 uxQueueLength * uxItemSize 个字节 */uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ];void vATask( void *pvParameters ){QueueHandle_t xQueue;/* 创建一个队列 */xQueue = xQueueCreateStatic( QUEUE_LENGTH, /* 队列深度 */ITEM_SIZE, /* 队列数据单元的单位 */ucQueueStorageArea,/* 队列的存储区域 */&xStaticQueue ); /* 队列的数据结构 *//* 剩下的其他代码 */
消息队列删除函数 vQueueDelete()
队列删除函数是根据消息队列句柄直接删除的,删除之后这个消息队列的所有信息都会被系统回收清空,而且不能再次使用这个消息队列了
void vQueueDelete( QueueHandle_t xQueue ){Queue_t * const pxQueue = ( Queue_t * ) xQueue;/* 断言 */configASSERT( pxQueue ); (1)traceQUEUE_DELETE( pxQueue );#if ( configQUEUE_REGISTRY_SIZE > 0 ){/* 将消息队列从注册表中删除,我们目前没有添加到注册表中,暂时不用理会 */vQueueUnregisterQueue( pxQueue ); (2)}#endif#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )&& ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) {/* 因为用的消息队列是动态分配内存的,所以需要调用 vPortFree 来释放消息队列的内存 */vPortFree( pxQueue ); (3)}}
向消息队列发送消息函数
xQueueSend()与 xQueueSendToBack()
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), \( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBackFromISR(xQueue,pvItemToQueue,pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), \( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
| 函
数
原
型 | BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void pvItemToQueue,
BaseType_t pxHigherPriorityTaskWoken);
| |
| —- | —- | —- |
| 功
能 | 在中断服务程序中用于向队列尾部发送一个消息。 | |
| 参
数 | xQueue | 队列句柄。 |
| | pvItemToQueue | 指针,指向要发送到队列尾部的消息。 |
| | pxHigherPriorityTaskWoken | 如果入队导致一个任务解锁,并且解锁的任务优先级高
于当前被中断的任务,则将*pxHigherPriorityTaskWoken
设置成pdTRUE, 然后在中断退出前需要进行一次上下
文切换,去执行被唤醒的优先级更高的任务。从
FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken作为一
个可选参数,可以设置为NULL. |
| 返
回
值 | 消息发送成功返回pdTRUE,否则返回errQUEUE FULL。 | |
xQueueSendFromISR()与 xQueueSendToBackFromISR()
#define xQueueSendToFrontFromISR(xQueue,pvItemToQueue,pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),\( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )#define xQueueSendToBackFromISR(xQueue,pvItemToQueue,pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), \( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
| 函 数 原 型 |
BaseType txQueueSendFromISR(QueueHandle t xQueue, const void pvItemToQueue, BaseType_ t pxHigherPriorityTaskWoken); |
|
|---|---|---|
| 功 能 |
在中断服务程序中用于向队列尾部发送一个消息。 | |
| 参 数 |
xQueue | 队列句柄。 |
| pvItemToQueue | 指针,指向要发送到队列尾部的消息。 | |
| pxHigherPriorityTaskWoken | 如果入队导致一个任务解锁,并且解锁的任务优先级高 于当前被中断的任务,则将*pxHighertPriorityTaskWoken 设置成pdTRUE, 然后在中断退出前需要进行一次上下 文切换,去执行被唤醒的优先级更高的任务。从 FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken 作为- 个可选参数,可以设置为NULL。 |
|
| 返 回 值 |
消息发送成功返回pdTRUE,否则返回errQUEUE_ FULL。 |
void vBufferISR( void ){char cIn;BaseType_t xHigherPriorityTaskWoken;/* 在 ISR 开始的时候,我们并没有唤醒任务 */xHigherPriorityTaskWoken = pdFALSE;/* 直到缓冲区为空 */do {/* 从缓冲区获取一个字节的数据 */cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );/* 发送这个数据 */xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );} while ( portINPUT_BYTE( BUFFER_COUNT ) );/* 这时候 buffer 已经为空,如果需要则进行上下文切换 */if ( xHigherPriorityTaskWoken ) {/* 上下文切换,这是一个宏,不同的处理器,具体的方法不一样 */taskYIELD_FROM_ISR ();}}
xQueueSendToFront()
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), \( xTicksToWait ), queueSEND_TO_FRONT )

xQueueSendToFrontFromISR()
#define xQueueSendToFrontFromISR( xQueue,pvItemToQueue,pxHigherPriorityTaskWoken ) \xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),\( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )

通用消息队列发送函数 xQueueGenericSend()(任务)
/*-----------------------------------------------------------*/2 BaseType_t xQueueGenericSend( QueueHandle_t xQueue, (1)3 const void * const pvItemToQueue, (2)4 TickType_t xTicksToWait, (3)5 const BaseType_t xCopyPosition ) (4)6 {7 BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;8 TimeOut_t xTimeOut;9 Queue_t * const pxQueue = ( Queue_t * ) xQueue;1011 /* 已删除一些断言操作 */1213 for ( ;; ) {14 taskENTER_CRITICAL(); (5)15 {16 /* 队列未满 */17 if ( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength )18 || ( xCopyPosition == queueOVERWRITE ) ) { (6)19 traceQUEUE_SEND( pxQueue );20 xYieldRequired =21 prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); (7)2223 /* 已删除使用队列集部分代码 */24 /* 如果有任务在等待获取此消息队列 */25 if ( listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive))==pdFALSE){ (8)26 /* 将任务从阻塞中恢复 */27 if ( xTaskRemoveFromEventList(28 &( pxQueue->xTasksWaitingToReceive ) )!=pdFALSE) { (9)29 /* 如果恢复的任务优先级比当前运行任务优先级还高,30 那么需要进行一次任务切换 */31 queueYIELD_IF_USING_PREEMPTION(); (10)32 } else {33 mtCOVERAGE_TEST_MARKER();34 }35 } else if ( xYieldRequired != pdFALSE ) {36 /* 如果没有等待的任务,拷贝成功也需要任务切换 */37 queueYIELD_IF_USING_PREEMPTION(); (11)38 } else {39 mtCOVERAGE_TEST_MARKER();40 }4142 taskEXIT_CRITICAL(); (12)43 return pdPASS;44 }45 /* 队列已满 */46 else { (13)47 if ( xTicksToWait == ( TickType_t ) 0 ) {48 /* 如果用户不指定阻塞超时时间,退出 */49 taskEXIT_CRITICAL(); (14)50 traceQUEUE_SEND_FAILED( pxQueue );51 return errQUEUE_FULL;52 } else if ( xEntryTimeSet == pdFALSE ) {53 /* 初始化阻塞超时结构体变量,初始化进入54 阻塞的时间 xTickCount 和溢出次数 xNumOfOverflows */55 vTaskSetTimeOutState( &xTimeOut ); (15)56 xEntryTimeSet = pdTRUE;57 } else {58 mtCOVERAGE_TEST_MARKER();59 }60 }61 }62 taskEXIT_CRITICAL(); (16)63 /* 挂起调度器 */64 vTaskSuspendAll();65 /* 队列上锁 */66 prvLockQueue( pxQueue );6768 /* 检查超时时间是否已经过去了 */69 if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait)==pdFALSE){ (17)70 /* 如果队列还是满的 */71 if ( prvIsQueueFull( pxQueue ) != pdFALSE ) { (18)72 traceBLOCKING_ON_QUEUE_SEND( pxQueue );73 /* 将当前任务添加到队列的等待发送列表中74 以及阻塞延时列表,延时时间为用户指定的超时时间 xTicksToWait */75 vTaskPlaceOnEventList(76 &( pxQueue->xTasksWaitingToSend ), xTicksToWait );(19)77 /* 队列解锁 */78 prvUnlockQueue( pxQueue ); (20)7980 /* 恢复调度器 */81 if ( xTaskResumeAll() == pdFALSE ) {82 portYIELD_WITHIN_API();83 }84 } else {85 /* 队列有空闲消息空间,允许入队 */86 prvUnlockQueue( pxQueue ); (21)87 ( void ) xTaskResumeAll();88 }89 } else {90 /* 超时时间已过,退出 */91 prvUnlockQueue( pxQueue ); (22)92 ( void ) xTaskResumeAll();9394 traceQUEUE_SEND_FAILED( pxQueue );95 return errQUEUE_FULL;96 }97 }98 }99 /*-----------------------------------------------------------*/
消息队列发送函数 xQueueGenericSendFromISR()(中断)
1 BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, (1)2 const void * const pvItemToQueue, (2)3 BaseType_t * const xHigherPriorityTaskWoken, (3)4 const BaseType_t xCopyPosition ) (4)5 {6 BaseType_t xReturn;7 UBaseType_t uxSavedInterruptStatus;8 Queue_t * const pxQueue = ( Queue_t * ) xQueue;/* 已删除一些断言操作 */uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();13 {14 /* 队列未满 */15 if ( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength )16 || ( xCopyPosition == queueOVERWRITE ) ) { (5)17 const int8_t cTxLock = pxQueue->cTxLock;18 traceQUEUE_SEND_FROM_ISR( pxQueue );1920 /* 完成消息拷贝 */21 (void)prvCopyDataToQueue(pxQueue,pvItemToQueue,xCopyPosition ); (6)2223 /* 判断队列是否上锁 */24 if ( cTxLock == queueUNLOCKED ) { (7)25 /* 已删除使用队列集部分代码 */26 {27 /* 如果有任务在等待获取此消息队列 */28 if ( listLIST_IS_EMPTY(29 &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) {(8)30 /* 将任务从阻塞中恢复 */31 if ( xTaskRemoveFromEventList(32 &( pxQueue->xTasksWaitingToReceive )) != pdFALSE ) {(9)33 if ( pxHigherPriorityTaskWoken != NULL ) {34 /* 解除阻塞的任务优先级比当前任务高,记录上下文切换请求,35 等返回中断服务程序后,就进行上下文切换 */36 *pxHigherPriorityTaskWoken = pdTRUE; (10)37 } else {38 mtCOVERAGE_TEST_MARKER();39 }40 } else {41 mtCOVERAGE_TEST_MARKER();42 }43 } else {44 mtCOVERAGE_TEST_MARKER();45 }46 }4748 } else {49 /* 队列上锁,记录上锁次数,等到任务解除队列锁时,50 使用这个计录数就可以知道有多少数据入队 */51 pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); (11)52 }5354 xReturn = pdPASS;55 } else {56 /* 队列是满的,因为 API 执行的上下文环境是中断,57 所以不能阻塞,直接返回队列已满错误代码 errQUEUE_FULL */58 traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); (12)59 xReturn = errQUEUE_FULL;60 }61 }62 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );6364 return xReturn;65 }
从消息队列读取消息函数
xQueueReceive()与 xQueuePeek()
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) \xQueueGenericReceive( ( xQueue ), ( pvBuffer ), \( xTicksToWait ), pdFALSE )

static void Receive_Task(void* parameter){BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为 pdPASS */uint32_t r_queue; /* 定义一个接收消息的变量 */while (1){xReturn = xQueueReceive( Test_Queue, /* 消息队列的句柄 */&r_queue, /* 发送的消息内容 */portMAX_DELAY); /* 等待时间 一直等 */if (pdTRUE== xReturn)printf("本次接收到的数据是:%d\n\n",r_queue); 11 elseprintf("数据接收出错,错误代码: 0x%lx\n",xReturn);}}
xQueueReceiveFromISR()与 xQueuePeekFromISR()



QueueHandle_t xQueue;/* 创建一个队列,并往队列里面发送一些数据 */void vAFunction( void *pvParameters ){char cValueToPost;const TickType_t xTicksToWait = ( TickType_t )0xff;/* 创建一个可以容纳 10 个字符的队列 */xQueue = xQueueCreate( 10, sizeof( char ) );if ( xQueue == 0 ) {/* 队列创建失败 */}/* ... 任务其他代码 *//* 往队列里面发送两个字符如果队列满了则等待 xTicksToWait 个系统节拍周期*/cValueToPost = 'a';xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );cValueToPost = 'b';xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );/* 继续往队列里面发送字符当队列满的时候该任务将被阻塞*/cValueToPost = 'c';xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );}/* 中断服务程序:输出所有从队列中接收到的字符 */void vISR_Routine( void ){BaseType_t xTaskWokenByReceive = pdFALSE;char cRxedChar;while ( xQueueReceiveFromISR( xQueue,( void * ) &cRxedChar,&xTaskWokenByReceive) ) {/* 接收到一个字符,然后输出这个字符 */vOutputCharacter( cRxedChar );/* 如果从队列移除一个字符串后唤醒了向此队列投递字符的任务,那么参数 xTaskWokenByReceive 将会设置成 pdTRUE,这个循环无论重复多少次,仅会有一个任务被唤醒 */}if ( xTaskWokenByReceive != pdFALSE ) {/* 我们应该进行一次上下文切换,当 ISR 返回的时候则执行另外一个任务 *//* 这是一个上下文切换的宏,不同的处理器,具体处理的方式不一样 */taskYIELD ();}}
xQueueGenericReceive()
/*-----------------------------------------------------------*/BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, (1)void * const pvBuffer, (2)TickType_t xTicksToWait, (3)const BaseType_t xJustPeeking ) (4){BaseType_t xEntryTimeSet = pdFALSE;TimeOut_t xTimeOut;int8_t *pcOriginalReadPosition;Queue_t * const pxQueue = ( Queue_t * ) xQueue;/* 已删除一些断言 */for ( ;; ) {taskENTER_CRITICAL(); (5){const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;/* 看看队列中有没有消息 */if ( uxMessagesWaiting > ( UBaseType_t ) 0 ) { (6)/*防止仅仅是读取消息,而不进行消息出队操作*/pcOriginalReadPosition = pxQueue->u.pcReadFrom; (7)/* 拷贝消息到用户指定存放区域 pvBuffer */prvCopyDataFromQueue( pxQueue, pvBuffer ); (8)if ( xJustPeeking == pdFALSE ) { (9)/* 读取消息并且消息出队 */traceQUEUE_RECEIVE( pxQueue );/* 获取了消息,当前消息队列的消息个数需要减一 */pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; (10)/* 判断一下消息队列中是否有等待发送消息的任务 */if ( listLIST_IS_EMPTY( (11)&( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) {/* 将任务从阻塞中恢复 */if ( xTaskRemoveFromEventList( (12)&( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) {/* 如果被恢复的任务优先级比当前任务高,会进行一次任务切换 */queueYIELD_IF_USING_PREEMPTION(); (13)} else {mtCOVERAGE_TEST_MARKER();}} else {mtCOVERAGE_TEST_MARKER();}} else { (14)/* 任务只是看一下消息(peek),并不出队 */traceQUEUE_PEEK( pxQueue );/* 因为是只读消息 所以还要还原读消息位置指针 */pxQueue->u.pcReadFrom = pcOriginalReadPosition; (15)/* 判断一下消息队列中是否还有等待获取消息的任务 */if ( listLIST_IS_EMPTY( (16)&( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) {/* 将任务从阻塞中恢复 */if ( xTaskRemoveFromEventList(&( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) {/* 如果被恢复的任务优先级比当前任务高,会进行一次任务切换 */queueYIELD_IF_USING_PREEMPTION();} else {mtCOVERAGE_TEST_MARKER();}} else {mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL(); (17)return pdPASS;} else { (18)/* 消息队列中没有消息可读 */if ( xTicksToWait == ( TickType_t ) 0 ) { (19)/* 不等待,直接返回 */taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;} else if ( xEntryTimeSet == pdFALSE ) {/* 初始化阻塞超时结构体变量,初始化进入阻塞的时间 xTickCount 和溢出次数 xNumOfOverflows */vTaskSetTimeOutState( &xTimeOut ); (20)xEntryTimeSet = pdTRUE;} else {mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();vTaskSuspendAll();prvLockQueue( pxQueue ); (21)9192 /* 检查超时时间是否已经过去了*/93 if ( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) {(22)94 /* 如果队列还是空的 */95 if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) {96 traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); (23)97 /* 将当前任务添加到队列的等待接收列表中98 以及阻塞延时列表,阻塞时间为用户指定的超时时间 xTicksToWait */99 vTaskPlaceOnEventList(100 &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );101 prvUnlockQueue( pxQueue );102 if ( xTaskResumeAll() == pdFALSE ) {103 /* 如果有任务优先级比当前任务高,会进行一次任务切换 */104 portYIELD_WITHIN_API();105 } else {106 mtCOVERAGE_TEST_MARKER();107 }108 } else {109 /* 如果队列有消息了,就再试一次获取消息 */110 prvUnlockQueue( pxQueue ); (24)111 ( void ) xTaskResumeAll();112 }113 } else {114 /* 超时时间已过,退出 */115 prvUnlockQueue( pxQueue ); (25)116 ( void ) xTaskResumeAll();117118 if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) {119 /* 如果队列还是空的,返回错误代码 errQUEUE_EMPTY */120 traceQUEUE_RECEIVE_FAILED( pxQueue );121 return errQUEUE_EMPTY; (26)122 } else {123 mtCOVERAGE_TEST_MARKER();124 }125 }126 }127 }128 /*----*/
