RTOS系统4-队列管理

时间:2020-12-25 20:13:36

回顾:

任务创建、任务状态切换、任务函数

1、队列概述

          基于 FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权限的小程序。这些独立的任务之间很可能会通过相互通信以提供有用的系统功能。FreeRTOS 中所有的通信与同步机制都是基于队列(queue)实现的 。

2、特性

数据存储:

        队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的深度。在队列创建时需要设定其深度和每个单元的大小。

        数据写入为先进先出(FIFO),可以队尾写入,对首读出;当然也可以对首写入,队尾读出

可被多任务存取:

        队列是具有自己独立权限的内核对象,并不属于或赋予任何任务。所有任务都可以向同一队列写入和读出。

读队列时阻塞 :

        当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态

写队列时阻塞:

        同读队列一样,任务也可以在写队列时指定一个阻塞超时时间。

        当被写队列已满时,任务进入阻塞态以等待队列空间有效的最长时间。 由于队列可以被多个任务写入,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列空间有效。这种情况下,一旦队列空间有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。

3、队列函数

xQueueCreate() API 函数 

函数定义:

xQueueHandle xQueueCreate(unsigned portBASE_TYPE uxQueueLength,//队列长度,即单元个数

                                                 unsigned portBASE_TYPE uxItemSize);//每个单元大小
看到一个非常熟悉的类型:portBASE_TYPE,在上篇博客中,该类型用于得到任务的优先级;
unsigned portBASE_TYPE uxPriority;
	uxPriority=uxTaskPriorityGet(NULL);//获取当前任务的优先级,所以参数NULL
发送数据到队列:
xQueueSendToBack()//发送到队尾

xQueueSendToFront()//发送到队头

xQueueSend()//同上,发送到对首

xQueueSendToBackFromISR()//中断服务中,发送数据到队尾

xQueueSendToFrontFromISR()//中断服务中,发送数据到队首

调用示例:

portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,//队列句柄
                                const void * pvItemToQueue,//发送数据的指针
                                portTickType xTicksToWait );//阻塞超时时间

注意:

如 果 把 xTicksToWait 设 置 为 portMAX_DELAY , 并 且 在FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1,那么阻塞等待将没有超时限制;

函数返回值: pdPASS  //成功

                        errQUEUE_FULL  //队列满,知道超时结束,队列依旧满

接受队列数据:

xQueueReceive();//接收队列数据,接一个数据,队首就删一个数据

xQueuePeek();//只接收数据,不对队列进行操作

xQueueReceiveFromISR()//中断中使用的队列接收

调用示例:

portBASE_TYPE xQueuePeek( xQueueHandle xQueue,
const void * pvBuffer,
portTickType xTicksToWait );

uxQueueMessagesWaiting() API 函数 

查询队列中有效数据单元个数

unsigned xportBASE_TYPE uxQueueMessagesWaiting(xQueueHandle xQueue)

队列任务示例:

一个任务不断地往队列中写数据,一个任务读取队列中的数据(读任务设定优先级更高)

因此,一旦队列中有数据,读队列的任务立刻结束阻塞,读取该数据,理论上队列中数据长度不会超过1.

首先是main.c

#include "stm32f4xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "list.h"
#include "my_include.h"//自定义头文件库
extern xQueueHandle xQueue;//声明 已经定义了队列
static void BSP_init(void);
int main(void)
{
	xQueue=xQueueCreate(5,sizeof(long));//创建队列
	BSP_init();
	APPTaskCreate();//建立任务
	vTaskStartScheduler();
	while(1);		
}

//设备初始化
static void BSP_init(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 中断分组:第四组,4位全用于设置抢占优先级,0位由于响应优先级,中断中也要设置为第四组
	LED_GPIO_Config();
	Key_GPIO_Config();
	BEEP_GPIO_Config();
	USART2_Config();
}

APPTaskCreate():

建立了三个任务,发送任务优先级为8,接收任务优先级为9

//任务汇总
void APPTaskCreate(void)
{
xTaskCreate(vTaskSender,"Task queue sender1",1000,(void *)100,8,NULL);//queue任务1 ,传入参数为100
xTaskCreate(vTaskSender,"Task queue sender2",1000,(void *)200,8,NULL);//queue任务2 ,传入参数为200
xTaskCreate(vTaskReceiver,"Task queue receive",1000,NULL,9,NULL);//queue 接收任务}

任务函数:

xQueueHandle xQueue;//全局队列
//队列发送任务
static void vTaskSender(void *length)//输入参数为队列要发送的值
{
	long lValueToSend;
	lValueToSend=(long) length;
	portBASE_TYPE xStatus;
	while(1)
	{
		xStatus=xQueueSendToBack(xQueue,&lValueToSend,0);
		if(xStatus!=pdPASS)//发送不成功
		{
			Usart_SendString(USART2,(uint8_t *)"发送失败\n");
		}
		//vTaskDelay(10);//阻塞10个心跳,若采用协作式调度,则使用taskYIELD();
		taskYIELD();
	}	
}
//队列接收任务
static void vTaskReceiver(void *pvParameters)
{
		long lReceiveValue;
		portBASE_TYPE xStatus;
		const portTickType wait_time=100/portTICK_PERIOD_MS;//设定时间为100ms
		while(1)
		{
			if(uxQueueMessagesWaiting(xQueue)!=0)//如果队列不为空
					Usart_SendString(USART2,(uint8_t *)"队列中有数据未被读取");
			
			xStatus=xQueueReceive(xQueue,&lReceiveValue,wait_time);
			
			if(xStatus==pdPASS)//1,读取成功
			{
					Usart_SendString(USART2,(uint8_t *)"数据为");
					Usart_SendString(USART2,(uint8_t *)lReceiveValue);		
			}
			else 
					Usart_SendString(USART2,(uint8_t *)"读取数据失败");
		}
}

因此实际通过USART发送的数据应该为:

100 200 100 200 100 200....交替

示例任务解释:

RTOS系统4-队列管理

任务流程:

  • Receiver任务优先级更高,因此优先执行
  • Receiver执行到xStatus=xQueueReceive(xQueue,&lReceiveValue,wait_time)时,由于队列为空,因此会进入阻塞状态等待
  • 接着执行次优先级Sender2任务(假设),任务中的发送函数执行完(但sender2尚未结束),队列中有数据了
  • Receiver阻塞立刻结束,Sender2被抢占,Receiver执行完回到Sender2任务
  • Sender2继续执行,执行到taskYIELD(),Sender2结束,让给同优先级的Sender1任务 
  • ......

上例,读队列任务具有最高优先级,所以队列不会拥有一个以上的数据单元。这是因为一旦数据被写队列任务写进队列,读队列任务立即抢占写队列任务,把刚写入的数据单元读走。

但如果写队列任务具有最高优先级,所以队列正常情况下一是处于满状态。这是因为一旦读队列任务从队列中读走一个数据单元,某个写队列任务就会立即抢占读队列任务,把刚刚读走的位置重新写入,之后便又转入阻塞态以等待队列空间有效