FreeRTOS源码分析-7 消息队列

news2025/1/21 12:06:50

目录

1 消息队列的概念和作用

2 应用

2.1功能需求

2.2接口函数API

2.3 功能实现

3 消息队列源码分析

3.1消息队列控制块

3.2消息队列创建

3.3消息队列删除

3.4消息队列在任务中发送

3.5消息队列在中断中发送

3.6消息队列在任务中接收

3.7消息队列在中断中接收 


1 消息队列的概念和作用

消息队列(queue),可以在任务与任务间、中断和任务间传递消息实现任务接收来自其他任务或中断的不固定长度的消息。后面的二值信号量、互斥信号量等都是基于消息队列衍生出来的。

队列是什么?

解决三个问题:

  • 排队无序的问题(先入先出,有序)
  • 不可能插队的问题(在一端插入)
  • 高效的问题(只要入队都能买到票)

FreeRTOS程序中的消息队列

中断和任务不断的发送消息

在固定时间内等待(Timeout,相当于osdelay挂起)消息,没有消息的时候把cpu交给其他任务。

 

 “深度”。在队列创建时需要设定其深度和每个单元的大小。

通常情况下,队列被作为 FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。当然,由队列首写人也是有可能的。

向队列写入数据是通过字节拷贝把数据存储到队列中,从队列读出数据使得把队列中的数据拷贝删除。

在FreeROTS中,操作消息队列控制块,只要对头有消息,就会取,直到去完为止。

2 应用

2.1功能需求

  • 1、使用消息队列检测串口输入
  • 2、通过串口发送字符串openled6,openled7,openled8,openled9,分别打开板载led6,led7,led8, led9
  • 3、通过串口发送字符串closeled6,closeled7,closeled8,closeled9,分别关闭板载led6,led7,led8, led9

2.2接口函数API

 一般在调度器开启之前创建

可以发送在队头、队尾,一般先入先出,有紧急消息可以队头插队,常用的是Send和SendtoBack

 启动调度器之前是不能调用此函数的,因为是在中断中触发的,在中断中不能阻塞。第三个参数NULL,已经不用了。

第三个参数NULL,已经不用了。 

2.3 功能实现

 CubeMX功能配置

led端口配置、usart1中断配置、创建消息队列

消息队列接收和发送功能开发

串口中断使能

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */
  //开启接收中断
  __HAL_UART_ENABLE_IT(uartHandle,UART_IT_RXNE);

  /* USER CODE END USART1_MspInit 1 */
  }
}

串口中断服务函数入队操作 

void USART1_IRQHandler(void)
{
	uint8_t u8Data;
	//接收中断标志位
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) == SET){
		//读取接收寄存器
		u8Data = huart1.Instance->DR;
		//进行入队操作
		xQueueSendFromISR(CmdQueueHandle,&u8Data,NULL);
	}

  HAL_UART_IRQHandler(&huart1);
}

从消息队列出队一直等待,当接收到第一个消息循环从消息队列出队,阻塞等待50ms,完成消息接收

uint8_t u8CmdBuff[20]; //全局变量

void Usart_Task(void const * argument)
{
	uint8_t u8Index;

  for(;;)
  {
	  //每次读取消息之前,把索引初始化为0
	  u8Index = 0;
	  //1、一直等待接收消息,第一个消息应该放在消息缓冲区的第一个元素上
		if(xQueueReceive(CmdQueueHandle,&u8CmdBuff[u8Index++],portMAX_DELAY)==pdPASS){
			while(xQueueReceive(CmdQueueHandle,&u8CmdBuff[u8Index++],50)){}
			u8CmdBuff[u8Index] = '\0';//保证一包完成字符串信息
			vParseString(u8CmdBuff);
			//完成解析以后,要清空接收缓冲区
		    memset(u8CmdBuff,0,20);
		
		}
  }
}

消息解析控制功能开发

