FreeRTOS系列|列表和列表项

本文详细介绍了FreeRTOS中的列表和列表项数据结构,包括它们的定义、初始化、插入、删除和遍历等操作。通过具体实例展示了如何在STM32CubeMX配置FreeRTOS并创建任务,以及如何在任务中使用列表和列表项进行操作,例如向列表中插入和删除列表项。此外,还提供了STM32工程的配置和编程示例,帮助读者理解FreeRTOS中列表和列表项的应用。
摘要由CSDN通过智能技术生成

列表和列表项

列表和列表项是FreeRTOS的一个数据结构,FreeRTOS大量使用到了列表和列表项,它是FreeRTOS的基石

1. 列表和列表项的定义
1.1 列表

列表是FreeRTOS中的一个数据结构,与链表类似,列表被用来跟踪FreeRTOS中的任务。其结构体 List_tlist.h 文件中被定义

typedef struct xLIST
{
	/* 列表内有效列表项个数 */
	configLIST_VOLATILE UBaseType_t uxNumberOfItems;
	/* 记录当前列表项索引号,用于遍历列表 */
	ListItem_t * configLIST_VOLATILE pxIndex;			
	/* 列表中最后一个列表项,表示列表结束 */
	MiniListItem_t xListEnd;							
} List_t;
1.2 列表项

列表项就是存放在列表中的项目,FreeRTOS提供两种类型的列表项:列表项和迷你列表项。列表项的结构体 ListItem_tlist.h 文件中被定义

struct xLIST_ITEM
{
	/* 列表项值 */
	configLIST_VOLATILE TickType_t xItemValue;			
	/* 指向下一个列表项值 */
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;		
	/* 指向上一个列表项值 */
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
	/* 当前列表项的拥有者 */
	void * pvOwner;										
	/* 当前列表项归属的列表 */
	void * configLIST_VOLATILE pvContainer;				
};
typedef struct xLIST_ITEM ListItem_t;
1.3 迷你列表项

有些情况下不需要列表项这么全的功能,为了避免造成内存浪费,定义了迷你列表项。迷你列表项的结构体 MiniListItem_tlist.h 文件中被定义

struct xMINI_LIST_ITEM
{
	/* 列表项值 */
	configLIST_VOLATILE TickType_t xItemValue;
	/* 指向下一个列表项值 */
	struct xLIST_ITEM * configLIST_VOLATILE pxNext;
	/* 指向上一个列表项值 */
	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

在这里插入图片描述

2. 列表和列表项的函数应用
  • 列表的初始化:新创建的列表需要对其做初始化处理,其实就是初始化List_t结构体中的各个成员变量,通过vListInitialise()函数来完成,该函数在list.c文件中定义
void vListInitialise( List_t * const pxList )
{
	/* 此时列表中只有一个列表项xListEnd,索引地址指向尾节点 */
	pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );			
	/* xListEnd列表项值初始化为portMAX_DELAY(portmacro.h中定义的宏)*/
	pxList->xListEnd.xItemValue = portMAX_DELAY;
	/* 初始化xListEnd的pxNext变量,此时指向自身 */
	pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );	
	/* 初始化xListEnd的pxPrevious变量,此时指向自身 */
	pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
	/* 当前有效的列表项个数为0,没有算xListEnd */
	pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}

在这里插入图片描述

  • 列表项的初始化:新创建的列表项也需要初始化,通过vListInitialiseItem()函数来完成,该函数在list.c文件中定义
void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* 列表项初始化为不归属任何任务列表所有 */
	pxItem->pvContainer = NULL;
}
  • 列表项的插入:将指定列表项插入到列表中,通过vListInsert()函数来完成
void vListInsert( List_t * const pxList, 	//列表项要插入的列表
				  ListItem_t * const pxNewListItem){	//要插入的列表项
	ListItem_t *pxIterator;
	/* 获取要插入的列表项值 */
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
	/* 如获取到的列表项值为最大时,直接插入到尾部 */
	if( xValueOfInsertion == portMAX_DELAY )
	{
		pxIterator = pxList->xListEnd.pxPrevious;
	}
	else{
		/* 从尾节点开始遍历,与下个节点的Value值进行比较,当要插入Value值大于遍历列表项Value时,就获得插入位置(按升序方式插入) */
		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) 
		{
			//空循环,什么也不做
		}
	}
	/* 更新后面节点信息 (前后指针进行赋值) */
	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	/* 更新前面节点信息(前后指针进行赋值) */
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;
	/* 把列表项归属于当前的列表 */
	pxNewListItem->pvContainer = ( void * ) pxList;
	/* 有效列表项数量进行累加 */
	( pxList->uxNumberOfItems )++;
}

