實現FreeRTOS系統傻瓜編程_第1頁
實現FreeRTOS系統傻瓜編程_第2頁
實現FreeRTOS系統傻瓜編程_第3頁
實現FreeRTOS系統傻瓜編程_第4頁
實現FreeRTOS系統傻瓜編程_第5頁
已閱讀5頁,還剩22頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、傻瓜實現FreeRTOS系統如果你1 希望修改FreeRTOS源代碼2 移植實時內核到另一個微控制器或者原型板(prototyping board)3 第一次接觸FreeRTOS,希望得到關于它們在操作和實現上的更多信息這些文檔會有用。本文檔分為兩個章節:1. 基本原理和RTOS概念包括多任務的背景信息和基本實時概念,這是為初學者準備的(is intended for beginners)2. 從底向上(from the bottom up)解釋實時內核源代碼FreeRTOS實時內核已經移植到許多不同的微控制器架構下。這份文檔是以Atmel A

2、VR為范例,因為:1. AVR架構簡單2. 有免費可用的開發工具 WinAVR (GCC) development tools.  3. 非常便宜的原型板STK500 prototyping board在本文的最后,還一步一步地詳細描述了一個完整的上下文切換(context switch)。RTOS基本原理多任務調度上下文切換實時應用實時調度這一節提供一個關于實時和多任務概念的簡介。讀下一節之前必須理解這些概念。多任務(Multitasking)在一個操作系統內部,內核kernel是最核心的部件。像Linux那樣的操作系統使用的內核,從表面上看(seem

3、ingly),允許用戶并發(simultaneously)訪問 計算機。多個用戶似乎(apparently)可以并行(concurrently)執行多個程序。在操作系統的控制下,每個正在執行的程序就是一個任務task。如果一個操作系統能夠以這種方法執行多個任務,這就叫做多任務multitasking.多任務操作系統的使用可以簡化應用程序的設計:1 操作系統的多任務和任務間通信的機制允許復雜的應用程序被分成一系列更小的和更多的可以管理的任務。2 (程序的)劃分(partitioning)讓軟件測試更容易, 團隊工作分解(work breakdown within teams)

4、,也有利于代碼復用。3 復雜的定時和先后順序的細節 可以從應用程序代碼中 刪除。(因為)這成為操作系統的職責。多任務Vs 并發傳統的(conventional)的處理器同時只能執行一個任務。但通過快速的任務切換,一個多任務操作系統可以使它看起來(appear)好像每個任務并行執行一樣。這可以下面的示意圖來描述(depicted)。它顯示了有關(with respect to)時間的3個任務的執行模式。任務名用顏色標注出來,寫在左手邊。時間從左到右增加,相應的顏色的線條 顯示該任務在某個特殊時間正在執行。上面的圖 演示的是用戶所覺察到的并行執行模式,下面的圖是實際的多任務執行模式。-所

5、有可用的任務都好像在執行,但實際上在任何一個時刻都只有一個任務在執行調度調度器(scheduler)是內核中負責 決定在某個特殊時間 哪個任務應該執行的部分。內核可以在任務的生命期(lifetime) 掛起(suspend) / 恢復(resume)一個任務許多次。調度策略(scheduling policy)是調度器用來決定哪個任務在哪個時間點執行的算法。一個(非實時)多用戶系統的策略很可能分配(allow)給每個任務一個"公平"(fair)的處理器時間片(proportion of processor time)。用在實時系統/嵌入式系統的策略稍后再描述。除了被RTOS

6、內核無意的掛起外,一個任務還可以自己掛起自己。如果一個任務想延遲一段固定的時間(也就是sleep),或者等待(也就是block)某個資源可用(比如一個串口),或者等待一個事件出現(比如一個鍵按下)。一個阻塞或者睡眠的任務是不能執行的,不會為它分配任何處理時間。上圖中提到的編號:1) Task1正在運行2) 內核掛起Task13) 恢復任務Task24) Task2正在執行,為獨占訪問(exclusive access),它鎖定一個處理器外設 5) 內核 掛起Task26) 恢復Task37) Task3試圖訪問同樣的處理器外

