從零開始構建實時作業系統1——任務切換

從零開始構建實時作業系統1——任務切換

1。前言

隨著計算機技術和微電子技術的迅速發展,嵌入式系統應用領域越來越廣泛,尤其是其具備低功耗技術的特點得到人們的重視。隨著工信部提出NB-IoT基站建設具體目標、三大運營商加速建設,即將迎來萬物互聯的新時代,這是資訊產業繼移動網際網路之後的下一個萬億級市場,這些為實時作業系統的應用提供了廣闊的前景。

嵌入式實時作業系統將會部署到越來越多的裝置中,這就要求工程師深入地瞭解嵌入式實時作業系統。

本系列文章將和大家一起從零開始構建一個嵌入式實時作業系統,我將用最簡單直白的方式一步一步搭建,我將用一篇文章的方式來總結搭建中的每個節點階段,並開源軟體工程和原始碼。

2。嵌入式實時作業系統

嵌入式實時作業系統是一個特殊的程式,是一個支援多工的執行環境。嵌入式實時作業系統最大的特點就是“實時性”,如果有一個任務需要執行,實時作業系統會立即執行該任務,不會有較長的延時。典型的實時作業系統有uCOS ,RT-Thread,FreeRTOS ,VxWorks,WinCE等。

從零開始構建實時作業系統1——任務切換

嵌入式實時作業系統是一個特殊的程式(通常稱為核心),它可以建立和控制所有任務

。嵌入式實時作業系統除了包含一個核心以外,還提供其他服務,如檔案系統,協議棧,圖形使用者介面等。本文的重點在於瞭解嵌入式實時作業系統核心的工作原理和結構,因此文中提到的實時作業系統通常指的是作業系統核心。

實時作業系統核心通常要佔用5%左右的CPU執行時間,另外核心是一個軟體程式碼,需要額外佔用ROM空間和RAM空間。

嵌入式實時操作系主要由以下3個子系統組成:

1、任務排程子系統

2、任務通訊子系統

3、記憶體管理子系統

從零開始構建實時作業系統1——任務切換

3。實現目標

本文講解構建嵌入式實時作業系統的第一個節點階段:實現簡單的任務切換功能。

程式碼區的資料是不變的,處理器暫存器的值和棧空間的值決定程式執行狀態。讓每個任務“獨享”一個棧空間,當我們將任務執行時的處理器暫存器的值儲存起來時,這樣就實現儲存任務的執行狀態。同樣的當我們把儲存的任務執行時的處理器暫存器的值裝載到處理的暫存器中時,這樣就恢復了任務的執行狀態,任務繼續執行起來。

切換任務的原理是:

每個任務有一個“獨享”棧空間,透過儲存和裝載任務執行時的處理器暫存器的值,實現任務的暫停和恢復執行。暫停一個任務後再恢復另外一個任務就完成了一次任務切換。

任務程式碼,任務棧空間和處理器狀態如下圖:

從零開始構建實時作業系統1——任務切換

4。實驗環境

硬體是基於意法半導體的STM32F401(ARM公司的Cortex-M4核心),軟體開發使用的是KEIL V5。2 開發工具。

從零開始構建實時作業系統1——任務切換

軟體工程如下:

從零開始構建實時作業系統1——任務切換

軟體工程中包含:main。c ,startup_stm32f401xc。s 和 readme三個檔案。startup_stm32f401xc。s檔案為STM32F401的啟動檔案,main。c檔案實現任務切換功能,readme檔案用於記錄版本修改日誌。

5。程式碼實現

切換任務的原理是讓每個任務都有一個“獨享”棧空間,透過儲存和裝載任務執行時的處理器暫存器的值,實現任務的暫停和恢復執行。

暫停一個任務後再恢復另外一個任務就完成了一次任務切換。

因此需要實現:

1、每個任務的獨立棧空間。

2、實現任務的暫停和恢復。

3、實現任務的排程。

5.1實現獨立棧空間

棧空間程式碼如下:

從零開始構建實時作業系統1——任務切換

