STM32使用FreeRTOS中的链表创建节点与列表项

2019-07-18来源: eefocus关键字:STM32  FreeRTOS  链表  创建节点  列表项

使用了STM32F103的FreeRTOS库来创建一个列表,其实FreeRTOS的list中链表是双向的循环链表,为了简单介绍,我直接说是列表了。 

芯片使用的是GD32F103CBT6,该芯片兼容ST的库,不影响使用效果。 

以下代码不全,仅提供思路,如有不足,请给我留言。


第一步


先定义变量,初始化列表及列表项


nodeDevice node_t; //节点数据

nodeDevice * pNode; //节点指针

 

xList list_t; //列表

#define pList ((xList *) &list_t) //列表指针

 

xListItem item_t; //列表项

xListItem * pItem;  //列表项指针

 

/*初始化列表、列表项*/

vListInitialise((xList *) &list_t);

vListInitialiseItem(pItem);

第二步


根据条件判断是否需要增加列表项与节点(每一个节点指针挂载在对应列表项的空指针上)。 

我的节点数据是通过CAN数据来获取的,其实通过其它总线也可以,但CAN总线可以挂载多个设备(收发器),就是现成的多个节点了。 

这部分代码是我是否创建新节点的逻辑代码。


/*****************************************************************************

**Name: DealAddressReq

**Function: 处理设备地址

**Args:

**Return:

******************************************************************************/

void DealAddressReq(CanRxMsgTypeDef * const pCAN_RxData, void * const pBaseInfo)

{

uint32_t NodeAddress;

nodeDevice *goalCurrentNode = NULL;//当前节点指针

 

/*获取设备ID*/

NodeAddress = (pCAN_RxData->Data[0]<<24)|(pCAN_RxData->Data[1]<<16)|(pCAN_RxData->Data[2]<<8)|(pCAN_RxData->Data[3]);

 

if(NodeAddress == NULLDEVICE)return;//地址为空则退出

 

/*如果列表为空*/

if(listLIST_IS_EMPTY(pList))

{

CreateNode(pCAN_RxData, pBaseInfo, NodeAddress);//加载第一项,创建第一个节点

}

else/*如果列表不为空*/

{

goalCurrentNode = GetNodeByUidOrCHID(NodeAddress, 0);//根据UID获取节点

 

/*如果不是地址池中的地址,则创建新的节点*/

if(goalCurrentNode == NULLNODE)

{

CreateNode(pCAN_RxData, pBaseInfo, NodeAddress);//创建新节点

}

else

{

return;

}

}

}

 

其中遍历列表,查找当前节点的函数


/*****************************************************************************

**Name: GetNodeByUid

**Function: 根据UID获取节点,或根据数据上下行通道获取节点

**Args: NodeAddress != 0: 使用UID获取节点

nodeChannelID != 0: 使用数据上下行通道获取节点

**Return:

******************************************************************************/

nodeDevice * GetNodeByUidOrCHID(uint32_t NodeAddress, uint32_t nodeChannelID)

{

int index;

nodeDevice *getCurrentNode = NULL;//当前节点指针

 

for(index = 0; index < LIST_LENGTH; index++)

{

/*遍历列表,返回的是列表中列表项的pxOwner成员*/

listGET_OWNER_OF_NEXT_ENTRY(getCurrentNode, pList);

 

//根据MAC查找结点

if(NodeAddress == (getCurrentNode->nodeAddress))

{

return getCurrentNode;

}

//地址应答帧ID是否与该节点的数据下行通道相配对

else if(nodeChannelID == (getCurrentNode->nodeChannelID_Low|0x01))

{

return getCurrentNode;

}

else

{

getCurrentNode = NULLNODE;

}

}

 

return getCurrentNode;

}

 

第三步


申请内存,创建节点与列表项,插入列表(链表) 

这一部分为重点,其实思路与网上大部分创建插入链表相同,只是更改了一些函数名与变量。


/*****************************************************************************

**Name: CreateNode

**Function: 创建列表项与节点

**Args:

**Return:

******************************************************************************/

void CreateNode(CanRxMsgTypeDef * const pCAN_RxData, void * const pBaseInfo, uint32_t UidAddress)

{

/*执行一次,则创建一个列表项与节点结构体*/

pItem = pvPortMalloc(sizeof(ListItem_t));//申请列表项内存

pNode = pvPortMalloc(sizeof(nodeDevice));//申请结构体内存

memset(pNode, 0, sizeof(nodeDevice));

 

pItem->xItemValue = GetNodePosition();//获取占位地址大小

vListInsert(pList, pItem);//插入列表项

 

pItem->pvOwner = pNode;//给列表项的空指针挂上我们的数据地址

 

/*发送通道ID(大端)*/

NodeDataInstall( pCAN_RxData, pBaseInfo, UidAddress, pNode);//数据加载

}

因为需要动态增加节点,所以pItem->xItemValue的值是需要在可控范围内获取的,并不一定是0~0xFFFFFFFFU的值,需要根据节点索引的数据类型来。 

这里有一个问题,比如定义了一个char型,就只能取0~255的数,若是超过了255怎么办呐?或者我是“整十”地定义索引,就只有25个数可取了.

之前说过了是“动态增加节点”,也就是说不只是增加,我们也可以删除,删除数据之后我们需要还原一些数据,并释放内存,当我们下一次增加新节点时使用的就是之前释放掉的内存,那么问题还是那个——我们的xItemValue值和节点定义的索引怎么办?

