FreeRTOS的queue源码分析
一、队列介绍
FreeRTOS的队列能够实现任务到任务、任务到中断、中断到任务之间的消息传递,采用值拷贝完成传输数据,队列由队列头和环形缓冲区组成。
二、队列关键结构体
typedef struct QueueDefinition
{
int8_t *pcHead; // 指向队列存储区域头的指针
int8_t *pcTail; // 指向队列存储区域尾的指针
int8_t *pcWriteTo; // 循环缓冲区的写指针
union{
int8_t *pcReadFrom; // 循环缓冲区的读指针
UBaseType_t uxRecursiveCallCount; // 调用计数,用作互斥量
}u;
List_t xTasksWaitingToSend; // 等待send的task挂的队列
List_t xTasksWaitingToReceive; // 待receive的task挂的队列
volatile UBaseType_t uxMessagesWaiting; // 队列中的item数量
UBaseType_t uxLength; // 队列缓冲区长度,创建队列设置,等于传入参数
UBaseType_t uxItemSize; // 队列缓冲区元素的大小,创建队列设置,等于传入参数
...
}xQUEUE;
typedef xQUEUE Queue_t;
三、队列的创建
// 队列、互斥量、信号量是一个实现,因此提供了一个通用的创建方法
#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; // 对垒缓冲区的指针
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); // 字节为单位的大小
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); // 分配队列头+缓冲区
pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
return pxNewQueue;
}
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
if( uxItemSize == ( UBaseType_t ) 0 )
{
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; // 队列长度为0,即是信号量,pcHead指向队列头
}
else
{
pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; // 队列长度不为0,队列,pcHead指向存储区域的首地址
}
pxNewQueue->uxLength = uxQueueLength; // 设置队列内容元素长度
pxNewQueue->uxItemSize = uxItemSize; // 设置队列内容元素大小
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
}
// 队列复位函数
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
taskENTER_CRITICAL(); // 关中断
pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); // 对于长度为0的队列,头指针和尾指针相等;对于长度不为0,设置队列尾指针
pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; // 队列空,元素数量=0
pxQueue->pcWriteTo = pxQueue->pcHead; // 循环缓冲区的写指针=pcHead
pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize ); // 循环缓冲区的读指针=最后一个存储元素
// 对于非首次创建的队列,将任务从xTasksWaitingToSend移除,并重新调度,也就是复位
// 对于首次创建的队列
vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); // 初始化队列
vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); // 初始化队列
taskEXIT_CRITICAL(); // 开中断
return pdPASS;
}
四、队列删除函数
void vQueueDelete( QueueHandle_t xQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
vPortFree( pxQueue );
}
五、队列写函数
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xYieldRequired;
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
for( ;; )
{
taskENTER_CRITICAL(); // 关中断
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) // 队列有空或者覆写模式
{
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); // 拷贝数据,FreeRTOS的队列是值拷贝模式
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) // 如果有任务在等待此队列产生数据
{
xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ); // 将一个任务从等待接收队列移除
queueYIELD_IF_USING_PREEMPTION(); // 发起调度
}
else if( xYieldRequired != pdFALSE ) // 需要调度?
{
queueYIELD_IF_USING_PREEMPTION(); // 发起调度
}
}
else
{
if( xTicksToWait == ( TickType_t ) 0 ) // 队列满,且不等待
{
taskEXIT_CRITICAL(); // 关中断
return errQUEUE_FULL; // 返回错误
}
else if( xEntryTimeSet == pdFALSE ) // 队列满且等待
{
vTaskInternalSetTimeOutState( &xTimeOut ); // 配置超时结构体
xEntryTimeSet = pdTRUE;
}
}
taskEXIT_CRITICAL(); // 开中断
vTaskSuspendAll(); // 停止任务
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) // 检查超时时间,当前tick会增加,要等待的tick会减少,pdFALSE表示时间还未到
{
if( prvIsQueueFull( pxQueue ) != pdFALSE ) // 队列满
{
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); // 将当前任务块挂入等待队列
( void ) xTaskResumeAll();
}
else
{
( void ) xTaskResumeAll();
}
}
else // 超时时间到
{
( void ) xTaskResumeAll(); // 调度
return errQUEUE_FULL;
}
}
}
六、队列读函数
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
for( ;; )
{
taskENTER_CRITICAL(); // 关中断
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; // 队列中元素数量
if( uxMessagesWaiting > ( UBaseType_t ) 0 ) // 队列中有数据
{
prvCopyDataFromQueue( pxQueue, pvBuffer ); // 拷贝数据
pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; // 计数减1
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) // 有任务在等待往队列send
{
xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ); // 从等待队列移除
queueYIELD_IF_USING_PREEMPTION(); // 调度
}
taskEXIT_CRITICAL(); // 开中断
return pdPASS;// 返回成功
}
else
{
if( xTicksToWait == ( TickType_t ) 0 ) // 队列中没有数据同时不想等待
{
taskEXIT_CRITICAL(); // 开中断
return errQUEUE_EMPTY; // 返回错误
}
else if( xEntryTimeSet == pdFALSE )
{
vTaskInternalSetTimeOutState( &xTimeOut ); // 配置超时结构体
xEntryTimeSet = pdTRUE;
}
}
taskEXIT_CRITICAL(); // 开中断
vTaskSuspendAll(); // 停止任务
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) // 检查超时时间,当前tick会增加,要等待的tick会减少,pdFALSE表示时间还未到
{
if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) // 队列空
{
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); // 将当前任务块挂入等待队列
( void ) xTaskResumeAll();
}
else
{
( void ) xTaskResumeAll();
}
}
else // 超时时间到
{
( void ) xTaskResumeAll(); // 调度
if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) // 如果队列空
return errQUEUE_EMPTY;
}
}
}
七、其他函数
// 可用数据个数
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ); // uxMessagesWaiting的值
// 覆写,长度必须为1
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) // 不会阻塞
// 查询
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ); // 和receive的区别是copy之后重新调整读指针,下一次读依旧是这个数
八、队列集
- 队列集本身也是队列,只不过里存储队列句柄
- 创建队列、创建队列集、将队列加入队列集、读取队列集返回队列句柄,再读队列。

浙公网安备 33010602011771号