STM32F1+HAL库+FreeTOTS学习7——列表和列表项

2024-08-31 07:52

本文主要是介绍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;
  1. 在该结构体中,包含了两个宏作为校验值,这两个宏用于存放确定已知常量,FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏,类似这样的宏定义在列表项和迷你列表项中也有出现。该功能一般用于调试,默认是不开启的。
  2. uxNumberOfItems 用于记录列表中列表项的个数(不包含 xListEnd),当往列表中插入列表项时,该值加 1;当从列表中移除列表项时,该值减 1。
  3. pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
  4. 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 */
  1. 列表项中也包含了两个用于检测列表项数据完整性的宏定义。
  2. xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
  3. pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项。
  4. pxOwner 用于指向包含列表项的对象(通常是任务控制块),因此,列表项和包含列表项的对象之间存在双向链接
  5. 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 */
  1. 迷你列表项相比于列表项,因为只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。
  2. 迷你列表项的值为最大值,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. 创建一个测试列表,三个测试列表项1、2、3,列表项的值分别为10、30、20。
  2. 初始化列表和列表项,依次升序插入列表项1、2、3。
  3. 删除列表项3。
  4. 在尾部插入列表项3。
  5. 每进行一步操作,打印列表及列表项相关信息。

在上述实验中,列表和列表项的结构变化如下:

  1. 升序插入列表项1
    在这里插入图片描述

  2. 升序插入列表项2
    在这里插入图片描述

  3. 升序插入列表项3
    在这里插入图片描述

  4. 删除列表项3
    在这里插入图片描述

  5. 尾部插入列表项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);										//初始化列表项1vListInitialiseItem(&ListItem2);										//初始化列表项2vListInitialiseItem(&ListItem3);										//初始化列表项3ListItem1.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);//第二步,插入列表项1vListInsert((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));//第三步,插入列表项2vListInsert((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));//第四步,插入列表项3vListInsert((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));//第五步,移除列表项3uxListRemove((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));//第六步尾部插入列表项3vListInsertEnd((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的任务情况。
  • 列表项中有成员指向对应的任务控制块,方便溯源。
  • 列表和列表项的使用存在一定难度,需要认真学习,仔细琢磨!!!

这篇关于STM32F1+HAL库+FreeTOTS学习7——列表和列表项的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123313

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件