先来说一下列表项的xItemValue值,它是FreeRTOS中list.h定义的一个列表项结构体的一个变量,目地是为了插入列表项的时候,更据它的大小比较进行排序后插入列表对应的位置,所以它很重要。为了不要让我们插入的位置冲突,最好初始化一个新的列表项的时候把它赋一个不同的值。 

我们节点的索引应该也一样具有唯一性,就和总线上的设备地址一样。所以值的设置我们使用占位法,思路与STM32的寄存器一样—— 一个功能占一个BIT,使用时将该位“置1”,使用完后将该位“清0”,等待下一次使用。 

当使用到一位时,将它“置1”后将它的实际大小返回,赋值给xItemValue。

例如:从小到大占位“0000 0000”,现在都是空的, 现在占用第1位后“0000 0001”,当前列表项xItemValue = 1; 

从小到大占位“0001 1111”,现在使用了5个位置,当前列表项xItemValue = 5,现在还原第3位后“0001 1011”,下次可继续使用第3位; 

注:位的运算方法推荐使用STM32库中对寄存器的运算!

第四步


自己设计删除节点的条件


    //......

    

//还原位置数据

RestoreNodePosition(bxCurrentNode->nodeChannelID_Low);

 

pxCurrentItem = pList->pxIndex;//当前列表项

uxListRemove( pxCurrentItem); //删除列表项

vPortFree(bxCurrentNode); //释放内存

vPortFree(pxCurrentItem);

    //......


关键字:STM32  FreeRTOS  链表  创建节点  列表项 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic468425.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:FreeRTOS学习笔记——基础知识与移植(STM32F103)
下一篇:几张图对比STM32各系列产品特性和外设兼容性

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32流水灯(基于库函数)
,GPIO_Pin_5);/ /PE.5 输出高}该代码里面就包含了一个函数 void LED_Init(void),该函数的功能就是用来实现配置 PB5和 PE5 为推挽输出。 这里需要注意的是: 在配置 STM32 外设的时候,任何时候都要先使能该外设的时钟。 GPIO 是挂载在 APB2 总线上的外设, 在固件库中对挂载在 APB2 总线上的外设时钟使能是通过函数 RCC_APB2PeriphClockCmd()来实现的。LED.h程序代码书写#ifndef __LED_H#define __LED_H#include "sys.h"//LED 端口定义#define LED0 PBout
发表于 2019-10-21
stm32入门——跑马灯(基于stm32f103zet6)
的驱动都要使能相应的时钟,首先看stm32系统的时钟框图经查阅资料可知,GPIO的时钟在APB2的外设时钟使能寄存器上,相关函数的定义在stm32f10x_rcc.h中 void   RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)其源代码为:void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState){  /* Check the parameters */ //检查值的有效性&nbs
发表于 2019-10-21
stm32入门——跑马灯(基于stm32f103zet6)
STM32流水灯的几种实现方法
#include “stm32f10x.h”void RCC_Configuration(void);//2void GPIO_Configuration(void);//GPIOvoid Delay(u32 count){u32 i=0;for(;i<count;i++);}int main(void){ RCC_Configuration();//3LED_Init();while(1){GPIO_SetBits(GPIOA,GPIO_Pin_0);//第一灯亮Delay(800000); //延时GPIO_ResetBits(GPIOA,GPIO_Pin_0); //第一灯灭Delay(800000); //延时
发表于 2019-10-21
STM32学习探究:流水灯的实现
流水灯的实现这里我们使用 stm32mini 开发板实现外接流水灯,这里我采用了三个灯(说明问题就可以了)。外接的引脚分别为PA2,PB8,PC13三个引脚,在连接的时候,采用灯的正极接GPIO引脚,负极接GND(这是一种合理的方式)。注意:虽然接VCC也可以发光,但是这样不是合理的连接方式,而且,发光的情况刚好与接GND的情况相反。参考的相关资料如下:时钟使能:端口配置:端口输出:具体实现的代码如下://led.h文件#ifndef __LED_H#define __LED_H  #include "sys.h"//LED端口定义#define LED0 PAout(2)#define LED1
发表于 2019-10-21
STM32学习探究:流水灯的实现
STM32学习探究:流水灯的实现
流水灯的实现这里我们使用 stm32mini 开发板实现外接流水灯,这里我采用了三个灯(说明问题就可以了)。外接的引脚分别为PA2,PB8,PC13三个引脚,在连接的时候,采用灯的正极接GPIO引脚,负极接GND(这是一种合理的方式)。注意:虽然接VCC也可以发光,但是这样不是合理的连接方式,而且,发光的情况刚好与接GND的情况相反。参考的相关资料如下:时钟使能:端口配置:端口输出:具体实现的代码如下://led.h文件#ifndef __LED_H#define __LED_H  #include "sys.h"//LED端口定义#define LED0 PAout(2) #define LED1
发表于 2019-10-21
STM32学习探究:流水灯的实现
STM32流水灯的几种实现方法
#include “stm32f10x.h”void RCC_Configuration(void);//2void GPIO_Configuration(void);//GPIOvoid Delay(u32 count){u32 i=0;for(;i<count;i++);}int main(void){ RCC_Configuration();//3LED_Init();while(1){GPIO_SetBits(GPIOA,GPIO_Pin_0);//第一灯亮 Delay(800000); //延时 GPIO_ResetBits(GPIOA,GPIO_Pin_0); //第一灯灭 Delay(800000); //延时
发表于 2019-10-21
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved