0+ 在FreeRTOS中,队列是为了任务与任务或任务与中断之间通信而专门准备的,它是任务与任务、任务与中断间传递消息的重要手段,所以我们也称之为消息队列。并且队列也是后面章节中信号量实现的基础,所以有必要深入了解队列及其源码。
typedef struct QueueDefinition
{
int8_t *pcHead; /*指向队列存储区首地址*/
int8_t *pcTail; /*指向队列存储区最后一个字节地址*/
int8_t *pcWriteTo; /*指向存储区下一个空闲区域 */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*指向最后一个出队队列项的首地址*/
UBaseType_t uxRecursiveCallCount;/*当使用互斥信号量时,用以保存互斥量被调用次数*/
} 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; /*队列上锁以后任务发送给队列的队列项的数量*/
/* 下面的条件编译部分可以先不看 */
#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;
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
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 )----1
{
xQueueSizeInBytes = ( size_t ) 0;
}
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 /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );----5
}
return pxNewQueue; ----6
}
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
( void ) ucQueueType;
if( uxItemSize == ( UBaseType_t ) 0 )----1
{
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
}
else
{
pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
}
pxNewQueue->uxLength = uxQueueLength;----2
pxNewQueue->uxItemSize = uxItemSize;----3
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );----4
/*下面是条件编译的一部分可以不看*/
#if ( configUSE_TRACE_FACILITY == 1 )
{
pxNewQueue->ucQueueType = ucQueueType;
}
#endif /* configUSE_TRACE_FACILITY */
#if( configUSE_QUEUE_SETS == 1 )
{
pxNewQueue->pxQueueSetContainer = NULL;
}
#endif /* configUSE_QUEUE_SETS */
traceQUEUE_CREATE( pxNewQueue );
}
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL();
{
pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );----1
pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;----2
pxQueue->pcWriteTo = pxQueue->pcHead;----3
pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );----4
pxQueue->cRxLock = queueUNLOCKED;----5
pxQueue->cTxLock = queueUNLOCKED;----6
if( xNewQueue == pdFALSE )---7
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )---8
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )---9
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* Ensure the event queues start in the correct state. */
vListInitialise( &( pxQueue->xTasksWaitingToSend ) );---10
vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );---11
}
}
taskEXIT_CRITICAL();
/* A value is returned for calling semantic consistency with previous
versions. */
return pdPASS;
}
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) 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( ;; )----1
{
taskENTER_CRITICAL();
{
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )----2
{
traceQUEUE_SEND( pxQueue );
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );----3
//队列集这部分忽略
...
...
taskEXIT_CRITICAL();
return pdPASS;----4
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )----5
{
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;----6
}
else if( xEntryTimeSet == pdFALSE )----7
{
vTaskSetTimeOutState( &xTimeOut );----8
xEntryTimeSet = pdTRUE;
}
else----9
{
/* Entry time was already set. */
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();
vTaskSuspendAll();----10
prvLockQueue( pxQueue );----11
/* Update the timeout state to see if it has expired yet. */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )----12
{
if( prvIsQueueFull( pxQueue ) != pdFALSE )----13
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );----14
prvUnlockQueue( pxQueue );----15
if( xTaskResumeAll() == pdFALSE )----16
{
portYIELD_WITHIN_API();
}
}
else----17
{
/* Try again. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else----18
{
/* The timeout has expired. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}
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 )----1
{
#if ( configUSE_MUTEXES == 1 )----2
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/* The mutex is no longer being held. */
xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
pxQueue->pxMutexHolder = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
}
else if( xPosition == queueSEND_TO_BACK )----3
{
( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); ----4
pxQueue->pcWriteTo += pxQueue->uxItemSize;----5
if( pxQueue->pcWriteTo >= pxQueue->pcTail ) ----6
{
pxQueue->pcWriteTo = pxQueue->pcHead;----7
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else----8
{
( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); ----9
pxQueue->u.pcReadFrom -= pxQueue->uxItemSize;----10
if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) ----11
{
pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize );----12
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xPosition == queueOVERWRITE )----13
{
if( uxMessagesWaiting > ( UBaseType_t ) 0 )----14
{
--uxMessagesWaiting;----15
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;----16
return xReturn;
}
#define prvLockQueue( pxQueue ) \
taskENTER_CRITICAL(); \
{ \
if( ( pxQueue )->cRxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \
} \
if( ( pxQueue )->cTxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \
} \
} \
taskEXIT_CRITICAL()
static void prvUnlockQueue( Queue_t * const pxQueue )
{
taskENTER_CRITICAL();
{
int8_t cTxLock = pxQueue->cTxLock;
while( cTxLock > queueLOCKED_UNMODIFIED )----1
{
...
...
//队列集部分代码忽略
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )----2
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )----3
{
/* The task waiting has a higher priority so record that
a context switch is required. */
vTaskMissedYield();----4
}
else
{
mtCOVERAGE_TEST_MARKER();
}
--cTxLock;----5
}
pxQueue->cTxLock = queueUNLOCKED;----6
}
taskEXIT_CRITICAL();
/* Do the same for the Rx lock. */
taskENTER_CRITICAL();
{
int8_t cRxLock = pxQueue->cRxLock;
while( cRxLock > queueLOCKED_UNMODIFIED )----7
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )----8
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )----9
{
vTaskMissedYield();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
--cRxLock;----10
}
else
{
break;
}
}
pxQueue->cRxLock = queueUNLOCKED;----11
}
taskEXIT_CRITICAL();
}
实验目标:在两个创建的两个任务之间用队列实现通信,通过按键发送队列消息,另一端接收队列消息
#define KEY_TASK_PRIO 5
#define KEY_STACK_SIZE 25
TaskHandle_t KeyTask_Handler;
void key_task(void *pvParameters);
#define LED1_TASK_PRIO 3
#define LED1_STACK_SIZE 50
TaskHandle_t Led1Task_Handler;
void led1_task(void *pvParameters);
QueueHandle_t Queue_Handler;
#define QUEUE_LENGTH 1
#define QUEUE_SIZE sizeof(u8)
void start_task(void *pvParameters)
{
Queue_Handler = xQueueCreate( QUEUE_LENGTH, QUEUE_SIZE );
if (Queue_Handler == NULL)
{
printf("Creat queue failed!!!\r\n");
}
xTaskCreate((TaskFunction_t )led1_task, //任务函数
(const char* )"led1_task", //任务名称
(uint16_t )LED1_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED1_TASK_PRIO, //任务优先级
(TaskHandle_t* )&Led1Task_Handler); //任务句柄
xTaskCreate((TaskFunction_t )key_task, //任务函数
(const char* )"key_task", //任务名称
(uint16_t )KEY_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )KEY_TASK_PRIO, //任务优先级
(TaskHandle_t* )&KeyTask_Handler); //任务句柄
vTaskDelete(StartTask_Handler);
}
u8 keyFlag=0;
void key_task(void* pvParameters)
{
BaseType_t err;
while(1)
{
if (Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
{
if ( (keyFlag&0x01) == 0x01)
keyFlag &= ~(0x01<<0);
else
keyFlag |= (0x01<<0);
if (Queue_Handler != NULL)
{
//err = xQueueOverwrite(Queue_Handler,&keyFlag);//复写
err = xQueueSend(Queue_Handler,&keyFlag,10);
}
if(err !=pdTRUE)
{
printf("Queue Send Failed!!!\r\n");
}
}
vTaskDelay(10);
}
}
void led1_task(void* pvParameters)
{
BaseType_t err;
while(1)
{
if (Queue_Handler!=NULL)
{
err = xQueueReceive(Queue_Handler,&keyFlag,portMAX_DELAY);
if (err != pdTRUE)
{
printf("receive Failed!!!\r\n");
}
else
{
printf("keyFlag=%#x\r\n",keyFlag);
}
LED1 = (keyFlag&0x01);
}
vTaskDelay(10);
}
}
中断级队列操作与这类似在此不展开叙述了。