基于正点原子的FreeRTOS笔记——队列

一、什么是队列

队列是任务到任务、任务到中断、中断到任务数据交流的一种机制。

在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。

在创建队列时要指定队列长度以及队列项目的大小。

二、队列特点

1、数据入队出队方式:队列通常采用”先进先出“(FIFO)先进入消息队列的消息先传给任务,

也可以配置为后进后出(LIFO)的方式。

2、数据传递方式:FreeRTOS消息队列传递的是实际数据即将数据拷贝到队列中进行传递,并不是数据地址,RTX,uCOS-II 和 uCOS-III 是传递的地址。FreeRTOS中可以通过传递指针地址方式来传递指针。

3、多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送或读取消息。

4、出队/入队阻塞:当任务向一个队列发送消息时可以指定一个阻塞时间。

  • 若阻塞时间为0 :直接返回不会等待。
  • 若为0~port_MAX_DELAY:等待设定的阻塞时间,超时后直接返回。
  • 若为port_MAX_DELAY:一直等到可以入队。

当一个任务试图从一个空队列中读取时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中的数据变得可用,或者阻塞时间过期。

当一个任务试图写入到一个满队列时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中出现可用空间,或者阻塞时间过期。

如果同一个队列上有多个处于阻塞状态的任务, 那么具有最高优先级的任务将最先解除阻塞。

如果优先级相同,等待时间最长的任务最先解除阻塞。

三、队列的函数

1、xQueueCreate

 QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                             UBaseType_t uxItemSize );

创建一个新队列并返回 可引用此队列的句柄。

 configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中被设置为 1,或保留未定义状态(此时,它默认 为 1) ,才能使用此 RTOS API 函数。

每个队列需要 RAM 用于保存队列状态和 包含在队列(队列存储区域)中的项目。 如果使用 xQueueCreate() 创建队列,则所需的 RAM 将自动 从 FreeRTOS 堆中分配。 如果使用 xQueueCreateStatic() 创建队列, 则 RAM 由应用程序编写者提供,这会产生更多的参数, 但这样能够在编译时静态分配 RAM 。

参数:

uxQueueLength  队列可同时容纳的最大项目数 。
uxItemSize  存储队列中的每个数据项所需的大小(以字节为单位)。

数据项按副本排队,而不是按引用排队, 因此该值为每个排队项目将被复制的字节数。队列中每个数据项 必须大小相同。

返回:

如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法 分配 ,则返回 NULL。

2、xQueueSend

BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

这是一个调用 xQueueGenericSend() 的宏。 

等同于 xQueueSendToBack()。

在队列中发布项目。不得从中断服务程序调用此函数。请参阅 xQueueSendFromISR() 以获取 可用于 ISR 的替代方案。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。时间在 tick 周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:如果成功发布项目,则返回 pdTRUE,否则返回 errQUEUE_FULL。

3、xQueueSendToBack

BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

这是一个调用 xQueueGenericSend() 的宏。

等同于 xQueueSend()。

从队列尾部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序调用此函数。 请参阅 xQueueSendToBackFromISR (),获取可在 ISR 中使用的 替代方案。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL。

4、xQueueSendToFront
 

BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

此宏用于调用 xQueueGenericSend()。

从队列头部入队一个数据项。 数据项通过复制 而非引用入队。 不得从中断服务程序 调用此函数。 请参阅 xQueueSendToFrontFromISR() 了解 可在 ISR 中使用的替代方法。

参数:

xQueue 队列的句柄,数据项将发布到此队列。
pvItemToQueue 指向待入队数据项的指针。创建队列时定义了队列将保留的项的大小,因此固定数量的字节将从 pvItemToQueue 复制到队列存储区域。
xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。如果设置为 0,调用将立即返回。时间以滴答周期为单位定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果成功发布项目,返回 pdTRUE,否则返回 errQUEUE_FULL。

5、xQueueReceive

BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );

这是一个调用 xQueueGenericReceive() 函数的宏。

从队列中接收项目。该项目通过复制接收,因此必须提供足够大小的缓冲区。创建队列时定义了复制到缓冲区中的字节数。

成功接收后会将数据删除。

中断服务程序中不得使用此函数。请参阅 xQueueReceiveFromISR 了解可以选择的替代方案。

参数:

xQueue 要从中接收项目的队列的句柄。
pvBuffer 指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。
xTicksToWait  如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 如果队列为空,将 xTicksToWait 设置为 0 将导致函数立即返回。时间在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE。

6、xQueuePeek
 

BaseType_t xQueuePeek( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );

这是一个调用 xQueueGenericReceive() 函数的宏。

