STM32F1+HAL库+FreeTOTS学习7——列表和列表项
STM32F1+HAL库+FreeTOTS学习7——列表和列表项
- 列表和列表项简介
- 列表
- 列表项
- 迷你列表项
- 列表项API函数介绍
- 1. 初始化列表
- 2. 初始化列表项
- 3. 列表末尾插入列表项
- 4. 列表插入列表项
- 5. 移除列表项
- 6. 补充:FreeRTOS中操作列表和列表项的宏
- 列表项的插入和删除实验
- 总结
上一期我们学习了FreeRTOS的内核中断管理以及中断屏蔽控制函数,下面我们来学习临界端代码保护函数的使用
列表和列表项简介
列表是 FreeRTOS 中的一个数据结构,在FreeRTOS的源码中有大量的使用,本质上就是数据结构里面的双向循环链表,列表被用来跟踪 FreeRTOS中的任务。而列表项则是存放在列表中的元素,可以认为是链表里面的节点。下面是列表和列表项的关系:
列表
列表是 FreeRTOS 中最基本的一种数据结构,不同于数组,其在物理存储单元上是非连续、非顺序的。列表在 FreeRTOS 中的应用十分广泛,要注意的是,FreeRTOS 中的列表是一个双向链表,在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;
- 在该结构体中,包含了两个宏作为校验值,这两个宏用于存放确定已知常量,FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏,类似这样的宏定义在列表项和迷你列表项中也有出现。该功能一般用于调试,默认是不开启的。
- uxNumberOfItems 用于记录列表中列表项的个数(不包含 xListEnd),当往列表中插入列表项时,该值加 1;当从列表中移除列表项时,该值减 1。
- pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
- xListEnd 是一个迷你列表项,列表中迷你列表项的值一般被设置为最大值,用于将列表中的所有列表项按升序排序时,排在最末尾;同时 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; /* 重定义成 ListItem_t */
- 列表项中也包含了两个用于检测列表项数据完整性的宏定义。
- xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
- pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项。
- pxOwner 用于指向包含列表项的对象(通常是任务控制块),因此,列表项和包含列表项的对象之间存在双向链接。
- pxContainer 用于指向列表项所在列表。
列表项结构示意图如下:
迷你列表项
迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项,用户是用不到迷你列表项的,在 list.h 文件中,有迷你列表项的相关定义:
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; /* 重定义成 MiniListItem_t */
- 迷你列表项相比于列表项,因为只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。
- 迷你列表项的值为最大值,0xffffffff,一般挂载在列表的最末尾。
下面是迷你列表项的结构示意图:
列表项API函数介绍
FreeRTOS 中列表和列表项相关的 API 函数如下表所示:
函数 | 描述 |
---|---|
vListInitialise() | 初始化列表 |
vListInitialiseItem() | 初始化列表项 |
vListInsertEnd() | 列表末尾插入列表项 |
vListInsert() | 列表插入列表项 |
uxListRemove() | 列表移除列表项 |
1. 初始化列表
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化,只有初始化后的列表,才能够正常地被使用。列表初始化的过程,其实就是初始化列表中的成员变量。
/*形参pxList为待初始化的列表*/
void vListInitialise(List_t * const pxList);
/具体代码如下:/
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 );
}
初始化之后的列表结构如图:
2. 初始化列表项
此函数用于初始化列表项,如同列表一样,在定义列表项之后,也需要先对其进行初始化,只有初始化有的列表项,才能够被正常地使用。列表项初始化的过程,也是初始化列表项中的成员变量。函数原型如下所示:
/*形参pxItem是待初始化的列表项,将列表项所在列表置为空,该函数定义在list.c中*/
void vListInitialiseItem(ListItem_t * const pxItem);
/*函数原型如下*/
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 );
}
初始化完之后的列表项如下图:
3. 列表末尾插入列表项
用于将待插入列表的列表项插入到列表 pxIndex 指针指向列表项的前面,是一种无序的插入方法。函数原型如下所示:
/*
pxList 为被插入的列表
pxNewListItem 为需要插入的列表项
*/
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem);
/*函数原型如下*/
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem)
{
/* 获取列表 pxIndex 指向的列表项 */
ListItem_t * const pxIndex = pxList->pxIndex;
/* 检查参数是否正确 */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 更新待插入列表项的指针成员变量 */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* 测试使用,不用理会 */
mtCOVERAGE_TEST_DELAY();
/* 更新列表中原本列表项的指针成员变量 */
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* 更新待插入列表项的所在列表成员变量 */
pxNewListItem->pxContainer = pxList;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )++;
}
[注意] : 列表初始完成后,如果没有对 pxIndex 进行修改,则pxIndex 默认指向迷你列表项。初始化完成
4. 列表插入列表项
用于将待插入列表的列表项按照列表项值升序排序的顺序,有序地插入到列表中。函数原型如下所示:
/*
pxList 为被插入的列表
pxNewListItem 为需要插入的列表项
*/
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem);
/*函数原型如下*/
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 )
{
/* 插入的位置为列表 xListEnd 前面 */
pxIterator = pxList->xListEnd.pxPrevious;
}
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 )++;
}
从上面的代码可以看出,此函数在将待插入列表项插入列表之前,会前遍历列表,找到待插入列表项需要插入的位置。待插入列表项需要插入的位置是依照列表中列表项的值按照升序确定的。函数 vListInsert()插入列表项后的列表结构示意图,如下图所示:
5. 移除列表项
用于将列表项从列表项所在列表中移除,函数原型如下所示:
/*pxItemToRemove 是需要被移除的列表项*/
/*返回值uxListRemove为移除该列表项后剩余的列表项数量*/
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove);
/下面是该函数原型/
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove)
{
List_t * const pxList = pxItemToRemove->pxContainer;
/* 从列表中移除列表项 */
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* 测试使用,不用理会 */
mtCOVERAGE_TEST_DELAY();
/* 如果 pxIndex 正指向待移除的列表项 */
if( pxList->pxIndex == pxItemToRemove )
{
/* pxIndex 指向上一个列表项 */
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 将待移除列表项的所在列表指针清空 */
pxItemToRemove->pxContainer = NULL;
/* 更新列表中列表项的数量 */
( pxList->uxNumberOfItems )--;
/* 返回列表项移除后列表中列表项的数量 */
return pxList->uxNumberOfItems;
}
要注意的是函数 uxListRemove()移除后的列表项,依然于列表有着单向联系,即移除后列表项中用于指向上一个和下一个列表项的指针,依然指向列表中的列表项。函数 uxListRemove()移除列表项后的列表结构示意图,如下图所示:
6. 补充:FreeRTOS中操作列表和列表项的宏
宏定义 | 描述 |
---|---|
listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) | 设置列表项的拥有者 |
listGET_LIST_ITEM_OWNER( pxListItem ) | 获取列表项的拥有者 |
listSET_LIST_ITEM_VALUE( pxListItem, xValue ) | 设置列表项的值 |
listGET_LIST_ITEM_VALUE( pxListItem ) | 获取列表项的值 |
listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) | 获取列表头部列表项的值 |
listGET_HEAD_ENTRY( pxList ) | |
listGET_NEXT( pxListItem ) | 获取列表项的下一个列表项 |
listGET_END_MARKER( pxList ) | 获取列表的尾部列表项 |
listLIST_IS_EMPTY( pxList ) | 判断列表是否为空 |
listCURRENT_LIST_LENGTH( pxList ) | 获取列表包含的列表项数量 |
listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) | 获取下一个列表项的拥有者 |
listREMOVE_ITEM( pxItemToRemove ) | 将列表项从列表中移除 |
listINSERT_END( pxList, pxNewListItem ) | 列表末尾插入列表项 |
listGET_OWNER_OF_HEAD_ENTRY( pxList ) | 获取列表头部列表项的拥有者 |
listIS_CONTAINED_WITHIN( pxList, pxListItem ) | 判断列表项是否在列表中 |
listLIST_ITEM_CONTAINER( pxListItem ) | 获取列表项所在列表 |
listLIST_IS_INITIALISED( pxList ) | 判断列表是否完成初始化 |
这些宏操作列表及列表项的实现都在Lsit.h里面,可以自行观看。
列表项的插入和删除实验
为了能够熟练的使用列表项各个API函数,我们设计如下实验:
- 创建一个测试列表,三个测试列表项1、2、3,列表项的值分别为10、30、20。
- 初始化列表和列表项,依次升序插入列表项1、2、3。
- 删除列表项3。
- 在尾部插入列表项3。
- 每进行一步操作,打印列表及列表项相关信息。
在上述实验中,列表和列表项的结构变化如下:
-
升序插入列表项1
-
升序插入列表项2
-
升序插入列表项3
-
删除列表项3
-
尾部插入列表项3
下面我们展示以下实验代码:
List_t TestList; /*定义测试列表*/
ListItem_t ListItem1; /*定义测试列表项1*/
ListItem_t ListItem2; /*定义测试列表项2*/
ListItem_t ListItem3; /*定义测试列表项3*/
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
taskENTER_CRITICAL(); /* 进入临界区,关闭中断,此时停止任务调度*/
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
taskEXIT_CRITICAL(); /* 退出临界区,重新开启中断,开启任务调度 */
vTaskStartScheduler(); //开启任务调度
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
while(1)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
/* LED0闪烁 */
vTaskDelay(1000); /* 延时1000ticks */
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
//初始化列表和列表项
vListInitialise(&TestList); //初始化列表
vListInitialiseItem(&ListItem1); //初始化列表项1
vListInitialiseItem(&ListItem2); //初始化列表项2
vListInitialiseItem(&ListItem3); //初始化列表项3
ListItem1.xItemValue = 10; //设置列表项的值
ListItem2.xItemValue = 30;
ListItem3.xItemValue = 20;
//第一步,打印列表及列表项的地址
printf("/**************第一步:打印列表和列表项的地址**************/\r\n");
printf("\t项目\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);
//第二步,插入列表项1
vListInsert((List_t * const)&TestList,(ListItem_t * const)&ListItem1);
printf("/*****************第二步:列表项1插入列表******************/\r\n");
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));
//第三步,插入列表项2
vListInsert((List_t * const)&TestList,(ListItem_t * const)&ListItem2);
printf("/*****************第四步:列表项2插入列表******************/\r\n");
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));
//第四步,插入列表项3
vListInsert((List_t * const)&TestList,(ListItem_t * const)&ListItem3);
printf("/*****************第四步:列表项3插入列表******************/\r\n");
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));
//第五步,移除列表项3
uxListRemove((ListItem_t * const)&ListItem3);
printf("/*******************第五步:移除列表项2********************/\r\n");
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", (ListItem2.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", (ListItem2.pxPrevious));
//第六步尾部插入列表项3
vListInsertEnd((List_t * const)&TestList,(ListItem_t * const)&ListItem3);
printf("/****************第六步:列表末尾添加列表项2****************/\r\n");
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));
while(1)
{
}
}
运行结果如下:
/**************第一步:打印列表和列表项的地址**************/
项目 地址
TestList 0x2000009c //列表地址
TestList->pxIndex 0x200000a4 //遍历列表的指针,初始化完成后指向迷你列表项
TestList->xListEnd 0x200000a4 //末尾列表项,地址和迷你列表项相同
ListItem1 0x200000b0 //列表项1地址
ListItem2 0x200000c4 //列表项2地址
ListItem3 0x200000d8 //列表项3地址
/*****************第二步:列表项1插入列表******************/
项目 地址
TestList->xListEnd->pxNext 0x200000b0 //末尾列表项的下一个:列表项1
ListItem1->pxNext 0x200000a4 //列表项1的下一个:迷你列表项(末尾列表项)
TestList->xListEnd->pxPrevious 0x200000b0 //末尾列表项的前一个:列表项1
ListItem1->pxPrevious 0x200000a4 //列表项1的前一个:迷你列表项(末尾列表项)
/*****************第四步:列表项2插入列表******************/
项目 地址
TestList->xListEnd->pxNext 0x200000b0 //末尾列表项的下一个:列表项1
ListItem1->pxNext 0x200000c4 //列表项1的下一个:列表项2
ListItem2->pxNext 0x200000a4 //列表项2的下一个:迷你列表项(末尾列表项)
TestList->xListEnd->pxPrevious 0x200000c4 //末尾列表项的前一个:列表项2
ListItem1->pxPrevious 0x200000a4 //列表项1的前一个:迷你列表项(末尾列表项)
ListItem2->pxPrevious 0x200000b0 //列表项2的前一个:列表项1
/*****************第四步:列表项3插入列表******************/
|t项目 地址
TestList->xListEnd->pxNext 0x200000b0 //末尾列表项的下一个:列表项1
ListItem1->pxNext 0x200000d8 //列表项1的下一个:列表项3
ListItem2->pxNext 0x200000a4 //列表项2的下一个:迷你列表项(末尾列表项)
ListItem3->pxNext 0x200000c4 //列表项3的下一个:列表项2
TestList->xListEnd->pxPrevious 0x200000c4 //末尾列表项的前一个:列表项2
ListItem1->pxPrevious 0x200000a4 //列表项1的前一个:迷你列表项(末尾列表项)
ListItem2->pxPrevious 0x200000d8 //列表项2的前一个:列表项3
ListItem3->pxPrevious 0x200000b0 //列表项3的前一个:列表项1
/*******************第五步:移除列表项3********************/
项目 地址
TestList->xListEnd->pxNext 0x200000b0 //末尾列表项的下一个:列表项1
ListItem1->pxNext 0x200000c4 //列表项1的下一个:列表项2
ListItem2->pxNext 0x200000a4 //列表项2的下一个:迷你列表项(末尾列表项)
TestList->xListEnd->pxPrevious 0x200000c4 //末尾列表项的前一个:列表项2
ListItem1->pxPrevious 0x200000a4 //列表项1的前一个:迷你列表项(末尾列表项)
ListItem2->pxPrevious 0x200000b0 //列表项2的前一个:列表项1
/****************第六步:列表末尾添加列表项3****************/
项目 地址
TestList->pxIndex 0x200000a4 //遍历列表的指针,初始化完成后指向迷你列表项
TestList->xListEnd->pxNext 0x200000b0 //末尾列表项的下一个:列表项1
ListItem1->pxNext 0x200000c4 //列表项1的下一个:列表项2
ListItem2->pxNext 0x200000d8 //列表项2的下一个:列表项3
ListItem3->pxNext 0x200000a4 //列表项3的下一个:迷你列表项(末尾列表项)
TestList->xListEnd->pxPrevious 0x200000d8 //末尾列表项的前一个:列表项3
ListItem1->pxPrevious 0x200000a4 //列表项1的前一个:迷你列表项(末尾列表项)
ListItem2->pxPrevious 0x200000b0 //列表项2的前一个:列表项1
ListItem3->pxPrevious 0x200000c4 //列表项3的前一个:列表项2
从上诉结果可以得出,实验结果与理论完全相符,成功掌握列表和列表项的API函数使用!本片内容介绍
总结
- 在FreeRTOS中存在各种任务,任务需要在各个状态列表中流转,过程中必然会涉及到列表和列表项的使用,熟练的掌握列表相关的API函数,能够更加深入的了解任务的流转情况,追踪FreeRTOS的任务情况。
- 列表项中有成员指向对应的任务控制块,方便溯源。
- 列表和列表项的使用存在一定难度,需要认真学习,仔细琢磨!!!