今天算是学习FreeRTOS的第二天,也是学习在CSDN上面写文章的第二天,主要目的既是为了帮助自己更高效的学习,梳理知识点,记录问题难点,也是相当于一种生活的记录吧。这篇文章的整体布局是直接套用的模板中的新手模板。前面的部分会用来记录学习过程,后面会总结遇到的问题。
目录
前言
这篇文章主要记录一下我在学习列表与列表项的实现过程中遇到的问题和学习内容
一、实战指南学习
在FreeRTOS中存在着大量的基础数据结构列表和列表项的操作,所以要想读懂FreeRTOS的源码就要明白列表和列表项的操作。
list---列表---链表
list item--列表项---节点
链表的节点本身不能存储大量数据,但可以挂很多数据。
链表: 1、单向链表(很少用)
2、双向链表(使用最多)
1、C语言链表简介
1.1单向链表
1、链表的定义
链表中共有n个节点,前一个节点都有一个箭头指向后一个节点,首尾相连,组成一个圈。
节点本身必须包含一个结点指针用于指向后一个节点,除了这个结点指针还可以携带一些私有信息,携带的方式分为两种:
(1)在节点的自定义类型结构体中可以有单个的数据、数组、指针数据和自定义的结构体数据类型。(很少用)节点结构体定义:
struct node
{
struct node *next;//指向链表的下一节点
char data1;//单个的数据
unsigned char arry[];//数组
unsigned long *prt;//指针数据
struct userstruct data2;//自定义结构体类型数据
}
(2)在节点里面只包含一个用于指向下一个节点的指针。要通过链表存储的数据内嵌一个节点。
struct node
{
struct node *next;//指向链表的下一个节点
}
struct userstruct
{
struct node *next;//在结构体中内嵌一个结点指针,通过这个节点将数据挂接到链表
/*AI回答中说这个不正确,应该是struct node next;*/
}
2、链表的操作
链表的常规操作:插入、删除。
一条链表会认为规定一个根节点,称之为生产者,根节点还会有一个节点计数器用于统计整条链表的节点个数
1.2双向链表(使用最多)
与单向链表的区别是节点中有两个结点指针,分别指向前后两个节点。其他一样
1.3 链表与数组的对比
1.链表通过节点把离散的数据连接成一个表,通过对节点的插入和删除实现数据的存取
数组是通过开辟一段连续的内存来存储数据
2、数组每个成员对应链表节点,成员和节点的数据类型可以是C类型或者是用户自定义的结构体。
3、数组有起始地址和结束地址
链表是一个圈,为了方便插入和删除人为规定了一个根节点
2、FreeRTOS中链表的实现
2.1实现链表节点
1、定义链表节点数据结构
链表节点的数据结构在list.h中定义。链表节点结构体定义:
struct xLIST_ITEM
{
TickType_t xItemValue;//辅助值,用于帮助节点做顺序排列
struct xLIST_ITEM * pxNext;//指向链表下一个节点
struct xLIST_ITEM * pxPrevious;//指向链表前一个节点
void *pvOwner;//指向拥有该结点的内核对象
void * pvContainer;//指向该节点所在的链表
}
typedef struct xLIST_ITEM ListItem_t;//节点数据类型重定义
xItemValue是一个辅助值,帮助节点做顺序排列,数据类型为TickType_t。
FreeRTOS会将标准的C数据类型用typedef重新去一个类型名,放在portmacro.h这个头文件中。
pvOwner用于指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员。
2、链表节点初始化
链表节点初始化函数在list.c中实现。
void vListInitialiseItem( ListItem_t *const pxItem)
{
/*初始化该节点所在链表为空,表示节点还没有插入任何链表*/
pxItem->pvContainer = NULL;
}
链表节点ListItem_t共有5个成员,初始化时只需将pvContainer初始化为空即可,表示节点还没有插入到任何链表。
2.2 实现链表根节点
1、定义链表根节点数据结构
链表根节点的数据结构定义在list.h中。
typedef struct xLIST
{
UBaseType_t uxNumberOfItems;//链表节点计数器
ListItem_t * pxIndex;//链表节点索引指针
MiniListItem_t xListEnd;//链表最后一个节点
}List_t;
链表节点计数器,用于表示该链表下有多少个节点,根节点除外。
链表节点索引指针,用于遍历节点。
链表最后一个节点,就是链表的最后一个节点,也是链表的第一个节点称之为生产者。该生产者的数据类型是一个精简的节点,也在list.h中定义。
链表精简节点结构体定义:
struct xMINI_LIST_ITEM
{
TickType_t xItemValue;//辅助值,用于帮助节点做升序排列
struct xLIST_ITEM * pxNext;//指向链表下一个节点
struct xLIST_ITEM * pxPrevious;//指向链表前一个结点
}
typedef struct xMINI_LIST_ITEM MiniListItem_t;//精简节点数据类型重定义
2、链表根节点初始化
链表根节点初始化函数在list.c中实现。
void vListInitalise(List_t * const pxList)
{
/*将链表索引指针指向最后一个节点*/
pxList->pxIndex = ( ListItem_t *)& ( pxList -> xListEnd);
/*将链表最后一个节点的辅助排序值设置为最大,确保该节点就是链表的最后节点*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
/*将最后一个节点的pxNext和pxPrevious指针指向节点自身,表示链表为空*/
pxList->xListEnd.pxNext = ( ListItem_t *) & ( pxList->xListEnd);
pxList->xListEnd.pxPrevious = ( ListItem_t * ) & ( pxList->xListEnd);
/*初始化链表节点计数器的值为0,表示链表为空*/
pxList->uxNumberOfItem = ( UBaseType_t)0u;
}
3、将节点插入到链表的尾部
将节点插入到链表的尾部就是将一个新的节点插入到一个空的链表。
void vListInsertEnd( List_t * const pxList, ListItem_t * cont pxNewListItem )
4、将节点按照升序排列插入到链表
将节点按照升序排列插入到链表,如果有两个节点的值相同,则新节点在旧节点的后面插入。
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem)
5、将节点从链表删除
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
6、节点带参宏小函数
在list.h中还定义了各种带参宏,方便对节点做一些简单的操作。如
初始化节点的拥有者、获取节点拥有者、初始化节点排序辅助值等
3、链表节点插入实验
新建一个根节点(链表)和三个普通节点,然后将这三个普通节点按照节点的排序辅助值做升序排列插入到链表中。
节点结构体定义中的TickType_t数据类型重定义在portmacro.h文件中,当configUSE_16_BIT_TICKS 为1时是16位,否则是32位。
configUSE_16_BIT_TICKS的值定义在FreeRTOSConfig.h文件中。
portable.h文件引用了portmcro.h.
FreeRTOS.h文件引用了FreeRTOSConfig.h和portable.h
list.h引用了FreeRTOS.h文件
list.h——FreeRTOS.h——FreeRTOSConfig.h——configUSE_16_BIT_TICKS
——portable.h——portmcro.h——TickType_t数据类型
二、遇到的问题
1、编译后警告no previous prototype for function ‘vListInitialiseItem‘ [-Wmissing-prototypes]
解决办法:打开魔法棒Options for Target,在ARM Complier选项中将Use defult complier version 6改为Use defult complier version 5
2、写代码的时候括号要注意是英文的括号。
总结
主要学习了链表在FreeRTOS中的实现,包括根节点的实现和初始化,节点的实现和初始化,还有链表的插入删除等一些操作。