FreeRTOS 列表
列表的数据结构:
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
/* Macros used for basic data corruption checks. */
#ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
#define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0
#endif
// 如果宏定义为1,则这两个链表检查项定义成变量
#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0)
/* Define the macros to do nothing. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE // 链表节点检查项
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE // 链表检查项
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
#else
/* Define macros that add new members into the list structures. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
// 链表检查项定义成变量
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
#endif
列表的元素分析:
列表的首尾两个元素,都是用来检查列表完整性的,需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1,开启以后会向这两个地方分别添加一个变量 xListIntegrityValue1 和 xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能;
uxNumberOfItems 用来记录列表中列表项的数量;
pxIndex 用来记录当前列表项索引号,用于遍历列表;
列表中最后一个列表项,用来表示列表结束,此变量类型为 MiniListItem_t,这是一个迷你列表项;
列表的结构图:
列表项:
列表项就是存放在列表中的项目,FreeRTOS 提供了两种列表项:列表项和迷你列表项;
/*
* Definition of the only type of object that a list can contain.
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM *configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM *configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void *pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
void *configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
首尾两项与列表类似,为完整性检查;
xItemValue 为列表项值;
pxNext 指向下一个列表项,pxPrevious 指向前一个列表项,和 pxNext 配合起来实现类似双向链表的功能;
pvOwner 记录此链表项归谁拥有,通常是任务控制块;
pvContainer 用来记录此列表项归哪个列表;
列表项的结构示意图:
任务控制块的数据结构:
迷你列表项:
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
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 列表的升序插入示意图:
插入单个列表项
插入多个列表项
列表的初始化 排序插入等操作,都不需要操作遍历指针 pxList->pxIndex 这个指针。
链表的尾部插入:
由于是环形链表,所以根本就没有头部和尾部之说。那么,如何确定链表开始的地方?这就要靠遍历指针 pxList->pxIndex
pxIndex这个指针指向的列表项,就是链表的开始,那么很明显,pxIndex->Previous指向的列表项就是链表的结尾;
我们所说的链表尾部插入,就是要插入到pxIndex->Previous处。
重点分析:
void vListInsertEnd(List_t *const pxList, ListItem_t *const pxNewListItem)
{
ListItem_t *const pxIndex = pxList->pxIndex;
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
// 在pxList中插入一个新的列表项,而不是对列表进行排序
// 使新的列表项成为调用listGET_OWNER_OF_NEXT_ENTRY()删除的最后一个项
// 这里所谓的末尾要根据列表的成员变量pxIndex来确定,pxIndex所指向的列表项就是要遍历的开始列表项
// 即pxIndex所指向的列表项就代表列表头,由于是环形列表,新列表项就应该插入到pxIndex所指向的列表项的前面
// 只有这样,才能保证调用listGET_OWNER_OF_NEXT_ENTRY()时被最后删除
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = (void *)pxList;
(pxList->uxNumberOfItems)++;
}
插入位置分析:
尾部插入的图示:
插入前:
插入后:
列表的遍历:
列表项的插入和删除实验
代码如下:
// 定义一个列表和三个列表项
List_t testList;
ListItem_t ListItem1;
ListItem_t ListItem2;
ListItem_t ListItem3;
void list_task(void *pvParameters)
{
vListInitialise(&testList); // 初始化列表
vListInitialiseItem(&ListItem1); // 初始化列表项1
vListInitialiseItem(&ListItem2); // 初始化列表项2
vListInitialiseItem(&ListItem3); // 初始化列表项3
ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
// 打印列表和其他列表项的地址
printf("\n/*********************列表和列表项地址************************/\n");
printf("项目 地址 \n");
printf("testList %#X \n", (int)&testList);
printf("testList->pxIndex %#X \n", (int)testList.pxIndex);
printf("testList->xListEnd %#X \n", (int)&testList.xListEnd);
printf("ListItem1 %#X \n", (int)&ListItem1);
printf("ListItem2 %#X \n", (int)&ListItem2);
printf("ListItem3 %#X \n", (int)&ListItem3);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
// testList插入ListItem1
vListInsert(&testList, &ListItem1); // 插入列表项1
printf("\n/*******************添加列表项ListItem1**********************/\n");
printf("项目 地址 \n");
printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
printf("/**********************前后向连接分割线***********************/\n");
printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
// testList插入ListItem2
vListInsert(&testList, &ListItem2); // 插入列表项2
printf("\n/*******************添加列表项ListItem2**********************/\n");
printf("项目 地址 \n");
printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
printf("/**********************前后向连接分割线***********************/\n");
printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
// testList插入ListItem3
vListInsert(&testList, &ListItem3); // 插入列表项3
printf("\n/*******************添加列表项ListItem3**********************/\n");
printf("项目 地址 \n");
printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
printf("/**********************前后向连接分割线***********************/\n");
printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
// testList删除ListItem2
uxListRemove(&ListItem2);
printf("\n/*******************删除列表项ListItem2**********************/\n");
printf("项目 地址 \n");
printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
printf("/**********************前后向连接分割线***********************/\n");
printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
// pxIndex向后移一项,这样pxIndex就会指向ListItem1
testList.pxIndex = testList.pxIndex->pxNext;
vListInsertEnd(&testList, &ListItem2); // 列表末尾添加列表项ListItem2
printf("\n/*****************在末尾添加列表项ListItem2******************/\n");
printf("项目 地址 \n");
printf("testList.pxIndex %#X \n", (int)testList.pxIndex);
printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
printf("/**********************前后向连接分割线***********************/\n");
printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
printf("/***************************结束*****************************/\n");
printf("按下K1按键继续!\r\n\r\n");
while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
printf("list_task开始任务调度\n");
for (;;)
{
printf("list_task run!\n");
vTaskDelay(500);
}
}
程序执行结果如下:
/*********************列表和列表项地址************************/
项目 地址
testList 0X20000080
testList->pxIndex 0X20000088
testList->xListEnd 0X20000088
ListItem1 0X20000094
ListItem2 0X200000A8
ListItem3 0X200000BC
/***************************结束*****************************/
按下K1按键继续!
/*******************添加列表项ListItem1**********************/
项目 地址
testList->xListEnd->pxNext 0X20000094
ListItem1->pxNext 0X20000088
/**********************前后向连接分割线***********************/
testList->xListEnd->pxPrevious 0X20000094
ListItem1->pxPrevious 0X20000088
/***************************结束*****************************/
按下K1按键继续!
/*******************添加列表项ListItem2**********************/
项目 地址
testList->xListEnd->pxNext 0X20000094
ListItem1->pxNext 0X200000A8
ListItem2->pxNext 0X20000088
/**********************前后向连接分割线***********************/
testList->xListEnd->pxPrevious 0X200000A8
ListItem1->pxPrevious 0X20000088
ListItem2->pxPrevious 0X20000094
/***************************结束*****************************/
按下K1按键继续!
/*******************添加列表项ListItem3**********************/
项目 地址
testList->xListEnd->pxNext 0X20000094
ListItem1->pxNext 0X200000BC
ListItem3->pxNext 0X200000A8
ListItem2->pxNext 0X20000088
/**********************前后向连接分割线***********************/
testList->xListEnd->pxPrevious 0X200000A8
ListItem1->pxPrevious 0X20000088
ListItem3->pxPrevious 0X20000094
ListItem2->pxPrevious 0X200000BC
/***************************结束*****************************/
按下K1按键继续!
/*******************删除列表项ListItem2**********************/
项目 地址
testList->xListEnd->pxNext 0X20000094
ListItem1->pxNext 0X200000BC
ListItem3->pxNext 0X20000088
/**********************前后向连接分割线***********************/
testList->xListEnd->pxPrevious 0X200000BC
ListItem1->pxPrevious 0X20000088
ListItem3->pxPrevious 0X20000094
/***************************结束*****************************/
按下K1按键继续!
/*****************在末尾添加列表项ListItem2******************/
项目 地址
testList.pxIndex 0X20000094
testList->xListEnd->pxNext 0X200000A8
ListItem2->pxNext 0X20000094
ListItem1->pxNext 0X200000BC
ListItem3->pxNext 0X20000088
/**********************前后向连接分割线***********************/
testList->xListEnd->pxPrevious 0X200000BC
ListItem2->pxPrevious 0X20000088
ListItem1->pxPrevious 0X200000A8
ListItem3->pxPrevious 0X20000094
/***************************结束*****************************/
按下K1按键继续!
list_task开始任务调度
list_task run!
list_task run!
list_task run!
list_task run!
list_task run!