列表和列表項
列表
列表是FreeRTOS中的一個數據結構,概念上和鏈表有點類型,是一個循環雙向鏈表,列表被用來跟蹤FreeRTOS中的任務。列表的類型是List_T,具體定義如下:
typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */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. */listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
- listFIRST_LIST_INTEGRITY_CHECK_VALUE和listSECOND_LIST_INTEGRITY_CHECK_VALUE都是用來檢查列表完整性的,需要將宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 設置為1,默認不開啟。
- uxNumberOfItems:記錄列表項的數量
- pxIndex:指向當前的列表項,可用來遍歷列表,類型是ListItem_t *
- xListEnd:作為一個標記,表示列表最后一個列表項,類型是MiniListItem_t 。
列表項
FreeRTOS提供了兩種列表項:列表項(ListItem_t 類型)和迷你列表項(MiniListItem_t 類型)。對于列表項,具體定義為:
struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */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). */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
- listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE檢查列表項完整性
- xItemValue:列表項的值
- pxNext:指向下一個列表項
- pxPrevious:指向前一個列表項
- pvOwner:記錄此列表項歸誰擁有,通常是任務控制塊
- pvContainer:記錄該列表項歸哪個列表
迷你列表項:
struct xMINI_LIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */configLIST_VOLATILE TickType_t xItemValue;struct xLIST_ITEM * configLIST_VOLATILE pxNext;struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
可以看出迷你列表項只是比列表項少了幾個成員變量,迷你列表項所有的成員變量列表項都有。
對于列表結構體List_t中的xListEnd是MiniListItem_t類型,表示最后一個列表項,pxIndex是ListItem_t指針類型,指向真正有數據的列表項。
列表和列表項初始化
列表初始化
新創建的或者定義的列表需要對其做初始化處理,列表初始化其實就是初始化列表結構體List_t中的各個成員變量,列表的初始化通過函數vListInitialise()來完成。
void vListInitialise( List_t * const pxList )
{/* The list structure contains a list item which is used to mark theend of the list. To initialise the list the list end is insertedas the only list entry. */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. *//* The list end value is the highest possible value in the list toensure it remains at the end of the list. */pxList->xListEnd.xItemValue = portMAX_DELAY;/* The list end next and previous pointers point to itself so we knowwhen the list is empty. */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. */pxList->uxNumberOfItems = ( UBaseType_t ) 0U;/* Write known values into the list ifconfigUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
函數的參數是一個列表
- pxIndex 指向強制類型轉換的xListEnd
- xItemValue 列表項的值為portMAX_DELAY
- pxNext :指向自己
- pxPrevious :指向自己
- uxNumberOfItems :列表中的列表項數目為0
下圖為初始化后的列表
列表項初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{/* Make sure the list item is not recorded as being on a list. */pxItem->pvContainer = NULL;/* Write known values into the list item ifconfigUSE_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 );
}
函數的參數是一個列表項指針,只是將列表項的pvContainer初始化為NULL
下圖為列表項初始后的列表項
列表項插入
列表項插入相當于和在循環雙向鏈表中按照數值的遞增插入數據原理是一樣的。
列表項的插入式通過函數vListInsert來完成的
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
參數:
- pxList:要插入的列表
- pxNewListItem :要插入的列表項
vListInsert是根據pxNewListItem 中的成員變量xItemValue的值來決定插入位置。根據xItemValue的升序方式排序。
具體插入過程如下:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/* Only effective when configASSERT() is also defined, these tests may catchthe list data structures being overwritten in memory. They will not catchdata errors caused by incorrect configuration or use of FreeRTOS. */listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );/* Insert the new list item into the list, sorted in xItemValue order.If the list already contains a list item with the same item value then thenew list item should be placed after it. This ensures that TCB's which arestored in ready lists (all of which have the same xItemValue value) get ashare of the CPU. However, if the xItemValue is the same as the back markerthe iteration loop below will not end. Therefore the value is checkedfirst, and the algorithm slightly modified if necessary. */if( xValueOfInsertion == portMAX_DELAY ){pxIterator = pxList->xListEnd.pxPrevious;}else{/* *** NOTE ***********************************************************If you find your application is crashing here then likely causes arelisted below. In addition see http://www.freertos.org/FAQHelp.html formore tips, and ensure configASSERT() is defined!http://www.freertos.org/a00110.html#configASSERT1) Stack overflow -see http://www.freertos.org/Stacks-and-stack-overflow-checking.html2) Incorrect interrupt priority assignment, especially on Cortex-Mparts where numerically high priority values denote low actualinterrupt priorities, which can seem counter intuitive. Seehttp://www.freertos.org/RTOS-Cortex-M3-M4.html and the definitionof configMAX_SYSCALL_INTERRUPT_PRIORITY onhttp://www.freertos.org/a00110.html3) Calling an API function from within a critical section or whenthe scheduler is suspended, or calling an API function that doesnot end in "FromISR" from an interrupt.4) Using a queue or semaphore before it has been initialised orbefore the scheduler has been started (are interrupts firingbefore vTaskStartScheduler() has been called?).**********************************************************************/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. */{/* There is nothing to do here, just iterating to the wantedinsertion position. */}}pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;/* Remember which list the item is in. This allows fast removal of theitem later. */pxNewListItem->pvContainer = ( void * ) pxList;( pxList->uxNumberOfItems )++;
}
- 獲取pxNewListItem的xItemValue值
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
- 檢查列表和列表項的完整性
listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
- 判斷插入的位置,如果等于portMAX_DELAY ,列表項的最大值,插入的位置是列表最末尾
if( xValueOfInsertion == portMAX_DELAY ){pxIterator = pxList->xListEnd.pxPrevious;}
- 不等于portMAX_DELAY ,則for循環找到插入位置,這個查找過程是按照升序的方式查找列表項插入點的,列表的xListEnd 可以想成鏈表的頭,不放數據,方便查詢用的,xListEnd 指向的列表項的xItemValue 值最小
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
- 將列表項插入到列表中
pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;/* Remember which list the item is in. This allows fast removal of theitem later. */pxNewListItem->pvContainer = ( void * ) pxList;
- 列表的列表項數目加1
( pxList->uxNumberOfItems )++;
列表項插入過程
一個初始化的空列表如下:
插入值為40的列表項后
插入值60的列表項
插入50后的列表項為
列表末尾插入
末尾插入就不根據xItemValue了,直接插入末端。原理和在循環雙向鏈表的末尾插入數據是一樣的
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
列表項的刪除
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );
- pxItemToRemove :要刪除的列表項
列表的遍歷
列表結構體中的pxIndex是用來遍歷鏈表的,在說列表項插入的時候,也用到了列表的遍歷,具體代碼如下:
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
FreeRTOS提供了一個函數來完成列表的遍歷,這個函數是listGET_OWNER_OF_NEXT_ENTRY。每調用一次該函數pxIndex變量就會指向下一個列表項,并且返回這個列表項的pxOwner變量值
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
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; \if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \{ \( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \} \( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
pxTCB用來保存pxIndex所指向的列表項pvOwner變量值。pxList是要遍歷的列表
將pxIndex指向下一個列表項
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
如果指向的列表項是xListEnd ,表示已經到了列表末尾,然后跳過末尾,再一次重新指向列表的第一個列表項。
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \{ \( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \}
將pxIndex所指向的新列表項的pvOwner賦值給pxTCB
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
列表項的插入和刪除實驗
實驗設計,三個任務:
start_task:創建其他兩個任務
task1_task:應用任務1,控制LED0閃爍,用來提示系統正在運行
task2_task:列表和列表項操作任務,調用列表和列表相關的API,并且通過串口輸出相應的信息來觀察這些API函數的運行過程。
任務設置
#define START_STACK_SIZE 128
#define START_TASK_PIO 1
TaskHandle_t Start_Handler;
void start_task(void * pvParameters);#define TASK1_STACK_SIZE 128
#define TASK1_TASK_PIO 2
TaskHandle_t Task1_Handler;
void task1_task(void * pvParameters);#define TASK2_STACK_SIZE 128
#define TASK2_TASK_PIO 3
TaskHandle_t Task2_Handler;
void task2_task(void * pvParameters);
列表項和列表的定義
//定義一個測試用的列表和是哪個列表項
List_t TestList;
ListItem_t ListItem1;
ListItem_t ListItem2;
ListItem_t ListItem3;
main函數
int main(void)
{HAL_Init(); //初始化HAL庫 Stm32_Clock_Init(360,25,2,8); //設置時鐘,180Mhzdelay_init(180); //初始化延時函數uart_init(115200); //初始化串口LED_Init(); //初始化LED KEY_Init(); //初始化按鍵SDRAM_Init(); //初始化SDRAMLCD_Init(); //初始化LCDPOINT_COLOR = RED;LCD_ShowString(30,10,200,16,16,"Apollo STM32F4/F7"); LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 7-1");LCD_ShowString(30,50,200,16,16,"list and listItem");LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,90,200,16,16,"2016/10/9");//創建開始任務xTaskCreate(start_task,"start_task",START_STACK_SIZE,NULL,START_TASK_PIO,&Start_Handler);vTaskStartScheduler();
}
任務函數
//開始任務任務函數
void start_task(void * pvParameters)
{taskENTER_CRITICAL(); //進入臨界區//創建任務xTaskCreate(task1_task,"task1_task",TASK1_STACK_SIZE,NULL,TASK1_TASK_PIO,&Task1_Handler);xTaskCreate(task2_task,"task1_task",TASK2_STACK_SIZE,NULL,TASK2_TASK_PIO,&Task2_Handler);vTaskDelete(Start_Handler);//退出臨界區taskEXIT_CRITICAL();
}//task1任務函數
void task1_task(void * pvParameters)
{while(1){LED0 = !LED0;vTaskDelay(500);}
}
//list任務函數
void task2_task(void * pvParameters)
{//初始化列表和列表項vListInitialise(&TestList);vListInitialiseItem(&ListItem1);vListInitialiseItem(&ListItem2);vListInitialiseItem(&ListItem3);ListItem1.xItemValue=40;ListItem2.xItemValue = 60;ListItem3.xItemValue=50;//第二步:打印列表和其他列表項的地址printf("/*******************列表和列表項地址*******************/\r\n");printf("項目 地址 \r\n");printf("TestList %#x \r\n",(int)&TestList);printf("TestList->pxIndex %#x \r\n",(int)TestList.pxIndex);printf("TestList->xListEnd %#x \r\n",(int)(&TestList.xListEnd));printf("ListItem1 %#x \r\n",(int)&ListItem1);printf("ListItem2 %#x \r\n",(int)&ListItem2);printf("ListItem3 %#x \r\n",(int)&ListItem3);printf("/************************結束**************************/\r\n");printf("按下KEY_UP鍵繼續!\r\n\r\n\r\n");while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //第三步:向列表TestList添加列表項ListItem1,并通過串口打印所有//列表項中成員變量pxNext和pxPrevious的值,通過這兩個值觀察列表//項在列表中的連接情況。vListInsert(&TestList,&ListItem1); //插入列表項ListItem1printf("/******************添加列表項ListItem1*****************/\r\n");printf("項目 地址 \r\n");printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));printf("/*******************前后向連接分割線********************/\r\n");printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));printf("/************************結束**************************/\r\n");printf("按下KEY_UP鍵繼續!\r\n\r\n\r\n");while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //第四步:向列表TestList添加列表項ListItem2,并通過串口打印所有//列表項中成員變量pxNext和pxPrevious的值,通過這兩個值觀察列表//項在列表中的連接情況。vListInsert(&TestList,&ListItem2); //插入列表項ListItem2printf("/******************添加列表項ListItem2*****************/\r\n");printf("項目 地址 \r\n");printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));printf("ListItem2->pxNext %#x \r\n",(int)(ListItem2.pxNext));printf("/*******************前后向連接分割線********************/\r\n");printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));printf("ListItem2->pxPrevious %#x \r\n",(int)(ListItem2.pxPrevious));printf("/************************結束**************************/\r\n");printf("按下KEY_UP鍵繼續!\r\n\r\n\r\n");while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //第五步:向列表TestList添加列表項ListItem3,并通過串口打印所有//列表項中成員變量pxNext和pxPrevious的值,通過這兩個值觀察列表//項在列表中的連接情況。vListInsert(&TestList,&ListItem3); //插入列表項ListItem3printf("/******************添加列表項ListItem3*****************/\r\n");printf("項目 地址 \r\n");printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));printf("ListItem3->pxNext %#x \r\n",(int)(ListItem3.pxNext));printf("ListItem2->pxNext %#x \r\n",(int)(ListItem2.pxNext));printf("/*******************前后向連接分割線********************/\r\n");printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));printf("ListItem3->pxPrevious %#x \r\n",(int)(ListItem3.pxPrevious));printf("ListItem2->pxPrevious %#x \r\n",(int)(ListItem2.pxPrevious));printf("/************************結束**************************/\r\n");printf("按下KEY_UP鍵繼續!\r\n\r\n\r\n");while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //第六步:刪除ListItem2,并通過串口打印所有列表項中成員變量pxNext和//pxPrevious的值,通過這兩個值觀察列表項在列表中的連接情況。uxListRemove(&ListItem2); //刪除ListItem2printf("/******************刪除列表項ListItem2*****************/\r\n");printf("項目 地址 \r\n");printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));printf("ListItem3->pxNext %#x \r\n",(int)(ListItem3.pxNext));printf("/*******************前后向連接分割線********************/\r\n");printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));printf("ListItem3->pxPrevious %#x \r\n",(int)(ListItem3.pxPrevious));printf("/************************結束**************************/\r\n");printf("按下KEY_UP鍵繼續!\r\n\r\n\r\n");while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //第七步:刪除ListItem2,并通過串口打印所有列表項中成員變量pxNext和//pxPrevious的值,通過這兩個值觀察列表項在列表中的連接情況。TestList.pxIndex=TestList.pxIndex->pxNext; //pxIndex向后移一項,這樣pxIndex就會指向ListItem1。vListInsertEnd(&TestList,&ListItem2); //列表末尾添加列表項ListItem2printf("/***************在末尾添加列表項ListItem2***************/\r\n");printf("項目 地址 \r\n");printf("TestList->pxIndex %#x \r\n",(int)TestList.pxIndex);printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));printf("ListItem2->pxNext %#x \r\n",(int)(ListItem2.pxNext));printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));printf("ListItem3->pxNext %#x \r\n",(int)(ListItem3.pxNext));printf("/*******************前后向連接分割線********************/\r\n");printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));printf("ListItem2->pxPrevious %#x \r\n",(int)(ListItem2.pxPrevious));printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));printf("ListItem3->pxPrevious %#x \r\n",(int)(ListItem3.pxPrevious));printf("/************************結束**************************/\r\n\r\n\r\n");while(1){LED1=!LED1;vTaskDelay(1000); //延時1s,也就是1000個時鐘節拍 }
}