7、設,發現它被鎖定,Task3不能繼續,所以自己掛起自己。8) 內核恢復Task1.9) 接下來(the next time),Task2在9處執行。它完成了對處理器外設的訪問,所以解鎖它10) 再下來,Task3在10處執行。它發現 現在可以訪問處理器外設了,于是開始執行,直到被內核掛起。上下文切換跟任何其他程序一樣,一個任務執行時,它使用 處理器/微控制器 的寄存器,訪問RAM ROM。這些資源(處理器的寄存器,stack等)一起組成任務的執行上下文(the task execution context).一個任務是一個連續有序的代碼片斷。它并不知道它將何時被內

8、核掛起或者恢復,甚至不知道這些事情(掛起或者恢復)在什么時候已經發生了。下面考查(Consider)的這個例子是用來求兩個處理器中的寄存器值之和,該任務在執行1條指令后就立即被掛起。->任務將要執行ADD指令時,被掛起->先前的指令已經把數取到寄存器(Reg1,Reg2)中了,而這些寄存器(Reg1,Reg2)將要被ADD指令用到。當這個任務被恢復后,ADD就是要執行的第1條指令。這個任務不知道是否有另一個的任務會在中間時期 修改 Reg1或者Reg2)當這個任務掛起時,其他任務繼續執行,可能會修改處理器寄存器的值。在恢復之后,這個任務也不知道處理器的寄存器被修改過(altered

9、).如果它使用這個修改過的值,就會導致計算的和的結果不正確。為了避免這類錯誤,必須保證,在恢復一個任務之后,其上下文環境跟 即將掛起前是一樣的。操作系統內核有責任 通過在任務掛起前保存其上下文 來確保這種狀況。當任務恢復時,保存的上下文 就被 操作系統內核恢復到先前的執行情況。保存一個被掛起的任務的上下文 并在 任務恢復時 恢復其上下文的這個處理過程就叫做上下文切換(context switching)實時應用實時操作系統(RTOS's)通過同樣的原理達到多任務的目的。但他們的目標與那些非實時系統相比是很不一樣的。不同的目標影響到不同的調度策略。實時/嵌入式系統設計成提供一個對 真實世

10、界的事件的及時響應(timely response)。出現在真實世界中的事件可能有一個時間限制(deadline),在此期限之前,實時/嵌入式系統必須給出響應,RTOS調度策略必須確保時間限制是恰當的(met).為了達到這個目的,軟件工程師必須首先為每個任務設置一個優先級(priority)。RTOS的調度策略 只是簡單地確認 能被執行的最高優先級別的任務 是分配了處理時間的任務(the task given processing time)。這可能要求在相同優先級的 任務之間公平的共享 處理時間,如果他們準備并發運行的話。代碼示例:最基本的例子是 一個由鍵盤和LCD組成的實時系統。用戶必須

11、在合理的時段 為 每個鍵盤按下 取得視覺反饋(visual feedback).。如果用戶不能 在這時段 看到 鍵盤按下 已經被接受,軟件產品將會很難使用(be awkward to use)。如果最長的接受期是100ms,那么在0到100ms的響應 可被接受。這個功能可以用一個 下面這樣的結構的獨立(autonomous)任務 實現:void vKeyHandlerTask( void *pvParameters )      /鍵盤處理是一個連續的過程。就像大多實時任務那樣,這個任務     /也是用

12、一個無限循環實現的。     for( ; )              Suspend waiting for a key press         Process the key press      現在假設 實時系統也執行一個依賴數字濾波輸入的控制功能。這個輸入必須被取樣(sampled),濾波(f

13、iltered),并且 每2ms執行一次控制循環。為了讓濾波器正常操作,取樣的時間規律(the temporal regularity)必須精確到0.5ms。 這個功能可以 用下面這個結構的 獨立任務 實現:void vControlTask( void *pvParameters )      for( ; )              Suspend waiting for 2ms since the start of t

14、he previous         cycleSample the input         Filter the sampled input         Perform control algorithm         Output result 

15、0;    軟件工程師必須設置 控制任務為 最高的優先級,因為:1 控制任務的時間限制(deadline) 比 鍵盤處理任務的 要嚴格;2 控制任務錯過最后期限(deadline)的后果 比 鍵盤處理任務 要嚴重。下面將演示 這些任務是如何被實時操作系統調度的.實時調度下面的圖 演示 前面定義的那些任務是如何被時實操作系統調度的。RTOS自己已經建立了一個任務-idle task-它只在沒有其他任務執行的時候才被執行。RTOS idle task 總是處于可以執行的狀態(注:也就是它不可能會因為等待什么外設資源而被阻塞,而是處于一種隨時待命的狀

16、態).上圖中:1. 在最開始,我們的兩個任務都不能被執行-vControlTask等待合適(correct)的時間開始新的控制循環,vKeyHandlerTask等待鍵盤按下。處理器時間分配給 RTOS的idle task.2. 在t1時刻,一個鍵盤按下(事件)出現. VKeyHandlerTask任務現在可以執行,它比RTOS的idle task有更高的優先級,所以處理器時間給它。3. 在t2時刻,vKeyHandlerTask已經完成了對按鍵的處理,并更新了LCD。它不能繼續,直到另一個鍵被按下,所以必須掛起它自己。RTOS idle task又被恢復執行。4

17、. 在t3時刻,一個定時器事件預示(indicates),可以執行下一個控制循環了。VControlTask現在可以執行,作為最高優先級的任務被立刻分配(scheduled)到處理器時間。5. 在t3和t4之間,當vControlTask任務還在執行的時候,一個鍵按下。VKeyHandlerTask不能被執行,因為它沒有vControlTask的優先級高。不能分配(scheduled)到任何處理器時間。6. 在t4時刻,vControlTask完成了控制循環的處理,不能夠重新開始,直到下一個時間事件出現,所以它自己掛起自己。而vKeyHandlerTask現在是最

18、高優先級的任務,可以運行了,所以,為了處理先前的鍵盤事件,分配(scheduled)到了處理器時間.7. 在t5時刻,鍵盤已經被處理。VkeyHandlerTask為了等待下一個鍵盤事件,自己掛起自己。現在,我們的兩個任務再度不能執行了。RTOS idle task分配到處理器時間。8. 在t5和t6之間,一個定時器事件被處理,但是沒有更多的鍵盤事件出現。9. 下一個鍵盤按下出現在t6時刻,但在vKeyHandlerTask完成處理鍵之前,一個定時器事件出現了。現在兩個任務都能被執行,而vControlTask比vKeyHandlerTask 有更多的優先級,所以

19、vKeyHandlerTask在它完成處理鍵盤之前就被掛起了。VControlTask分配到處理器時間。10. 在t8時刻,vControlTask完成處理控制循環,掛起自己以等待下一個事件。VKeyHandlerTask再次成為最高優先級的任務,能夠運行,所以分配到處理器時間,從而鍵盤按下事件 處理能夠完成。RTOS實現模塊(Building Block)詳細實例(Detailed Example)這一節從底向上描述了RTOS上下文切換的源代碼。使用FreeRTOS Atmel AVR微控制器移植的代碼作為例子。本節的最后還一步一步地瀏覽(step by step look)了一個

20、完整的上下文切換。C開發工具FreeRTOS的目標是簡單且易于理解。為了達到這個目標(To this end),RTOS的源代碼的大部分都是用C寫的,而不是匯編。這里演示的例子使用了 WinAVR development tools。 WinAVR是一個自由/免費的在windows下的AVR交叉編譯器,它是基于GCC的。RTOS Tick睡眠時,一個任務將指定多長時間后它會醒來。阻塞時,一個任務將指定一個 希望最多等多久的時間。FreeRTOS實時內核用tick count變量 來度量時間的。定時器中斷(RTOS tick interrupt) 用嚴格的時間精度(temporal accura

21、cy )來增加 tick count- 允許實時內核  用一個指定的 定時器中斷頻率的精度(resolution)來測量 時間。每次tick count增加后,實時內核 必須檢查,看現在是否 解除阻塞 或者 喚醒 一個任務。一個 比被中斷的任務有更高的優先級的 任務 在 tick ISR期間 被 喚醒或者解除阻塞 是可能的。如果是這種情況,tick ISR應該返回到新的喚醒/解鎖的任務-實際(effectively)中斷一個任務,卻返回到另一個任務。如下所述:上圖提到的幾個點:1) RTOS idle task正在運行2) RTOS tick出現,控制轉移到tic

