1.列表
- 列表是FreeRTOSde中的一个数据结构,概念上和链表雷士,列表被用来跟踪FreeRTOS中的任务,与列表相关的全放在list.c和list.h中,在list.h中定义了一个结构体如下:
typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE //用来检查列表的完整性 configLIST_VOLATILE UBaseType_t uxNumberOfItems; //用来记录列表中列表项的数量 ListItem_t * configLIST_VOLATILE pxIndex; //用来记录当前列表项索引号,用于遍历列表 MiniListItem_t xListEnd; //列表中最后一个列表项,用来表示列表结束,,这是一个mini列表项 listSECOND_LIST_INTEGRITY_CHECK_VALUE //用来检查列表的完整性 } List_t;
2.列表项
- 列表项就是存放在列表中的项目,FreeRTOS提供了两种列表项:列表项和迷你列表项,定义在list.h文件中;
- 列表项,跟双链表相似
struct xLIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE //检查列表的完整性 configLIST_VOLATILE TickType_t xItemValue; //列表项值 struct xLIST_ITEM * configLIST_VOLATILE pxNext; //pxNext指向下一个列表项 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //pxPrevious指向前一个列表项 void * pvOwner; //记录此列表项归谁拥有,通常是任务控制块 void * configLIST_VOLATILE pvContainer; //用来记录此列表项归哪个列表 listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE //检查列表的完整性 }; typedef struct xLIST_ITEM ListItem_t;
- 迷你列表项,定义在list.h文件中,如下:
struct xMINI_LIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE //检查迷你列表项的完整性 configLIST_VOLATILE TickType_t xItemValue; //记录列表项的值 struct xLIST_ITEM * configLIST_VOLATILE pxNext; //pxNext指向下一个列表项 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; //pxPrevious指向上一个列表项 }; typedef struct xMINI_LIST_ITEM MiniListItem_t;
- 列表项和迷你列表项的区别:迷你列表项别列表项少了几个成员变量;在有些情况下我们不需要列表项这么全的功能,可能只需其中的几个成员变量,如果此时使用列表项的话会造成内存浪费;
3.列表初始化
- 列表的初始化其实是初始化列表结构体List_t中的各个成员变量,列表通过vListInitialse()来完成,定于于list.c中
void vListInitialise( List_t * const pxList ) { pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); //xListEnd用来表示列表的末尾,pxIndex表示列表项的索引号 pxList->xListEnd.xItemValue = portMAX_DELAY; //xListEnd根据MCU不同,值也不同,stm32为32位,所以 XListEnd为oxffffffffUL pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); //初始化列表项xListEnd的pxNext变量 pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );//初始化列表项xListEnd的pxPrevious变量,指向xListEnd自身 pxList->uxNumberOfItems = ( UBaseType_t ) 0U; //此时没有其他列表项,因此值为0(这里没有算xListEnd) listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); //初始化列表项中用于完整性检查的字段,不同MCU值不同,32位系统为0x5a5a5a5aUL listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); //初始化列表项中用于完整性检查的字段,不同MCU值不同,32位系统为0x5a5a5a5aUL }
4.列表项初始化
- 列表项的初始化是通过vListInitialiseItem()来完成,定于于list.c中
void vListInitialiseItem( ListItem_t * const pxItem ) { pxItem->pvContainer = NULL; listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );}
- 列表项的初始化比较简单,只需要将pvContainer的初始值设为NULL,并且给完整性检查的变量赋值即可;
5.列表项插入
- 列表项插入分为:头插入和尾插入。
头插入
- 通过函数VListInert()来完成,函数原型为:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) //pxList:需要插入的列表, pxNewListItem:需要插入的列表项
- 列表项的插入过程如图所示:
尾插入
- 通过函数vListInsertEnd()来实现,函数原型为:
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) //pxList:列表项要插入的列表, pxNewListItem:要插入的列表项
- 尾插入过程如下图所示:
6.列表项的删除
- 列表项的删除是通过uxListRemove()函数来完成的,函数原型为:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) //pxItemToRemove:要删除的列表项
7.列表的遍历
- 列表的遍历是通过listGET_OWNER_OF_NEXT_ENTRY()函数来实现的,每调用一次这个函数,列表的pxIndex变量就会指向下一个列表项,并返回这个列表项的pxOwner变量值,函数原型如下:
//pxTCB:用来保存pxIndex所指向的列表项的pvOwner变量值 //pxList:变量指向下一个列表项 #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
8.程序验证
- 部分程序如下所示:
//list_task 任务 void list_task(void *pvParameters); // 任务函数 #define list_task_zise 300 // 任务堆栈的大小 #define list_task_prio 3 // 任务优先级 TaskHandle_t list_task_handler; // 任务句柄 // 定义一个列表和一个列表项 List_t test_list; //定义一个列表 ListItem_t test_listItem1; //定义一个列表项test_listItem1 ListItem_t test_listItem2; //定义一个列表项test_listItem2 ListItem_t test_listItem3; //定义一个列表项test_listItem3 void start_task( void * pvParameters ) { taskENTER_CRITICAL(); // 创建临界区 // 创建led1任务 xTaskCreate((TaskFunction_t )led1_task, (const char * )"led1_task", (uint16_t )start_task_zise, (void * )NULL, (UBaseType_t )led1_task_prio, (TaskHandle_t * )&led1_task_handler); // 创建led2任务 xTaskCreate((TaskFunction_t )list_task, (const char * )"list_task", (uint16_t )list_task_zise, (void * )NULL, (UBaseType_t )list_task_prio, (TaskHandle_t * )&list_task_handler); vTaskDelete(start_task_handler); //删除开始任务 taskEXIT_CRITICAL(); // 退出临界区 } //list任务函数 void list_task( void * pvParameters ) { vListInitialise(&test_list); //初始化列表 vListInitialiseItem(&test_listItem1); //初始化列表项 vListInitialiseItem(&test_listItem2); //初始化列表项 vListInitialiseItem(&test_listItem3); //初始化列表项 test_listItem1.xItemValue = 40; //设置test_listItem1列表项值为40 test_listItem2.xItemValue = 50; //设置test_listItem2列表项值为50 test_listItem3.xItemValue = 60; //设置test_listItem3列表项值为60 // 打印列表和其他列表项的地址 printf("/*******************列表和列表项地址*******************/\r\n"); printf("项目 地址 \r\n"); printf("test_list %#x \r\n",(int)&test_list); printf("test_list->pxIndex %#x \r\n",(int)test_list.pxIndex); printf("test_list->xListEnd %#x \r\n",(int)(&test_list.xListEnd)); printf("test_listItem1 %#x \r\n",(int)&test_listItem1); printf("test_listItem2 %#x \r\n",(int)&test_listItem2); printf("test_listItem3 %#x \r\n",(int)&test_listItem3); printf("/************************结束**************************/\r\n\r\n"); vListInsert(&test_list, &test_listItem1); //插入列表项test_listItem1 vListInsert(&test_list, &test_listItem2); //插入列表项test_listItem2 vListInsert(&test_list,&test_listItem3); //插入列表项test_listItem3 printf("/******************添加列表项test_listItem123*****************/\r\n"); printf("项目 地址 \r\n"); printf("test_list->xListEnd->pxNext %#x \r\n",(int)(test_list.xListEnd.pxNext)); printf("test_listItem1->pxNext %#x \r\n",(int)(test_listItem1.pxNext)); printf("test_listItem2->pxNext %#x \r\n",(int)(test_listItem2.pxNext)); printf("test_listItem3->pxNext %#x \r\n",(int)(test_listItem3.pxNext)); printf("/*******************前后向连接分割线********************/\r\n"); printf("test_list->xListEnd->pxPrevious %#x \r\n",(int)(test_list.xListEnd.pxPrevious)); printf("test_listItem1->pxPrevious %#x \r\n",(int)(test_listItem1.pxPrevious)); printf("test_listItem2->pxPrevious %#x \r\n",(int)(test_listItem2.pxPrevious)); printf("test_listItem3->pxPrevious %#x \r\n",(int)(test_listItem3.pxPrevious)); printf("/************************结束**************************/\r\n\r\n"); uxListRemove(&test_listItem2); //删除列表项test_listItem2 printf("/******************删除列表项test_listItem2*****************/\r\n"); printf("项目 地址 \r\n"); printf("test_list->xListEnd->pxNext %#x \r\n",(int)(test_list.xListEnd.pxNext)); printf("test_listItem1->pxNext %#x \r\n",(int)(test_listItem1.pxNext)); printf("test_listItem3->pxNext %#x \r\n",(int)(test_listItem3.pxNext)); printf("/*******************前后向连接分割线********************/\r\n"); printf("test_list->xListEnd->pxPrevious %#x \r\n",(int)(test_list.xListEnd.pxPrevious)); printf("test_listItem1->pxPrevious %#x \r\n",(int)(test_listItem1.pxPrevious)); printf("test_listItem3->pxPrevious %#x \r\n",(int)(test_listItem3.pxPrevious)); printf("/************************结束**************************/\r\n\r\n"); test_list.pxIndex=test_list.pxIndex->pxNext; //pxIndex向后移一项,这样pxIndex就会指向ListItem1。 vListInsertEnd(&test_list,&test_listItem2); //列表末尾添加列表项ListItem2 printf("/******************添加列表项test_listItem2*****************/\r\n"); printf("项目 地址 \r\n"); printf("test_list->xListEnd->pxNext %#x \r\n",(int)(test_list.xListEnd.pxNext)); printf("test_listItem2->pxNext %#x \r\n",(int)(test_listItem2.pxNext)); printf("test_listItem1->pxNext %#x \r\n",(int)(test_listItem1.pxNext)); printf("test_listItem3->pxNext %#x \r\n",(int)(test_listItem3.pxNext)); printf("/*******************前后向连接分割线********************/\r\n"); printf("test_list->xListEnd->pxPrevious %#x \r\n",(int)(test_list.xListEnd.pxPrevious)); printf("test_listItem2->pxPrevious %#x \r\n",(int)(test_listItem2.pxPrevious)); printf("test_listItem1->pxPrevious %#x \r\n",(int)(test_listItem1.pxPrevious)); printf("test_listItem3->pxPrevious %#x \r\n",(int)(test_listItem3.pxPrevious)); printf("/************************结束**************************/\r\n\r\n"); }
- 打印信息为:
/*******************列表和列表项地址*******************/ 项目 地址 test_list 0x200000b4 test_list->pxIndex 0x200000bc test_list->xListEnd 0x200000bc test_listItem1 0x200000c8 test_listItem2 0x200000dc test_listItem3 0x200000f0 /************************结束**************************/ /******************添加列表项test_listItem123*****************/ 项目 地址 test_list->xListEnd->pxNext 0x200000c8 test_listItem1->pxNext 0x200000dc test_listItem2->pxNext 0x200000f0 test_listItem3->pxNext 0x200000bc /*******************前后向连接分割线********************/ test_list->xListEnd->pxPrevious 0x200000f0 test_listItem1->pxPrevious 0x200000bc test_listItem2->pxPrevious 0x200000c8 test_listItem3->pxPrevious 0x200000dc /************************结束**************************/ /******************删除列表项test_listItem2*****************/ 项目 地址 test_list->xListEnd->pxNext 0x200000c8 test_listItem1->pxNext 0x200000f0 test_listItem3->pxNext 0x200000bc /*******************前后向连接分割线********************/ test_list->xListEnd->pxPrevious 0x200000f0 test_listItem1->pxPrevious 0x200000bc test_listItem3->pxPrevious 0x200000c8 /************************结束**************************/ /******************添加列表项test_listItem2*****************/ 项目 地址 test_list->xListEnd->pxNext 0x200000dc test_listItem2->pxNext 0x200000c8 test_listItem1->pxNext 0x200000f0 test_listItem3->pxNext 0x200000bc /*******************前后向连接分割线********************/ test_list->xListEnd->pxPrevious 0x200000f0 test_listItem2->pxPrevious 0x200000bc test_listItem1->pxPrevious 0x200000dc test_listItem3->pxPrevious 0x200000c8 /************************结束**************************/