FreeRTOS操作系统(详细速通篇)——— 第八章

本专栏将对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;
    1. 在该结构体中,包含了两个宏,这两个宏是确定的已知常量。FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中是否遭到破坏,该功能一般用于调试,默认是不开启的。
    1. 成员 `uxNumberOfItems 用于记录列表中列表项的个数(不包含 `xListEnd`)。
    1. 成员 `pxIndex` 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项。
    1. 成员变量 `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; 	
    1. 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
    1. 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
    1. 成员变量 `pvOwner` 指向包含该列表项的对象(通常是任务控制块)。
    1. 成员变量 `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;
    1. 成员变量 `xItemValue` 是列表项的值,主要用于将列表项按升序排序。
    1. 成员变量 `pxNext` 和 `pxPrevious` 分别指向列表中下一个和上一个列表项。
    1. 迷你列表项仅用于标记列表的末尾和挂载其他插入的列表项,因此不需要 `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 为最大值,pxNextpxPrevious 均指向 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。每个列表项初始化后,pxContainerNULL

3.插入列表项到列表末尾:

cpp 复制代码
vListInsertEnd(&myList, &listItem1);
vListInsertEnd(&myList, &listItem2);

listItem1listItem2 插入到列表 myList 的末尾。此时列表包含 listItem1, listItem2xListEnd,列表项数量为 2。

4.按值插入列表项:

cpp 复制代码
vListInsert(&myList, &listItem3);

listItem3 按值插入到列表 myList 中。此时列表包含 listItem1, listItem3, listItem2xListEnd,列表项数量为 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, listItem3xListEnd,列表项数量为 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的各部分内容。期待诸君的订阅和关注!

相关推荐
yutian06061 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
析木不会编程4 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
Uu_05kkq6 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
枯无穷肉8 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6778 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普9 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣9 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
A懿轩A9 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
云山工作室9 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设