22、k ISR(3)3) RTOS tick ISR使得vControlTask準備運行,當vControlTask比RTOS idle task有更高的優先級,切換上下文到 vControlTask.。4) 現在的執行上下文是vControlTask的。從ISR(4)退出 返回到vControlTask,vControlTask從(5)開始執行。以這種方式出現的上下文切換,稱為Preemptive。 因為被中斷的任務 沒有自愿(voluntarily)地掛起它自己就被搶占了(preempted)。FreeRTOS的AVR移植版本 用一個在定時器1(timer1)的比較匹配(c

23、ompare match)事件來產生RTOS tick. 后續將描述RTOS tick ISR是如何用WinAVR開發工具實現的。GCC信號屬性(Signal Attribute)GCC development tools允許用C來寫中斷程序。一個在AVR 定時器1外設的比較匹配事件  可以用下面的 語法(syntax)實現:void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal ) );void SIG_OUTPUT_COMPARE1A( void )    /* ISR C code for

24、RTOS tick. */    vPortYieldFromTick();在函數原型前的 '_attribute_ ( ( signal ) )' 指示符 告知 編譯器,這個函數是一個ISR,會引起編譯器輸出的兩個重要改變:1. signal屬性保證,每個在ISR 期間 被修改的 處理器的寄存器,在從ISR中退出時恢復到它原來的值。這就要求,當中斷將要執行時,編譯器不能做任何假定。所以,不能優化哪個處理器寄存器要求保護或者不保護。  2'signal'也強制使用 一個 從中斷返回('return from int

25、errupt')指令(RETI),而不是返回(return)指令RET.  AVR微控制器 在進入ISR前禁止中斷,RETI指令要求在 退出時重新打開中斷。下面是由編譯器輸出的代碼:;void SIG_OUTPUT_COMPARE1A( void );    ; -    ; CODE GENERATED BY THE COMPILER TO SAVE    ; THE REGISTERS THAT GET ALTERED BY THE    ; APPLI

26、CATION CODE DURING THE ISR.    PUSH    R1           PUSH    R0           IN      R0,0x3F        

27、0;     PUSH    R0                   CLR     R1                   PUSH  

28、;  R18                  PUSH    R19                  PUSH    R20       

29、;           PUSH    R21                  PUSH    R22                &#

30、160; PUSH    R23                  PUSH    R24                  PUSH    R25   

31、60;              PUSH    R26                  PUSH    R27            &

32、#160;     PUSH    R30                  PUSH    R31                  ; -    ; COD

33、E GENERATED BY THE COMPILER FROM THE    ; APPLICATION C CODE.    ;vTaskIncrementTick();    CALL    0x0000029B       ;Call subroutine;    ; -    ; CODE GENERATED BY THE COMPILER TO

34、    ; RESTORE THE REGISTERS PREVIOUSLY    ; SAVED.    POP     R31                  POP     R30       

35、;           POP     R27                  POP     R26              

36、0;   POP     R25                  POP     R24                  POP    

37、 R23                  POP     R22                  POP     R21      &#

38、160;           POP     R20                  POP     R19              &

39、#160;   POP     R18                  POP     R0                   OUT  

40、0;  0x3F,R0    POP     R0                   POP     R1    RETI             

41、            ; -GCC Naked 屬性前一節講述了如何在C中用 signal屬性來寫一個ISR.,以及它是如何使 執行上下文自動保存的(只有那些被ISR修改過的處理器寄存器才會得到保存)。然而,執行一個上下文切換需要保存完整的上下文。應用程序代碼能夠 在進入ISR時,明確(explicitly)地 保存所有寄存器,但是這樣會使 某些處理器寄存器 保存兩次-一次是由編譯器生成的代碼,另一次是由應用程序自己。這不是我們所需要的,可以在'signal'屬性后 添加 &#

42、39;naked'屬性來避免:void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) );void SIG_OUTPUT_COMPARE1A( void )    /* ISR C code for RTOS tick. */    vPortYieldFromTick();'naked'屬性阻止編譯器生成任何函數入口或退出代碼。現在變異這段代碼,會得到更少的編譯器輸出:;void SIG_OUTPUT_COMPARE1A( void

43、);    ; -    ; NO COMPILER GENERATED CODE HERE TO SAVE     ; THE REGISTERS THAT GET ALTERED BY THE    ; ISR.    ; -    ; CODE GENERATED BY THE COMPILER FROM THE    ; APPLICATION C CODE. 

