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

news2024/9/28 21:21:32

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);										//初始化列表项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的任务情况。
  • 列表项中有成员指向对应的任务控制块,方便溯源。
  • 列表和列表项的使用存在一定难度,需要认真学习,仔细琢磨!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2088918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能时代,AI数据服务行业面临的机遇与挑战

随着大数据、云、物联网、人工智能等信息科技技术的发展以及互联网设备的普及&#xff0c;各行业产生了前所未有海量的AI数据服务需求&#xff0c;彻底宣告了数据时代的来临。 首先&#xff0c;物联网的发展更使线下业务产生的大量数据被采集起来&#xff0c;世界各地的AI数据…

需求管理软件对比:2024年10款推荐工具

本文将介绍10款需求管理软件&#xff1a;1.PingCode&#xff1b;2. Worktile&#xff1b;3. 开源中国社区 Gitee&#xff1b;4. 金动力&#xff1b;5. 氚云&#xff1b;6. 协作云&#xff1b;7. 麦塔在线&#xff1b;8. Height&#xff1b;9. Plaky&#xff1b;10. ActiveColla…

万龙觉醒辅助:屠龙攻略大全!VMOS云手机带你组团抓龙!

在《万龙觉醒》中&#xff0c;使用VMOS云手机能够为玩家提供专属定制版的云手机&#xff0c;不仅内置游戏安装包&#xff0c;还无需重新下载安装游戏。这一切都让玩家的游戏体验更加便捷和高效。VMOS云手机能够辅助游戏的自动化运行&#xff0c;支持24小时云端运行&#xff0c;…

论文《Adversarial Examples on Graph Data: Deep Insights into Attack and Defense》笔记

【IG-Attack 2019 IJCAI】本文提出了一种基于integrated gradients的对抗攻击和防御算法。对于攻击&#xff0c;本文证明了通过引入integrated gradients可以很容易解决离散问题&#xff0c;integrated gradients可以准确反映扰动某些特征或边的影响&#xff0c;同时仍然受益于…

【并发编程】从AQS机制到同步工具类

AQS机制 Java 中常用的锁主要有两类&#xff0c;一种是 Synchronized 修饰的锁&#xff0c;被称为 Java 内置锁或监视器锁。另一种就是在 JUC 包中的各类同步器&#xff0c;包括 ReentrantLock&#xff08;可重入锁&#xff09;、Semaphore&#xff08;信号量&#xff09;、Co…

走出新手村,软件测试银行项目怎么测试 + 面试题(答案)

前言 业务&#xff1a; 银行类app具体模块业务讲解&#xff1a; 掌上生活&#xff08;消费/理财&#xff09; 消费&#xff1a; 与电商平台业务类似 饭票&#xff1a;GPS定位城市服务、饭票购买消费、餐厅查询、代金券、周三五折、banner图、我的饭票、我的抵扣券 影票 …

RKNPU2从入门到实践 ---- 【9】使用RKNPU2的C API接口将RKNN模型部署在RK3588开发板上

注&#xff1a;作者使用的平台为Ubuntu20.04虚拟系统&#xff0c;开发板为RK3588&#xff0c;开发板上面的系统为Ubuntu22.04。 前言 本博文我们要学习使用 RKNPU2 提供的 C API 接口将RKNN模型部署在RK3588开发板上&#xff0c;完成测试图片在开发板上的推理工作。C API接口…

【书生3.2】Lagent 自定义你的 Agent 智能体

Lagent 自定义你的 Agent 智能体 1 环境准备2 启动模型3 部署lagent4 可视化页面及配置5 自定义工具的智能体 1 环境准备 直接复用之前的xtuner或者llamaindex环境 # 安装其他依赖包 conda activate llamainde pip install termcolor2.4.0 pip install lmdeploy0.5.2pip inst…

Gazebo Harmonic gz-harmonic 和 ROS2 Jazzy 思考题 建图和导航 SLAM Navigation

仿真 效果还挺好的。 SLAM建图 SLAM 仿真测试录屏 配置环境 Gazebo Harmonic ROS Jazzy 导航 …… 提示 这篇文档详细介绍了如何在ROS 2环境中使用SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;即同时定位与地图构建&#xff09;和Nav2&#xff08;N…

XXE-labs靶场通关攻略

