FreeRTOS消息队列应用

工程链接

读队列时阻塞

发送消息至队列的任务的优先级低于从队列接收消息的任务的优先级,这意味着队列项数永远不会超过一项,一旦有数据发送至队列,接收消息的任务就会解锁,抢占发送任务然后移除队列中的消息,使队列内容再次为空。

QueueHandle_t xQueue;  //声明消息队列

//发送任务
static void vSenderTask(void *pvParameters)
{
    
    
    int valuetosend;
    BaseType_t xStatus;
    const TickType_t xTicksToWait=pdMS_TO_TICKS(0);
    
    valuetosend=(int)pvParameters;
    for(;;)
    {
    
    
        xStatus=xQueueSendToBack(xQueue,&valuetosend,xTicksToWait);
    	if(xStatus!=pdPASS)
        {
    
    
        	printf("Could not send to the queue.\r\n");
   	 	}
    }
}

//接收任务
static void vReceiverTask(void *pvParameters)
{
    
    
    int ReceivedValue;
    BaseType_t xStatus;
    const TickType_t xTicksToWait=pdMS_TO_TICKS(100);
    
    for(;;)
    {
    
    
        if(uxQueueMessagesWaiting( xQueue ) != 0)
        {
    
    
            printf("Queue should have been empty!\r\n");
        }
        xStatus = xQueueReceive( xQueue, &ReceivedValue, xTicksToWait );
        if( xStatus == pdPASS )
		{
    
    
		/* Data was successfully received from the queue, print out the received value. */
			printf( "Received =%d \r\n", ReceivedValue );
		}
		else
        {
    
    
			printf( "Could not receive from the queue.\r\n" );
		}
    }
}

int main()
{
    
    
    xQueue = xQueueCreate( 5, sizeof( int));  //创建一个消息队列
    
        if(xQueue!=NULL)
        {
    
    
         /*创建两个任务,具有相同的优先级,两个任务会依次发送消息至队列*/
            xTaskCreate( vSenderTask,  //任务函数名
                        "Sender1",      //任务名
                        1000,           //任务堆栈大小
                        ( void * ) 100, //传递给任务函数的参数
                        1,              //任务优先级
                        NULL );  		//暂无任务句柄
		   xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );
            /*接收任务*/
            xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );   
            vTaskStartScheduler();  //开启任务调度
        }
    	else
        {
    
    
              printf("The Queue could not be created!\r\n");
        }
    for(;;);
    
}

在这里插入图片描述

  • 接收任务因为优先级较高先开始运行,但从队列中读取消息失败,因为队列为空进入阻塞态,sender2 发送任务在接收任务进入阻塞态后开始运行
  • sender2 发送任务发送消息至队列,接收任务退出阻塞态,因接收任务具有较高优先级抢占sender2 ,占用CPU
  • 接收任务清空消息队列再次进入阻塞态,此时轮到sender1 执行
  • sender1 发送消息至队列中,使得接收任务又退出阻塞态,并抢占sender1,清空消息队列,循环往复。。。。。。

队列传递结构体(多个发送源情况)

接收方接收由多个发送源发送至队列中的消息时,需要判断数据的来源,使用方式:利用队列传递结构体,在结构体中包含数据值和数据来源信息
在这里插入图片描述

typedef struct
{
    
    
    int  iValue;  //数据值
    int iMeaning; //数据来源信息
}xData;

举例:发送消息的任务具有更高的优先级,因此队列会满!

typedef enum   
{
    
    
    esender1,
    esender2
}DataSource_t;  //定义枚举类型变量,限定源的范围

typedef struct
{
    
    
    uint8_t ucValue,
    DataSource_t eDataSource
}Data_t;       //定义存储至队列中的结构体类型Data_t

/* 声明两个Data_t类型的变量 */
static const Data_t xStructsToSend[2]=   
{
    
    
    {
    
    100,esender1},
    {
    
    200,esender2}
};