下图演示了向一个空列表中依次插入40、60和50三个列表项的插入过程

在这里插入图片描述

  • 列表项的末尾插入:将指定列表项插入到列表末尾,通过vListInsertEnd()函数来完成
void vListInsertEnd( List_t * const pxList, 	//列表项要插入的列表
					 ListItem_t * const pxNewListItem )	//要插入的列表项
{
	/* 获取当前列表索引值	*/
	ListItem_t * const pxIndex = pxList->pxIndex;
	/* 插入到索引值之前,先进行尾部更新	*/
	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;
	/* 再进行头部更新 */
	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;
	/* 列表项归属于列表 */
	pxNewListItem->pvContainer = ( void * ) pxList;
	/* 更新列表项数目 */
	( pxList->uxNumberOfItems )++;
}

下图演示了向一个列表末尾插入列表项的插入过程

在这里插入图片描述

  • 列表项的删除:从列表中删除指定的列表项,通过uxListRemove()函数来完成
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
	/* 获取列表项所在的列表地址 */
	List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
	/* 将要删除的列表项的前后两个列表项进行连接 */
	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
	/* Only used during decision coverage testing. */
	mtCOVERAGE_TEST_DELAY();
	/* 索引是否需要更新 */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
	/* 被删除列表项的成员变量pvContainer清零 */
	pxItemToRemove->pvContainer = NULL;
	/* 有效列表项数量减一 */
	( pxList->uxNumberOfItems )--;
	/* 返回当前列表的有效列表项数量 */
	return pxList->uxNumberOfItems;
}
  • 列表项的遍历:List_t中的成员变量pxIndex是用来遍历列表的,FreeRTOS使用如下函数(宏)来完成列表的遍历,每调用一次这个函数,列表的pxIndex变量就会指向下一个列表项,并返回这个列表项的pvOwner变量值