根据板载LED端口数量循环遍历,openledX字符串进行比较,如果字符串比较成功打开相关led端口

根据板载LED端口数量循环遍历,closeledX字符串进行比较,如果字符串比较成功打开相关led端口

uint8_t *OpenString[LED_NUM] = {

"openled6",
"openled7",
"openled8",
"openled9",

};

uint8_t *CloseString[LED_NUM] = {

"closeled6",
"closeled7",
"closeled8",
"closeled9",

};


void vParseString(uint8_t *buff){
  
    
    uint8_t i;
    for(i=0;i<LED_NUM;i++){
      if(strcmp((char const*)buff,(char const*)OpenString[i]) == 0){
        HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_RESET);
        printf("Cmd is %s\n",OpenString[i]);
		  return;
      
      }
    
    }
    for(i=0;i<LED_NUM;i++){
      if(strcmp((char const*)buff,(char const*)CloseString[i]) == 0){
      HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_SET);
      printf("Cmd is %s\n",CloseString[i]);
		  return;
      
      }
	 
    
    }

}

3 消息队列源码分析

3.1消息队列控制块

 

3.2消息队列创建

//queue.h

//队列实现,实际是xQueueGenericCreate实现的
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )

//队列的创建类型
#define queueQUEUE_TYPE_BASE				( ( uint8_t ) 0U )  //基本类型
#define queueQUEUE_TYPE_SET					( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_MUTEX 				( ( uint8_t ) 1U )  //互斥锁
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE	( ( uint8_t ) 2U )  //计数信号量
#define queueQUEUE_TYPE_BINARY_SEMAPHORE	( ( uint8_t ) 3U )  //二值信号量
#define queueQUEUE_TYPE_RECURSIVE_MUTEX		( ( uint8_t ) 4U )  //递归互斥锁

/*
    参数:
        uxQueueLength 队列长度
        uxItemSize    队列项大小
        ucQueueType   队列类型
    返回值:
        QueueHandle_t 队列的句柄 其实就是队列控制块地址
*/
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
    Queue_t *pxNewQueue;
    size_t xQueueSizeInBytes;
    uint8_t *pucQueueStorage;

    //队列内存空间为空
	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/*队列字节大小赋值为0 */
		xQueueSizeInBytes = ( size_t ) 0;
	}
	else
	{
		/* 队列字节大小赋值为 长度*每个队列项大小 */
		xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); 
	}

    // 申请内存空间  消息队列控制块大小+消息队列空间大小
	pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

	if( pxNewQueue != NULL )
	{
		/* 找到消息队列操作空间的首地址 */
		pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );

        //初始化一个新的消息队列
		prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
	}

	return pxNewQueue;
}

/*
    参数:
        uxQueueLength
        uxItemSize
        pucQueueStorage:队列操作空间的首地址
        ucQueueType:队列类型
        pxNewQueue:队列的句柄
*/
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 )//是否有队列空间
	{
		/* 把队列控制块首地址赋值到队列头指针 ??这是互斥信号使用,后面分析*/
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/* 把队列空间的首地址赋值给队列头指针 */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}

	/* 长度、单元大小 */
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;

    //队列重置函数
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );

}


/*
    参数:
        xQueue 队列句柄
        xNewQueue 操作队列的状态是什么,新建pdTRUE还是已经创建好了
    返回值:

*/
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;

    //进入临界段,这时候操作队列控制块,不允许打断
	taskENTER_CRITICAL();
	{
        /*
            1、头地址赋值
            2、等待处理的消息个数为0
            3、写入指针赋值为队列头指针
            4、读出指针写入最后一个可用消息
            5、赋值队列锁位解锁状态
        */
		pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
		pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
		pxQueue->pcWriteTo = pxQueue->pcHead;
		pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );
		pxQueue->cRxLock = queueUNLOCKED;
		pxQueue->cTxLock = queueUNLOCKED;

        //判断是否位新建队列
		if( xNewQueue == pdFALSE )//不是新任务
		{
			/* 判断发送等待列表里面是否有任务 */
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
                // 移除事件列表中的任务,如延时任务
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
                    //进行上下文切换
					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 ) );
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	taskEXIT_CRITICAL();

	/* A value is returned for calling semantic consistency with previous
	versions. */
	return pdPASS;
}

