1. 程式人生 > 其它 >嵌入式系統是如何設計的

嵌入式系統是如何設計的

嵌入式系統是如何設計的

1.多工機制

其實在單一CPU 的情況下,是不存在真正的多工機制的,存在的只有不同的任務輪流使用CPU,所以本質上還是單任務的。但由於CPU執行速度非常快,加上任務切換十分頻繁並且切換的很快,所以我們感覺好像有很多工同時在執行一樣。這就是所謂的多工機制。

實時系統的特徵是延時可預測,能夠在一個規定的時間內(通常是 ms 級別的)對某些訊號做出反應。

2.任務的狀態

任務有下面的特性:任務並不是隨時都可以執行的,而一個已經執行的任務並不能保證一直佔有 CPU 直到執行完。一般有就緒態,執行態,掛起態等。

  • 執行態:一個執行態的任務是一個正在使用 CPU 的任務。任何時刻有且只有一個執行著的任務。
  • 就緒態:一個就緒態任務是可執行的,等待佔有 CPU 的任務釋放 CPU。
  • 掛起態:某些條件不滿足而掛起不能執行的狀態。

3. 如何轉化為就緒態

INT32U OSRdyTbl; /* 就緒任務表 */

上面定義一個 32 位變數,每一位代表一個任務,0 表示掛起狀態,1 表示就緒狀態。它記錄了各任務的就緒與否狀態,稱它為就緒表。OSRdyTbl 定義為 32 位變數,對應32 個任務。當然,定義為 64 位的話,便最多能支援 64 個任務。這樣,可以定義兩個巨集,實現把任務的狀態變為就緒或掛起態。

/* 在就緒表中登記就緒任務 */

#define OSSetPrioRdy(prio) { OSRdyTbl |= 0x01<<prio;} //把相應位置1

/* 從就緒表中刪除任務 */

#define OSDelPrioRdy(prio) { OSRdyTbl &= ~(0x01<<prio); }//把相應位清零

任務之間互相獨立,不存在互相呼叫的關係。所有任務在邏輯上都是平等的。由於任務之間互相看不見,所以他們之間的資訊傳輸就無法當面完成。這就需要各種通訊機制如訊號量,訊息郵箱,佇列等來實現。

4.什麼是搶佔式排程?

排程的概念,通俗的說就是系統在多個任務中選擇合適的任務執行。系統如何知道何時該執行哪個任務?可以為每個任務安排一個唯一的優先級別,當同時有多個任務就緒時,優先執行優先順序較高的任務。同時,任務的優先順序也作為任務的唯一標識號。程式碼中都是對標識號來完成對任務的操作的。

所謂“搶佔式排程”是指:一旦就緒狀態中出現優先權更高的任務,便立即剝奪當前任務的執行權,把CPU分配給更高優先順序的任務。這樣CPU 總是執行處於就緒條件下優先順序最高的任務。

5.多工系統的時間管理

與人一樣,多工系統也需要一個“心跳”來維持其正常執行,這個心跳叫做時鐘節拍,通常由定時器產生一個固定週期的中斷來充當。

OSTimeDly 函式就是以時鐘節拍為基準來延時的(在時鐘的中斷服務函式中,依次對各個延時任務的延時節拍數減1。若發現某個任務的延時節拍數變為0,則把它從掛起態置為就緒態。)。這個函式完成功能很簡單,就是先掛起當起當前任務,設定其延時節拍數,然後進行任務切換,在指定的時鐘節拍數到來之後,將當前任務恢復為就緒狀態。任務必須通過OSTimeDly或 OSTaskSuspend 讓出CPU的使用權(延時或等待事件),使更低優先順序任務有機會執行。

6.如何實現多工?

只有一個CPU,如何在同一時間實現多個獨立程式的執行?要實現多工,條件是每個任務互相獨立。人如何才能獨立,有自己的私有財產。任務也一樣,如果一個任務有自己的CPU,堆疊,程式程式碼,資料儲存區,那這個任務就是一個獨立的任務。(CPU是通過多工機制獲得的,其他的需要你分配)

TIPS:

如果一個任務正在執行某個公共函式時(如Printf), 被另一個高優先順序的任務搶佔,那麼當這個高優先順序的任務也呼叫同一個公共函式時,極有可能破壞原任務的資料。因為兩個任務可能共用一套資料。為了防止這種情況發生,常採用兩種措施:可重入設計和互斥呼叫。

可重入函式中所有的變數均為區域性變數,區域性變數在呼叫時臨時分配空間,所以不同的任務在不同的時刻呼叫該函式時,它們的同一個區域性變數所分配的儲存空間並不相同(任務私有棧中),互不干擾。另外,如果可重入函式呼叫了其他函式,則這些被呼叫的函式也必須是可重入函式。