从队列中接收项目,而无须从队列中删除该项目。 项目由复制接收,因此必须提供适当大小的缓冲区 。 队列创建时,复制到缓冲区中的字节数已定义 。

成功接收的数据不会删除其仍在队列中。

中断服务例程中不得使用此宏。

参数:

xQueue 要从中接收项目的队列的句柄。
pvBuffer 指针,指向将复制收到的项目的缓冲区。 它必须至少足够大,才能容纳创建队列时定义的队列项的大小。
xTicksToWait 如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 时间已在滴答周期中定义,因此如果需要,应使用常量 portTICK_PERIOD_MS 来将其转换为实时。

如果 INCLUDE_vTaskSuspend 设置为 “1” ,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时)。

返回:

如果从队列中成功接收(窥视)项目,则返回 pdTRUE,否则返回 pdFALSE。

四、实验程序

 1、句柄声明和数组声明

QueueHandle_t Key_Hander  = NULL;
QueueHandle_t BigData_Hander  = NULL;
char BigData[50] = {"123zxcv123vbnm"};

2、 创建队列和开始任务

void freertos_demo(void)
{
	 Key_Hander = xQueueCreate(2,sizeof(uint8_t));
	 if(Key_Hander != NULL)
	 {
		 printf("小数据队列创建成功\r\n");
	 }
	 BigData_Hander = xQueueCreate(1,sizeof(char *));
	 if(BigData_Hander != NULL)
	 {
		 printf("大数据队列创建成功\r\n");
	 }
	 
		xTaskCreate( (TaskFunction_t        ) start_task,
								 (char *                ) "start_task",
								 (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
								 (void *                ) NULL,
								 (UBaseType_t           ) START_TASK_PRIO,
								 (TaskHandle_t *        ) &start_task_handler );
    vTaskStartScheduler();
}

 3、开始任务

void start_task( void * pvParameters )
{
	taskENTER_CRITICAL();/*进入临界区*/
		xTaskCreate( (TaskFunction_t        ) task1,
								 (char *                ) "task1",
								 (configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
								 (void *                ) NULL,
								 (UBaseType_t           ) TASK1_PRIO,
								 (TaskHandle_t *        ) &task1_handler );
								 
		xTaskCreate( (TaskFunction_t        ) task2,
								 (char *                ) "task2",
								 (configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
								 (void *                ) NULL,
								 (UBaseType_t           ) TASK2_PRIO,
								 (TaskHandle_t *        ) &task2_handler );						 
		
		xTaskCreate( (TaskFunction_t        ) task3,
								 (char *                ) "task3",
								 (configSTACK_DEPTH_TYPE) TASK3_STACK_SIZE,
								 (void *                ) NULL,
								 (UBaseType_t           ) TASK3_PRIO,
								 (TaskHandle_t *        ) &task3_handler );			
								 
		vTaskDelete(NULL);
		taskEXIT_CRITICAL(); 		/*退出临界区*/				 
}

 4、任务1、2、3

/* 入队 */
void task1( void * pvParameters )
{
	uint8_t key = 0;
	BaseType_t backValue = 0;
	char *buf;
	buf = &BigData[0];
	while(1)
	{
		key = key_scan(0);
		if(key == KEY0_PRES)
		{
			backValue = xQueueSendToBack(Key_Hander,&key,portMAX_DELAY); 
			if(backValue == pdTRUE)
			{
				printf("小数据入队成功\r\n");
				
			}
			else printf("小数据入队失败\r\n");
		}
		else if(key == KEY1_PRES)
		{
			backValue = xQueueSendToBack(BigData_Hander,&buf,portMAX_DELAY); 
			if(backValue == pdTRUE)
			{
				printf("大数据入队成功\r\n");
				
			}
			else printf("大数据入队失败\r\n");
			
		}
	}
}


/* 出队 */
void task2( void * pvParameters )
{
	uint8_t key = 0;
	BaseType_t backValue = 0;
	while(1)
	{
		backValue = xQueueReceive(Key_Hander,&key,portMAX_DELAY);
		if(backValue == pdTRUE)
			{
				printf("小数据出队成功\r\n");
				
			}
			else printf("小数据出队失败\r\n");
	}
	
	
}

void task3( void * pvParameters )
{
	char *bigData;
	BaseType_t backValue = 0;
	while(1)
	{
		backValue = xQueueReceive(BigData_Hander,&bigData,portMAX_DELAY);
		if(backValue == pdTRUE)
			{
				
				printf("大数据出队成功\r\n%s\r\n",bigData);
						
			}
			else printf("大数据出队失败\r\n");
	}
	
	
}

五、实验现象

1、按下KEY0按键,小数据队列入队出队

数据入队后由于任务2优先级大于任务1,所以任务2会抢占任务1

2、按下KEY1,大数据队列入队出队

六、发送大数据指针分析

memcpy函数原型 

void *memcpy(void *str1, const void *str2, size_t n)

参数: 

  • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
  • n -- 要被复制的字节数。

发送函数xQueueGenericSend会调用prvCopyDataToQueue将数据复制到队列

static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue,
                                      const void * pvItemToQueue,
                                      const BaseType_t xPosition )
{
    BaseType_t xReturn = pdFALSE;
    UBaseType_t uxMessagesWaiting;
 /********省略******************/
    if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
    {
         /********省略******************/
    }
    else if( xPosition == queueSEND_TO_BACK )
    {
        ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); 
    }
    else
    {
        ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); 
        /********省略******************/
    }
 /********省略******************/
}
/*-----------------------------------------------------------*/

