FreeRTOS中的列表与列表项本质是数据结构中的链表链表和节点。
1. 列表与列表项
1.1 列表
列表属于RTOS中基本的数据结构,物理单元上非连续、非顺序。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
- pxIndex用于指向列表中的某一个列表项(个人认为类似于链表中的头节点)
- xListEnd迷你列表项,升序排列挂在最末尾
结构图大致如下:
2. 列表项
列表项的定义如下:
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用于指向上一个与下一个
- pxContainer用于指向列表项所在的列表
- pvOwner用于指向包含列表项的对象(如任务控制块)
结构如下所示:
迷你列表项定义如下:
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,以节省内存开销。
示意图如下:
2. 相关API
- vListInitialise() 初始化列表,参数是待初始化的列表。初始化的时候,xListEnd手拉手
- vListInitialiseItem() 初始化列表项,参数是待初始化的列表项
- vListInsertEnd() 在pIndex前插入列表项,参数是列表,待插入的列表项
- vListInsert() 插入列表项,参数是列表,待插入的列表项
- uxListRemove() 列表移除列表项,参数是待移除的列表项
初始化后的列表与列表项结构如下:
3. 列表项插入与删除实验
我们在任务1中测试列表项的插入与删除,前面几篇文章也说了好几遍如何创建任务了,这里就不在赘述了。
void task1(void *pvParameters) {
/* 第一步:初始化列表和列表项 */
vListInitialise(&TestList); /* 初始化列表 */
vListInitialiseItem(&ListItem1); /* 初始化列表项1 */
vListInitialiseItem(&ListItem2); /* 初始化列表项2 */
vListInitialiseItem(&ListItem3); /* 初始化列表项3 */
/* 第二步:打印列表和其他列表项的地址 */
printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
printf("项目\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);
printf("/**************************结束***************************/\r\n");
printf("按下KEY0键继续!\r\n\r\n\r\n");
while (key_scan(0) != KEY0_PRES)
{
vTaskDelay(10);
}
/* 第三步:列表项1插入列表 */
printf("/*****************第三步:列表项1插入列表******************/\r\n");
vListInsert(
(List_t* )&TestList, /* 列表 */
(ListItem_t* )&ListItem1); /* 列表项 */
/* 打印信息,代码省略 */
while (key_scan(0) != KEY0_PRES)
{
vTaskDelay(10);
}
/* 第四步:列表项2插入列表 */
printf("/*****************第四步:列表项2插入列表******************/\r\n");
vListInsert(
(List_t* )&TestList, /* 列表 */
(ListItem_t* )&ListItem2); /* 列表项 */
/* 打印信息,代码省略 */
while (key_scan(0) != KEY0_PRES)
{
vTaskDelay(10);
}
/* 第五步:列表项3插入列表 */
printf("/*****************第五步:列表项3插入列表******************/\r\n");
vListInsert(
(List_t* )&TestList, /* 列表 */
(ListItem_t* )&ListItem3); /* 列表项 */ /*
打印信息,代码省略 */
while (key_scan(0) != KEY0_PRES) {
vTaskDelay(10);
}
/* 第六步:移除列表项2 */
printf("/*******************第六步:移除列表项2********************/\r\n");
uxListRemove((ListItem_t*)&ListItem2); /* 移除列表项 */
/* 打印信息,代码省略 */
while (key_scan(0) != KEY0_PRES) {
vTaskDelay(10);
}
/* 第七步:列表末尾添加列表项2 */
printf("/****************第七步:列表末尾添加列表项2****************/\r\n");
vListInsertEnd(
(List_t* )&TestList, /* 列表 */
(ListItem_t* )&ListItem2); /* 列表项 */
/* 打印信息,代码省略 */
while(1) {
vTaskDelay(10);
}
}