3.3消息队列删除

void vQueueDelete( QueueHandle_t xQueue )
{
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;


	#if ( configQUEUE_REGISTRY_SIZE > 0 )
	{
		vQueueUnregisterQueue( pxQueue );
	}
	#endif

	#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
	{
		/* 释放消息队列的内存空间 */
		vPortFree( pxQueue );
	}
	#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
	{
		/* 释放消息队列的内存空间 */
		if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
		{
			vPortFree( pxQueue );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#else
	{
		( void ) pxQueue;
	}
	#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}

3.4消息队列在任务中发送

分支一:

  • 队列未满或覆盖入队,那么看下是否有接收任务,有的话解除一个接收任务,退出临界段。   
  • 这队列已满,不需要等待的话直接退出临界段。如果需要等待,初始化超时结构体。最后退出临界段挂起调度器。

分支二:

  • 锁定队列,判断超时等待是否溢出,溢出解锁跌了恢复调度。
  • 没有溢出,再次判断一下队列是否满,没满解锁队列恢复调度器,如果满了将任务插入等待发送列表,解锁队列,恢复调度。

问:为什么在进入临界段?

因为操作的消息是共享资源,可以被多个任务或中断接收和发送,那么操作的时候是不希望被别的任务打断的。

问:为什么要挂起调度器?

不让内核调度,不让其他任务打断下面程序的处理。

问:为什么要锁定队列?

因为消息队列是可以从任务中发送的,锁定队列是告诉任务不要打断下面程序的查询队列和插入队列,否则整个程序会打乱。



/*
    queueSEND_TO_BACK ?
    由于信号量都是有消息队列实现的,这个时候,操作系统,定义了消息队列的类型
    类型:
        #define	queueSEND_TO_BACK		( ( BaseType_t ) 0 ) 1从队尾加入
        #define	queueSEND_TO_FRONT		( ( BaseType_t ) 1 ) 2从对头加入
        #define queueOVERWRITE			( ( BaseType_t ) 2 ) 3覆盖入队
*/
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

/*
    最终实现消息队列发送的是xQueueGenericSend接口
    参数:
        xQueue  消息队列句柄
        pvItemToQueue 要发送的消息的地址
        xTicksToWait  超时时间
        xCopyPosition 队列操作类型
    返回值:BaseTyp_t

*/
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;

	/* 
        使用for循环的目的:为了快速处理消息拷贝,消息处理功能
     */
	for( ;; )
	{
        //进入临界段
		taskENTER_CRITICAL();
		{
			/* 
                1、判断消息队列是否满了
                2、判断是否允许覆盖入队
                任何一个成立,执行入队操作
             */
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
                //拷贝数据到队列操作空间内
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

				#if ( configUSE_QUEUE_SETS == 1 )
				{
					//宏定义不看
				}
				#else /* configUSE_QUEUE_SETS */
				{
					/* 判断等待接收的列表是否为空. */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
                        //移除等待接收任务的列表,改变等待接收任务的状态为就绪态
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* 操作成功,进行上下文切换,让任务赶紧处理 */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
                    //如果在拷贝数据的时候提示需要调度
					else if( xYieldRequired != pdFALSE )
					{
						/* 再次调度,进行上下文切换*/
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_QUEUE_SETS */
                //退出临界段,返回成功
				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else  //如果不允许入队
			{
                //是否需要阻塞
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 不需要阻塞,退出临界段,之后返回队列队满 */
					taskEXIT_CRITICAL();
					traceQUEUE_SEND_FAILED( pxQueue );
					return errQUEUE_FULL;
				}
                //超时结构体是否操作过
				else if( xEntryTimeSet == pdFALSE )
				{
					/* 超时结构体初始化 */
					vTaskSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
        //退出临界段
		taskEXIT_CRITICAL();

		/* 后面的代码都是允许阻塞处理 */

		/* 
			1、挂起了调度器 ----不让其他任务打断
			2、队列上锁------不让中断打断 (因为之前已经退出临界段了)

		*/

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* 判断阻塞时间是否超时了 */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			//判断队列是否满
			if( prvIsQueueFull( pxQueue ) != pdFALSE )
			{
				//队满,把当前任务,添加到等待发送的事件列表中,内部还把任务添加到延时列表中去
				traceBLOCKING_ON_QUEUE_SEND( pxQueue );
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

				/* 解锁*/
				prvUnlockQueue( pxQueue );

				/* 恢复调度器 */
				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;
		}
	}
}

3.5消息队列在中断中发送

问:上锁的目的是什么?

如果上锁了,在中断中不会处理,上面3.5在任务中发送的功能。如果中断能打断,那么中断也能操作队列、中断也能操作队列,那么优先级会出现混乱。没有上锁,那么可以操作。

/*
	最终调用发送消息接口是xQueueGenericSendFromISR???为什么
	由于信号量都是有消息队列实现的,这个时候,操作系统,定义了消息队列的类型
	类型:
	#define	queueSEND_TO_BACK		( ( BaseType_t ) 0 ) 1、从队尾加入
	#define	queueSEND_TO_FRONT		( ( BaseType_t ) 1 ) 2、从队头加入
	#define queueOVERWRITE			( ( BaseType_t ) 2 ) 3、覆盖入队
*/
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) 
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
/*
	参数:
		xQueue
		pvItemToQueue
		NULL
		queueSEND_TO_BACK
	返回值:BaseType_t 

*/
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 = ( Queue_t * ) xQueue;


	/*带返回值的关闭中断,需要保存上次关闭中断的状态值,恢复时候,写入 */
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		//队满?覆盖入队?
		if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
		{
			//获取了队列发送锁的状态值
			const int8_t cTxLock = pxQueue->cTxLock;
			//拷贝数据到队列操作空间内
			( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

			/* 判断队列是否上锁 */
			/*
				#define queueUNLOCKED					( ( int8_t ) -1 ) 解锁状态
				#define queueLOCKED_UNMODIFIED			( ( int8_t ) 0 )  上锁状态初值
			*/
			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();
					}
				}
				#endif /* configUSE_QUEUE_SETS */
			}
			else //队列已经上锁
			{
				/* 发送锁加一 */
				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;
}
//队列上锁
把发送和接受锁都赋值为上锁的初始值
#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 )
{
	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */

	/* 进入临界段 */
	taskENTER_CRITICAL();
	{
		//获取发送锁的状态值
		int8_t cTxLock = pxQueue->cTxLock;

		/* 遍历解锁  直到解锁为止 */
		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 */
			{
				/* 解除等待消息任务,进行上下文切换 */
				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. */
						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();
	{
		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();
}

3.6消息队列在任务中接收

 分支一:

是出队删除,还是出队不删除,不删除是留个多个任务读取一个消息的情况使用的。

分支二:

接收消息为空,需要判断是否允许阻塞

/*
	最终调用发送消息接口是xQueueGenericReceive???为什么
	队列出队,有两种模式
		一种是:出队后,删除已经读取到队列项或者消息空间
		另一种是:出队后,不删除,然后恢复出队记录地址,让其他任务或者中断,继续读取使用
	类型:
		pdFALSE		删除
		pdTRUE		不删除

*/
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) 
xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )

BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )

				//重点分析这一个,其他流程和发送流程差不多
				//判断是否删除已经接收到的消息空间
 				if( xJustPeeking == pdFALSE )
 				{
 					traceQUEUE_RECEIVE( pxQueue );
						
					//更新消息等待的记录值,让它减一
 					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
 				}
 				else
 				{
					//不删除 就重新赋值未读取消息之前的地址,到出队指针
 					pxQueue->u.pcReadFrom = pcOriginalReadPosition;
 
 				}

3.7消息队列在中断中接收 

 源码分析,参考3.5中断中发送最后一部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/812976.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【RTT驱动框架分析03】- sfus flash 操作库的分析和基于STM32F103RCT6+CUBEMX的SFUS移植教程

sfus flash 操作库的分析 sfus 抽象 /*** serial flash device*/ typedef struct {char *name; /**< serial flash name */size_t index; /**< index of flash device information table see flash_…

filter shape、padding、strides三者之间的关系

filter shape 在深度学习中&#xff0c;“filter shape”&#xff08;滤波器形状&#xff09;指的是卷积神经网络中滤波器&#xff08;也称为卷积核&#xff09;的维度或大小。滤波器是用于在卷积层中提取特征的重要组件。 滤波器形状通常是一个四维张量&#xff0c;具体取决…

小研究 - 一种复杂微服务系统异常行为分析与定位算法(二)

针对极端学生化偏差&#xff08;&#xff25;&#xff58;&#xff54;&#xff52;&#xff45;&#xff4d;&#xff45; &#xff33;&#xff54;&#xff55;&#xff44;&#xff45;&#xff4e;&#xff54;&#xff49;&#xff5a;&#xff45;&#xff44; &#…

并发编程 - CompletableFuture

文章目录 Pre概述FutureFuture的缺陷类继承关系功能概述API提交任务的相关API结果转换的相关APIthenApplyhandlethenRunthenAcceptthenAcceptBoththenCombinethenCompose 回调方法的相关API异常处理的相关API获取结果的相关API DEMO实战注意事项 Pre 每日一博 - Java 异步编程…

DPN(Dual Path Network)网络结构详解

论文&#xff1a;Dual Path Networks 论文链接&#xff1a;https://arxiv.org/abs/1707.01629 代码&#xff1a;https://github.com/cypw/DPNs MXNet框架下可训练模型的DPN代码&#xff1a;https://github.com/miraclewkf/DPN 我们知道ResNet&#xff0c;ResNeXt&#xff0c;D…

面向对象【对象数组的使用与内存分析、方法重载、可变个数形参】

文章目录 对象数组实例对象内存分析 方法的重载重载方法调用打印方法的重载 可变个数形参的方法特点传递任意数量的参数与其他参数共存传递数组或多个参数 对象数组 存储对象引用的数组。它允许您在单个数组中存储多个对象&#xff0c;并通过索引访问和操作这些对象。 实例 创…

使用SpringBoot+SpringMVC+Mybatis+Redis实现个人博客管理平台

文章目录 前言1. 项目概述2. 项目需求2.1功能需求2.2 其他需求2.3 系统功能模块图 3. 开发环境4. 项目结构5. 部分功能介绍5.1 数据库密码密文存储5.2 统一数据格式返回5.3 登录拦截器 6. 项目展示 前言 在几个月前实现了一个servlet版本的博客系统&#xff0c;本项目则是在原…

JWT无状态理解

JSON Web Tokens (JWT) 被称为无状态&#xff0c;因为授权服务器不需要维护任何状态&#xff1b;令牌本身就是验证令牌持有者授权所需的全部内容。 JWTs都签订使用数字签名算法&#xff08;例如RSA&#xff09;不能被伪造。因此&#xff0c;任何信任签名者证书的人都可以放心地…

二维深度卷积网络模型下的轴承故障诊断

1.数据集 使用凯斯西储大学轴承数据集&#xff0c;一共有4种负载下采集的数据&#xff0c;每种负载下有10种 故障状态&#xff1a;三种不同尺寸下的内圈故障、三种不同尺寸下的外圈故障、三种不同尺寸下的滚动体故障和一种正常状态 2.模型&#xff08;二维CNN&#xff09; 使…

基于传统检测算法hog+svm实现图像多分类

直接上效果图&#xff1a; 代码仓库和视频演示b站视频005期&#xff1a; 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示&#xff1a; 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…

iOS开发-自定义TabbarController与Tabbar按钮Badge角标

iOS开发-自定义Tabbar按钮Badge角标 Tabbar是放在APP底部的控件。UITabbarController是一个非常常见的一种展示模式了。比如微信、QQ都是使用tabbar来进行功能分类的管理。 一、实现自定义Tabbar 我这里Tabbar继承于系统的UITabBar&#xff0c;定义背景图、线条的颜色、tab…

Redis系列 2- Redis 的持久化

Redis系列 2- Redis 的持久化 1、关于 Redis 持久化2、RDB 持久化2.1、RDB 文件的创建与载入2.2、RDB 自动间隔性保存的配置2.3、Redis 快照2.4、RDB 重点总结 3、AOF 持久化3.1、命令追加(append)3.2、AOF 文件的写入与同步3.3、AOF 工作原理3.4、AOF 的文件载入与数据还原3.5…

QGIS3.28的二次开发一:编译工程

环境&#xff1a;VS2019OSGeo4WCMake_3.26Cygwin64QGIS_3.28 注意&#xff1a;一定要按照步骤顺序来&#xff01; 一、配置环境 &#xff08;一&#xff09;VS2019 VS2019下载链接https://my.visualstudio.com/Downloads?qvisual%20studio%202019&wt.mc_ido~msft~vsco…

day50-Insect Catch Game(捉虫游戏)

50 天学习 50 个项目 - HTMLCSS and JavaScript day50-Insect Catch Game&#xff08;捉虫游戏&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport"…

基于SpringBoot+Vue的大学生租房系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

第 356 场力扣周赛题解

A 满足目标工作时长的员工数目 签到题 class Solution { public:int numberOfEmployeesWhoMetTarget(vector<int> &hours, int target) {int res 0;for (auto x: hours)if (x > target)res;return res;} };B 统计完全子数组的数目 枚举子数组&#xff1a;枚举子数…

小研究 - 主动式微服务细粒度弹性缩放算法研究(四)

微服务架构已成为云数据中心的基本服务架构。但目前关于微服务系统弹性缩放的研究大多是基于服务或实例级别的水平缩放&#xff0c;忽略了能够充分利用单台服务器资源的细粒度垂直缩放&#xff0c;从而导致资源浪费。为此&#xff0c;本文设计了主动式微服务细粒度弹性缩放算法…

溟㠭篆刻艺术……“瀚1”

每个人生犹如一颗颗繁星&#xff0c;在时空交错中汇聚成一条星汉灿烂的银河&#xff0c;在静谧深邃的宇宙中清澈回响&#xff0c;熠熠生辉。妻晓蕾题注溟㠭刊…… 溟㠭 篆刻作品“瀚1” 溟㠭 篆刻作品“瀚1” 溟㠭 篆刻作品“瀚1” 文/晓蕾

质效卓越,科技前沿—QECon北京站线下盛会成功落幕

7月28日-29日&#xff0c;第八届QECon质量效能大会在北京成功召开&#xff0c;这是质量效能领域备受期待的一场盛会&#xff0c;从2020年第一届QECon开启以来&#xff0c;历经四年QECon北京站终于首次线下落地。本次大会的核心主旨为“数生智慧&#xff0c;高质量发展新引擎”&…

虚拟局域网VLAN

概述 广播域 使用一个或多个以太网交换机互连接起来的交互式以太网&#xff0c;其所有站点都属于同一个广播域&#xff0c;随着交换式以太网规模的扩大&#xff0c;广播域响应扩大&#xff0c;从而形成一个巨大的广播域。 但是巨大的广播域会带来很多的弊端&#xff1a; 广…