04. FreeRTOS的列表和列表项
一、FreeRTOS的列表和列表项简介
1.1、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;
在该结构体中,包含了两个宏,分别为 listFIRST_LIST_INTEGRITY_CHECK_VALUE
和 listSECOND_LIST_INTEGRITY_CHECK_VALUE
,这两个宏用于存放确定已知常量,FreeRTOS 通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏,类似这样的宏定义在列表项和迷你列表项中也有出现。该功能一般用于调试,默认是不开启的。
成员变量 uxNumberOfItems
用于 记录列表中列表项的个数(不包含 xListEnd),当往列表中插入列表项时,该值加 1;当从列表中移除列表项时,该值减 1。
成员变量 pxIndex
用于 指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
成员变量 xListEnd
是一个 迷你列表项,列表中迷你列表项的值一般被设置为最大值,用于将列表中的所有列表项按升序排序时,排在最末尾;同时 xListEnd 也用于挂载其他插入到列表中的列表项。
1.2、FreeRTOS的列表项
列表项是列表中用于存放数据的地方,在 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
用于 指向列表项所在列表。
1.3、迷你列表项
迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项,用户是用不到迷你列表项的,在 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
迷你列表项中也同样包含用于检测列表项数据完整性的宏定义。
成员变量 xItemValue
为 列表项的值,这个值多用于按升序对列表中的列表项进行排序。
成员变量 pxNext
和 pxPrevious
分别用于指向列表中列表项的 下一个列表项 和 上一个列表项。
迷你列表项相比于列表项,因为只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销。
二、FreeRTOS列表和列表项相关API函数
2.1、初始化列表
void vListInitialise( List_t * const pxList ); // pxList为待初始化列表
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化,只有初始化后的列表,才能够正常地被使用。列表初始化的过程,其实就是初始化列表中的成员变量。
函数 vListInitialise()
在 list.c 文件中有定义,具体的代码如下所示:
void vListInitialise( List_t * const pxList )
{
traceENTER_vListInitialise( pxList );
// 初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
// ListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后
pxList->xListEnd.xItemValue = portMAX_DELAY;
// 初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
#if ( configUSE_MINI_LIST_ITEM == 0 )
{
pxList->xListEnd.pvOwner = NULL;
pxList->xListEnd.pxContainer = NULL;
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
}
#endif
// 初始化时,列表中的列表项数量为(不包含xListEnd)
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
// 初始化用于检测列表数据完整性的校验值
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
traceRETURN_vListInitialise();
}
函数 vListInitialise()
初始化后的列表结构示意图,如下图所示:
2.2、初始化列表项
void vListInitialiseItem( ListItem_t * const pxItem ); // pxItem为待初始化列表项
此函数用于初始化列表项,如同列表一样,在定义列表项之后,也需要先对其进行初始化,只有初始化有的列表项,才能够被正常地使用。列表项初始化的过程,也是初始化列表项中的成员变量。
函数 vListInitialiseItem()
在 list.c 文件中有定义,具体的代码如下所示:
void vListInitialiseItem( ListItem_t * const pxItem )
{
traceENTER_vListInitialiseItem( pxItem );
// 初始化时,列表项所在列表设为空
pxItem->pxContainer = NULL;
// 初始化用于检测列表项数据完整性的校验值
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
traceRETURN_vListInitialiseItem();
}
该函数比较简单,只需将列表项所在列表设置为空,以保证列表项不再任何一个列表项中即可。函数 vListInitialiseItem()
初始化后的列表项结构示意图,如下图所示:
2.3、列表末尾插入列表项
void vListInsertEnd( List_t * const pxList, // 列表
ListItem_t * const pxNewListItem ); // 待插入列表项
此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向列表项的前面,是一种无序的插入方法。
函数 vListInsertEnd()
在 list.c 文件中有定义,具体的代码如下所示:
void vListInsertEnd(List_t * const pxList,
ListItem_t * const pxNewListItem);
{
// 获取列表 pxIndex 指向的列表项
ListItem_t * const pxIndex = pxList->pxIndex;
traceENTER_vListInsertEnd( pxList, pxNewListItem );
// 检查参数是否正确
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 ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
traceRETURN_vListInsertEnd();
}
从上面的代码可以看出,此函数就是将待插入的列表项插入到列表 pxIndex 指向列表项的前面,要注意的时,pxIndex 不一定指向 xListEnd,而是有可能指向列表中任意一个列表项。函数 vListInsertEnd()
插入列表项后的列表结构示意图,如下图所示:
2.4、列表插入列表项
void vListInsert( List_t * const pxList, // 列表
ListItem_t * const pxNewListItem ); // 待插入列表项
函数 vListInsert()
在 list.c 文件中有定义,具体的代码如下所示:
void vListInsert(List_t * const pxList
ListItem_t * const pxNewListItem)
{
ListItem_t * pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
traceENTER_vListInsert( pxList, pxNewListItem );
// 检查参数是否正确
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 ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
traceRETURN_vListInsert();
}
从上面的代码可以看出,此函数在将待插入列表项插入列表之前,会前遍历列表,找到待插入列表项需要插入的位置。待插入列表项需要插入的位置是依照列表中列表项的值按照升序排序确定的。函数 vListInsert()
插入列表项后的列表结构示意图,如下图所示:
2.5、列表移除列表项
// 返回值:待移除列表项移除后,所在列表剩余列表项的数量
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ); // pxItemToRemove为待移除的列表项
函数 uxListRemove()
在 list.c 文件中有定义,具体的代码如下所示:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = pxItemToRemove->pxContainer;
// 从列表中移除列表项
traceENTER_uxListRemove( pxItemToRemove );
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 ) = ( UBaseType_t ) ( pxList->uxNumberOfItems - 1U );
traceRETURN_uxListRemove( pxList->uxNumberOfItems );
// 返回列表项移除后列表中列表项的数量
return pxList->uxNumberOfItems;
}
函数 uxListRemove()
移除后的列表项,依然于列表有着单向联系,即移除后列表项中用于指向上一个和下一个列表项的指针,依然指向列表中的列表项。函数 uxListRemove()
移除列表项后的列表结构示意图,如下图所示:
2.6、FreeRTOS操作列表和列表项的宏
在 list.h 文件中定义了大量的宏,用来操作列表以及列表项,如下所示:
// 设置列表项的拥有者
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
// 获取列表项的拥有者
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
// 设置列表项的值
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
// 获取列表项的值
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
// 获取列表头部列表项的值
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
// 获取列表的头部列表项
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
// 获取列表项的下一个列表项
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
// 获取列表的尾部列表项
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
// 判断列表是否为空
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
// 获取列表包含的列表项数量
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
// 获取下一个列表项的拥有者
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
do { \
List_t * const pxConstList = ( pxList ); \
\
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->xListEnd.pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
} while( 0 )
// 将列表项从列表中移除
#define listREMOVE_ITEM( pxItemToRemove ) \
do { \
List_t * const pxList = ( pxItemToRemove )->pxContainer; \
\
( pxItemToRemove )->pxNext->pxPrevious = ( pxItemToRemove )->pxPrevious; \
( pxItemToRemove )->pxPrevious->pxNext = ( pxItemToRemove )->pxNext; \
\
if( pxList->pxIndex == ( pxItemToRemove ) ) \
{ \
pxList->pxIndex = ( pxItemToRemove )->pxPrevious; \
} \
\
( pxItemToRemove )->pxContainer = NULL; \
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) - 1U ); \
} while( 0 )
// 列表末尾插入列表项
#define listINSERT_END( pxList, pxNewListItem ) \
do { \
ListItem_t * const pxIndex = ( pxList )->pxIndex; \
\
listTEST_LIST_INTEGRITY( ( pxList ) ); \
listTEST_LIST_ITEM_INTEGRITY( ( pxNewListItem ) ); \
\
( pxNewListItem )->pxNext = pxIndex; \
( pxNewListItem )->pxPrevious = pxIndex->pxPrevious; \
\
pxIndex->pxPrevious->pxNext = ( pxNewListItem ); \
pxIndex->pxPrevious = ( pxNewListItem ); \
\
( pxNewListItem )->pxContainer = ( pxList ); \
\
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) + 1U ); \
} while( 0 )
// 获取列表头部列表项的拥有者
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner )
// 判断列表项是否在列表中
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
// 获取列表项所在列表
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
// 判断列表是否完成初始化
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
三、实验例程
main() 函数:
int main(void)
{
HAL_Init();
System_Clock_Init(8, 336, 2, 7);
Delay_Init(168);
UART_Init(&g_usart1_handle, USART1, 115200);
freertos_demo();
return 0;
}
FreeRTOS 的入口函数:
/**
* @brief FreeRTOS的入口函数
*
*/
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t ) start_task, // 任务函数
(char * ) "start_task", // 任务名
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE, // 任务栈大小
(void * ) NULL, // 入口参数
(UBaseType_t ) START_TASK_PRIORITY, // 任务优先级
(TaskHandle_t * ) start_task_handle); // 任务句柄
vTaskStartScheduler(); // 开启任务调度器
}
start_task 任务配置:
/**
* START_TASK 任务配置
* 包括: 任务优先级 任务栈大小 任务句柄 任务函数
*/
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handle;
void start_task(void *pvParameters );
/**
* @brief 开始任务的任务函数
*
* @param pvParameters 任务函数的入口参数
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); // 进入临界区,关闭中断
xTaskCreate((TaskFunction_t ) task1, // 任务函数
(char * ) "task1", // 任务名
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE, // 任务栈大小
(void * ) NULL, // 入口参数
(UBaseType_t ) TASK1_PRIORITY, // 任务优先级
(TaskHandle_t * ) &task1_handle); // 任务句柄
vTaskDelete(NULL); // 删除任务自身
taskEXIT_CRITICAL(); // 退出临界区,重新开启中断
}
task1 任务配置:
List_t list; // 列表
ListItem_t listItem1; // 列表项1
ListItem_t listItem2; // 列表项2
ListItem_t listItem3; // 列表项3
/**
* TASK1 任务配置
* 包括: 任务优先级 任务栈大小 任务句柄 任务函数
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);
/**
* @brief 任务1的任务函数
*
* @param pvParameters 任务函数的入口参数
*/
void task1(void *pvParameters)
{
// 第一步:初始化列表和列表项
vListInitialise(&list); // 初始化列表
vListInitialiseItem(&listItem1); // 初始化列表项
vListInitialiseItem(&listItem2); // 初始化列表项
vListInitialiseItem(&listItem3); // 初始化列表项
listItem1.xItemValue = 40; // 列表项的值
listItem2.xItemValue = 60; // 列表项的值
listItem3.xItemValue = 30; // 列表项的值
printf("打印列表和列表项地址:\r\n");
printf("list: 0x%p\r\n", &list); // 列表的地址
printf("list->pxIndex: 0x%p\r\n", list.pxIndex); // 列表索引指向的列表项地址
printf("list->xListEnd: 0x%p\r\n", (&list.xListEnd)); // 列表末尾的列表项的地址
printf("ListItem1: 0x%p\r\n", &listItem1); // 列表项1地址
printf("ListItem2: 0x%p\r\n", &listItem2); // 列表项2地址
printf("ListItem3: 0x%p\r\n\r\n", &listItem3); // 列表项3地址
vListInsert(&list, &listItem1); // 升序插入列表项1
printf("升序插入列表项1:\r\n");
printf("list->xListEnd->pxNext: 0x%p\r\n", list.xListEnd.pxNext); // 列表的末尾列表项下一个列表项
printf("listItem1->pxNext: 0x%p\r\n", listItem1.pxNext); // 列表项1的下一个列表项
printf("list->xListEnd->pxPrevious: 0x%p\r\n", list.xListEnd.pxPrevious); // 列表的末尾列表项上一个列表项
printf("listItem1->pxPrevious : 0x%p\r\n\r\n", listItem1.pxPrevious); // 列表项1的上一个列表项
vListInsert(&list, &listItem2); // 升序插入列表项2
printf("升序插入列表项2:\r\n");
printf("list->xListEnd->pxNext: 0x%p\r\n", list.xListEnd.pxNext); // 列表的末尾列表项下一个列表项
printf("listItem1->pxNext: 0x%p\r\n", listItem1.pxNext); // 列表项1的下一个列表项
printf("listItem2->pxNext: 0x%p\r\n", listItem2.pxNext); // 列表项2的下一个列表项
printf("list->xListEnd->pxPrevious: 0x%p\r\n", list.xListEnd.pxPrevious); // 列表的末尾列表项上一个列表项
printf("listItem1->pxPrevious : 0x%p\r\n", listItem1.pxPrevious); // 列表项1的上一个列表项
printf("listItem2->pxPrevious : 0x%p\r\n\r\n", listItem2.pxPrevious); // 列表项2的上一个列表项
vListInsert(&list, &listItem3); // 升序插入列表项3
printf("升序插入列表项3:\r\n");
printf("list->xListEnd->pxNext: 0x%p\r\n", list.xListEnd.pxNext); // 列表的末尾列表项下一个列表项
printf("listItem1->pxNext: 0x%p\r\n", listItem1.pxNext); // 列表项1的下一个列表项
printf("listItem2->pxNext: 0x%p\r\n", listItem2.pxNext); // 列表项2的下一个列表项
printf("listItem3->pxNext: 0x%p\r\n", listItem3.pxNext); // 列表项3的下一个列表项
printf("list->xListEnd->pxPrevious: 0x%p\r\n", list.xListEnd.pxPrevious); // 列表的末尾列表项上一个列表项
printf("listItem1->pxPrevious : 0x%p\r\n", listItem1.pxPrevious); // 列表项1的上一个列表项
printf("listItem2->pxPrevious : 0x%p\r\n", listItem2.pxPrevious); // 列表项2的上一个列表项
printf("listItem3->pxPrevious : 0x%p\r\n\r\n", listItem3.pxPrevious); // 列表项3的上一个列表项
uxListRemove(&listItem2); // 移除列表项2
printf("移除列表项2:\r\n");
printf("list->xListEnd->pxNext: 0x%p\r\n", list.xListEnd.pxNext); // 列表的末尾列表项下一个列表项
printf("listItem1->pxNext: 0x%p\r\n", listItem1.pxNext); // 列表项1的下一个列表项
printf("listItem3->pxNext: 0x%p\r\n", listItem3.pxNext); // 列表项3的下一个列表项
printf("list->xListEnd->pxPrevious: 0x%p\r\n", list.xListEnd.pxPrevious); // 列表的末尾列表项上一个列表项
printf("listItem1->pxPrevious : 0x%p\r\n", listItem1.pxPrevious); // 列表项1的上一个列表项
printf("listItem3->pxPrevious : 0x%p\r\n\r\n", listItem3.pxPrevious); // 列表项3的上一个列表项
list.pxIndex = &listItem1; // 修改pxIndex指向的列表项
vListInsertEnd(&list, &listItem2); // 列表末尾插入列表项2,插入到列表 pxIndex 指针指向列表项的前面
printf("列表末尾插入列表项2:\r\n");
printf("升序插入列表项3:\r\n");
printf("list->xListEnd->pxNext: 0x%p\r\n", list.xListEnd.pxNext); // 列表的末尾列表项下一个列表项
printf("listItem1->pxNext: 0x%p\r\n", listItem1.pxNext); // 列表项1的下一个列表项
printf("listItem2->pxNext: 0x%p\r\n", listItem2.pxNext); // 列表项2的下一个列表项
printf("listItem3->pxNext: 0x%p\r\n", listItem3.pxNext); // 列表项3的下一个列表项
printf("list->xListEnd->pxPrevious: 0x%p\r\n", list.xListEnd.pxPrevious); // 列表的末尾列表项上一个列表项
printf("listItem1->pxPrevious : 0x%p\r\n", listItem1.pxPrevious); // 列表项1的上一个列表项
printf("listItem2->pxPrevious : 0x%p\r\n", listItem2.pxPrevious); // 列表项2的上一个列表项
printf("listItem3->pxPrevious : 0x%p\r\n\r\n", listItem3.pxPrevious); // 列表项3的上一个列表项
while (1)
{
vTaskDelay(1000);
}
}