1. 队列
队列是为了任务与任务、任务与中断之间通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项。创建队列的时候需要指定数据项目的大小和队列的长度。
1.1 数据存储
●队列采用的是先进先出(FIFO)的缓存机制
往队列发送数据(入队):永远发送到队列的尾部
从队列提取数据(出队):永远从队列头部提取
FreeRTOS也提供了后进后出(LIFO)数据缓存机制
●队列将要发送的数据复制到队列中(值传递)
队列中存储的是数据的原始值,而非数据的指针(引用),当然也可以使用指针传递。
1.2 多任务访问
队列不属于某个特别指定的任务,任何任务都可以向队列中发送消息或者从队列中提取消息
1.3 出队阻塞
出队就是指的是从队列中读取消息的任务,阻塞是指当任务尝试从一个队列中读取消息时,可以指定一个阻塞时间。比如任务A从队列Q中读取数据,如果此时队列Q为空,那么任务A有三种选择:
1.直接返回:阻塞时间设置为0
2.等待一会儿:等待时间为设置的阻塞时间
3.一直等待,直到有数据才会返回:阻塞时间被设置为了portMAX_DELAY
值得一提的是,当多个任务读取空队列时,这些任务都会进入阻塞状态:有多个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入就绪态?
●优先级最高的任务
●如果大家的优先级相同,那等待时间最久的任务会进入就绪态
1.4 入队阻塞
入队阻塞和出队阻塞基本上一样,就是入队时如果队列已满,其处理方法和出队一样。
2. 队列结构体
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. 队列创建
队列的创建有两种方法:动态分配内存、静态分配内存
●动态分配内存:xQueueCreate,队列的内存在函数内部动态分配,实际调用:xQueueGenericCreate()
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数 | 说明 |
uxQueueLength | 队列长度,最多能存放多少个数据(item) |
uxItemSize | 每个数据(item)的大小:以字节为单位 |
返回值 | 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为内存不足 |
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType )
参数 | 说明 |
uxQueueLength | 队列长度,最多能存放多少个数据(item) |
uxItemSize | 每个数据(item)的大小:以字节为单位 |
ucQueueType | 队列类型。由于FreeRTOS中的信号量等也是通过队列来实现的,创建信号量的函数最终也是使用此函数的,因此,在创建的时候需要指定此队列的用途,也就是队列类型,一共有6种类型 |
返回值 | 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为内存不足 |
参数 | 说明 |
queueQUEUE_TYPE_BASE | 普通的队列消息 |
queueQUEUE_TYPE_SET | 队列集 |
queueQUEUE_TYPE_MUTEX | 互斥信号量 |
queueQUEUE_TYPE_COUNTING_SEMAPHORE | 计数型信号量 |
queueQUEUE_TYPE_BINARY_SEMAPHORE | 二值信号量 |
queueQUEUE_TYPE_RECURSIVE_MUTEX | 递归信号量 |
- 静态分配内存:xQueueCreateStatic,队列的内存要事先分配好,实际调用:xQueueGenericCreateStatic()
QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer
);
参数 | 说明 |
uxQueueLength | 队列长度,最多能存放多少个数据(item) |
uxItemSize | 每个数据(item)的大小:以字节为单位 |
pucQueueStorageBuffer | 如果uxItemSize非0,pucQueueStorageBuffer必须指向一个uint8_t数组,此数组大小至少为"uxQueueLength * uxItemSize" |
pxQueueBuffer | 必须执行一个StaticQueue_t结构体,用来保存队列的数据结构 |
返回值 | 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为pxQueueBuffer为NULL |
QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
StaticQueue_t * pxStaticQueue,
const uint8_t ucQueueType )
- 队列创建函数分析(动态创建)
//队列动态创建函数
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;
}
队列初始化完成之后的结构如下所示:
4. 向队列发送消息
可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:
/* 等同于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
);
这些函数的参数都类似,这里统一说明一下:
下面讲解一下任务级入队函数xQueueGenericSend()函数(通用入队函数,上面的非中断入队函数最终都是调用的这个):
//入队函数
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 */
}
中断级通用入队函数: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. 队列上锁和解锁
- 队列上锁函数: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
- 队列解锁函数:
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. 从队列读取消息
/* 从队列中读取队列项,完成以后删除掉队列项
* 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,
);