44、0;  ;vTaskIncrementTick();    CALL    0x0000029B       ;Call subroutine    ; -    ; NO COMPILER GENERATED CODE HERE TO RESTORE    ; THE REGISTERS OR RETURN FROM THE ISR.    ;

45、-;看看,入口 和 出口代碼都沒有了吧使用 naked 屬性,編譯器不會生成任何入口和出口代碼,所以必須明確(explicitly)加入。portSAVE_CONTEXT()和portRESTORE_CONTEXT()這兩個宏 是用來保存和恢復完整的執行上下文的:void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) );void SIG_OUTPUT_COMPARE1A( void )    /* Macro that explicitly saves the execution 

46、60;   context. */    portSAVE_CONTEXT();    /* ISR C code for RTOS tick. */    vPortYieldFromTick();    /* Macro that explicitly restores the     execution context. */    portRESTORE_CONTEXT(); &

47、#160;  /* The return from interrupt call must also    be explicitly added. */    asm volatile ( "reti" );naked屬性給了應用程序完整的控制權,在何時 ,怎么樣保存AVR的上下文。如果應用程序代碼在進入ISR前保存了完整的上下文,在執行上下文切換時不必再保存,所以不會有處理器的寄存器被保存兩次。FreeRTOS Tick CodeFreeRTOS的AVR移植版本的實際源代碼 與 前一節的例子有些輕微的

