延時列表的實現(xiàn))
在本章之前 為了實現(xiàn)任務(wù)的阻塞延時在任務(wù)控制塊(TCB)中內(nèi)置了一個延時變量xTicksToDelay。每當(dāng)任務(wù)需要延時的時候就初始化 xTicksToDelay 需要延時的時間 然后將任務(wù)掛起這里的掛起只是將任務(wù)在優(yōu)先級位圖表 uxTopReadyPriority 中對應(yīng)的位清零并不會將任務(wù)從就緒列表中刪除。一個32位的變量每一位就代表每一個優(yōu)先級假如25個優(yōu)先級的一個任務(wù)需要掛起怎么做的也是先將它25位清零就回去運行24位的這個任務(wù)如果恢復(fù)的時候就將25位置一。掛起的時候在更新時基的函數(shù)中就把任務(wù)中的xTicksToDelay的值自減為什么自減就是為了判斷是否到了時間如果不自減的話怎么判斷他有沒有到時間。如果到了時間就將該位 置一恢復(fù)該任務(wù)的運行。這種延時有什么缺點呢每次更新時基的時候都要把任務(wù)掃描一遍有很多任務(wù)時太耗時間也有一個優(yōu)點就是方便理解。這里我們慢慢過渡一下延時列表有什么作用它的實現(xiàn)有什么好處在FreeRTOS中有一個任務(wù)延時列表其實有兩個為了方便講解原理我們假設(shè)一個但它們的原理是一樣的當(dāng)任務(wù)需要延時的時候則先將任務(wù)掛起即先將任務(wù)從就緒列表刪除然后插入到任務(wù)延時列表同時更新下一個任務(wù)的解鎖時刻變量xNextTaskUnblockTime的值。xNextTaskUnblockTime的值等于系統(tǒng)時基計數(shù)器的值xTickCount加上任務(wù)需要延時的值xTicksToDelay。任務(wù)延時列表維護(hù)著一條雙向鏈表每個節(jié)點代表正在延時的任務(wù)節(jié)點按照延時時間大小做升序排列。每次在SysTick中斷就比較一下系統(tǒng)時基計數(shù)器的值xTickCount與下一個任務(wù)的解鎖時刻變量xNextTaskUnblockTime的值。假設(shè)當(dāng)前xTickCount100,也就是說現(xiàn)在系統(tǒng)已經(jīng)跑了100個Tick,此刻我們調(diào)用了延時的函數(shù)延時了50個Tick,那我應(yīng)該什么時候喚醒它10050150當(dāng)我系統(tǒng)跑到150個Tick的時候就把任務(wù)喚醒。所以xNextTaskUnblockTime150個Tick每次更新系統(tǒng)時基的時候就比較一下xTickCount的值有沒有到150假如現(xiàn)在到了110跟150作比較還是沒到所以也不用喚醒它假如150剛好到了那么就可以立即去喚醒一個阻塞的任務(wù)假如還有個任務(wù)延時100個Tick,那喚醒的時間是不是200那100個Tick沒到的時候200個Tick肯定也沒到。當(dāng)我喚醒150的時候要更新一下xNextTaskUnblockTime下一個可能也就200了那么系統(tǒng)會繼續(xù)跑160的時候比較也沒到到200的時候我就可以喚醒200的這個東西了。時間沒到的時候就單獨的記錄xTickCount的值就好了。FreeRTOS定義了兩個任務(wù)延時列表當(dāng)系統(tǒng)時基計數(shù)器xTickCount沒有溢出時用一條列表當(dāng)xTickCount溢出后用另外一條列表。簡單來說一條是正常的列表一條是溢出的列表。見下表(在task.c中定義)static List_t xDelayedTaskList1; static List_t xDelayedTaskList2; static List_t * volatile pxDelayedTaskList; static List_t * volatile pxOverflowDelayedTaskList;我們再來實現(xiàn)以下任務(wù)延時列表的初始化任務(wù)延時列表屬于任務(wù)列表的一種也是在prvInitialiseTaskLists()函數(shù)中初始化(在task.c中添加標(biāo)號1的內(nèi)容)/* 初始化任務(wù)相關(guān)的列表 */ void prvInitialiseTaskLists( void ) { UBaseType_t uxPriority; for( uxPriority ( UBaseType_t ) 0U; uxPriority ( UBaseType_t ) configMAX_PRIORITIES; uxPriority ) { vListInitialise( ( pxReadyTasksLists[ uxPriority ] ) ); } vListInitialise( xDelayedTaskList1 );//初始化列表1(1) vListInitialise( xDelayedTaskList2 );//初始化列表2(1) pxDelayedTaskList xDelayedTaskList1;//將延時列表指向列表1(1) pxOverflowDelayedTaskList xDelayedTaskList2;//將溢出列表指向列表2(1) }接下來我們再實現(xiàn)一個變量xNextTaskUnblockTime:他是一個在task.c中定義的一個靜態(tài)變量。還有一個溢出的計數(shù)我們一起COPY過來。static volatile TickType_t xNextTaskUnblockTime ( TickType_t ) 0U; static volatile BaseType_t xNumOfOverflows ( BaseType_t ) 0;當(dāng)系統(tǒng)計數(shù)器的值xTickCount的值與xNextTaskUnblockTime相等時就表示有任務(wù)延時到期了需要將該任務(wù)就緒。xNextTaskUnblockTime在vTaskStartScheduler()時初始化一個最大的值portMAX_DELAY放在創(chuàng)建空閑任務(wù)之后。見下面代碼/* 在task.c的vTaskStartScheduler()中添加 */ xNextTaskUnblockTime portMAX_DELAY; xTickCount ( TickType_t ) 0U;//是一個系統(tǒng)時間的計數(shù)值這里的portMAX_DELAY和xTickCount 我們之前都已經(jīng)提前聲明定義過。后面也是修改代碼的過程一步步接近目標(biāo)我們接著往下看1.修改vTaskDelay()需要將當(dāng)前任務(wù)插入到延時列表中在task.c中的vTaskDelay()中修改void vTaskDelay( const TickType_t xTicksToDelay ) { TCB_t *pxTCB NULL; /* 獲取當(dāng)前任務(wù)的TCB */ pxTCB pxCurrentTCB; /* 設(shè)置延時時間 */ pxTCB-xTicksToDelay xTicksToDelay; /* 將任務(wù)從就緒列表移除 */ //uxListRemove( ( pxTCB-xStateListItem ) ); // taskRESET_READY_PRIORITY( pxTCB-uxPriority ); /* 將任務(wù)插入到延時列表 */ prvAddCurrentTaskToDelayedList( xTicksToDelay ); /* 任務(wù)切換 */ taskYIELD(); }目前這個函數(shù)prvAddCurrentTaskToDelayedList()還沒有實現(xiàn)。我們實現(xiàn)一下//函數(shù)聲明 static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait)//一定要記得提前聲明一下 //函數(shù)定義 static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait) { TickType_t xTimeToWake; /* 獲取系統(tǒng)時基計數(shù)器xTickCount的值 */ const TickType_t xConstTickCount xTickCount; /* 將任務(wù)從就緒列表中移除 */ if(uxListRemove((pxCurrentTCB-xStateListItem)) (UBaseType_t)0) { /* 將任務(wù)在優(yōu)先級位圖中對應(yīng)的位清除 */ portRESET_READY_PRIORITY(pxCurrentTCB-uxPriority, uxTopReadyPriority); } /* 計算延時到期時系統(tǒng)時基計數(shù)器xTickCount的值是多少 */ xTimeToWake xConstTickCount xTicksToWait; //150 - 200 - 300 /* 將延時到期的值設(shè)置為節(jié)點的排序值 */ listSET_LIST_ITEM_VALUE((pxCurrentTCB - xStateListItem), xTimeToWake); //設(shè)置value值 /* 溢出 */ if(xTimeToWake xConstTickCount) { vListInser(pxOverflowDelayedTaskList, (pxCurrentTCB-xStateListItem)); } else /* 沒有溢出 */ { vListInser(pxDelayedTaskList, (pxCurrentTCB-xStateListItem)); /* 更新下一個任務(wù)解鎖時刻變量xNextTaskUnblockTime的值 */ if(xTimeToWake xNextTaskUnblockTime) { xNextTaskUnblockTime xTimeToWake; } } }2.修改xTaskIncrementTick():更新時基在更新的過程中判斷是否到期void xTaskIncrementTick( void ) { TCB_t * pxTCB; TickType_t xItemValue; const TickType_t xConstTickCount xTickCount 1; xTickCount xConstTickCount; /* 如果xConstTickCount溢出則切換延時列表 */ if( xConstTickCount ( TickType_t ) 0U ) { taskSWITCH_DELAYED_LISTS(); } /* 最近的延時任務(wù)延時到期 */ if( xConstTickCount xNextTaskUnblockTime ) { for( ;; ) { if( listLIST_IS_EMPTY( pxDelayedTaskList ) ! pdFALSE ) { /* 延時列表為空設(shè)置xNextTaskUnblockTime為可能的最大值 */ xNextTaskUnblockTime portMAX_DELAY; break; } else /* 延時列表不為空 */ { pxTCB ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); xItemValue listGET_LIST_ITEM_VALUE( ( pxTCB-xStateListItem ) ); /* 直到將延時列表中所有延時到期的任務(wù)移除才跳出for循環(huán) */ if( xConstTickCount xItemValue ) { xNextTaskUnblockTime xItemValue; break; } /* 將任務(wù)從延時列表移除消除等待狀態(tài) */ ( void ) uxListRemove( ( pxTCB-xStateListItem ) ); /* 將解除等待的任務(wù)添加到就緒列表 */ prvAddTaskToReadyList( pxTCB ); } } }/* xConstTickCount xNextTaskUnblockTime */ /* 任務(wù)切換 */ portYIELD(); }實現(xiàn)taskSWITCH_DELAYED_LISTS()切換延時列表實際就是更換pxDelayedTaskList和pxOverflowDelayedTaskList這兩個指針的指向//在task.c中添加 /* *當(dāng)系統(tǒng)時基計數(shù)器溢出的時候延時列表pxDelayedTaskList和 *pxOverflowDelayedTaskList要互相切換 */ #define taskSWITCH_DELAYED_LISTS()\ {\ List_t *pxTemp;\ pxTemp pxDelayedTaskList;\ pxDelayedTaskList pxOverflowDelayedTaskList;\ pxOverflowDelayedTaskList pxTemp;\ xNumOfOverflows;\ prvResetNextTaskUnblockTime();\ }static void prvResetNextTaskUnblockTime( void )//放在前面聲明一下 //函數(shù)定義 static void prvResetNextTaskUnblockTime( void ) { TCB_t *pxTCB; if( listLIST_IS_EMPTY( pxDelayedTaskList ) ! pdFALSE ) { /* The new current delayed list is empty. Set xNextTaskUnblockTime to the maximum possible value so it is extremely unlikely that the if( xTickCount xNextTaskUnblockTime ) test will pass until there is an item in the delayed list. */ xNextTaskUnblockTime portMAX_DELAY;//設(shè)置為最大值表示沒有任務(wù)阻塞在這里 } else { /* The new current delayed list is not empty, get the value of the item at the head of the delayed list. This is the time at which the task at the head of the delayed list should be removed from the Blocked state. */ ( pxTCB ) ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); xNextTaskUnblockTime listGET_LIST_ITEM_VALUE( ( ( pxTCB )-xStateListItem ) ); } }將此處設(shè)置為1相關(guān)函數(shù)是#define taskRESET_READY_PRIORITY( uxPriority ) \ { \ if( listCURRENT_LIST_LENGTH( ( pxReadyTasksLists[ ( uxPriority ) ] ) ) ( UBaseType_t ) 0 ) \ { \ portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ } \ }最后驗證PASS有些地方不是很理解但是還是要慢慢消化后面會貫通的。