当前位置 博文首页 > freemote的博客:FreeRTOS队列使用

    freemote的博客:FreeRTOS队列使用

    作者:[db:作者] 时间:2021-07-18 19:10

    ?队列的基本特性

    队列是任务间通信的主要形式。常被用于任务与任务、中断与任务之间的消息传递,通常是FIFO的形式。

    队列阻塞任务

    ?当一个任务读取空队列时,这个任务将进入阻塞态(不消耗cpu,cpu会去运行其他任务),直到队列不为空或者阻塞时长超过设定的阻塞时间,将进入就绪态。

    当一个任务向满队列写时,这个任务会进入阻塞态(不消耗cpu,cpu会去运行其他任务),直到队列不为满或者阻塞时长超过设定的阻塞时间,将进入就绪态。

    如果有多个任务因为同一个队列而进入阻塞态,当队列满足条件的时候,优先级最高的任务先解除阻塞态,其他任务继续阻塞。

    中断里面只能使用后缀带有“FromISR”的API。

    ?队列使用(部分API使用)

    typedef void * QueueHandle_t; //队列句柄
    
    QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize );//创建队列
    
    BaseType_t xQueueSend( QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);//向队列写数据
    
    BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);//从队列里面读取数据
    
    BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void   *pvItemToQueue,BaseType_t*pxHigherPriorityTaskWoken); 
    //向队列里面写入数据,用在中断中

    测试程序

    总体设计:总共4个任务,2个队列

    lcdtask:显示当前计数,并向队列TaskToTaskQueue里面写入当前计数;

    testtask0:读取队列TaskToTaskQueue里面的数据,并串口打印;

    testtask1:1s串口打印一次数据;

    queuetask:读取队列TaskToIrqQueue的数据并打印出来;

    串口中断:收到数据后往队列TaskToIrqQueue里面写入收到的数据。

    创建任务和队列:

    #define LCD_TASK_PRIO		        1   //任务优先级
    #define LCD_TASK_STK_SIZE 		80  //任务堆栈大小
    TaskHandle_t LCDTaskHandler;                //任务句柄
    void LCDTaskFunc(void *pvParameters);       //任务函数
    
    #define TEST_TASK0_PRIO		        2   //任务优先级
    #define TEST_TASK0_STK_SIZE 		50  //任务堆栈大小
    TaskHandle_t TestTask0Handler;              //任务句柄
    void TestTask0Func(void *pvParameters);     //任务函数
    
    #define TEST_TASK1_PRIO			3   //任务优先级
    #define TEST_TASK1_STK_SIZE 		80  //任务堆栈大小
    TaskHandle_t TestTask1Handler;              //任务句柄
    void TestTask1Func(void *pvParameters);     //任务函数
    
    #define QUEUE_TASK_PRIO		         4  //任务优先级
    #define QUEUE_TASK_STK_SIZE 		80  //任务堆栈大小
    TaskHandle_t QueueTaskHandler;              //任务句柄
    void QueueTaskFunc(void *pvParameters);     //任务函数
    
    QueueHandle_t TaskToIrqQueue;	//信息队列句柄
    QueueHandle_t TaskToTaskQueue;	//信息队列句柄
    
    void OtherTest(void )
    {
        BaseType_t ret;
    	
        BoardInitMcu();
        BoardInitPeriph();
    	
        TaskToIrqQueue=xQueueCreate(1,255); //创建一个队列,队列有1项,每个项长为255byte。
        TaskToTaskQueue=xQueueCreate(5,2); //创建一个队列,队列有5项,每个项长为2byte
    	
        ret=xTaskCreate((TaskFunction_t )LCDTaskFunc,     	
    		    (const char*    )"lcdtask",   	
    		    (uint16_t       )LCD_TASK_STK_SIZE, 
    		    (void*          )NULL,				
    		    (UBaseType_t    )LCD_TASK_PRIO,	
    		    (TaskHandle_t*  )&LCDTaskHandler); 
    	
        ret=xTaskCreate((TaskFunction_t )TestTask0Func,     	
    		    (const char*    )"testtask0",   	
    		    (uint16_t       )TEST_TASK0_STK_SIZE, 
    		    (void*          )NULL,				
    		    (UBaseType_t    )TEST_TASK0_PRIO,	
    		    (TaskHandle_t*  )&TestTask0Handler);   
    				
        ret=xTaskCreate((TaskFunction_t )TestTask1Func,     
    		    (const char*    )"testtask1",   
    		    (uint16_t       )TEST_TASK1_STK_SIZE, 
    		    (void*          )NULL,
    		    (UBaseType_t    )TEST_TASK1_PRIO,
    		    (TaskHandle_t*  )&TestTask1Handler); 
    	
        ret=xTaskCreate((TaskFunction_t )QueueTaskFunc,     
    		    (const char*    )"queue task",   
    		    (uint16_t       )QUEUE_TASK_STK_SIZE, 
    		    (void*          )NULL,
    		    (UBaseType_t    )QUEUE_TASK_PRIO,
    		    (TaskHandle_t*  )&QueueTaskHandler); 
    				
        vTaskStartScheduler(); 
    }
    
    

    各个任务函数:

    //LCD显示任务当前计数,并把当前计数写入TaskToTaskQueue队列函数 
    void  LCDTaskFunc(void *pvParameters)
    {
        char  string[21] = {0};
        static uint8_t i=0;
    
        for(;;)
        {
            i++;
            sprintf(string, "%03d ", i);
            dis_string(1,0,(uint8_t *)string,1);
    				
    	xQueueSend(TaskToTaskQueue,&i,20);
    		
    	vTaskDelay(500);                 //延时500ms,也就是500个时钟节拍	
        }    
    }
    
    //Test0任务函数,读取 TaskToTaskQueue的数据并打印
    void TestTask0Func(void *pvParameters)
    {
        uint16_t i=0;
    	
        while(1)
        {	
    	if(TaskToTaskQueue!=NULL)	
    	{			
    	    xQueueReceive(TaskToTaskQueue,&i,portMAX_DELAY);
    	    printf("current count:%d\r\n",i);
    	}
        }
    }   
    
    //Test1任务函数
    void TestTask1Func(void *pvParameters)
    {
        while(1)
        {
            vTaskDelay(1000);
    	printf("task1\r\n");
        }
    }
    
    //从队列读取串口收到的数据并打印任务函数
    void QueueTaskFunc(void *pvParameters)
    {
        for(;;)
        {
    	if(TaskToIrqQueue!=NULL)
    	{
    	    if(xQueueReceive(TaskToIrqQueue,str,10))
    	    {
    	        printf("serial:%s\r\n",str);
    	    }
    	}
        }
    }

    串口中断函数中写数据到队列:

    void USART1_IRQHandler( void )
    {
        BaseType_t pxHigherPriorityTaskWoken=pdFALSE;
    	
        ……//省略部分代码
    		
        xQueueSendFromISR(TaskToIrqQueue,SerialFrame.Buff,&pxHigherPriorityTaskWoken);
        //向队列写入数据,如果写队列使某个任务就绪,并且这个任务是当前所有任务中优先级最高的,那么pxHigherPriorityTaskWoken将会被设置为pdTRUE
    		
        portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
        //如果pxHigherPriorityTaskWoken为真将进行任务切换
    }

    ?运行结果:

    ?

    cs