/* pxTCB用来保存pxIndex所指向的列表项的pvOwner, pxList表示要遍历的列表 */
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )	\
{	\
	/* 首先获取当前列表 */
	List_t * const pxConstList = ( pxList );	\
	/* 列表的pxIndex变量指向下一个列表项 */
	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;	\
	/* 如果pxIndex指向了列表的xListEnd成员变量,表示到了列表末尾 */
	if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )	\
	{	\
		/* 此时就跳过xListEnd,pxIndex再次指向列表头的列表项,这样就完成了一次列表遍历 */
		( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;	\
	}	\
	/* 获取当前pxIndex指向的所有者(其实就是任务控制块)*/
	( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;	\
}
3. 列表和列表项的应用实例

使用STM32CubeMX将FreeRTOS移植到工程中,并创建两个任务:

Led_Task:D2指示灯闪烁,用来提示系统正在运行
List_Task:列表和列表项操作任务,调用列表和列表项相关的API函数,并通过串口输出的信息来观察这些函数的运行过程

3.1 STM32CubeMX设置
  • RCC设置外接HSE,时钟设置为72M
  • PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • PA0设置为GPIO输入模式、下拉模式;PE2/PE3/PE4设置为GPIO输入模式、上拉模式
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • 激活FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数

在这里插入图片描述
在这里插入图片描述

  • 使用FreeRTOS操作系统,一定要将HAL库的Timebase Source从SysTick改为其他定时器,选好定时器后,系统会自动配置TIM

在这里插入图片描述

  • 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM软件编程
  • 创建按键驱动文件key.c和key.h,参考按键输入例程
  • 添加Led_Task、List_Task任务函数代码
/******************Led_Task*******************/
void Led_Task(void const * argument){
  for(;;){
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
    osDelay(500);  //1ms时基
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
    osDelay(500);  //1ms时基
  }
}
/*****************List_Task*******************/
void List_Task(void const * argument){
	//列表和列表项初始化
	vListInitialise(&TestList);
	vListInitialiseItem(&ListItem1);
	vListInitialiseItem(&ListItem2);
	vListInitialiseItem(&ListItem3);	
	ListItem1.xItemValue=40;	//ListItem1列表项值位40
	ListItem2.xItemValue=60;	//ListItem2列表项值位60
	ListItem3.xItemValue=50;	//ListItem3列表项值位50	
	//打印列表和列表项的地址
	printf("/**************列表和列表项地址**************/\r\n");
	printf("项目                      		地址		\r\n");
	printf("TestList                  		%#x			\r\n",(int)&TestList);
	printf("TestList->pxIndex        		%#x			\r\n",(int)TestList.pxIndex);
	printf("TestList->xListEnd        		%#x			\r\n",(int)(&TestList.xListEnd));
	printf("ListItem1                 		%#x			\r\n",(int)&ListItem1);
	printf("ListItem2                 		%#x			\r\n",(int)&ListItem2);
	printf("ListItem3                 		%#x			\r\n",(int)&ListItem3);
	printf("/*******************结束*********************/\r\n");
	printf("PRESS KEY_UP to Continue!\r\n\r\n\r\n");
	while(KEY_Scan(0)!= KEY_UP_PRES) HAL_Delay(10);					
	
	//向列表中添加列表项ListItem1
	vListInsert(&TestList,&ListItem1);	//添加列表项ListItem1
	printf("/*************添加列表项ListItem1************/\r\n");
	printf("项目                         	  地址		\r\n");
	printf("TestList->xListEnd->pxNext   	  %#x		\r\n",(int)(TestList.xListEnd.pxNext));
	printf("ListItem1->pxNext            	  %#x		\r\n",(int)(ListItem1.pxNext));
	printf("TestList->xListEnd->pxPrevious    %#x		\r\n",(int)(TestList.xListEnd.pxPrevious));
	printf("ListItem1->pxPrevious             %#x		\r\n",(int)(ListItem1.pxPrevious));
	printf("/*******************结束*********************/\r\n");
	printf("PRESS KEY_UP to Continue!\r\n\r\n\r\n");
	while(KEY_Scan(0)!= KEY_UP_PRES) HAL_Delay(10);				
	
	//向列表中添加列表项ListItem2
	vListInsert(&TestList,&ListItem2);	//添加列表项ListItem2
	printf("/*************添加列表项ListItem2************/\r\n");
	printf("项目                              地址		 \r\n");
	printf("TestList->xListEnd->pxNext        %#x		 \r\n",(int)(TestList.xListEnd.pxNext));
	printf("ListItem1->pxNext                 %#x		 \r\n",(int)(ListItem1.pxNext));
	printf("ListItem2->pxNext                 %#x		 \r\n",(int)(ListItem2.pxNext));
	printf("TestList->xListEnd->pxPrevious    %#x		 \r\n",(int)(TestList.xListEnd.pxPrevious));
	printf("ListItem1->pxPrevious             %#x		 \r\n",(int)(ListItem1.pxPrevious));
	printf("ListItem2->pxPrevious             %#x		 \r\n",(int)(ListItem2.pxPrevious));
	printf("/*******************结束*********************/\r\n");
	printf("PRESS KEY_UP to Continue!\r\n\r\n\r\n");
	while(KEY_Scan(0)!= KEY_UP_PRES) HAL_Delay(10);					
	
	//向列表中添加列表项ListItem3
	vListInsert(&TestList,&ListItem3);	//添加列表项ListItem3
	printf("/*************添加列表项ListItem3************/\r\n");
	printf("项目                              地址		    \r\n");
	printf("TestList->xListEnd->pxNext        %#x			\r\n",(int)(TestList.xListEnd.pxNext));
	printf("ListItem1->pxNext                 %#x			\r\n",(int)(ListItem1.pxNext));
	printf("ListItem3->pxNext                 %#x			\r\n",(int)(ListItem3.pxNext));
	printf("ListItem2->pxNext                 %#x			\r\n",(int)(ListItem2.pxNext));
	printf("TestList->xListEnd->pxPrevious    %#x			\r\n",(int)(TestList.xListEnd.pxPrevious));
	printf("ListItem1->pxPrevious             %#x			\r\n",(int)(ListItem1.pxPrevious));
	printf("ListItem3->pxPrevious             %#x			\r\n",(int)(ListItem3.pxPrevious));
	printf("ListItem2->pxPrevious             %#x			\r\n",(int)(ListItem2.pxPrevious));
	printf("/*******************结束*********************/\r\n");
	printf("PRESS KEY_UP to Continue!\r\n\r\n\r\n");
	while(KEY_Scan(0)!= KEY_UP_PRES) HAL_Delay(10);					
	
	//从列表中删除列表项ListItem2
	uxListRemove(&ListItem2);						//删除列表项ListItem2
	printf("/*************删除列表项ListItem2************/\r\n");
	printf("项目                              地址		    \r\n");
	printf("TestList->xListEnd->pxNext        %#x			\r\n",(int)(TestList.xListEnd.pxNext));
	printf("ListItem1->pxNext                 %#x			\r\n",(int)(ListItem1.pxNext));
	printf("ListItem3->pxNext                 %#x			\r\n",(int)(ListItem3.pxNext));
	printf("TestList->xListEnd->pxPrevious    %#x			\r\n",(int)(TestList.xListEnd.pxPrevious));
	printf("ListItem1->pxPrevious             %#x			\r\n",(int)(ListItem1.pxPrevious));
	printf("ListItem3->pxPrevious             %#x			\r\n",(int)(ListItem3.pxPrevious));
	printf("/*******************结束*********************/\r\n");
	printf("PRESS KEY_UP to Continue!\r\n\r\n\r\n");
	while(KEY_Scan(0)!= KEY_UP_PRES) HAL_Delay(10);					
	
	//向列表末尾添加列表项ListItem2
	TestList.pxIndex=TestList.pxIndex->pxNext;	
	vListInsertEnd(&TestList,&ListItem2);				//列表末尾添加列表项ListItem2
	printf("/***********末尾添加列表项ListItem2**********/\r\n");
	printf("项目                              地址		    \r\n");
	printf("TestList->pxIndex                 %#x			\r\n",(int)TestList.pxIndex);
	printf("TestList->xListEnd->pxNext        %#x			\r\n",(int)(TestList.xListEnd.pxNext));
	printf("ListItem2->pxNext                 %#x			\r\n",(int)(ListItem2.pxNext));
	printf("ListItem1->pxNext                 %#x			\r\n",(int)(ListItem1.pxNext));
	printf("ListItem3->pxNext                 %#x			\r\n",(int)(ListItem3.pxNext));
	printf("TestList->xListEnd->pxPrevious    %#x			\r\n",(int)(TestList.xListEnd.pxPrevious));
	printf("ListItem2->pxPrevious             %#x			\r\n",(int)(ListItem2.pxPrevious));
	printf("ListItem1->pxPrevious             %#x			\r\n",(int)(ListItem1.pxPrevious));
	printf("ListItem3->pxPrevious             %#x			\r\n",(int)(ListItem3.pxPrevious));
	printf("/*******************结束*********************/\r\n\r\n\r\n");
  for(;;){
	printf("ListTask is Runing!\r\n");
    osDelay(1000);
  }
}
3.3 下载验证

编译无误下载到开发板后,打开串口调试助手,可以看到串口首先打印出列表和列表项地址信息;根据提示按下K_UP按键,依次执行插入三个列表项、删除列表项2、列表末尾插入列表项2的程序并通过串口打印出相关地址信息;最后执行for循环中的printf语句,D2指示灯开始闪烁

/**************列表和列表项地址**************/
项目                              地址				
TestList                          0x20000080					
TestList->pxIndex                 0x20000088					
TestList->xListEnd                0x20000088					
ListItem1                         0x20000094					
ListItem2                         0x200000a8					
ListItem3                         0x200000bc					
/*******************结束*********************/
PRESS KEY_UP to Continue!

/*************添加列表项ListItem1************/
项目                              地址		    
TestList->xListEnd->pxNext        0x20000094					
ListItem1->pxNext                 0x20000088					
TestList->xListEnd->pxPrevious    0x20000094					
ListItem1->pxPrevious             0x20000088					
/*******************结束*********************/
PRESS KEY_UP to Continue!

/*************添加列表项ListItem2************/
项目                              地址		    
TestList->xListEnd->pxNext        0x20000094					
ListItem1->pxNext                 0x200000a8					
ListItem2->pxNext                 0x20000088					
TestList->xListEnd->pxPrevious    0x200000a8					
ListItem1->pxPrevious             0x20000088					
ListItem2->pxPrevious             0x20000094					
/*******************结束*********************/
PRESS KEY_UP to Continue!

/*************添加列表项ListItem3************/
项目                              地址		    
TestList->xListEnd->pxNext        0x20000094					
ListItem1->pxNext                 0x200000bc					
ListItem3->pxNext                 0x200000a8					
ListItem2->pxNext                 0x20000088					
TestList->xListEnd->pxPrevious    0x200000a8					
ListItem1->pxPrevious             0x20000088					
ListItem3->pxPrevious             0x20000094					
ListItem2->pxPrevious             0x200000bc					
/*******************结束*********************/
PRESS KEY_UP to Continue!

/*************删除列表项ListItem2************/
项目                              地址		    
TestList->xListEnd->pxNext        0x20000094					
ListItem1->pxNext                 0x200000bc					
ListItem3->pxNext                 0x20000088					
TestList->xListEnd->pxPrevious    0x200000bc					
ListItem1->pxPrevious             0x20000088					
ListItem3->pxPrevious             0x20000094					
/*******************结束*********************/
PRESS KEY_UP to Continue!

/***********末尾添加列表项ListItem2**********/
项目                              地址		    
TestList->pxIndex                 0x20000094					
TestList->xListEnd->pxNext        0x200000a8					
ListItem2->pxNext                 0x20000094					
ListItem1->pxNext                 0x200000bc					
ListItem3->pxNext                 0x20000088					
TestList->xListEnd->pxPrevious    0x200000bc					
ListItem2->pxPrevious             0x20000088					
ListItem1->pxPrevious             0x200000a8					
ListItem3->pxPrevious             0x20000094					
/*******************结束*********************/

ListTask is Runing!
ListTask is Runing!
ListTask is Runing!

关注我的公众号,在公众号里发如下消息,即可获取相应的工程源代码:

FreeRTOS列表和列表项实例

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
FreeRTOS中,任务、队列、信号量等都是以列表的形式存储的,因此,列表的遍历是非常常见的操作。FreeRTOS提供了一些API来遍历不同类型的列表。 以任务列表为例,FreeRTOS提供了以下API来遍历任务列表: 1. `vTaskList()`:此API将任务列表的详细信息打印到标准输出。 2. `vTaskGetRunTimeStats()`:此API将任务列表中每个任务的运行时间、CPU使用率等信息打印到标准输出。 如果你需要在代码中遍历任务列表,可以使用以下API: 1. `vTaskSuspendAll()`:此API将所有任务暂停,以便安全地遍历任务列表。 2. `xTaskGetNextTask()`:此API返回一个指向下一个任务控制块的指针。 3. `xTaskResumeAll()`:此API将所有任务恢复,以便它们可以继续执行。 以下是一个示例代码,展示了如何使用上述API来遍历任务列表: ``` void vTaskListAll(void) { TaskStatus_t *pxTaskStatusArray; volatile UBaseType_t uxArraySize, x; // 获取任务数量 uxArraySize = uxTaskGetNumberOfTasks(); // 分配存储任务状态信息的内存 pxTaskStatusArray = pvPortMalloc(sizeof(TaskStatus_t) * uxArraySize); // 获取任务状态信息 uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL); // 打印任务状态信息 printf("TaskName\t\tPriority\tStatus\t\tStackHighWaterMark\n"); for(x = 0; x < uxArraySize; x++) { printf("%s\t\t%d\t\t%s\t\t%d\n", pxTaskStatusArray[x].pcTaskName, pxTaskStatusArray[x].uxCurrentPriority, pxTaskStatusArray[x].eCurrentState == eRunning ? "Running" : pxTaskStatusArray[x].eCurrentState == eReady ? "Ready" : pxTaskStatusArray[x].eCurrentState == eBlocked ? "Blocked" : "Unknown", pxTaskStatusArray[x].usStackHighWaterMark); } // 释放存储任务状态信息的内存 vPortFree(pxTaskStatusArray); } ``` 这个函数将列出所有任务的名称、优先级、状态和堆栈高水位标记。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安迪西嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值