48、不同。VPortYieldFromTick()是作為一個naked函數的 它自己的實現,上下文在vPortYieldFromTick().里被保存和恢復。這樣做的目的是為了實現一個non-preemptive的上下文切換(這里,一個任務自己阻塞自己).這里暫時不講這種non-preemptive切換。RTOS tick是這樣在FreeRTOS中實現的(看代碼中注釋片斷獲取更多細節):void SIG_OUTPUT_COMPARE1A( void ) _attribute_ ( ( signal, naked ) );void vPortYieldFromTick( void ) _attrib

49、ute_ ( ( naked ) );/*-*/* RTOS tick中斷服務程序. */void SIG_OUTPUT_COMPARE1A( void )    /*調用tick函數. */vPortYieldFromTick();/*從中斷返回. 如果出現上下文切換,將返回到一個不同的任務中 */    asm volatile ( "reti" );/*-*/void vPortYieldFromTick( void )    /* 這是一個naked 函數,所以需要保存上下文

50、*/portSAVE_CONTEXT();/* 增加tick count,檢查新的tick count值是否引起一個延遲周期過期,這個函數調用可導致一個任務變成準備運行. */vTaskIncrementTick();/*檢查是否要求上限文切換。如果 由vTaskIncrementTick()準備好的任務比已經中斷的任務有更高優先級,就切換過去 */vTaskSwitchContext();/*恢復上下文.如果發生了上下文切換,這將恢復要繼續運行的任務的上下文 */portRESTORE_CONTEXT();/*從這naked 函數返回. */    asm vo

