本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。适用于快速了解FreeRTOS并进行开发、突击面试、对新手小白非常友好。期待您的后续关注和订阅!
目录
[1 列表与列表项简介](#1 列表与列表项简介)
[1.1 定义](#1.1 定义)
[1.2 结构体介绍](#1.2 结构体介绍)
[2 相关API函数介绍](#2 相关API函数介绍)
[2.1 初始化列表](#2.1 初始化列表)
[2.2 初始化列表项](#2.2 初始化列表项)
[2.3 列表末尾插入列表项](#2.3 列表末尾插入列表项)
[2.4 列表插入列表项](#2.4 列表插入列表项)
[2.5 列表移除列表项](#2.5 列表移除列表项)
[3 例子介绍](#3 例子介绍)
列表与列表项管理
1 列表与列表项简介
1.1 定义
在FreeRTOS中,列表是一个双向链表,包含多个列表项。每个列表具有一个列表根(List Root)和一个指向列表尾的指针。列表通过列表项来管理数据,每个列表项都存储了一个数值(通常是时间戳或优先级)和指向相应数据结构的指针。
++简而言之,列表就是数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务;列表项就是放在列表中的项目。++
++列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加进来的个数决定的,随时可以改变 。列表就中包含着许多的列表项,而列表项就像是在列表内环形排列,如下图所示:++
1.2 结构体介绍
(1)列表结构体:
cpp
typedef struct xLIST {
listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 用于调试和错误检测的完整性检查值 */
struct xLIST_ITEM *pxIndex; /* 指向列表中的当前节点 */
MiniListItem_t xListEnd; /* 列表的尾部节点 */
UBaseType_t uxNumberOfItems; /* 列表中项目的数量 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /* 用于调试和错误检测的完整性检查值 */
} List_t;
-
- 在该结构体中,包含了两个宏,这两个宏是确定的已知常量。FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中是否遭到破坏,该功能一般用于调试,默认是不开启的。
-
- 成员 `uxNumberOfItems 用于记录列表中列表项的个数(不包含 `xListEnd`)。
-
- 成员 `pxIndex` 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
-
- 成员变量 `xListEnd` 是一个迷你列表项,排在列表的最末尾。
列表的结构示意图
(2)列表项结构体:
cpp
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;
-
- 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
-
- 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
-
- 成员变量 `pvOwner` 指向包含该列表项的对象(通常是任务控制块)。
-
- 成员变量 `pxContainer` 指向列表项所在的列表。
列表的结构示意图
列表结构体有两种一种是正常的列表项,另一种是正常的列表项,另外一种是迷你列表项。迷你列表项一般放在列表的末尾处如下所示:
cpp
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;
-
- 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
-
- 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
-
- 迷你列表项仅用于标记列表的末尾和挂载其他插入的列表项,因此不需要 `pvOwner` 和 `pxContainer` 成员变量,以节省内存开销。
迷你列表项结构示意图
2 相关API函数介绍
在列表的运用中,一般采用的函数如下所示,我们需要掌握其用法,其如何定义的我们仅需要掌握
|-----------------------|-----------|
| 函数 | 描述 |
| vListInitialise() | 初始化列表 |
| vListInitialiseItem() | 初始化列表项 |
| vListInsertEnd() | 列表末尾插入列表项 |
| vListInsert() | 列表插入列表项 |
| uxListRemove() | 列表移除列表项 |
2.1 初始化列表
vListInitialise(List_t * const pxList)
其中pxList为等待初始化的列表
cpp
void vListInitialise(List_t * const pxList){
// 将 pxIndex 指向 xListEnd
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
// 将 xListEnd 的 xItemValue 初始化为最大值
pxList->xListEnd.xItemValue = portMAX_DELAY;
// 将 xListEnd 的 pxNext 和 pxPrevious 都指向 xListEnd 本身
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
// 将列表项数量初始化为 0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
// 设置列表数据完整性的校验值
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
2.2 初始化列表项
vListInitialiseItem( ListItem_t * const pxItem )
其中pxItem为等待初始化的列表项
cpp
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 );
}
2.3 列表末尾插入列表项
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
参数pxList为要插入的列表,pxNewListItem为要插进去的列表项
++这里超过并不是指列表中的最末端,将待插入列表的列表项插入到列表 pxIndex 指针指向的列表项前面,是一种无序的插入方法。++
cpp
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ){
// 获取列表 pxIndex 指向的列表项
ListItem_t * const pxIndex = pxList->pxIndex;
// 更新待插入列表项的指针成员变量
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
// 更新列表中原本列表项的指针成员变量
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
// 更新待插入列表项的所在列表成员变量
pxNewListItem->pxContainer = pxList;
// 更新列表中列表项的数量
( pxList->uxNumberOfItems )++;
}
2.4 列表插入列表项
vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
参数pxList为要插入的列表,pxNewListItem为要插进去的列表项
++列表插入列表项依赖每个列表项的值(++ ++xItemValue ),在插入的时候会对已经存在列表中的列表项进行比较该值,即:插入的列表要处于比它xItemValue值小的右边,并处于比它xItemValue值大的左边。++
cpp
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 )++;
}
2.5 列表移除列表项
uxListRemove( ListItem_t * const pxItemToRemove )
pxItemToRemove为等待移除的列表项,并且其具备一个返回值,返回的值为移除掉这个列表项之后,剩余列表项的数量。
cpp
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ){
List_t * const pxList = pxItemToRemove->pxContainer;
// 更新指针,移除列表项
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
// 如果 pxIndex 正指向待移除的列表项
if( pxList->pxIndex == pxItemToRemove ) {
// pxIndex 指向上一个列表项
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
// 将待移除的列表项的所在列表指针清空
pxItemToRemove->pxContainer = NULL;
// 更新列表中列表项的数量
( pxList->uxNumberOfItems )--;
// 返回移除后的列表中列表项的数量
return pxList->uxNumberOfItems;
}
3 例子介绍
(1)整体流程
通过以上几个函数,进行简单写一个例子我们来串联一下思路
cpp
#include "FreeRTOS.h"
#include "list.h"
#include <stdio.h>
int main() {
// 初始化列表
List_t myList;
vListInitialise(&myList);
// myList 初始化后包含 xListEnd,xListEnd 的 xItemValue 为最大值,pxNext 和 pxPrevious 均指向 xListEnd 自身,uxNumberOfItems 为 0
// 初始化列表项1
ListItem_t listItem1;
vListInitialiseItem(&listItem1);
listItem1.xItemValue = 40; // 设置列表项1的值
// listItem1 初始化后,pxContainer 为 NULL,xItemValue 为 40
// 初始化列表项2
ListItem_t listItem2;
vListInitialiseItem(&listItem2);
listItem2.xItemValue = 60; // 设置列表项2的值
// listItem2 初始化后,pxContainer 为 NULL,xItemValue 为 60
// 初始化列表项3
ListItem_t listItem3;
vListInitialiseItem(&listItem3);
listItem3.xItemValue = 50; // 设置列表项3的值
// listItem3 初始化后,pxContainer 为 NULL,xItemValue 为 50
// 将列表项1插入到列表末尾
vListInsertEnd(&myList, &listItem1);
// myList 现在包含 listItem1 和 xListEnd,listItem1 的 pxNext 指向 xListEnd,pxPrevious 指向 xListEnd,pxContainer 指向 myList,uxNumberOfItems 为 1
// 将列表项2插入到列表末尾
vListInsertEnd(&myList, &listItem2);
// myList 现在包含 listItem1, listItem2 和 xListEnd,listItem2 的 pxNext 指向 xListEnd,pxPrevious 指向 listItem1,pxContainer 指向 myList,uxNumberOfItems 为 2
// 将列表项3按值插入到列表中
vListInsert(&myList, &listItem3);
// myList 现在包含 listItem1, listItem3, listItem2 和 xListEnd,listItem3 的 pxNext 指向 listItem2,pxPrevious 指向 listItem1,pxContainer 指向 myList,uxNumberOfItems 为 3
// 打印列表项值
ListItem_t *currentItem;
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
// 打印列表中所有项的值
// 从列表中移除列表项2
uxListRemove(&listItem2);
// myList 现在包含 listItem1, listItem3 和 xListEnd,listItem2 的 pxContainer 为 NULL,uxNumberOfItems 为 2
// 打印列表项值
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
// 再次打印列表中所有项的值
return 0;
}
(2)分开解析
1.初始化列表:
cpp
List_t myList;
vListInitialise(&myList);
初始化一个列表 myList
。该列表包含 xListEnd
,其 xItemValue
为最大值,pxNext
和 pxPrevious
均指向 xListEnd
自身,uxNumberOfItems
为 0。
2.初始化列表项:
cpp
ListItem_t listItem1, listItem2, listItem3;
vListInitialiseItem(&listItem1);
listItem1.xItemValue = 40;
vListInitialiseItem(&listItem2);
listItem2.xItemValue = 60;
vListInitialiseItem(&listItem3);
listItem3.xItemValue = 50;
初始化三个列表项,并设置它们的值分别为 40、60 和 50。每个列表项初始化后,pxContainer
为 NULL
。
3.插入列表项到列表末尾:
cpp
vListInsertEnd(&myList, &listItem1);
vListInsertEnd(&myList, &listItem2);
将 listItem1
和 listItem2
插入到列表 myList
的末尾。此时列表包含 listItem1
, listItem2
和 xListEnd
,列表项数量为 2。
4.按值插入列表项:
cpp
vListInsert(&myList, &listItem3);
将 listItem3
按值插入到列表 myList
中。此时列表包含 listItem1
, listItem3
, listItem2
和 xListEnd
,列表项数量为 3。
5.打印列表项值:
cpp
ListItem_t *currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
遍历并打印列表中所有项的值。
6.移除列表项:
cpp
uxListRemove(&listItem2);
从列表 myList
中移除 listItem2
。此时列表包含 listItem1
, listItem3
和 xListEnd
,列表项数量为 2。
7.再次打印列表项值:
cpp
currentItem = (ListItem_t *) myList.xListEnd.pxNext;
while(currentItem != &(myList.xListEnd)) {
printf("List item value: %d\n", (int) currentItem->xItemValue);
currentItem = (ListItem_t *) currentItem->pxNext;
}
遍历并再次打印列表中所有项的值。
本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。期待诸君的订阅和关注!