Freertos内核从0开始编写之链表篇

1,一些说明

Freertos的内核编写系列文章是学习野火Freertos教程时一些困惑,问题,思路整理的学习记录,有些问题笔者的理解也许并不完全正确,欢迎读者们进行指正。

完整工程下载。

链接:https://pan.baidu.com/s/1SVFKnNaBsRUkA76AYKHCpQ?pwd=rm24 
提取码:rm24

2,正篇

2.1仿真文件创建

1.随便一个位置创建一个文件夹 《1链表练习》

2. 《1链表练习》里面创建这些个文件夹 

3.在freertos里创建这些文件夹 

4.打开Keil开始创建 

  

然后创建main.c放入user文件中,创建list.c放入freertos文件夹中的src文件夹,创建list.h、freertos.h、freertosConfig.h放入freertos文件夹中的inc文件夹,创建port.c、port.h文件放入freertos文件夹中的port文件夹,最后在keil中加入。

加入头文件路径消除警报

完成

 除了main.c文件有内容其他的皆为空白后面再写。配置keil的设置,因为使用的是ARM3故为25

采用软件仿真 

2.2双向链表结构体的创建

C语言中的双向链表是一种数据结构,它由一系列节点组成,每个节点包含数据和指向前一个节点和后一个节点的指针。双向链表可以在任何位置进行插入和删除操作,因为每个节点都有指向前一个节点和后一个节点的指针,这使得在双向链表中进行插入和删除操作更加高效。双向链表就像一列火车,每节车厢都有指向前一节车厢和后一节车厢的连接。这样,可以在任何位置插入或移除车厢,而不需要重新连接整列火车。Freertos的双向链表是闭环的,像一条蛇咬住了自己的尾巴,即火车头和尾连在了一起,火车头此时即可以是头也可以是尾,一般我们叫它根节点。有点啰嗦了。在开始前我们还要对变量进行一些宏定义放入port.h中。

/*
************************************************************************
*                                变量重命名
************************************************************************
*/

#define portCHAR		char
#define portFLOAT		float
#define portDOUBLE		double
#define portLONG		long
#define portSHORT		short
#define portSTACK_TYPE	uint32_t
#define portBASE_TYPE	long
typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
#if( configUSE_16_BIT_TICKS == 1 )
	typedef uint16_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffff
#else
	typedef uint32_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
	#define portTICK_TYPE_IS_ATOMIC 1
#endif

/* 函数指针别名 */
typedef void (*TaskFunction_t)( void * );

/*
************************************************************************
*                                快捷判断宏
************************************************************************
*/

#define pdFALSE			( ( BaseType_t ) 0 )
#define pdTRUE			( ( BaseType_t ) 1 )

#define pdPASS			( pdTRUE )
#define pdFAIL			( pdFALSE )

节点定义即车厢(以下代码都定义在list.h中)

/* 节点结构体定义 */
struct xLIST_ITEM
{
	/* 辅助值,用于帮助节点做顺序排列 */
	TickType_t xItemValue; 
	/* 指向链表下一个节点 */
	struct xLIST_ITEM *  pxNext; 
	/* 指向链表前一个节点 */
	struct xLIST_ITEM *  pxPrevious;
	/* 指向拥有该节点的内核对象,通常是TCB */
	void * pvOwner;
	/* 指向该节点所在的链表 */
	void *  pvContainer;		       
};
/* 节点数据类型重定义 */
typedef struct xLIST_ITEM ListItem_t;

 根节点定义即火车头或尾

/* 链表结构体定义 */
typedef struct xLIST
{
	/* 链表节点计数器 */
	UBaseType_t uxNumberOfItems; 
	/* 链表节点索引指针 */
	ListItem_t *  pxIndex;
	/* 链表最后一个节点 */
	MiniListItem_t xListEnd;		
} List_t;

这个火车头不仅有普通车厢没有的东西还有一个阉割的普通车厢即 MiniListItem_t xListEnd

/* mini节点结构体定义,作为双向链表的结尾因为双向链表是首尾相连的,头即是尾,尾即是头 */
struct xMINI_LIST_ITEM
{
	/* 辅助值,用于帮助节点做升序排列 */
	TickType_t xItemValue;
	/* 指向链表下一个节点 */
	struct xLIST_ITEM *  pxNext; 
	/* 指向链表前一个节点 */
	struct xLIST_ITEM *  pxPrevious;           
};
/* 最小节点数据类型重定义 */
typedef struct xMINI_LIST_ITEM MiniListItem_t;

2.3双向链表的相关操作

 初始链表函数(以下代码都在list.c中)

/*
 *函数名字:vListInitialise
 *作用:链表初始化
 *参数pxList:要初始化的链表指针
 *返回值:无
*/
void vListInitialise( 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->uxNumberOfItems = ( UBaseType_t ) 0U;
}

这个函数应该没什么难度portMAX_DELAYheUBaseType_t都是一个在port.h的宏定义。 

节点初始化

/*
 *函数名字:vListInitialiseItem
 *作用:节点初始化
 *参数pxList:要初始化的节点指针
 *返回值:无
*/
void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
	pxItem->pvContainer = NULL;
}

 将节点插入到链表的尾部

/*
 *函数名字:vListInsertEnd
 *作用:将节点插入到链表的尾部
 *参数pxList:要插入的链表
 *参数pxNewListItem:要插入的节点
 *返回值:无
*/
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t * const pxIndex = pxList->pxIndex;

	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex->pxPrevious;
	pxIndex->pxPrevious->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

	/* 记住该节点所在的链表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}

 这个要理解必须要画图,3节点为pxNewListItem即新的节点,尾部插入函数过程如下:

ListItem_t * const pxIndex = pxList->pxIndex;

pxNewListItem->pxNext = pxIndex; 

pxNewListItem->pxPrevious = pxIndex->pxPrevious;  

pxIndex->pxPrevious->pxNext = pxNewListItem; 

pxIndex->pxPrevious = pxNewListItem; 

将节点按照升序排列插入到链表 

/*
 *函数名字:vListInsert
 *作用:将节点按照升序排列插入到链表
 *参数pxList:要插入的链表
 *参数pxNewListItem:要插入的节点
 *返回值:无
*/
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t *pxIterator;
	
	/* 获取节点的排序辅助值 */
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	/* 节点要插入到链表的尾部 */
	if( xValueOfInsertion == portMAX_DELAY )
	{
		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->pvContainer = ( void * ) pxList;

	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}

找到插入位置后准备将2节点插入(3节点和END是连在一起的为美观省略了

pxNewListItem->pxNext = pxIterator->pxNext; 

 pxNewListItem->pxNext->pxPrevious = pxNewListItem;

pxNewListItem->pxPrevious = pxIterator;

pxIterator->pxNext = pxNewListItem;  

 

将节点从链表中删除 

/*
 *函数名字:uxListRemove
 *作用:将节点从链表中删除
 *参数pxItemToRemove:要删除的节点
 *返回值:剩余节点的个数
*/
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
	/* 获取节点所在的链表 */
	List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

	/* 如果删除的节点被索引指向,将索引前移 */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}

	/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
	pxItemToRemove->pvContainer = NULL;
	
	/* 链表节点计数器-- */
	( pxList->uxNumberOfItems )--;

	/* 返回链表中剩余节点的个数 */
	return pxList->uxNumberOfItems;
}

 将2节点从链表中移除

 pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;

pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; 

 

3,总结 

面对链表问题画图理解是一种很好的方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值