列表及列表项概念及其应用
列表和列表项
任务调度列表介绍
其他组件列表介绍
双向链表结构
1:插入与删除效率高,只要操作一次就能完成。
2:对数据的管理更加灵活与便捷,有利于编写逻辑清晰的程序。
列表与列表项
freeRTOS列表操作API
vListInitialise()
vListInitialiseItem()
vListInsert()
vListInsertEnd()
uxListRemove()
列表及列表项函数应用
功能需求
功能业务划分
void Delay_Task(void const * argument)
{
/* USER CODE BEGIN Delay_Task */
EventBits_t KeyEventBits;
uint8_t index;
static uint8_t record = 0;
ListItem_t* pListItem;
/*
1、列表及列表项初始化
*/
vListInitialise(&ListTest);
for(index=0;index<ITEM_NUMBER;index++){
vListInitialiseItem(&ListItemTest[index]);
ListItemTest[index].xItemValue = index;
}
/* Infinite loop */
for(;;)
{
/*
1、修改事件标志组等待状态为 触发后清除标志
2、检测按键,处理相关功能
*/
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT,
pdTRUE,
pdFALSE,
portMAX_DELAY);
printf("Key is Down Key Event Bit is %x\r\n",KeyEventBits);
switch(KeyEventBits){
case KEY3_EVENT_BIT:
/*
当列表项在范围内,允许插入
插入完毕后,记录值record加一
*/
if(record < ITEM_NUMBER){
vListInsert(&ListTest,&ListItemTest[record++]);
}
else{
printf("plese press K4 remove!\r\n");
}
break;
case KEY4_EVENT_BIT:
/*
当记录值有效时,允许移除
插入完毕后,记录值record减一
*/
if((record != 0)&&(record <= ITEM_NUMBER)){
uxListRemove(&ListItemTest[--record]);
}
else{
printf("plese press K3 insert!\r\n");
}
break;
case KEY5_EVENT_BIT:
/*
1、打印有效列表项地址
2、打印有效列表项(ItemValue)
3、打印有效列表项前节点地址
4、打印有效列表项后节点地址
*/
for(pListItem = ListTest.xListEnd.pxNext;pListItem != (ListItem_t*)&ListTest.xListEnd;pListItem=pListItem->pxNext){
printf("pListItem addr = %x\r\n",pListItem);
printf("pListItem item value = %d\r\n",pListItem->xItemValue);
printf("pListItem previous addr = %x\r\n",pListItem->pxPrevious);
printf("pListItem next addr = %x\r\n",pListItem->pxNext);
}
break;
default:
break;
}
osDelay(10);
}
/* USER CODE END Delay_Task */
}
列表及列表项数据结构
列表初始化
/*
列表项结构体原型
*/
struct xLIST_ITEM
{
//列表项值 -----一般用于排序
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
//指向下个节点地址
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
//指向上一个节点地址
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
//当前列表项的拥有者
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
//当前列表项归属的列表
void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
struct xMINI_LIST_ITEM
{
//列表项值
configLIST_VOLATILE TickType_t xItemValue;
//指向下个节点地址
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
//指向上一个节点地址
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
//列表内有效列表项个数
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
//列表遍历时用到的索引
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
//列表尾节点
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
} List_t;
列表项初始化
/*
列表初始化
参数:指向列表的指针
*/
void vListInitialise( List_t * const pxList )
{
/* 索引地址指向尾节点 */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/*
尾节点初始化
1、value = 最大值,按序排列在最后面
2、pxNext、pxPrevious都指向尾节点本身,表示列表为空
*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//当前有效的列表个数为0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}
/*
列表项初始化
参数:指向列表项的指针
*/
void vListInitialiseItem( ListItem_t * const pxItem )
{
/*列表项初始化不归属任务列表所有 */
pxItem->pvContainer = NULL;
}
列表及列表项插入
列表项按序插入
/*
有序插入列表项
参数:
1、指向列表的指针
2、指向列表项的指针
*/
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
{
//从尾节点开始遍历,然后与下个节点的Value值进行比较,当要插入Value值大于遍历列表项Value时,就获得插入位置
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
}
}
//更新后面节点信息 (前后指针进行赋值)
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
//更新前面节点信息(前后指针进行赋值)
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* 把列表项归属于当前的列表 */
pxNewListItem->pvContainer = ( void * ) pxList;
/* 有效列表项数量进行累加 */
( pxList->uxNumberOfItems )++;
}
列表项尾部插入
/*
尾部插入法
参数:
1、指向列表的指针
2、指向列表项的指针
跟尾节点,没有半毛钱关系,这个接口,只能用freertos使用,尾部插入,主要用于任务调度
*/
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 )++;
}
列表项移除
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;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
/* 索引是否需要更新 */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//移除列表
pxItemToRemove->pvContainer = NULL;
//有效列表项数量减一
( pxList->uxNumberOfItems )--;
//返回当前列表的有效列表项数量
return pxList->uxNumberOfItems;
}
FreeRTOS操作列表接口
深入理解调度器实现原理