QueueHandle_t xQueue;  //声明消息队列

//发送任务
static void vSenderTask(void *pvParameters)
{
    
    
    BaseType_t xStatus;
    const TickType_t xTicksToWait=pdMS_TO_TICKS(100);
   
    for(;;)
    {
    
    
        xStatus=xQueueSendToBack(xQueue,pvParameters,xTicksToWait);  //发送消息
    	if(xStatus!=pdPASS)  //消息发送失败
        {
    
    
        	printf("Could not send to the queue.\r\n");
   	 	}
    }
}

//接收任务
static void vReceiverTask(void *pvParameters)
{
    
    
    Data_t xReceiveStructure;
    BaseType_t xStatus;
    const TickType_t xTicksToWait=pdMS_TO_TICKS(0);
    
    for(;;)
    {
    
    
        if(uxQueueMessagesWaiting( xQueue ) != 3)
        {
    
    
            printf("Queue should have been fill!\r\n");
        }
        xStatus = xQueueReceive( xQueue, &xReceiveStructure, xTicksToWait );
        if( xStatus == pdPASS )
		{
    
    
		/* Data was successfully received from the queue, print out the received value. */
            if(xReceiveStructure.eDataSource==esender1)
            {
    
    
                printf("From sender 1=%d \r\n",xReceiveStructure.ucValue);
            }
            else
            {
    
    
                printf( "From sender 2=%d \r\n",xReceiveStructure.ucValue);
            }
			
		}
		else
        {
    
    
			printf( "Could not receive from the queue.\r\n" );
		}
    }
}

int main()
{
    
    
    xQueue = xQueueCreate( 3, sizeof( Data_t));  //创建一个消息队列
    
        if(xQueue!=NULL)
        {
    
    
         /*创建两个任务,具有相同的优先级,两个任务会依次发送消息至队列*/
            xTaskCreate( vSenderTask,  //任务函数名
                        "Sender1",      //任务名
                        1000,           //任务堆栈大小
                       (void *)&( xStructsToSend[ 0 ] ), //传递给任务函数的参数
                        2,              //任务优先级
                        NULL );  		//暂无任务句柄
		   xTaskCreate( vSenderTask, "Sender2", 1000, (void *)&( xStructsToSend[ 1] ), 2, NULL );
     
            /*接收任务*/
            xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );   
            vTaskStartScheduler();  //开启任务调度
        }
    	else
        {
    
    
              printf("The Queue could not be created!\r\n");
        }
    for(;;);
    
}

在这里插入图片描述

  • t1 sender1任务执行,发送3个消息至队列中

  • t2 sender1 任务因为队列满而进入阻塞态,等待下次消息发送,此时sender2 任务因为具有最高的优先级而进入就绪态

  • t3 sender2 任务发现队列满,因此进入阻塞态等待第一次消息发送。此时接收任务成为最高优先级任务而进入就绪态

  • t4 具有比接收任务更高优先级的sender1 和sender2 都在等待队列有可用空间,因此一旦接收任务从队列中移除一个消息,就会被抢占。sender1 和sender2 具有相同的优先级,因此调度器选择等待时间最长的任务sender1 进入就绪态

  • t5 sender1 发送第四个数据至队列中,此时队列满,sender1 任务再次进入阻塞态,接收任务具有最高优先级而移除一个消息

    此时sender1 已经发送4个消息至队列中,sender2 依然在等待发送第一个消息至队列中。

  • t6 具有比接收任务更高优先级的sender1 和sender2 都在等待队列有可用空间,因此一旦接收任务从队列中移除一个消息,就会被抢占。此时sender1 和sender2 都在等待,而sender2 等待时间比sender1 等待时间更长,因此调度器选择sender2 进入就绪态

  • t7 sender2 终于成功发送第一个消息至队列中,队列变满,sender2 再次进入阻塞态,此时接收任务开始执行,移除一个消息

  • 之后会重复进行sender1 和sender2 依次发送消息至队列中,因此结果如下图所示,sender1 发送4个消息后,才有sender2 的第一消息

