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之后重新调整读指针,下一次读依旧是这个数

八、队列集

  1. 队列集本身也是队列,只不过里存储队列句柄
  2. 创建队列、创建队列集、将队列加入队列集、读取队列集返回队列句柄,再读队列。
posted @ 2025-07-07 23:51  gramming  阅读(390)  评论(0)    收藏  举报