接收函数xQueueReceive 调用prvCopyDataFromQueue将数据从队列中复制

static void prvCopyDataFromQueue( Queue_t * const pxQueue,
                                  void * const pvBuffer )
{
    if( pxQueue->uxItemSize != ( UBaseType_t ) 0 )
    {
       /********省略******************/

       
        ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); 
    }
}

由于函数使用memcpy函数,需要传入数据的地址来进行复制,任务1中

char *buf; buf = &BigData[0];

 xQueueSendToBack(BigData_Hander,&buf,portMAX_DELAY); 

buf存放的是数组BigData的地址,&buf传递的是buf的地址相当于传递了数组地址的地址,接收队列复制后的数据就是数组的地址。任务3 bigData就等于&BigData[0];

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 正点原子freertos pdf是一份详细的技术文档,主要介绍了正点原子FreeRTOS实时操作系统的基本架构和实现方法。该文档详细介绍了FreeRTOS的任务管理、时间管理、内存管理、中断管理、队列管理等相应的应用、算法、数据结构、函数和宏等的使用方法和实现原理。该文档还包括了多个实际例子,展示了如何使用FreeRTOS在真实项目中实现任务调度、时间管理、共享资源管理等功能。 该文档对于想要学习和掌握FreeRTOS实时操作系统的开发人员来说,是一份非常有价值的资料。通过学习该文档,开发人员可以深入了解FreeRTOS的设计思路、内部实现和应用场景,掌握FreeRTOS的核心功能和使用方法,从而更加熟练地应用FreeRTOS进行实时操作系统开发。同时,该文档还提供了大量的代码示例,让开发人员可以更加快速地理解和应用FreeRTOS,帮助开发人员在项目开发过程中更高效地完成任务。总之,正点原子freertos pdf是一份对于实时操作系统开发人员非常有用的学习资料,可以帮助开发人员更快速地掌握FreeRTOS技术,提高开发效率。 ### 回答2: 正点原子freertos是一种嵌入式实时操作系统,采用轻量级设计的freertos内核。它具有高度可移植性、可扩展性、可配置性、可靠性等优点,可以很好地处理实时任务和多任务之间的优先级和调度问题,满足各种应用的不同需求。正点原子freertos提供了丰富的API接口,支持线程、信号量、消息队列、定时器、互斥锁等功能,方便编写实时应用程序。在使用过程中,用户可以根据系统需求进行配置参数,如内存大小、最大线程数量、时钟频率等。同时,正点原子freertos还支持跨平台开发,通过移植可以在不同的硬件平台上运行,提高了应用程序的可移植性和可重用性。normal,正点原子freertos官方也提供了详细的编程手册和示例代码,方便开发者学习和参考。总之,正点原子freertos是一款高效、灵活、可靠的实时操作系统,可以为嵌入式系统的开发提供强有力的支持。 ### 回答3: 正点原子是一家国内知名的开源电子硬件平台品牌,其推出了一款名为 FreeRTOS 的操作系统软件。该软件旨在让开发者和工程师们更加方便地进行电子设备的开发和编程。此外,正点原子还为该软件提供了一份详细的使用手册——FreeRTOS PDF。 FreeRTOS PDF 是一份面向初学者和资深工程师的完整使用手册,其中包括了从安装软件、核心概念、任务管理、队列和信号量、定时器等各方面的详细教程和指导。通过这份手册,用户可以更加深入地理解 FreeRTOS 软件的运作机制和一些常见问题的解决方案。同时,手册也提供了丰富的示例代码,方便用户在开发过程中进行参考和借鉴。 总之,正点原子FreeRTOS PDF 告诉我们,通过该软件可以轻松地实现完整的多任务管理系统,支持多个处理器、各种架构和设备等,并能保证高性能和实时性。而该手册则是完美地展现了 FreeRTOS 的功能和使用方法,为广大工程师和开发者提供了强有力的支持和指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值