目录
初始化列表项函数:vListInitialiseItem()
1.列表和列表项
列表:FreeRTOS中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。
列表项:存放在列表中的项目(列表的子集)
(列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表)
列表特点(优势):
- 列表项间的地址非连续,是人为的连接到一起的。
- 列表项的数目是由后期添加的个数决定的,随时可以改变
不同于数组,数组成员地址是连续的,数组在最初确定了成员数量后期无法改变
在OS中任务的数量是不确定的,并且任务状态是会发生改变的,所以非常适用列表(链表)这种数据结构
列表结构体:
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 是一个迷你列表项,排在最末尾
列表项结构体:
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 用于指向列表项所在列表。
迷你列表项
迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项
(末尾项就是迷你列表项)
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,以节省内存开销
列表项挂载到列表
实质就是先根据 xItemValue 确定列表项插入的位置,然后修改列表项及其上下两个列表项pxNext、pxPrevious,确定他们的指向关系
PS:末尾列表项的 xItemValue 始终为0XFFFFFFFFF
2.列表相关API函数(重点)
插入或移除列表项其实都是修改相关列表项之间pdNext和pxPrevious的关系
初始化列表函数:vListInitialise()
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 );
初始化列表项函数:vListInitialiseItem()
形参 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 );
}
列表插入列表项函数:vListInsert():
形参 pxList :插入的列表
pxNewListItem:待插入的列表项
实质是将待插入列表的列表项按照列表项值升序进行排序,有序地插入到列表中
PS:若插入的列表项值也为0XFFFFFFFF,则插入到末尾列表项的前面
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 )++;
}
末尾插入列表项:vListInsertEnd()
形参 pxList :插入的列表
pxNewListItem:待插入的列表项
(实质是将待插入列表的列表项插入到列表 pxIndex 指针指向的列表项前面,是一种无序的插入方法)
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 )++;
}
列表移除列表项函数:uxListRemove()
形参 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;
}
3.列表项的插入和删除实验
实验目的:对FreeRTOS 列表和列表项的操作函数使用,并观察运行结果和理论分析是否一致
实验设计:将设计三个任务:start_task、task1、task2
start_task:用来创建其他的2个任务
task1:实现LED0每500ms闪烁一次,用来提示系统正在运行
task2:调用列表和列表项相关API函数,并且通过串口输出相应的信息,进行观察
(其实就是插入三个不同列表项值的地址,观察pxNext、pxPrevious等之间的关系,没啥特别的所以不做)