為每個任務定義一個靜態陣列,當任務執行時將處理器的棧指標指向任務“自己的”靜態陣列,從而實現獨立棧空間。棧空間用來存放區域性變數,中斷呼叫和函式呼叫時的處理器暫存器的值。任務切換時需要將處理器暫存器的值儲存到任務的獨立棧空間。

在儲存任務執行狀態時需要儲存處理器暫存器值到棧空間,因此需要深入瞭解處理器暫存器的用途和出入棧順序,Cortex-M4核心的暫存器和暫存器中斷自動入棧的順序圖如下:

從零開始構建實時作業系統1——任務切換

初始化棧空間的程式碼如下:

從零開始構建實時作業系統1——任務切換

棧空間初始化後的狀態如下:

從零開始構建實時作業系統1——任務切換

棧是一中先入後出的資料結構,Cortex-M4核心的棧操作方式倍設定成了向下生長

。psp_array用於儲存任務棧指標,psp_array[0]任務0棧指標指向task0_stack[112],其中task0_stack[116]儲存PC程式指標值,task0_stack[117]儲存狀態暫存器(

符合Cortex-M4核心暫存器出棧順序:手動出棧8個暫存器,硬體自動出棧8個暫存器

)。

5.2實現任務的暫停和恢復

程式碼如下:

從零開始構建實時作業系統1——任務切換

cortex-M4核心有一個PendSV(可掛起的系統呼叫)異常,其異常編號為14並且具有可程式設計的優先順序。當軟體將PendSV設定成掛起時,程式將進入PendSV異常(中斷)。

將PendSV異常優先順序設定為最低,其它中斷函式都可以得到正常響應,不會受到PendSV異常影響,在PendSV異常中執行任務切換

,時序框圖如下:

從零開始構建實時作業系統1——任務切換

PendSV_Handler為Cortex-M4核心中斷服務函式,進入中斷函式時處理器自動儲存了R0,R1,R2,R3, R12,LR,PC,XPSR,在PendSV_Handler中斷程式中完成R4~R11入棧儲存工作,從而實現任務儲存工作。

/* 讀取當前程序棧指標數值 */MRS R0,PSP /* 儲存R4-R11八個暫存器的值到當前任務棧中 同時將回寫的地址寫入R0 */STMDB R0!,{R4-R11}

psp_array[0]為任務0的棧指標, psp_array[1]為任務1的棧指標。以下程式碼實現任務棧指標切換

/* 讀取psp_array 地址 */LDR R3, =__cpp(&psp_array) /* 將當前程序PSP指標值 寫入 相應的 PSP_array 位置 */STR R0,[R3,R2,LSL #2] /* 獲取下個程序序號 */LDR R4,=__cpp(&next_task) LDR R4,[R4]/* R1為&curr_task 將下個程序序號寫入curr_task中 */STR R4,[R1] /* psp_array讀取更新後的curr_task的PSP指標數值 */LDR R0,[R3,R4,LSL #2]

在PendSV_Handler中斷程式中完成R4~R11暫存器出棧,PendSV_Handler中斷程式返回時處理器自動出棧R0,R1,R2,R3, R12,LR,PC,XPSR,從而實現任務恢復工作。

/* 出棧 R4-R11八個暫存器 */LDMIA R0!,{R4-R11} /* 設定PSP指標 */MSR PSP,R0 /* 中斷返回 */BX LR

5.3實現任務的排程

任務排程的程式碼如下:

從零開始構建實時作業系統1——任務切換

SysTick_Handler為定時器中斷程式,實現時間片輪流改變目標任務,並掛起PendSV_Handle中斷,退出SysTick_Handler中斷程式時進入PendSV_Handle中斷程式。

6。執行結果

程式碼模擬執行如下:

從零開始構建實時作業系統1——任務切換

從零開始構建實時作業系統1——任務切換

執行程式碼後task_num0和task_num1這兩個變數依次自加,程式碼實現任務輪流切換功能。

希望獲取原始碼和軟體工程的朋友們在評論區裡留言。

未完待續…實時作業系統系列將持續更新創作不易希望朋友們點贊,轉發,評論,關注。您的點贊,轉發,評論,關注將是我持續更新的動力作者:李巍Github:liyinuoman2017CSDN:liyinuo2017今日頭條:程式猿李巍