概述
今天继续来介绍FreeRTOS中的列表和列表项。主要是介绍列表和列表项的API。
一、列表初始化
新创建或者定义的列表需要对其做初始化处理,列表的初始化其实就是初始化列表结构体
List_t中的各个成员变量,列表的初始化通过使函数vListInitialise()来完成,此函数在list.c中有定义,函数如下:
void vListInitialise( List_t * const pxList )
{
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); (1)
pxList->xListEnd.xItemValue = portMAX_DELAY; (2)
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); (3)
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); (4)
pxList->uxNumberOfItems = ( UBaseType_t ) 0U; (5)
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); (6)
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); (7)
}
- (1)、xListEnd用来表示列表的末尾,而pxIndex表示列表项的索引号,此时列表只有一个列表项,那就是xListEnd,所以pxIndex指向xListEnd。
- (2)、xListEnd的列表项值初始化为portMAX_DELAY,portMAX_DELAY是个宏,在文件portmacro.h中有定义。根据所使用的MCU的不同,portMAX_DELAY值也不相同,可以为Oxffff或者0xffffffffUL。具体看对应的MCU的位数。
- (3)、初始化列表项xListEnd的pxNext变量,因为此时列表只有一个列表项xListEnd,因此pxNext只能指向自身。
- (4)、同(3)一样,初始化xListEnd的pxPrevious变量,指向xListEnd自身。
- (5)、由于此时没有其他的列表项,因此uxNumberOfItems为0,注意,这里没有算xListEnd。
- (6)和(7)、初始化列表项中用于完整性检查字段,只有宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES为1的时候才有效。同样的根据所选的MCU不同其写入的值也不同,可以为0×5a5a或者0x5a5a5a5aUL。STM32是32位系统写入的是0x5a5a5a5aUL。
列表初始化完以后如下图所示:
二、列表项初始化
和列表一样,列表项在使用的时候也需要初始化,列表项初始化由函数vListInitialiseItem()完成。函数如下:
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
//初始化pvContainer 为NULL
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//初始化用于完整性检查的变量
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
三、列表项插入
列表项的插入操作通过函数vListInsert()来完成,函数原型为:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; (1)
listTEST_LIST_INTEGRITY( pxList ); (2)
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
if( xValueOfInsertion == portMAX_DELAY ) (3)
{
pxIterator = pxList->xListEnd.pxPrevious; (4)
}
else
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); (5)
pxIterator->pxNext->xItemValue <= xValueOfInsertion;
pxIterator = pxIterator->pxNext )
{
//空循环
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
pxNewListItem->pxNext = pxIterator->pxNext; (6)
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
pxNewListItem->pvContainer = ( void * ) pxList; (7)
( pxList->uxNumberOfItems )++; (8)
}
其中形参中的pxList为列表项要插入的列表,pxNewListItem 为要插入的列表项
- (1)、获取要插入的列表项值,即列表项成员变量xItemValue的值,因为要根据这个值来确 定列表项要插入的位置。
- (2)、这一行和下一行代码用来检查列表和列表项的完整性的。其实就是检查列表和列表项中用于完整性检查的变量值是否被改变。
- (3)、要插入列表项,第一步就是要获取该列表项要插入到什么位置!如果要插入的列表项的值等于portMAX_DELAY,也就是说列表项值为最大值,这种情况最好办了,要插入的位置就是列表最末尾了。
- (4)、获取要插入点,注意!列表中的xListEnd用来表示列表末尾,在初始化列表的时候
xListEnd的列表值也是portMAX_DELAY,此时要插入的列表项的列表值也portMAX_DELAY通过这行代码可以看出要插入的列表项会被放到xListEnd前面。 - (5)、要插入的列表项的值如果不等于portMAX_DELAY那么就需要在列表中一个一个的找自己的位置,这个for循环就是找位置的过程,当找到合适列表项的位置的时候就会跳出。由于这个for循环是用来寻找列表项插入点的,所以for循环体里面没有任何东西。这个查找过程是按照升序的方式查找列表项插入点的。
- (6)、经过上面的查找,已经找到列表项的插入点了,**从本行开始接下来的四行代码就是将列表项插入到列表中,插入过程和数据结构中双向链表的插入类似。**像FreeRTOS这种RTOS系统和一些协议栈都会大量用到数据结构的知识。
- (7)、列表项已经插入到列表中了,那么列表项的成员变量pvContainer也该记录此列表项属于哪个列表的了。
- (8)、列表的成员变量uxNumberOfltems加1,表示又添加了一个列表项。
四、列表项末尾插入
列表项末尾插入列表项的操作通过函数vListInsertEnd()完成。函数原型如下:
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
listTEST_LIST_INTEGRITY( pxList ); (1)
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
pxNewListItem->pxNext = pxIndex; (2)
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
pxNewListItem->pvContainer = ( void * ) pxList; (3)
( pxList->uxNumberOfItems )++; (4)
}
其中形参中的pxList为列表项要插入的列表,pxNewListItem 为要插入的列表项。
- (1)、与下面的一行代码完成对列表和列表项的完整性检查。
- (2)、从本行开始到(3)之间的代码就是将要插入的列表项插入到列表末尾。使用函数
VListInsert()向列表中插入一个列表项的时候这个列表项的位置是通过列表项的值,也就是列表项成员变量xItemValue来确定。vListInsertEnd()是往列表的末尾添加列表项的,我们知道列表中的xListEnd成员变量表示列表末尾的,那么函数vListInsertEnd()插入一个列表项是不是就是插到xListEnd的前面或后面啊?这个是不一定的,这里所谓的末尾要根据列表的成员变量pxIndex来确定的。 前面说了列表中的pxIndex成员变量是用来遍历列表的,pxIndex所指向的列表项就是要遍历的开始列表项,也就是说pxIndex所指向的列表项就代表列表头!由于是个环形列表,所以新的列表项就应该插入到pxIndex所指向的列表项的前面。 - (3)、标记新的列表项pxNewListItem属于列表pxList。
- (4)、记录列表中的列表项数目的变量加1,更新列表项数目。
五、列表项的删除
有列表项的插入,自然就有列表项的删除,列表项的删除是通过函数uxListRemove()完成的。函数原型如下:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; (1)
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; (2)
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
mtCOVERAGE_TEST_DELAY();
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious; (3)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pvContainer = NULL; (4)
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems; (5)
}
其中函数的形参pxItemToRemove 为要删除的列表项,返回值为删除列表项以后列表剩余的列表项数目。
- (1)、要删除一个列表项得先知道这个列表项处于哪个列表中,直接读取列表项中的成员变量pvContainer就可以得到此列表项处于哪个列表中。
- (2)、与下面一行完成列表项的删除,其实就是将要删除的列表项的前后两个列表项"连接" 在一起。
- (3)、如果列表的pxIndex正好指向要删除的列表项,那么在删除列表项以后要重新给pxIndex找个"对象",这个新的对象就是被删除的列表项的前一个列表项。
- (4)、被删除列表项的成员变量pvContainer清零。
- (5)、返回新列表的当前列表项数目。
六、列表项的遍历
介绍列表结构体的时候说过列表List_t中的成员变量pxIndex是用来遍历列表的,FreeRTOS
提供了一个函数来完成列表的遍历,这个函数是listGET_OWNER_OF_NEXT_ENTRY()。每调用一次这个函数列表的pxIndex变量就会指向下一个列表项,并且返回这个列表项的pxOwner变量值。这个函数本质上是一个宏,这个宏在文件list.h中如下定义:
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) (1)
{
List_t * const pxConstList = ( pxList );
/* Increment the index to the next item and return the item, ensuring */
/* we don't return the marker used at the end of the list. */
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; (2)
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) (3)
{
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; (4)
}
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; (5)
}
- (1)、pxTCB用来保存pxIndex所指向的列表项的pvOwner变量值,也就是这个列表项属于谁的?通常是一个任务的任务控制块。pxList表示要遍历的列表。
- (2)、列表的pxIndex变量指向下一个列表项。
- (3)、如果pxIndex指向了列表的xListEnd成员变量,表示到了列表末尾。
- (4)、如果到了列表末尾的话就跳过xListEnd,pxIndex再一次重新指向处于列表头的列表 项,这样就完成了一次对列表的遍历。
- (5)、将pxIndex所指向的新列表项的pvOwner赋值给pxTCB。此函数用于从多个同优先级的就绪任务中查找下一个要运行的任务。
列表和列表项的API就介绍到这里啦!!!
最后
以上就是爱撒娇猫咪为你收集整理的FreeRTOS列表和列表项(2)的全部内容,希望文章能够帮你解决FreeRTOS列表和列表项(2)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复