关于FreeRTOS的list函数,可参考另一篇的链接:
网上的freeRTOS链表教程 - yintianlan的博客 - 优快云博客
https://blog.youkuaiyun.com/yintianlan/article/details/84530622
使用了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);
//......