51、latile ( "ret" );/*-*/The AVR Context上下文切換要求保存完整的上下文。在AVR MCU中,上下文包括1 32位通用寄存器。Gcc開發工具假定寄存器R1設定為02 狀態寄存器。狀態寄存器的值影響指令的執行,必須通過上下文切換保存(preserved)3 程序計數器(PC).恢復執行后,一個任務必須從上次被掛起的地方繼續執行。(a task must continue execution from the instruction that was about to be executed immediately

52、prior to its suspension.)4 兩個stack指針寄存器Saving the Context每個時實任務都有它自己的stack 內存區域,所以上下文可 簡單的通過將寄存器壓入到任務棧 來保存上下文。保存AVR的上下文是一個不可避免的要使用到匯編語言的地方。portSAVE_CONTEXT() 是作為一個宏來實現的,源代碼在下面給出#define portSAVE_CONTEXT()           asm volatile (   

53、;                     "push  r0                    nt" (1)  "in    r0,

54、 _SREG_          nt" (2)  "cli                         nt" (3)  "push  r0    

55、60;               nt" (4)  "push  r1                    nt" (5)  "clr   r1  

56、0;                 nt" (6)  "push  r2                    nt" (7)  "push  r3 

57、0;                  nt"   "push  r4                    nt"   "push  r5 

58、0;                  nt"     :    :    :  "push  r30                

59、60;  nt"   "push  r31                   nt"   "lds   r26, pxCurrentTCB     nt" (8)  "lds   r27, pxCurrentTCB + 1 nt

60、" (9)  "in    r0, _SP_L_          nt" (10)  "st    x+, r0                nt" (11)  "in   

61、r0, _SP_H_          nt" (12)  "st    x+, r0                nt" (13);在上面的源代碼中:1 寄存器R0首先被保存,因為當狀態寄存器被保存時,它會被用到。所以必須先把它的原始值保存下來。2 狀態寄存器被搬移

62、到 R0  (2),所以它能保存到stack (4)3 禁止處理器中斷。如果portSAVE_CONTEXT(),只是從ISR中調用,就不需要明確的禁止中斷,因為AVR已經這么做了。portSAVE_CONTEXT()宏用在中斷服務程序的外部(一個任務自己掛起自己的時候),中斷應該盡可能早的明確清除(也就是禁止中斷  CLI)4 從ISR 的C源代碼中,由編譯器生成的代碼假定R1被設置為0。R1的原始值 在R1被清除(6)前保存(5)5.  在(7)和(8),所有寄存器都被按順序保存6. 現在被掛起的任務的棧中還有一個任務執行上下文的

63、拷貝。內核保存任務的棧指針,所以 當任務恢復執行時,上下文可以取得并恢復。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().的逆過程。要恢復執行的任務的上下文被預先存儲到任務棧中。實時內核為該任務取得棧指針,然后彈出POP上下文到

64、0;     正確的寄存器中。#define portRESTORE_CONTEXT()        asm volatile (   "lds  r26, pxCurrentTCB      nt" (1)  "lds  r27, pxCurrentTCB + 1  nt" (2)  "ld  

65、 r28, x+                nt"   "out  _SP_L_, r28          nt" (3)  "ld   r29, x+         &#

66、160;      nt"   "out  _SP_H_, r29          nt" (4)  "pop  r31                    nt"  

67、; "pop  r30                    nt"     :    :    :  "pop  r1           

68、0;         nt"   "pop  r0                     nt" (5)  "out  _SREG_, r0           nt" (6)  "po

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論