在这里插入图片描述

传递数据的指针

  1. 指针指向的内存空间所有权必须明确

共享内存在其指针发送到队列之前,其内容只允许被发送任务访问;

共享内存指针从队列中被读出之后,其内容亦只允许被接收任务访问

  1. 指针指向的内存空间必须有效

指针指向的内存空间是动态分配的或是从预分配的缓存区池中获取的,则只应该有一个任务对其进行内存释放,当这段内存空间被释放后,就不应该有其他任务再次访问这段空间。

指针不能用于访问分配在任务栈空间上的数据,一旦任务堆栈空间结构改变,数据将无效

/*声明一个队列句柄*/
QueueHandle_t xPointerQueue;
/*创建一个队列项为5 的存储指针地址的队列*/
xPointerQueue = xQueueCreate( 5, sizeof( char * ) );

/*任务获取缓冲区,将字符串写入缓冲区中,然后发送缓冲区的地址至创建的队列中*/
void vStringSendingTask( void *pvParameters )
{
    
    
	char *pcStringToSend;   //声明一个char*类型指针
	const size_t xMaxStringLength = 50;  //限定分配内存空间大小
    const TickType_t xTicksToWait=pdMS_TO_TICKS(100);
	BaseType_t xStringNumber = 0;
	for( ;; )
	{
    
    
		pcStringToSend = ( char * ) pvPortMalloc( xMaxStringLength ); //动态分配缓存区
		/*写字符串至缓冲区中 */
		snprintf( pcStringToSend, xMaxStringLength, "String number %d\r\n", xStringNumber );
		/* 此任务的每次迭代中字符串不同 */
		xStringNumber++;
		/*发送缓冲区的地址至队列中*/
		xQueueSend( xPointerQueue, /* 队列句柄 */
					&pcStringToSend, /* 指向缓冲区指针的地址 */
					xTicksToWait );  /*阻塞等待时间*/
	} 
}

/*任务接收队列中缓冲区的地址,*/
void vStringReceivingTask( void *pvParameters )
{
    
    
	char *pcReceivedString;
    const TickType_t xTicksToWait=pdMS_TO_TICKS(100);
	for( ;; )
	{
    
    
		/* 接收缓冲区的地址 */
		xQueueReceive( xPointerQueue, 
					  &pcReceivedString, /* 在指针pcReceiveString中存储缓冲区的地址 */
					  xTicksToWait );
		/* The buffer holds a string, print it out. */
		printf("%s", pcReceivedString );
		/* 释放内存空间 */
		vPortFree( pcReceivedString );
	}
}

int main()
{
    
    
	
		uart_init(9600);
    /*创建一个队列项为5 的存储指针地址的队列*/
		xPointerQueue = xQueueCreate( 5, sizeof( char * ) );
    
        if(xPointerQueue!=NULL)
        {
    
    
         /*创建发送任务*/
            xTaskCreate( vStringSendingTask,  //任务函数名
                        "Sender1",      //任务名
                         1000,           //任务堆栈大小
					   NULL, //传递给任务函数的参数
                         2,              //任务优先级
                         NULL );  		//暂无任务句柄
				
            /*接收任务*/
            xTaskCreate( vStringReceivingTask,
                        "Receiver", 
                        1000,
                        NULL,
                        3,
                        NULL );   
            vTaskStartScheduler();  //开启任务调度
        }
    	else
        {
    
    
              printf("The Queue could not be created!\r\n");
        }
    for(;;);
    
}

在这里插入图片描述

以上内容均来自 FreeRTOS官方文档 FreeRTOS_Real_Time_Kernel
下载官方链接

猜你喜欢

转载自blog.csdn.net/weixin_44333597/article/details/107725480