当前位置:   article > 正文

【正点原子FreeRTOS学习笔记】————(6)FreeRTOS的列表和列表项

【正点原子FreeRTOS学习笔记】————(6)FreeRTOS的列表和列表项

一、列表和列表项的简介(熟悉)

列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。
列表项就是存放在列表中的项目
在这里插入图片描述
列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表
列表的特点:列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加的个数决定的,随时可以改变
数组的特点:数组成员地址是连续的,数组在最初确定了成员数量后期无法改变

在OS中任务的数量是不确定的,并且任务状态是会发生改变的,所以非常适用列表(链表)这种数据结构

列表
有关于列表的东西均在文件 list.c 和 list.h 中,首先我们先看下在list.h中的,列表相关结构体

typedef struct xLIST
{
     listFIRST_LIST_INTEGRITY_CHECK_VALUE			/* 校验值 */
     volatile UBaseType_t uxNumberOfItems;			/* 列表中的列表项数量 *
     ListItem_t * configLIST_VOLATILE pxIndex	    /* 用于遍历列表项的指针 */
     MiniListItem_t xListEnd					    /* 末尾列表项 */
     listSECOND_LIST_INTEGRITY_CHECK_VALUE		    /* 校验值 */
} List_t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

列表结构示意图
在这里插入图片描述
1、在该结构体中, 包含了两个宏,这两个宏是确定的已知常量, FreeRTOS通过检查这两个常量的值,
来判断列表的数据在程序运行过程中,是否遭到破坏 ,该功能一般用于调试, 默认是不开启的

2、成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd)

3、成员 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项

4、成员变量 xListEnd 是一个迷你列表项,排在最末尾

列表项
列表项是列表中用于存放数据的地方,在 list.h 文件中,有列表项的相关结构体定义

struct xLIST_ITEM
{
   listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/* 用于检测列表项的数据完整性 */
   configLIST_VOLATILE TickType_t xItemValue			/* 列表项的值 */
   struct xLIST_ITEM * configLIST_VOLATILE pxNext		/* 下一个列表项 */
   struct xLIST_ITEM * configLIST_VOLATILE pxPrevious	/* 上一个列表项 */
   void * pvOwner							            /* 列表项的拥有者 */
   struct xLIST * configLIST_VOLATILE pxContainer; 		/* 列表项所在列表 */
   listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE			/* 用于检测列表项的数据完整性 */
};
typedef struct xLIST_ITEM ListItem_t; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

列表项结构示意图
在这里插入图片描述
1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
2、成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
3、成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块)
4、成员变量 pxContainer 用于指向列表项所在列表。

迷你列表项
迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项

struct xMINI_LIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE 			    /* 用于检测数据完整性 */
    configLIST_VOLATILE TickType_t xItemValue;				/* 列表项的值 */
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;		    /* 上一个列表项 */
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; 	/* 下一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

迷你列表项结构示意图
在这里插入图片描述
1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
2、成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
3、迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销

列表和列表项的关系
列表初始状态,以及即将插入的两个列表项如下:
在这里插入图片描述

二、列表相关API函数介绍(掌握)

在这里插入图片描述
初始化列表vListInitialise()
初始化后列表结构

在这里插入图片描述

void vListInitialise(List_t * const pxList)
{
   /*  初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd */
   pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
   /* xListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后 */
   pxList->xListEnd.xItemValue = portMAX_DELAY;
   /* 初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身 */
   pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
   pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
   /* 初始化时,列表中的列表项数量为 0(不包含 xListEnd) */
   pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
   /* 初始化用于检测列表数据完整性的校验值 */
   listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
   listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

函数 vListInitialiseItem()
初始化后的列表项结构
在这里插入图片描述
在这里插入图片描述