實現互斥(獨佔)訪問的方法有關中斷,關排程,互斥訊號量,計數訊號量等。

6.1 一個任務如何擁有自己的程式程式碼

對於如何實現多工,首先是程式程式碼,每個任務的程式程式碼與函式一樣,與51 的裸奔程式一樣,每個任務都是一個大迴圈。然後是資料儲存區,由於全域性變數是系統共用的,各個任務共享,不是任務私有,所以這裡的資料儲存區是指任務的私有變數,如何變成私有?區域性變數也。編譯器是把區域性變數儲存在棧裡的,所以好辦,只要任務有個私有的棧就行。

TIPS:

臨界資源是一次僅允許一個任務使用的共享資源。每個任務中訪問臨界資源的那段程式稱為臨界區。

在多工系統中,為保障資料的可靠性和完整性,共享資源要互斥(獨佔)訪問,所以全域性變數(只讀的除外)不能同時有多個任務訪問,即一個任務訪問的時候不能被其他任務打斷。共享資源是一種臨界資源。

6.2 一個任務如何擁有自己的堆疊、資料儲存區

私有棧的作用是存放區域性變數,函式的引數,它是一個線性的空間,所以可以申請一個靜態陣列,把棧頂指標SP指向棧的陣列的首元素(遞增棧)或最後一個元素(遞減棧)。即可打造一個人工的棧出來。每個任務還要有記錄自己棧頂指標的變數,儲存在任務控制塊(TCB)中。

什麼是任務控制塊?

系統中的每個任務具有一個任務控制塊,任務控制塊記錄任務執行的環境,這裡的任務控制塊比較簡單,只包含了任務的堆疊指標和任務延時節拍數。任務控制塊是任務的身份證。它把任務的程式與資料聯絡起來,找到它就可以得到任務的所有資源。

6.3 一個任務如何擁有自己的CPU

最後來看看任務是如何“擁有”自己的CPU 的。只有一個 CPU,各個任務共享,輪流使用。如何才能實現?我們先來看看中斷的過程,當中斷來臨時,CPU 把當前程式的執行地址,暫存器等現場資料儲存起來(一般儲存在棧裡),然後跳到中斷服務程式執行。待執行完畢,再把先前儲存的資料裝回CPU 又回到原來的程式執行。這樣就實現了兩個不同程式的交叉執行。

借鑑這種思想不就能實現多工了嗎!模仿中斷的過程就可以實現任務切換執行。任務切換時,把當前任務的現場資料儲存在自己的任務棧裡面,再把待執行的任務的資料從自己的任務棧裝載到CPU中,改變 CPU 的 PC,SP,暫存器等。可以說,任務的切換是任務執行環境的切換。而任務的執行環境儲存在任務棧中,也就是說,任務切換的關鍵是把任務的私有堆疊指標賦予處理器的堆疊指標SP。

建立一個任務。它接收三個引數,分別是任務的入口地址,任務堆疊的首地址和任務的優先順序。呼叫本函式後,系統會根據使用者給出的引數初始化任務棧,並把棧頂指標儲存到任務控制塊中,在任務就緒表標記該任務為就緒狀態。最後返回,這樣一個任務就建立成功了。

當一個任務將要執行時,便通過取得它的堆疊指標(儲存在任務控制塊中)將這些暫存器出棧裝入CPU 相應的位置即可。

6.4 如何實現搶佔式排程?

基於任務優先順序的搶佔式排程,也就是最高優先順序的任務一旦處於就緒狀態,則立即搶佔正在執行的低優先順序任務的處理器資源。為了保證CPU 總是執行處於就緒條件下優先順序最高的任務,每當任務狀態改變後,即判斷當前執行的任務是否是就緒任務中優先順序最高的,否則進行任務切換。

任務狀態會在什麼時候發生改變呢?有下面兩種情況:

1、高優先順序的任務因為需要某種資源或延時,主動請求掛起,讓出處理器,此時將排程就緒狀態的低優先順序任務獲得執行,這種排程稱為任務級的切換。如任務執行OSTimeDly()OSTaskSuspend()把自身掛起就屬於這種。

2、高優先順序的任務因為時鐘節拍到來,或在中斷處理結束後,核心發現更高優先順序任務獲得了執行條件(如延時的時鐘到時)則在中斷後直接切換到更高優先順序任務執行。這種排程也稱為中斷級的切換。

6.5 掛起/恢復任務

  1. 掛起任務

通過 OSTaskSuspend()可以主動掛起一個任務。OSTaskSuspend()會把任務從任務就緒表中移出,最後重新啟動系統排程。這個函式可以掛起任務本身也可以掛起其他任務。

  1. 恢復任務(OSTaskResume()

可以讓被OSTaskSuspendOSTimeDly 掛起的任務恢復就緒態,然後進行任務排程。