FreeRTOS的列表和列表项
列表和列表項
列表
列表是FreeRTOS中的一個數(shù)據(jù)結(jié)構(gòu),概念上和鏈表有點類型,是一個循環(huán)雙向鏈表,列表被用來跟蹤FreeRTOS中的任務(wù)。列表的類型是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 設(shè)置為1,默認(rèn)不開啟。
- uxNumberOfItems:記錄列表項的數(shù)量
- pxIndex:指向當(dāng)前的列表項,可用來遍歷列表,類型是ListItem_t *
- xListEnd:作為一個標(biāo)記,表示列表最后一個列表項,類型是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:記錄此列表項歸誰擁有,通常是任務(wù)控制塊
- 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;可以看出迷你列表項只是比列表項少了幾個成員變量,迷你列表項所有的成員變量列表項都有。
對于列表結(jié)構(gòu)體List_t中的xListEnd是MiniListItem_t類型,表示最后一個列表項,pxIndex是ListItem_t指針類型,指向真正有數(shù)據(jù)的列表項。
列表和列表項初始化
列表初始化
新創(chuàng)建的或者定義的列表需要對其做初始化處理,列表初始化其實就是初始化列表結(jié)構(gòu)體List_t中的各個成員變量,列表的初始化通過函數(shù)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 ); }函數(shù)的參數(shù)是一個列表
- pxIndex 指向強(qiáng)制類型轉(zhuǎn)換的xListEnd
- xItemValue 列表項的值為portMAX_DELAY
- pxNext :指向自己
- pxPrevious :指向自己
- uxNumberOfItems :列表中的列表項數(shù)目為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 ); }函數(shù)的參數(shù)是一個列表項指針,只是將列表項的pvContainer初始化為NULL
下圖為列表項初始后的列表項
列表項插入
列表項插入相當(dāng)于和在循環(huán)雙向鏈表中按照數(shù)值的遞增插入數(shù)據(jù)原理是一樣的。
列表項的插入式通過函數(shù)vListInsert來完成的
參數(shù):
- pxList:要插入的列表
- pxNewListItem :要插入的列表項
vListInsert是根據(jù)pxNewListItem 中的成員變量xItemValue的值來決定插入位置。根據(jù)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 )++; }列表項插入過程
一個初始化的空列表如下:
插入值為40的列表項后
插入值60的列表項
插入50后的列表項為
列表末尾插入
末尾插入就不根據(jù)xItemValue了,直接插入末端。原理和在循環(huán)雙向鏈表的末尾插入數(shù)據(jù)是一樣的
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )列表項的刪除
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );- pxItemToRemove :要刪除的列表項
列表的遍歷
列表結(jié)構(gòu)體中的pxIndex是用來遍歷鏈表的,在說列表項插入的時候,也用到了列表的遍歷,具體代碼如下:
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )FreeRTOS提供了一個函數(shù)來完成列表的遍歷,這個函數(shù)是listGET_OWNER_OF_NEXT_ENTRY。每調(diào)用一次該函數(shù)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指向下一個列表項
如果指向的列表項是xListEnd ,表示已經(jīng)到了列表末尾,然后跳過末尾,再一次重新指向列表的第一個列表項。
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \{ \( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \}將pxIndex所指向的新列表項的pvOwner賦值給pxTCB
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;列表項的插入和刪除實驗
實驗設(shè)計,三個任務(wù):
start_task:創(chuàng)建其他兩個任務(wù)
task1_task:應(yīng)用任務(wù)1,控制LED0閃爍,用來提示系統(tǒng)正在運行
task2_task:列表和列表項操作任務(wù),調(diào)用列表和列表相關(guān)的API,并且通過串口輸出相應(yīng)的信息來觀察這些API函數(shù)的運行過程。
任務(wù)設(shè)置
#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函數(shù)
int main(void) {HAL_Init(); //初始化HAL庫 Stm32_Clock_Init(360,25,2,8); //設(shè)置時鐘,180Mhzdelay_init(180); //初始化延時函數(shù)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");//創(chuàng)建開始任務(wù)xTaskCreate(start_task,"start_task",START_STACK_SIZE,NULL,START_TASK_PIO,&Start_Handler);vTaskStartScheduler(); }任務(wù)函數(shù)
//開始任務(wù)任務(wù)函數(shù) void start_task(void * pvParameters) {taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)//創(chuàng)建任務(wù)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);//退出臨界區(qū)taskEXIT_CRITICAL(); }//task1任務(wù)函數(shù) void task1_task(void * pvParameters) {while(1){LED0 = !LED0;vTaskDelay(500);} } //list任務(wù)函數(shù) 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("/************************結(jié)束**************************/\r\n");printf("按下KEY_UP鍵繼續(xù)!\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("/************************結(jié)束**************************/\r\n");printf("按下KEY_UP鍵繼續(xù)!\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("/************************結(jié)束**************************/\r\n");printf("按下KEY_UP鍵繼續(xù)!\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("/************************結(jié)束**************************/\r\n");printf("按下KEY_UP鍵繼續(xù)!\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("/************************結(jié)束**************************/\r\n");printf("按下KEY_UP鍵繼續(xù)!\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("/************************結(jié)束**************************/\r\n\r\n\r\n");while(1){LED1=!LED1;vTaskDelay(1000); //延時1s,也就是1000個時鐘節(jié)拍 } }總結(jié)
以上是生活随笔為你收集整理的FreeRTOS的列表和列表项的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FreeRTOS中断配置与临界段
- 下一篇: FreeRTOS时间管理