




已閱讀5頁,還剩18頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
FreeRTOS 是如何工作的本部分內(nèi)容將介紹FreeRTOS的實(shí)現(xiàn)原理。 從基本原理到實(shí)現(xiàn) 多任務(wù)和調(diào)度 上下文切換 實(shí)時(shí)應(yīng)用 實(shí)時(shí)調(diào)度 實(shí)現(xiàn)細(xì)節(jié) GCC信號(hào)屬性 GCC Naked屬性 Tick Code AVR 上下文切換 上下文切換的完整例子 從基本原理到實(shí)現(xiàn)FreeRTOS是如何工作的 -從基本原理到實(shí)現(xiàn)本文描述FreeRTOS是如何實(shí)現(xiàn)的。如果你1 希望修改FreeRTOS源代碼2 移植實(shí)時(shí)內(nèi)核到另一個(gè)微控制器或者原型板(prototyping board)3 第一次接觸FreeRTOS,希望得到關(guān)于它們?cè)诓僮骱蛯?shí)現(xiàn)上的更多信息這些文檔會(huì)有用。本文檔分為兩個(gè)章節(jié):1. 基本原理和RTOS概念包括多任務(wù)的背景信息和基本實(shí)時(shí)概念,這是為初學(xué)者準(zhǔn)備的(is intended for beginners)2. 從底向上(from the bottom up)解釋實(shí)時(shí)內(nèi)核源代碼FreeRTOS實(shí)時(shí)內(nèi)核已經(jīng)移植到許多不同的微控制器架構(gòu)下。這份文檔是以Atmel AVR為范例,因?yàn)?1. AVR架構(gòu)簡(jiǎn)單2. 有免費(fèi)可用的開發(fā)工具 WinAVR (GCC) development tools.3. 非常便宜的原型板STK500 prototyping board在本文的最后,還一步一步地詳細(xì)描述了一個(gè)完整的上下文切換(context switch)。多任務(wù)和調(diào)度這一節(jié)提供一個(gè)關(guān)于實(shí)時(shí)和多任務(wù)概念的簡(jiǎn)介。讀下一節(jié)之前必須理解這些概念。多任務(wù)(Multitasking)在一個(gè)操作系統(tǒng)內(nèi)部,內(nèi)核kernel是最核心的部件。像Linux那樣的操作系統(tǒng)使用的內(nèi)核,從表面上看(seemingly),允許用戶并 發(fā)(simultaneously)訪問 計(jì)算機(jī)。多個(gè)用戶似乎(apparently)可以并行(concurrently)執(zhí)行多個(gè)程序。在操作系統(tǒng)的控制下,每個(gè)正在執(zhí)行的程序就是一個(gè)任務(wù)task。如果一個(gè)操作系統(tǒng)能夠以這種方法執(zhí)行多個(gè)任務(wù),這就叫做多任務(wù)multitasking.多任務(wù)操作系統(tǒng)的使用可以簡(jiǎn)化應(yīng)用程序的設(shè)計(jì):1 操作系統(tǒng)的多任務(wù)和任務(wù)間通信的機(jī)制允許復(fù)雜的應(yīng)用程序被分成一系列更小的和更多的可以管理的任務(wù)。2 (程序的)劃分(partitioning)讓軟件測(cè)試更容易, 團(tuán)隊(duì)工作分解(work breakdown within teams),也有利于代碼復(fù)用。3 復(fù)雜的定時(shí)和先后順序的細(xì)節(jié) 可以從應(yīng)用程序代碼中 刪除。(因?yàn)椋┻@成為操作系統(tǒng)的職責(zé)。多任務(wù)Vs 并發(fā)傳統(tǒng)的(conventional)的處理器同時(shí)只能執(zhí)行一個(gè)任務(wù)。但通過快速的任務(wù)切換,一個(gè)多任務(wù)操作系統(tǒng)可以使它看起來 (appear)好像每個(gè)任務(wù)并行執(zhí)行一樣。這可以下面的示意圖來描述(depicted)。它顯示了有關(guān)(with respect to)時(shí)間的3個(gè)任務(wù)的執(zhí)行模式。任務(wù)名用顏色標(biāo)注出來,寫在左手邊。時(shí)間從左到右增加,相應(yīng)的顏色的線條顯示該任務(wù)在某個(gè)特殊時(shí)間正在執(zhí)行。上面的圖 演示的是用戶所覺察到的并行執(zhí)行模式,下面的圖是實(shí)際的多任務(wù)執(zhí)行模式。所有可用的任務(wù)都好像在執(zhí)行,但實(shí)際上在任何一個(gè)時(shí)刻都只有一個(gè)任務(wù)在執(zhí)行調(diào)度調(diào)度器(scheduler)是內(nèi)核中負(fù)責(zé) 決定在某個(gè)特殊時(shí)間 哪個(gè)任務(wù)應(yīng)該執(zhí)行的部分。內(nèi)核可以在任務(wù)的生命期(lifetime) 掛起(suspend) / 恢復(fù)(resume)一個(gè)任務(wù)許多次。調(diào)度策略(scheduling policy)是調(diào)度器用來決定哪個(gè)任務(wù)在哪個(gè)時(shí)間點(diǎn)執(zhí)行的算法。一個(gè)(非實(shí)時(shí))多用戶系統(tǒng)的策略很可能分配(allow)給每個(gè)任務(wù)一個(gè)公平 (fair)的處理器時(shí)間片(proportion of processor time)。用在實(shí)時(shí)系統(tǒng)/嵌入式系統(tǒng)的策略稍后再描述。除了被RTOS內(nèi)核無意的掛起外,一個(gè)任務(wù)還可以自己掛起自己。如果一個(gè)任務(wù)想延遲一段固定的時(shí)間(也就是sleep),或者等待(也就 是 block)某個(gè)資源可用(比如一個(gè)串口),或者等待一個(gè)事件出現(xiàn)(比如一個(gè)鍵按下)。一個(gè)阻塞或者睡眠的任務(wù)是不能執(zhí)行的,不會(huì)為它分配任何處理時(shí)間。上圖中提到的編號(hào):1) Task1正在運(yùn)行2) 內(nèi)核掛起Task13) 恢復(fù)任務(wù)Task24) Task2正在執(zhí)行,為獨(dú)占訪問(exclusive access),它鎖定一個(gè)處理器外設(shè)5) 內(nèi)核 掛起Task26) 恢復(fù)Task37) Task3試圖訪問同樣的處理器外設(shè),發(fā)現(xiàn)它被鎖定,Task3不能繼續(xù),所以自己掛起自己。8) 內(nèi)核恢復(fù)Task1.9) 接下來(the next time),Task2在9處執(zhí)行。它完成了對(duì)處理器外設(shè)的訪問,所以解鎖它10) 再下來,Task3在10處執(zhí)行。它發(fā)現(xiàn) 現(xiàn)在可以訪問處理器外設(shè)了,于是開始執(zhí)行,直到被內(nèi)核掛起。上下文切換跟任何其他程序一樣,一個(gè)任務(wù)執(zhí)行時(shí),它使用 處理器/微控制器 的寄存器,訪問RAM ROM。這些資源(處理器的寄存器,stack等)一起組成任務(wù)的執(zhí)行上下文(the task execution context).一個(gè)任務(wù)是一個(gè)連續(xù)有序的代碼片斷。它并不知道它將何時(shí)被內(nèi)核掛起或者恢復(fù),甚至不知道這些事情(掛起或者恢復(fù))在什么時(shí)候已經(jīng)發(fā)生了。下面考查(Consider)的這個(gè)例子是用來求兩個(gè)處理器中的寄存器值之和,該任務(wù)在執(zhí)行1條指令后就立即被掛起。-任務(wù)將要執(zhí)行ADD指令時(shí),被掛起-先前的指令已經(jīng)把數(shù)取到寄存器(Reg1,Reg2)中了,而這些寄存器(Reg1,Reg2)將要被ADD指令用到。當(dāng)這個(gè)任務(wù)被恢復(fù)后,ADD就是要執(zhí)行的第1條指令。這個(gè)任務(wù)不知道是否有另一個(gè)的任務(wù)會(huì)在中間時(shí)期 修改 Reg1或者Reg2)當(dāng)這個(gè)任務(wù)掛起時(shí),其他任務(wù)繼續(xù)執(zhí)行,可能會(huì)修改處理器寄存器的值。在恢復(fù)之后,這個(gè)任務(wù)也不知道處理器的寄存器被修改過(altered).如果它使用這個(gè)修改過的值,就會(huì)導(dǎo)致計(jì)算的和的結(jié)果不正確。為了避免這類錯(cuò)誤,必須保證,在恢復(fù)一個(gè)任務(wù)之后,其上下文環(huán)境跟 即將掛起前是一樣的。操作系統(tǒng)內(nèi)核有責(zé)任 通過在任務(wù)掛起前保存其上下文來確保這種狀況。當(dāng)任務(wù)恢復(fù)時(shí),保存的上下文 就被 操作系統(tǒng)內(nèi)核恢復(fù)到先前的執(zhí)行情況。保存一個(gè)被掛起的任務(wù)的上下文 并在 任務(wù)恢復(fù)時(shí)恢復(fù)其上下文的這個(gè)處理過程就叫做上下文切換(context switching)實(shí)時(shí)應(yīng)用實(shí)時(shí)操作系統(tǒng)(RTOSs)通過同樣的原理達(dá)到多任務(wù)的目的。但他們的目標(biāo)與那些非實(shí)時(shí)系統(tǒng)相比是很不一樣的。不同的目標(biāo)影響到不同的調(diào)度策略。 實(shí)時(shí)/嵌入式系統(tǒng)設(shè)計(jì)成提供一個(gè)對(duì) 真實(shí)世界的事件的及時(shí)響應(yīng)(timely response)。出現(xiàn)在真實(shí)世界中的事件可能有一個(gè)時(shí)間限制(deadline),在此期限之前,實(shí)時(shí)/嵌入式系統(tǒng)必須給出響應(yīng),RTOS調(diào)度策略必 須確保時(shí)間限制是恰當(dāng)?shù)?met).為了達(dá)到這個(gè)目的,軟件工程師必須首先為每個(gè)任務(wù)設(shè)置一個(gè)優(yōu)先級(jí)(priority)。RTOS的調(diào)度策略 只是簡(jiǎn)單地確認(rèn)能被執(zhí)行的最高優(yōu)先級(jí)別的任務(wù) 是分配了處理時(shí)間的任務(wù)(the task given processing time)。這可能要求在相同優(yōu)先級(jí)的任務(wù)之間公平的共享 處理時(shí)間,如果他們準(zhǔn)備并發(fā)運(yùn)行的話。代碼示例:最基本的例子是 一個(gè)由鍵盤和LCD組成的實(shí)時(shí)系統(tǒng)。用戶必須 在合理的時(shí)段 為 每個(gè)鍵盤按下取得視覺反饋(visual feedback).。如果用戶不能 在這時(shí)段 看到 鍵盤按下 已經(jīng)被接受,軟件產(chǎn)品將會(huì)很難使用(be awkward to use)。如果最長(zhǎng)的接受期是100ms,那么在0到100ms的響應(yīng) 可被接受。這個(gè)功能可以用一個(gè)下面這樣的結(jié)構(gòu)的獨(dú)立(autonomous)任務(wù) 實(shí)現(xiàn):view sourceprint?01void vKeyHandlerTask( void *pvParameters ) 02 03/鍵盤處理是一個(gè)連續(xù)的過程。就像大多實(shí)時(shí)任務(wù)那樣,這個(gè)任務(wù) 04/也是用一個(gè)無限循環(huán)實(shí)現(xiàn)的。 05for( ; ) 06 07Suspend waiting for a key press 08Process the key press 09 10 現(xiàn)在假設(shè) 實(shí)時(shí)系統(tǒng)也執(zhí)行一個(gè)依賴數(shù)字濾波輸入的控制功能。這個(gè)輸入必須被取樣(sampled),濾波(filtered),并且每2ms執(zhí)行一次控制循環(huán)。為了 讓濾波器正常操作,取樣的時(shí)間規(guī)律(the temporal regularity)必須精確到0.5ms。這個(gè)功能可以 用下面這個(gè)結(jié)構(gòu)的 獨(dú)立任務(wù) 實(shí)現(xiàn):view sourceprint?01void vControlTask( void *pvParameters ) 02 03for( ; ) 04 05Suspend waiting for 2ms since the start of the previous 06cycle 0708Sample the input 09Filter the sampled input 10Perform control algorithm 11Output result 12 13 軟件工程師必須設(shè)置 控制任務(wù)為 最高的優(yōu)先級(jí),因?yàn)椋? 控制任務(wù)的時(shí)間限制(deadline) 比 鍵盤處理任務(wù)的 要嚴(yán)格;2 控制任務(wù)錯(cuò)過最后期限(deadline)的后果 比 鍵盤處理任務(wù) 要嚴(yán)重。下面將演示 這些任務(wù)是如何被實(shí)時(shí)操作系統(tǒng)調(diào)度的.實(shí)時(shí)調(diào)度下面的圖 演示 前面定義的那些任務(wù)是如何被時(shí)實(shí)操作系統(tǒng)調(diào)度的。RTOS自己已經(jīng)建立了一個(gè)任務(wù)-idle task-它只在沒有其他任務(wù)執(zhí)行的時(shí)候才被執(zhí)行。RTOS idle task 總是處于可以執(zhí)行的狀態(tài)(注:也就是它不可能會(huì)因?yàn)榈却裁赐庠O(shè)資源而被阻塞,而是處于一種隨時(shí)待命的狀態(tài)).上圖中:1. 在最開始,我們的兩個(gè)任務(wù)都不能被執(zhí)行-vControlTask等待合適(correct)的時(shí)間開始新的控制循環(huán),vKeyHandlerTask等待鍵盤按下。處理器時(shí)間分配給 RTOS的idle task. 2. 在t1時(shí)刻,一個(gè)鍵盤按下(事件)出現(xiàn). VKeyHandlerTask任務(wù)現(xiàn)在可以執(zhí)行,它比RTOS的idle task有更高的優(yōu)先級(jí),所以處理器時(shí)間給它。 3. 在t2時(shí)刻,vKeyHandlerTask已經(jīng)完成了對(duì)按鍵的處理,并更新了LCD。它不能繼續(xù),直到另一個(gè)鍵被按下,所以必須掛起它自己。RTOS idle task又被恢復(fù)執(zhí)行。 4. 在t3時(shí)刻,一個(gè)定時(shí)器事件預(yù)示(indicates),可以執(zhí)行下一個(gè)控制循環(huán)了。VControlTask現(xiàn)在可以執(zhí)行,作為最高優(yōu)先級(jí)的任務(wù)被立刻分配(scheduled)到處理器時(shí)間 5. 在t3和t4之間,當(dāng)vControlTask任務(wù)還在執(zhí)行的時(shí)候,一個(gè)鍵按下。VKeyHandlerTask不能被執(zhí)行,因?yàn)樗鼪]有vControlTask的優(yōu)先級(jí)高。不能分配(scheduled)到任何處理器時(shí)間。6. 在t4時(shí)刻,vControlTask完成了控制循環(huán)的處理,不能夠重新開始,直到下一個(gè)時(shí)間事件出現(xiàn),所以它自己掛起自己。而 vKeyHandlerTask現(xiàn)在是最高優(yōu)先級(jí)的任務(wù),可以運(yùn)行了,所以,為了處理先前的鍵盤事件,分配(scheduled)到了處理器時(shí)間. 7. 在t5時(shí)刻,鍵盤已經(jīng)被處理。VkeyHandlerTask為了等待下一個(gè)鍵盤事件,自己掛起自己。現(xiàn)在,我們的兩個(gè)任務(wù)再度不能執(zhí)行了。RTOS idle task分配到處理器時(shí)間。 8. 在t5和t6之間,一個(gè)定時(shí)器事件被處理,但是沒有更多的鍵盤事件出現(xiàn)。 9. 下一個(gè)鍵盤按下出現(xiàn)在t6時(shí)刻,但在vKeyHandlerTask完成處理鍵之前,一個(gè)定時(shí)器事件出現(xiàn)了。現(xiàn)在兩個(gè)任務(wù)都能被執(zhí)行,而 vControlTask比vKeyHandlerTask 有更多的優(yōu)先級(jí),所以vKeyHandlerTask在它完成處理鍵盤之前就被掛起了。VControlTask分配到處理器時(shí)間。 10. 在t8時(shí)刻,vControlTask完成處理控制循環(huán),掛起自己以等待下一個(gè)事件。VKeyHandlerTask再次成為最高優(yōu)先級(jí)的任務(wù),能夠運(yùn)行,所以分配到處理器時(shí)間,從而鍵盤按下事件 處理能夠完成。實(shí)現(xiàn)細(xì)節(jié)這一節(jié)從底向上描述了RTOS上下文切換的源代碼。使用FreeRTOS Atmel AVR微控制器移植的代碼作為例子。本節(jié)的最后還一步一步地瀏覽(step by step look)了一個(gè)完整的上下文切換。C開發(fā)工具FreeRTOS的目標(biāo)是簡(jiǎn)單且易于理解。為了達(dá)到這個(gè)目標(biāo)(To this end),RTOS的源代碼的大部分都是用C寫的,而不是匯編。這里演示的例子使用了 WinAVR development tools。 WinAVR是一個(gè)自由/免費(fèi)的在windows下的AVR交叉編譯器,它是基于GCC的。RTOS Tick睡眠時(shí),一個(gè)任務(wù)將指定多長(zhǎng)時(shí)間后它會(huì)醒來。阻塞時(shí),一個(gè)任務(wù)將指定一個(gè) 希望最多等多久的時(shí)間。FreeRTOS實(shí)時(shí)內(nèi)核用tick count變量 來度量時(shí)間的。定時(shí)器中斷(RTOS tick interrupt) 用嚴(yán)格的時(shí)間精度(temporal accuracy )來增加 tick count- 允許實(shí)時(shí)內(nèi)核 用一個(gè)指定的定時(shí)器中斷頻率的精度(resolution)來測(cè)量 時(shí)間。每次tick count增加后,實(shí)時(shí)內(nèi)核 必須檢查,看現(xiàn)在是否 解除阻塞 或者 喚醒 一個(gè)任務(wù)。一個(gè) 比被中斷的任務(wù)有更高的優(yōu)先級(jí)的任務(wù) 在 tick ISR期間 被 喚醒或者解除阻塞 是可能的。如果是這種情況,tick ISR應(yīng)該返回到新的喚醒/解鎖的任務(wù)-實(shí)際(effectively)中斷一個(gè)任務(wù),卻返回到另一個(gè)任務(wù)。如下所述:上圖提到的幾個(gè)點(diǎn):1) RTOS idle task正在運(yùn)行2) RTOS tick出現(xiàn),控制轉(zhuǎn)移到tick ISR(3)3) RTOS tick ISR使得vControlTask準(zhǔn)備運(yùn)行,當(dāng)vControlTask比RTOS idle task有更高的優(yōu)先級(jí),切換上下文到 vControlTask.。4) 現(xiàn)在的執(zhí)行上下文是vControlTask的。從ISR(4)退出 返回到vControlTask,vControlTask從(5)開始執(zhí)行。以這種方式出現(xiàn)的上下文切換,稱為Preemptive。 因?yàn)楸恢袛嗟娜蝿?wù) 沒有自愿(voluntarily)地掛起它自己就被搶占了(preempted)。FreeRTOS的AVR移植版本 用一個(gè)在定時(shí)器1(timer1)的比較匹配(compare match)事件來產(chǎn)生RTOS tick. 后續(xù)將描述RTOS tick ISR是如何用WinAVR開發(fā)工具實(shí)現(xiàn)的。GCC信號(hào)屬性GCC development tools允許用C來寫中斷程序。一個(gè)在AVR 定時(shí)器1外設(shè)的比較匹配事件 可以用下面的 語法(syntax)實(shí)現(xiàn):view sourceprint?1void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal ) ); 2void SIG_OUTPUT_COMPARE1A( void ) 3 4/* ISR C code for RTOS tick. */5vPortYieldFromTick(); 6 在函數(shù)原型前的 _attribute_ ( ( signal ) ) 指示符 告知 編譯器,這個(gè)函數(shù)是一個(gè)ISR,會(huì)引起編譯器輸出的兩個(gè)重要改變:1. signal屬性保證,每個(gè)在ISR 期間 被修改的 處理器的寄存器,在從ISR中退出時(shí)恢復(fù)到它原來的值。這就要求,當(dāng)中斷將要執(zhí)行時(shí),編譯器不能做任何假定。所以,不能優(yōu)化哪個(gè)處理器寄存器要求保護(hù)或者不保護(hù)。2signal也強(qiáng)制使用 一個(gè) 從中斷返回(return from interrupt)指令(RETI),而不是返回(return)指令RET. AVR微控制器 在進(jìn)入ISR前禁止中斷,RETI指令要求在 退出時(shí)重新打開中斷。下面是由編譯器輸出的代碼:view sourceprint?01;void SIG_OUTPUT_COMPARE1A( void ) 02; 03; - 04; CODE GENERATED BY THE COMPILER TO SAVE 05; THE REGISTERS THAT GET ALTERED BY THE 06; APPLICATION CODE DURING THE ISR. 07PUSH R1 08PUSH R0 09IN R0,0x3F 10PUSH R0 11CLR R1 12PUSH R18 13PUSH R19 14PUSH R20 15PUSH R21 16PUSH R22 17PUSH R23 18PUSH R24 19PUSH R25 20PUSH R26 21PUSH R27 22PUSH R30 23PUSH R31 24; - 25; CODE GENERATED BY THE COMPILER FROM THE 26; APPLICATION C CODE. 27;vTaskIncrementTick(); 28CALL 0x0000029B ;Call subroutine 29; 30; - 31; CODE GENERATED BY THE COMPILER TO 32; RESTORE THE REGISTERS PREVIOUSLY 33; SAVED. 34POP R31 35POP R30 36POP R27 37POP R26 38POP R25 39POP R24 40POP R23 41POP R22 42POP R21 43POP R20 44POP R19 45POP R18 46POP R0 47OUT 0x3F,R0 48POP R0 49POP R1 50RETI 51; - GCC Naked屬性前一節(jié)講述了如何在C中用 signal屬性來寫一個(gè)ISR.,以及它是如何使 執(zhí)行上下文自動(dòng)保存的(只有那些被ISR修改過的處理器寄存器才會(huì)得到保存)。然而,執(zhí)行一個(gè)上下文切換需要保存完整的上下文。應(yīng)用程序代碼能夠 在進(jìn)入ISR時(shí),明確(explicitly)地 保存所有寄存器,但是這樣會(huì)使 某些處理器寄存器保存兩次-一次是由編譯器生成的代碼,另一次是由應(yīng)用程序自己。這不是我們所需要的,可以在signal屬性后 添加 naked屬性來避免:view sourceprint?01void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) ); 02void SIG_OUTPUT_COMPARE1A( void ) 03 04/* ISR C code for RTOS tick. */05vPortYieldFromTick(); 06 0708naked屬性阻止編譯器生成任何函數(shù)入口或退出代碼?,F(xiàn)在變異這段代碼,會(huì)得到更少的編譯器輸出: 0910;void SIG_OUTPUT_COMPARE1A( void ) 11; 12; - 13; NO COMPILER GENERATED CODE HERE TO SAVE 14; THE REGISTERS THAT GET ALTERED BY THE 15; ISR. 16; - 17; CODE GENERATED BY THE COMPILER FROM THE 18; APPLICATION C CODE. 19;vTaskIncrementTick(); 20CALL 0x0000029B ;Call subroutine 21; - 22; NO COMPILER GENERATED CODE HERE TO RESTORE 23; THE REGISTERS OR RETURN FROM THE ISR. 24; - 25; 看看,入口 和 出口代碼都沒有了吧使用 naked 屬性,編譯器不會(huì)生成任何入口和出口代碼,所以必須明確(explicitly)加入。 portSAVE_CONTEXT()和portRESTORE_CONTEXT()這兩個(gè)宏 是用來保存和恢復(fù)完整的 執(zhí)行上下文的:view sourceprint?01void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) ); 02void SIG_OUTPUT_COMPARE1A( void ) 03 04/* Macro that explicitly saves the execution 05context. */06portSAVE_CONTEXT(); 07/* ISR C code for RTOS tick. */08vPortYieldFromTick(); 09/* Macro that explicitly restores the 10execution context. */11portRESTORE_CONTEXT(); 12/* The return from interrupt call must also 13be explicitly added. */14asm volatile ( reti ); 15 naked屬性給了應(yīng)用程序完整的控制權(quán),在何時(shí) ,怎么樣保存AVR的上下文。如果應(yīng)用程序代碼在進(jìn)入ISR前保存了完整的上下文,在執(zhí)行上下文切換時(shí)不必再保存,所以不會(huì)有處理器的寄存器被保存兩次。Tick CodeFreeRTOS的AVR移植版本的實(shí)際源代碼 與前一節(jié)的例子有些輕微的不同。VPortYieldFromTick()是作為一個(gè)naked函數(shù)的它自己的實(shí)現(xiàn),上下文在 vPortYieldFromTick().里被保存和恢復(fù)。這樣做的目的是為了實(shí)現(xiàn)一個(gè)non-preemptive的上下文切換(這里,一個(gè)任務(wù)自己 阻塞自己).這里暫時(shí)不講這種non-preemptive切換。RTOS tick是這樣在FreeRTOS中實(shí)現(xiàn)的(看代碼中注釋片斷獲取更多細(xì)節(jié)):view sourceprint?01void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) ); 02void vPortYieldFromTick( void ) _attribute_ ( ( naked ) ); 030405/*-*/06/* RTOS tick中斷服務(wù)程序. */07void SIG_OUTPUT_COMPARE1A( void ) 08 09/*調(diào)用tick函數(shù). */10vPortYieldFromTick(); 1112/*從中斷返回. 如果出現(xiàn)上下文切換,將返回到一個(gè)不同的任務(wù)中 */13asm volatile ( reti ); 14 151617/*-*/18void vPortYieldFromTick( void ) 19 20/* 這是一個(gè)naked 函數(shù),所以需要保存上下文 */21portSAVE_CONTEXT(); 2223/* 增加tick count,檢查新的tick count值是否引起一個(gè)延遲周期過期, 24這個(gè)函數(shù)調(diào)用可導(dǎo)致一個(gè)任務(wù)變成準(zhǔn)備運(yùn)行. */25vTaskIncrementTick(); 2627/*檢查是否要求上限文切換。如果 由vTaskIncrementTick()準(zhǔn)備好的任 28務(wù)比已經(jīng)中斷的任務(wù)有更高優(yōu)先級(jí),就切換過去 */29vTaskSwitchContext(); 3031/*恢復(fù)上下文.如果發(fā)生了上下文切換,這將恢復(fù)要繼續(xù)運(yùn)行的任務(wù)的上下文 */32portRESTORE_CONTEXT(); 3334/*從這naked 函數(shù)返回. */35asm volatile ( ret ); 36 37/*-*/AVR 上下文切換上下文切換要求保存完整的上下文。在AVR MCU中,上下文包括1 32位通用寄存器。Gcc開發(fā)工具假定寄存器R1設(shè)定為02 狀態(tài)寄存器。狀態(tài)寄存器的值影響指令的執(zhí)行,必須通過上下文切換保存(preserved)3 程序計(jì)數(shù)器(PC).恢復(fù)執(zhí)行后,一個(gè)任務(wù)必須從上次被掛起的地方繼續(xù)執(zhí)行。(a task must continue execution from the instruction that was about to be executed immediately prior to its suspension.)4 兩個(gè)stack指針寄存器每個(gè)時(shí)實(shí)任務(wù)都有它自己的stack 內(nèi)存區(qū)域,所以上下文可 簡(jiǎn)單的通過將寄存器壓入到任務(wù)棧 來保存上下文。保存AVR的上下文是一個(gè)不可避免的要使用到匯編語言的地方。portSAVE_CONTEXT() 是作為一個(gè)宏來實(shí)現(xiàn)的,源代碼在下面給出view sourceprint?01#define portSAVE_CONTEXT() 02asm volatile ( 03push r0 nt (1) 04in r0, _SREG_ nt (2) 05cli nt (3) 06push r0 nt (4) 07push r1 nt (5) 08clr r1 nt (6) 09push r2 nt (7) 10push r3 nt 11push r4 nt 12push r5 nt 13: 14: 15: 16push r30 nt 17push r31 nt 18lds r26, pxCurrentTCB nt (8) 19lds r27, pxCurrentTCB + 1 nt (9) 20in r0, _SP_L_ nt (10) 21st x+, r0 nt (11) 22in r0, _SP_H_ nt (12) 23st x+, r0 nt (13) 24); 在上面的源代碼中:1 寄存器R0首先被保存,因?yàn)楫?dāng)狀態(tài)寄存器被保存時(shí),它會(huì)被用到。所以必須先把它的原始值保存下來。2 狀態(tài)寄存器被搬移到 R0 (2),所以它能保存到stack (4)3 禁止處理器中斷。如果portSAVE_CONTEXT(),只是從ISR中調(diào)用,就不需要明確的禁止中斷,因?yàn)锳VR已經(jīng)這么做了。 portSAVE_CONTEXT()宏用在中斷服務(wù)程序的外部(一個(gè)任務(wù)自己掛起自己的時(shí)候),中斷應(yīng)該盡可能早的明確清除(也就是禁止中斷 CLI)4 從ISR 的C源代碼中,由編譯器生成的代碼假定R1被設(shè)置為0。R1的原始值 在R1被清除(6)前保存(5)5. 在(7)和(8),所有寄存器都被按順序保存6. 現(xiàn)在被掛起的任務(wù)的棧中還有一個(gè)任務(wù)執(zhí)行上下文的拷貝。內(nèi)核保存任務(wù)的棧指針,所以 當(dāng)任務(wù)恢復(fù)執(zhí)行時(shí),上下文可以取得并恢復(fù)。The X processor register is loaded with the address to which the stack pointer is to be saved (8 and 9). 載入棧指針7. 棧指針保存,先低位(10,11)后高位(12 13)Restoring the ContextportRESTORE_CONTEXT()是portSAVE_CONTEXT().的逆過程。要恢復(fù)執(zhí)行的任務(wù)的上下文被預(yù)先存儲(chǔ)到任務(wù)棧中。實(shí)時(shí)內(nèi)核為該任務(wù)取得棧指針,然后彈出POP上下文到正確的寄存器中。view sourceprint?01#define portRESTORE_CONTEXT() 02asm volatile ( 03lds r26, pxCurrentTCB nt (1) 04lds r27, pxCurrentTCB + 1 nt (2) 05ld r28, x+ nt 06out _SP_L_, r28 nt (3) 07ld r29, x+ nt 08out _SP_H_, r29 nt (4) 09pop r31 nt 10pop r30 nt 11: 12: 13: 14pop r1 nt 15pop r0 nt (5) 16out _SREG_, r0 nt (6) 17pop r0 nt (7) 18); 上面的代碼中:1. pxCurrentTCB含有任務(wù)棧指針被取得的地址。這被載入到X寄存器(1和2)2. 被恢復(fù)的任務(wù)的棧指針 載入到AVR的stack pointer,先低字節(jié)(3),后高半字節(jié)nibble(4)3.寄存器以相反的順序從棧中,一直到R14.狀態(tài)寄存其保存在stack中的位置是在R1和R0之間,所以,它在R0(7)前重新恢復(fù)(6)上下文切換的完整例子下面介紹一個(gè)完整的上下文切換的例子連起來(Putting It All Together)在第2節(jié)的最后一部分 展示了這些 積木模塊(building blocks)和源代碼模塊(source code modules)使如何來達(dá)到 一個(gè)在AVR微控制器上進(jìn)行RTOS上下文切換的目的的。這個(gè)例子分幾步演示了從一個(gè)低的優(yōu)先級(jí)的任務(wù)Task A,切換到高優(yōu)先級(jí)的任務(wù)Task B的。源代碼與WinAVR C開發(fā)工具兼容。上下文切換-第1步在RTOS tick 中斷發(fā)生之前這個(gè)例子,以TaskA的執(zhí)行開始。TaskB先前已經(jīng)被掛起,所以它的上下文已經(jīng)被保存到TaskB的stack里面。TaskA的上下文如下圖所示:在每個(gè)寄存器里標(biāo)上(A),表示,這寄存器含有 任務(wù)A的上下文的正確值。上下文切換第2步RTOS tick中斷 發(fā)生了當(dāng)TaskA 將要執(zhí)行到一個(gè)LDI指令的時(shí)候,RTOS tick 發(fā)生。當(dāng)中斷發(fā)生時(shí),AVR微控制器會(huì)在跳入到RTOS tickISR之前,自動(dòng)放置當(dāng)前的P
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 設(shè)備風(fēng)險(xiǎn)評(píng)估管理制度
- 設(shè)計(jì)單位業(yè)務(wù)管理制度
- 設(shè)計(jì)規(guī)章制度管理制度
- 診所中醫(yī)醫(yī)師管理制度
- 診所收費(fèi)票據(jù)管理制度
- 試劑耗材入庫管理制度
- 財(cái)務(wù)管理公司管理制度
- 財(cái)富顧問薪金管理制度
- 貨架汽配倉庫管理制度
- 貨物道路運(yùn)輸管理制度
- 售票員崗前培訓(xùn)
- 教科版六年級(jí)下冊(cè)科學(xué)第一單元《小小工程師》教材分析及全部教案(定稿;共7課時(shí))
- 2024屆北京市海淀區(qū)101中學(xué)語文八年級(jí)第二學(xué)期期末檢測(cè)試題含解析
- 國家自然科學(xué)基金申請(qǐng)經(jīng)驗(yàn)匯總課件
- 《新產(chǎn)程管理》課件
- 管理ABC-干嘉偉(美團(tuán)網(wǎng)COO)
- 雙活數(shù)據(jù)中心與災(zāi)備解決方案
- 國有資產(chǎn)委托經(jīng)營管理協(xié)議
- 中試車間安全培訓(xùn)
- 醫(yī)療衛(wèi)生機(jī)構(gòu)6S常態(tài)化管理打分表
- 2023年江西南昌軌道交通集團(tuán)運(yùn)營分公司招聘327人筆試參考題庫(共500題)答案詳解版
評(píng)論
0/150
提交評(píng)論