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