概念
队列又称消息队列,是一种常用于任务间通信的数据结构
消息队列是一种异步的通信方式。
作用
队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息。
当队列中的消息是空时,读取消息的任务将被阻塞,
用户还可以指定阻塞的任务时间 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;
}
#endif
prvInitialiseNewQueue( 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;
10
11 /* 已删除一些断言操作 */
12
13 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)
22
23 /* 已删除使用队列集部分代码 */
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 }
41
42 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 );
67
68 /* 检查超时时间是否已经过去了 */
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)
79
80 /* 恢复调度器 */
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();
93
94 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 );
19
20 /* 完成消息拷贝 */
21 (void)prvCopyDataToQueue(pxQueue,pvItemToQueue,xCopyPosition ); (6)
22
23 /* 判断队列是否上锁 */
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 }
47
48 } else {
49 /* 队列上锁,记录上锁次数,等到任务解除队列锁时,
50 使用这个计录数就可以知道有多少数据入队 */
51 pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); (11)
52 }
53
54 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 );
63
64 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 else
printf("数据接收出错,错误代码: 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)
91
92 /* 检查超时时间是否已经过去了*/
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();
117
118 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 /*----*/