环境地址自行查找 1.寻找靶机地址 使用工具goby进行扫描 因为我的靶场是搭在ubuntu上 直接查找系统是Ubuntu的就可以找到 靶机IP 172.16.1.183 2.访问靶场 3.使用目录扫描工具进行扫描 使用kali自带的dirsearch进行扫描 可以看到一个robots.txt文件 4.访问robots.txt文件 …

白夜剧场再出爆款《边水往事》,这次能“带飞”优酷吗?

这个暑期档&#xff0c;优酷靠着白夜剧场悬疑新作《边水往事》再下一城。 谁能想到刚开播时无人问津的《边水往事》竟然“爆”了&#xff0c;8月16日首播当天市占率仅仅1.73%&#xff0c;播放量不到700万&#xff0c;而在上线第十天&#xff0c;《边水往事》终于逆袭夺冠&…

#QT 笔记一

重点&#xff1a;面试考试大概率涉及&#xff0c;需要不借助任何资料掌握。掌握&#xff1a;面试考试可能涉及&#xff0c;需要不借助任何资料掌握。熟悉&#xff1a;面试考试可能涉及&#xff0c;可以稍微参考资料掌握。了解&#xff1a;面试考试小概率涉及&#xff0c;面试拔…

Navicat连接SqlServer

一、前提条件 本地已经安装好了SqlServer和navicat 二、操作 打开sqlserver配置管理器 tcp默认关闭&#xff0c;我们右击打开 需要重启服务才能生效 三、效果

【ACM出版,EIScopus快检索-高录用】2024年数字经济与计算机科学国际学术会议(DECS2024,9月20-22)

欢迎参加2024年数字经济与计算机科学国际学术会议&#xff08;DECS2024&#xff09;&#xff0c;本次大会得到了马来西亚理工大学、北京科技大学经济管理学院、南京信息工程大学、马来西亚敦胡先翁大学的大力支持&#xff01; 旨在汇聚全球在数字经济与计算机科学领域内的研究者…

《黑神话:悟空》爆火,对程序员的 5 点启示(1)

# 前言 2024年8月&#xff0c;被誉为 “首部国产 3A 大作” 的《黑神话&#xff1a;悟空》一段13分钟的实机演示视频&#xff0c;简直像是给全球玩家投下了一颗冲击弹&#xff0c;瞬间炸开了一片热闹场面&#xff0c;点燃了海内外游戏和西游文化爱好者的热情&#xff0c;迅速地…

How to work with OpenAI maximum context length is 2049 tokens?

题意&#xff1a;"如何处理OpenAI的最大上下文长度为2049个tokens的问题&#xff1f;" 问题背景&#xff1a; Id like to send the text from various PDFs to OpenAIs API. Specifically the Summarize for a 2nd grader or the TL;DR summarization APIs. "…

重谈地址空间

虚拟地址是如何转化的物理地址的&#xff1f; 我们以32位计算机为例子 32 为计算机的虚拟地址就是32位。 32 位的虚拟地址 分为三个部分 为 10 10 12&#xff1b; 这是由页表内部的结构决定的。 页表分为两级 虚拟地址的前十位为一级页表对应的位置&#xff0c; 次10位表示…

鸿蒙(API 12 Beta3版)【识别本地图片】

基本概念 图片识码能力支持对图库中的码图进行扫描识别&#xff0c;并获取信息。 场景介绍 图片识码能力支持对图库中的条形码、二维码、MULTIFUNCTIONAL CODE进行识别&#xff0c;并获得码类型、码值、码位置信息。该能力可用于一图单码和一图多码的识别&#xff0c;比如条…

【HarmonyOS】模仿个人中心头像图片,调用系统相机拍照,从系统相册选择图片和圆形裁剪显示 (二)

【HarmonyOS】模仿个人中心头像图片&#xff0c;调用系统相机拍照&#xff0c;从系统相册选择图片和圆形裁剪显示 &#xff08;二&#xff09; Demo效果展示&#xff1a; 方案思路&#xff1a; 1.修改调用相机的方式&#xff0c;使用cameraKit进行相机的调用&#xff0c;拍照…

怎么扫描试卷去除笔迹?建议试试这样做

怎么扫描试卷去除笔迹&#xff1f;在现代教育和办公环境中&#xff0c;电子版试卷的管理和使用变得越来越普遍。然而&#xff0c;手写答案和批注常常使得电子版试卷难以恢复到原始的空白状态。为了满足这一需求&#xff0c;市场上涌现出许多能够扫描试卷并去除笔迹的技术和工具…