void vListInitialiseItem( ListItem_t * const pxItem )
{
   /* 初始化时,列表项所在列表设为空 */
   pxItem->pxContainer = NULL;
   /* 初始化用于检测列表项数据完整性的校验值 */
   listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
   listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

函数vListInsert()
此函数用于将待插入列表的列表项按照列表项值升序进行排序,有序地插入到列表中

在这里插入图片描述

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) 
{
    ListItem_t * pxIterator; 
	const TickType_t  xValueOfInsertion  =  pxNewListItem->xItemValue; 	/* 获取列表项的数值依据数值升序排列 */
	listTEST_LIST_INTEGRITY( pxList ); 						/* 检查参数是否正确 */
	listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); 				/* 如果待插入列表项的值为最大值 */ 
	if( xValueOfInsertion == portMAX_DELAY )
 	{ 
		pxIterator = pxList->xListEnd.pxPrevious; 				/* 插入的位置为列表 xListEnd 前面 */ 
	}
	 
	 else 
	{
		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->pxContainer = pxList; 						/* 更新待插入列表项所在列表 */ 
	( pxList->uxNumberOfItems )++;							/* 更新列表中列表项的数量 */ 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

插入值为40的列表项
在这里插入图片描述
将列表项xList_Item1,插入列表List中

在前面的基础上,在插入值为60的列表项,结果如下:
在这里插入图片描述
在前面的基础上,在插入值为50的列表项,结果如下:
在这里插入图片描述
总结:函数vListInsert(),是将待插入列表的列表项按照列表项值升序进行排序,有序地插入到列表中

函数 vListInsertEnd()
在这里插入图片描述

void vListInsertEnd (  List_t * const pxList ,   ListItem_t * const pxNewListItem  )
{
    省略部分非关键代码 … …
    /* 获取列表 pxIndex 指向的列表项 */
   	ListItem_t * const pxIndex = pxList->pxIndex;
   	/* 更新待插入列表项的指针成员变量 */
   	pxNewListItem->pxNext = pxIndex;
   	pxNewListItem->pxPrevious = pxIndex->pxPrevious;
   	/* 更新列表中原本列表项的指针成员变量 */
   	pxIndex->pxPrevious->pxNext = pxNewListItem;
   	pxIndex->pxPrevious = pxNewListItem;
   	/* 更新待插入列表项的所在列表成员变量 */
   	pxNewListItem->pxContainer = pxList;
   	/* 更新列表中列表项的数量 */
   	( pxList->uxNumberOfItems )++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向的列表项前面,是一种无序的插入方法

列表项末尾插入图示
假设默认列表如下:
在这里插入图片描述
插入值为30的列表项
在这里插入图片描述
假设默认列表如下:
在这里插入图片描述
插入值为30的列表项
在这里插入图片描述
总结:函数vListInsertEnd(),是将待插入的列表项插入到列表 pxIndex 指针指向的列表项前面;
它是一种无序的插入方法!!

函数 uxListRemove()
此函数用于将列表项从列表项所在列表中移除
在这里插入图片描述

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) 
{
  List_t * const pxList = pxItemToRemove->pxContainer; 
  /* 从列表中移除列表项 */ 
  pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
  pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; 	
  /*如果 pxIndex 正指向待移除的列表项 */ 
  if( pxList->pxIndex == pxItemToRemove ) 
  {
     /*pxIndex 指向上一个列表项*/ 
     pxList->pxIndex = pxItemToRemove->pxPrevious;
  }
  else 
	{ 
		mtCOVERAGE_TEST_MARKER(); 
	} 
	/*将待移除的列表项的所在列表指针清空*/ 
	pxItemToRemove->pxContainer = NULL; 
	/*更新列表中列表项的数量*/ 
	( pxList->uxNumberOfItems )--; 
	/*返回移除后的列表中列表项的数量*/ 
	return pxList->uxNumberOfItems; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

列表项的删除图示
在这里插入图片描述

三、列表项的插入和删除实验(掌握)

1、实验目的:学会对FreeRTOS 列表和列表项的操作函数使用,并观察运行结果和理论分
析是否一致
2、实验设计:将设计两个任务:start_task、task1

首先要定义列表和列表项

List_t                  TestList;           /* 定义测试列表 */
ListItem_t              ListItem1;          /* 定义测试列表项1 */
ListItem_t              ListItem2;          /* 定义测试列表项2 */
ListItem_t              ListItem3;          /* 定义测试列表项3 */

  • 1
  • 2
  • 3
  • 4
  • 5

在task1中

void task1(void *pvParameters)
{
   /* 第一步:初始化列表和列表项 */
    vListInitialise(&TestList);                 /* 初始化列表 */
    vListInitialiseItem(&ListItem1);            /* 初始化列表项1 */
    vListInitialiseItem(&ListItem2);            /* 初始化列表项2 */
    vListInitialiseItem(&ListItem3);            /* 初始化列表项3 */

     /* 第二步:打印列表和其他列表项的地址 */
    printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
    printf("项目\t\t\t地址\r\n");
    printf("TestList\t\t0x%p\t\r\n", &TestList);
    printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
    printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
    printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
    printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
    printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
    printf("/**************************结束***************************/\r\n");
    printf("按下KEY0键继续!\r\n\r\n\r\n");
    
 while (key_scan(0) != KEY0_PRES)
    {
        vTaskDelay(10);
    }
 
   /* 第三步:列表项1插入列表 */
    printf("/*****************第三步:列表项1插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem1);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    printf("按下KEY0键继续!\r\n\r\n\r\n");
    while (key_scan(0) != KEY0_PRES)
    {
        vTaskDelay(10);
    }

     /* 第四步:列表项2插入列表 */
    printf("/*****************第四步:列表项2插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem2);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    printf("按下KEY0键继续!\r\n\r\n\r\n");
    while (key_scan(0) != KEY0_PRES)
    {
        vTaskDelay(10);
    }
    
    /* 第五步:列表项3插入列表 */
    printf("/*****************第五步:列表项3插入列表******************/\r\n");
    vListInsert((List_t*    )&TestList,         /* 列表 */
                (ListItem_t*)&ListItem3);       /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    printf("按下KEY0键继续!\r\n\r\n\r\n");
    while (key_scan(0) != KEY0_PRES)
    {
        vTaskDelay(10);
    }
    
    /* 第六步:移除列表项2 */
    printf("/*******************第六步:移除列表项2********************/\r\n");
    uxListRemove((ListItem_t*   )&ListItem2);   /* 移除列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/**************************结束***************************/\r\n");
    printf("按下KEY0键继续!\r\n\r\n\r\n");
    while (key_scan(0) != KEY0_PRES)
    {
        vTaskDelay(10);
    }
    
    /* 第七步:列表末尾添加列表项2 */
    printf("/****************第七步:列表末尾添加列表项2****************/\r\n");
    vListInsertEnd((List_t*     )&TestList,     /* 列表 */
                   (ListItem_t* )&ListItem2);   /* 列表项 */
    printf("项目\t\t\t\t地址\r\n");
    printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
    printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
    printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
    printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
    printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
    printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
    printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
    printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
    printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
    printf("/************************实验结束***************************/\r\n");
    
    while(1)
    {
        vTaskDelay(10);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

实验结果

LCD ID:9341
/第二步:打印列表和列表项的地址/
项目 地址
TestList 0x200000cc
TestList->pxIndex 0x200000d4
TestList->xListEnd 0x200000d4

ListItem1 0x200000e0
ListItem2 0x200000f4
ListItem3 0x20000108
/结束*/
按下KEY0键继续!

/第三步:列表项1插入列表*/
项目 地址
TestList->xListEnd->pxNext 0x200000e0
ListItem1->pxNext 0x200000d4
TestList->xListEnd->pxPrevious 0x200000e0
ListItem1->pxPrevious 0x200000d4
/结束*/
按下KEY0键继续!

/第四步:列表项2插入列表*/
项目 地址
TestList->xListEnd->pxNext 0x200000e0
ListItem1->pxNext 0x200000f4
ListItem2->pxNext 0x200000d4
TestList->xListEnd->pxPrevious 0x200000f4
ListItem1->pxPrevious 0x200000d4
ListItem2->pxPrevious 0x200000e0
/结束*/
按下KEY0键继续!

/第五步:列表项3插入列表*/
项目 地址
TestList->xListEnd->pxNext 0x200000e0
ListItem1->pxNext 0x200000f4
ListItem2->pxNext 0x20000108
ListItem3->pxNext 0x200000d4
TestList->xListEnd->pxPrevious 0x20000108
ListItem1->pxPrevious 0x200000d4
ListItem2->pxPrevious 0x200000e0
ListItem3->pxPrevious 0x200000f4
/结束*/
按下KEY0键继续!

/第六步:移除列表项2*/
项目 地址
TestList->xListEnd->pxNext 0x200000e0
ListItem1->pxNext 0x20000108
ListItem3->pxNext 0x200000d4
TestList->xListEnd->pxPrevious 0x20000108
ListItem1->pxPrevious 0x200000d4
ListItem3->pxPrevious 0x200000e0
/结束*/
按下KEY0键继续!

/第七步:列表末尾添加列表项2/
项目 地址
TestList->pxIndex 0x200000d4
TestList->xListEnd->pxNext 0x200000e0
ListItem1->pxNext 0x20000108
ListItem2->pxNext 0x200000d4
ListItem3->pxNext 0x200000f4
TestList->xListEnd->pxPrevious 0x200000f4
ListItem1->pxPrevious 0x200000d4
ListItem2->pxPrevious 0x20000108

ListItem3->pxPrevious 0x200000e0
/实验结束***/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/325375
推荐阅读
相关标签
  

闽ICP备14008679号